View Javadoc

1   /**
2    *  MicroEmulator
3    *  Copyright (C) 2006-2007 Bartek Teodorczyk <barteo@barteo.net>
4    *  Copyright (C) 2006-2007 Vlad Skarzhevskyy
5    *
6    *  It is licensed under the following two licenses as alternatives:
7    *    1. GNU Lesser General Public License (the "LGPL") version 2.1 or any newer version
8    *    2. Apache License (the "AL") Version 2.0
9    *
10   *  You may not use this file except in compliance with at least one of
11   *  the above two licenses.
12   *
13   *  You may obtain a copy of the LGPL at
14   *      http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
15   *
16   *  You may obtain a copy of the AL at
17   *      http://www.apache.org/licenses/LICENSE-2.0
18   *
19   *  Unless required by applicable law or agreed to in writing, software
20   *  distributed under the License is distributed on an "AS IS" BASIS,
21   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22   *  See the LGPL or the AL for the specific language governing permissions and
23   *  limitations.
24   *
25   *  @version $Id: MIDletThread.java 1605 2008-02-25 21:07:14Z barteo $
26   */
27  package org.microemu.app.util;
28  
29  import java.util.Iterator;
30  import java.util.Map;
31  import java.util.WeakHashMap;
32  
33  import org.microemu.MIDletBridge;
34  import org.microemu.MIDletContext;
35  import org.microemu.log.Logger;
36  import org.microemu.util.ThreadUtils;
37  
38  /**
39   * MIDletContext is used to hold keys to running Threads created by  MIDlet  
40   * 
41   * @author vlads
42   */
43  public class MIDletThread extends Thread {
44  
45  	public static int graceTerminationPeriod = 5000;
46  	
47  	private static final String THREAD_NAME_PREFIX = "MIDletThread-";
48  	
49  	private static boolean terminator = false;
50  	
51  	private static Map midlets = new WeakHashMap();
52  	
53      private static int threadInitNumber;
54      
55      private String callLocation;
56      
57      private static synchronized int nextThreadNum() {
58      	return threadInitNumber++;
59      }
60      
61  	public MIDletThread() {
62  		super(THREAD_NAME_PREFIX + nextThreadNum());
63  		register(this);
64  	}
65  	
66  	public MIDletThread(Runnable target) {
67  		super(target, THREAD_NAME_PREFIX + nextThreadNum());
68  		register(this);
69  	}
70  	
71  	public MIDletThread(Runnable target, String name) {
72  		super(target, THREAD_NAME_PREFIX + name);
73  		register(this);
74  	}
75  	
76  	public MIDletThread(String name) {
77  		super(THREAD_NAME_PREFIX + name);
78  		register(this);
79  	}
80  	
81  	private static void register(MIDletThread thread) {
82  		MIDletContext midletContext = MIDletBridge.getMIDletContext();
83  		if (midletContext == null) {
84  			Logger.error("Creating thread with no MIDlet context", new Throwable());
85  			return;
86  		}
87  		thread.callLocation  = ThreadUtils.getCallLocation(MIDletThread.class.getName());
88  		Map threads = (Map)midlets.get(midletContext);
89  		if (threads == null) {
90  			threads = new WeakHashMap();
91  			midlets.put(midletContext, threads);
92  		}
93  		threads.put(thread, midletContext);
94  	}
95  	
96  	//TODO overrite run() in user Threads using ASM
97  	public void run() {
98  		 try {
99  			super.run();
100 		} catch (Throwable e) {
101 			Logger.debug("MIDletThread throw", e);
102 		}
103 		//Logger.debug("thread ends, created from " + callLocation);	
104 	 }
105 	
106 	/**
107 	 * Terminate all Threads created by MIDlet
108 	 * @param previousMidletAccess
109 	 */
110 	public static void contextDestroyed(final MIDletContext midletContext) {
111 		if (midletContext == null) {
112 			return;
113 		}
114 		final Map threads = (Map)midlets.remove(midletContext);
115 		if ((threads != null) && (threads.size() != 0)) {
116 			terminator = true;
117 			Thread terminator = new Thread("MIDletThreadsTerminator") {
118 				public void run() {
119 					terminateThreads(threads);
120 				}
121 			};
122 			terminator.start();
123 		}
124 		MIDletTimer.contextDestroyed(midletContext);
125 	}
126 	
127 	public static boolean hasRunningThreads(MIDletContext midletContext) {
128 		//return (midlets.get(midletContext) != null);
129 		return terminator;
130 	}
131 	
132 	private static void terminateThreads(Map threads) {
133 		long endTime = System.currentTimeMillis() + graceTerminationPeriod;
134 		for (Iterator iter = threads.keySet().iterator(); iter.hasNext();) {
135 			Object o = iter.next();
136 			if (o == null) {
137 				continue;
138 			}
139 			if (o instanceof MIDletThread) {
140 				MIDletThread t = (MIDletThread) o;
141 				if (t.isAlive()) {
142 					Logger.info("wait thread [" + t.getName() + "] end");
143 					while ((endTime > System.currentTimeMillis()) && (t.isAlive())) {
144 						try {
145 							t.join(700);
146 						} catch (InterruptedException e) {
147 							break;
148 						}
149 					}
150 					if (t.isAlive()) {
151 						Logger.warn("MIDlet thread [" + t.getName() + "] still running" + ThreadUtils.getTreadStackTrace(t));
152 						if (t.callLocation != null) {
153 							Logger.info("this thread [" + t.getName() + "] was created from " + t.callLocation);
154 						}
155 						t.interrupt();
156 					}
157 				}
158 			} else {
159 				Logger.debug("unrecognized Object [" + o.getClass().getName() + "]");
160 			}
161 		};
162 		Logger.debug("all "+ threads.size() + " thread(s) finished");
163 		terminator = false;
164 	}
165 
166 }