diff -r 1f0034e370aa -r 71ad690e91f5 javauis/eswt_qt/org.eclipse.swt/Eclipse_SWT_PI/common_j2me/org/eclipse/swt/internal/qt/EventLoop.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javauis/eswt_qt/org.eclipse.swt/Eclipse_SWT_PI/common_j2me/org/eclipse/swt/internal/qt/EventLoop.java Mon Oct 04 11:29:25 2010 +0300 @@ -0,0 +1,287 @@ +/******************************************************************************* + * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Nokia Corporation - initial implementation + *******************************************************************************/ + +package org.eclipse.swt.internal.qt; + +import org.eclipse.swt.internal.qt.midp.UIThreadLauncher; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Internal_PackageSupport; + +import com.nokia.mj.impl.rt.support.ApplicationUtils; +import com.nokia.mj.impl.rt.support.ShutdownListener; + +/** + * The internal event loop which will run until the application requests the UI + * thread. Then UI thread is handed over to the application which will continue + * running the loop as it pleases. If the application exits cleanly the UI + * thread will return back to here and the event loop can continue to run + * until the runtime signals shutdown. + * + * This class is the owner of the internal Display instance. + */ +public final class EventLoop { + + /* + * The internal Display instance. Owned by this class. + */ + private static Display internalDisplay; + + /* + * Event loop can run here before the application's loop and after the UI + * thread exits from the application's loop. These states are identifying + * which of the two loops is currently running. + */ + private static final int PRE_APPLICATION = 0; + private static final int POST_APPLICATION = 1; + + /* + * Boolean used to detect when application has requested the UI thread and + * the hand-over must begin. + */ + private static volatile boolean applicationRequestedUIThread; + + /* + * Boolean used to detect when the runtime wants to bring the UI down and + * the event loop must come down also. + */ + private static volatile boolean shuttingDownReceived; + + /* + * Boolean used to store if starting has been attempted already. + */ + private static boolean started; + + /* + * Runnable that gets called in the UI thread when starting up. Named + * class declared to be able to make it static. + */ + private static final class LoopRunner implements Runnable { + public void run() { + EventLoop.run(); + } + } + + /* + * Not to be instantiated. + */ + private EventLoop() { + } + + /** + * Returns the internal Display owned by the EventLoop class. Caller must + * not dispose it. + * @return The internal Display instance + * @throws RuntimeException + */ + public static Display getInternalDisplay() { + synchronized(EventLoop.class) { + ensureStartedOrThrow(); + return internalDisplay; + } + } + + /* + * Starts the event loop. If this returns without throwing then the + * internal Display instance has been successfully created and can be + * asked with getInternalDisplay(). + * + * Any calls after the first successful call are ignored. Any calls after + * the first failed call will not retry but will throw. + */ + private static void ensureStartedOrThrow() { + // This is called synchronized with the Display creation that takes + // place in the UI thread. + + if(!started) { + started = true; + + // Start the UI thread + if(!UIThreadLauncher.startInUIThread(new LoopRunner())) { + // Failed to create the UI thread, release the starting thread + EventLoop.class.notify(); + } + + // Wait until the Display gets created in the UI thread + try { + EventLoop.class.wait(); + } catch(InterruptedException e) { + // Nothing to do + } + } + + // Check Display creation status + if(internalDisplay == null) { + throw new RuntimeException("Failed to start"); + } + } + + /* + * Start listening for the application requesting the UI thread via the + * eSWT's UIThreadSupport API. + */ + private static void listenApplicationUIThreadRequest() { + UIThreadHandOverManager.setApplicationUIListener(new ApplicationUIListener() { + /* + * Can get called in any thread but the UI thread. + */ + public void applicationUIThreadRequest() { + // Synchronized with the Display creation and destruction + synchronized(EventLoop.class) { + applicationRequestedUIThread = true; + // It's ok to skip wake if Display has not been created yet + if(internalDisplay != null && !internalDisplay.isDisposed()) { + internalDisplay.wake(); + } + } + } + }); + } + + /* + * Start listening for the runtime's shutting down message. + */ + private static void listenRuntimeShutdownRequest() { + ApplicationUtils.getInstance().addShutdownListener( + /* + * Can get called in any thread but the UI thread. + */ + new ShutdownListener() { + public void shuttingDown() { + // Synchronized with the Display creation and destruction + synchronized(EventLoop.class) { + shuttingDownReceived = true; + // It's ok to skip wake if Display has not been created yet + if(internalDisplay != null && !internalDisplay.isDisposed()) { + internalDisplay.wake(); + } + } + } + }); + } + + /* + * Creates the internal Display and runs the event loop. Called in the UI + * thread. + */ + private static void run() { + try { + // Add the appropriate listeners. + listenRuntimeShutdownRequest(); + listenApplicationUIThreadRequest(); + + // Create the internal Display. + createInternalDisplay(); + + // Run event loop until the application wants to take over or the + // runtime signals shutdown. In the latter case exit from the UI + // thread immediately. + loop(PRE_APPLICATION); + if(shuttingDownReceived) { + return; + } + + // Hand over the UI thread to the application. The application is + // responsible of the event loop until it allows the UI thread to + // return back here. + UIThreadHandOverManager.runApplicationUI(); + + // Application allowed its event loop to exit. Continue + // dispatching the events until the runtime wants to shut down. + loop(POST_APPLICATION); + + } finally { + destroy(); + } + } + + /* + * Creates the Display. Synchronized with the starting thread that + * waits until the Display has been created. + */ + private static void createInternalDisplay() { + synchronized(EventLoop.class) { + try { + // Will throw if fails + internalDisplay = Internal_PackageSupport.internalInstance(); + } finally { + // Release the starting thread + EventLoop.class.notify(); + } + } + } + + /* + * Frees all the owned resources when shutting down. Ideally this would be + * executed only after all the application's threads have been finalized. + * If the application's threads are still running it's possible that + * someone would try to access the internal Display after this. This would + * cause an exception with "device is disposed". + */ + private static void destroy() { + // Synchronized with the shutting down message and the application UI + // thread request. + synchronized(EventLoop.class) { + if(internalDisplay != null) { + // If this throws then Display.dispose() was called on the + // internal Display by someone who doesn't own it. + if(internalDisplay.isDisposed()) { + throw new RuntimeException( + "Internal Display has been disposed by someone who " + + "doesn't own it"); + } + + // If the application's Display has been disposed then + // disposing also the internal Display here would destroy the + // QApplication and all the UI resources tied to it. Disposing + // is not done but the resources are allowed to leak on + // purpose: + // + // In MIDP many of the related APIs are thread-safe and are + // exclusively using garbage collection for memory management. + // Disposing the UI resources of API objects that still may be + // referenced would create a deviation from the pattern and + // that would need special handling all around the API + // implementations. That handling is not attempted but instead + // the internal Display instance is allowed to live until the + // process terminates. + } + } + } + + /* + * The event loop that can run before and after the application's loop. + * State parameter tells which one of the two this is. The exit + * condition depends on the state. + */ + private static void loop(final int state) { + while(!loopExitCondition(state)) { + if(!internalDisplay.readAndDispatch()) { + internalDisplay.sleep(); + } + } + } + + /* + * Checks if the loop exit condition is met. The condition is different + * depending on if the loop is running before or after the application's + * loop. + */ + private static boolean loopExitCondition(final int state) { + switch(state) { + case PRE_APPLICATION: + return shuttingDownReceived || applicationRequestedUIThread; + case POST_APPLICATION: + return shuttingDownReceived; + default: + return true; + } + } +}