|
1 /******************************************************************************* |
|
2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. This program and the accompanying materials |
|
4 * are made available under the terms of the Eclipse Public License v1.0 |
|
5 * which accompanies this distribution, and is available at |
|
6 * http://www.eclipse.org/legal/epl-v10.html |
|
7 * |
|
8 * Contributors: |
|
9 * Nokia Corporation - initial implementation |
|
10 *******************************************************************************/ |
|
11 |
|
12 package org.eclipse.swt.internal.qt; |
|
13 |
|
14 import org.eclipse.swt.internal.qt.midp.UIThreadLauncher; |
|
15 import org.eclipse.swt.widgets.Display; |
|
16 import org.eclipse.swt.widgets.Internal_PackageSupport; |
|
17 |
|
18 import com.nokia.mj.impl.rt.support.ApplicationUtils; |
|
19 import com.nokia.mj.impl.rt.support.ShutdownListener; |
|
20 |
|
21 /** |
|
22 * The internal event loop which will run until the application requests the UI |
|
23 * thread. Then UI thread is handed over to the application which will continue |
|
24 * running the loop as it pleases. If the application exits cleanly the UI |
|
25 * thread will return back to here and the event loop can continue to run |
|
26 * until the runtime signals shutdown. |
|
27 * |
|
28 * This class is the owner of the internal Display instance. |
|
29 */ |
|
30 public final class EventLoop { |
|
31 |
|
32 /* |
|
33 * The internal Display instance. Owned by this class. |
|
34 */ |
|
35 private static Display internalDisplay; |
|
36 |
|
37 /* |
|
38 * Event loop can run here before the application's loop and after the UI |
|
39 * thread exits from the application's loop. These states are identifying |
|
40 * which of the two loops is currently running. |
|
41 */ |
|
42 private static final int PRE_APPLICATION = 0; |
|
43 private static final int POST_APPLICATION = 1; |
|
44 |
|
45 /* |
|
46 * Boolean used to detect when application has requested the UI thread and |
|
47 * the hand-over must begin. |
|
48 */ |
|
49 private static volatile boolean applicationRequestedUIThread; |
|
50 |
|
51 /* |
|
52 * Boolean used to detect when the runtime wants to bring the UI down and |
|
53 * the event loop must come down also. |
|
54 */ |
|
55 private static volatile boolean shuttingDownReceived; |
|
56 |
|
57 /* |
|
58 * Boolean used to store if starting has been attempted already. |
|
59 */ |
|
60 private static boolean started; |
|
61 |
|
62 /* |
|
63 * Runnable that gets called in the UI thread when starting up. Named |
|
64 * class declared to be able to make it static. |
|
65 */ |
|
66 private static final class LoopRunner implements Runnable { |
|
67 public void run() { |
|
68 EventLoop.run(); |
|
69 } |
|
70 } |
|
71 |
|
72 /* |
|
73 * Not to be instantiated. |
|
74 */ |
|
75 private EventLoop() { |
|
76 } |
|
77 |
|
78 /** |
|
79 * Returns the internal Display owned by the EventLoop class. Caller must |
|
80 * not dispose it. |
|
81 * @return The internal Display instance |
|
82 * @throws RuntimeException |
|
83 */ |
|
84 public static Display getInternalDisplay() { |
|
85 synchronized(EventLoop.class) { |
|
86 ensureStartedOrThrow(); |
|
87 return internalDisplay; |
|
88 } |
|
89 } |
|
90 |
|
91 /* |
|
92 * Starts the event loop. If this returns without throwing then the |
|
93 * internal Display instance has been successfully created and can be |
|
94 * asked with getInternalDisplay(). |
|
95 * |
|
96 * Any calls after the first successful call are ignored. Any calls after |
|
97 * the first failed call will not retry but will throw. |
|
98 */ |
|
99 private static void ensureStartedOrThrow() { |
|
100 // This is called synchronized with the Display creation that takes |
|
101 // place in the UI thread. |
|
102 |
|
103 if(!started) { |
|
104 started = true; |
|
105 |
|
106 // Start the UI thread |
|
107 if(!UIThreadLauncher.startInUIThread(new LoopRunner())) { |
|
108 // Failed to create the UI thread, release the starting thread |
|
109 EventLoop.class.notify(); |
|
110 } |
|
111 |
|
112 // Wait until the Display gets created in the UI thread |
|
113 try { |
|
114 EventLoop.class.wait(); |
|
115 } catch(InterruptedException e) { |
|
116 // Nothing to do |
|
117 } |
|
118 } |
|
119 |
|
120 // Check Display creation status |
|
121 if(internalDisplay == null) { |
|
122 throw new RuntimeException("Failed to start"); |
|
123 } |
|
124 } |
|
125 |
|
126 /* |
|
127 * Start listening for the application requesting the UI thread via the |
|
128 * eSWT's UIThreadSupport API. |
|
129 */ |
|
130 private static void listenApplicationUIThreadRequest() { |
|
131 UIThreadHandOverManager.setApplicationUIListener(new ApplicationUIListener() { |
|
132 /* |
|
133 * Can get called in any thread but the UI thread. |
|
134 */ |
|
135 public void applicationUIThreadRequest() { |
|
136 // Synchronized with the Display creation and destruction |
|
137 synchronized(EventLoop.class) { |
|
138 applicationRequestedUIThread = true; |
|
139 // It's ok to skip wake if Display has not been created yet |
|
140 if(internalDisplay != null && !internalDisplay.isDisposed()) { |
|
141 internalDisplay.wake(); |
|
142 } |
|
143 } |
|
144 } |
|
145 }); |
|
146 } |
|
147 |
|
148 /* |
|
149 * Start listening for the runtime's shutting down message. |
|
150 */ |
|
151 private static void listenRuntimeShutdownRequest() { |
|
152 ApplicationUtils.getInstance().addShutdownListener( |
|
153 /* |
|
154 * Can get called in any thread but the UI thread. |
|
155 */ |
|
156 new ShutdownListener() { |
|
157 public void shuttingDown() { |
|
158 // Synchronized with the Display creation and destruction |
|
159 synchronized(EventLoop.class) { |
|
160 shuttingDownReceived = true; |
|
161 // It's ok to skip wake if Display has not been created yet |
|
162 if(internalDisplay != null && !internalDisplay.isDisposed()) { |
|
163 internalDisplay.wake(); |
|
164 } |
|
165 } |
|
166 } |
|
167 }); |
|
168 } |
|
169 |
|
170 /* |
|
171 * Creates the internal Display and runs the event loop. Called in the UI |
|
172 * thread. |
|
173 */ |
|
174 private static void run() { |
|
175 try { |
|
176 // Add the appropriate listeners. |
|
177 listenRuntimeShutdownRequest(); |
|
178 listenApplicationUIThreadRequest(); |
|
179 |
|
180 // Create the internal Display. |
|
181 createInternalDisplay(); |
|
182 |
|
183 // Run event loop until the application wants to take over or the |
|
184 // runtime signals shutdown. In the latter case exit from the UI |
|
185 // thread immediately. |
|
186 loop(PRE_APPLICATION); |
|
187 if(shuttingDownReceived) { |
|
188 return; |
|
189 } |
|
190 |
|
191 // Hand over the UI thread to the application. The application is |
|
192 // responsible of the event loop until it allows the UI thread to |
|
193 // return back here. |
|
194 UIThreadHandOverManager.runApplicationUI(); |
|
195 |
|
196 // Application allowed its event loop to exit. Continue |
|
197 // dispatching the events until the runtime wants to shut down. |
|
198 loop(POST_APPLICATION); |
|
199 |
|
200 } finally { |
|
201 destroy(); |
|
202 } |
|
203 } |
|
204 |
|
205 /* |
|
206 * Creates the Display. Synchronized with the starting thread that |
|
207 * waits until the Display has been created. |
|
208 */ |
|
209 private static void createInternalDisplay() { |
|
210 synchronized(EventLoop.class) { |
|
211 try { |
|
212 // Will throw if fails |
|
213 internalDisplay = Internal_PackageSupport.internalInstance(); |
|
214 } finally { |
|
215 // Release the starting thread |
|
216 EventLoop.class.notify(); |
|
217 } |
|
218 } |
|
219 } |
|
220 |
|
221 /* |
|
222 * Frees all the owned resources when shutting down. Ideally this would be |
|
223 * executed only after all the application's threads have been finalized. |
|
224 * If the application's threads are still running it's possible that |
|
225 * someone would try to access the internal Display after this. This would |
|
226 * cause an exception with "device is disposed". |
|
227 */ |
|
228 private static void destroy() { |
|
229 // Synchronized with the shutting down message and the application UI |
|
230 // thread request. |
|
231 synchronized(EventLoop.class) { |
|
232 if(internalDisplay != null) { |
|
233 // If this throws then Display.dispose() was called on the |
|
234 // internal Display by someone who doesn't own it. |
|
235 if(internalDisplay.isDisposed()) { |
|
236 throw new RuntimeException( |
|
237 "Internal Display has been disposed by someone who " + |
|
238 "doesn't own it"); |
|
239 } |
|
240 |
|
241 // If the application's Display has been disposed then |
|
242 // disposing also the internal Display here would destroy the |
|
243 // QApplication and all the UI resources tied to it. Disposing |
|
244 // is not done but the resources are allowed to leak on |
|
245 // purpose: |
|
246 // |
|
247 // In MIDP many of the related APIs are thread-safe and are |
|
248 // exclusively using garbage collection for memory management. |
|
249 // Disposing the UI resources of API objects that still may be |
|
250 // referenced would create a deviation from the pattern and |
|
251 // that would need special handling all around the API |
|
252 // implementations. That handling is not attempted but instead |
|
253 // the internal Display instance is allowed to live until the |
|
254 // process terminates. |
|
255 } |
|
256 } |
|
257 } |
|
258 |
|
259 /* |
|
260 * The event loop that can run before and after the application's loop. |
|
261 * State parameter tells which one of the two this is. The exit |
|
262 * condition depends on the state. |
|
263 */ |
|
264 private static void loop(final int state) { |
|
265 while(!loopExitCondition(state)) { |
|
266 if(!internalDisplay.readAndDispatch()) { |
|
267 internalDisplay.sleep(); |
|
268 } |
|
269 } |
|
270 } |
|
271 |
|
272 /* |
|
273 * Checks if the loop exit condition is met. The condition is different |
|
274 * depending on if the loop is running before or after the application's |
|
275 * loop. |
|
276 */ |
|
277 private static boolean loopExitCondition(final int state) { |
|
278 switch(state) { |
|
279 case PRE_APPLICATION: |
|
280 return shuttingDownReceived || applicationRequestedUIThread; |
|
281 case POST_APPLICATION: |
|
282 return shuttingDownReceived; |
|
283 default: |
|
284 return true; |
|
285 } |
|
286 } |
|
287 } |