javauis/eswt_qt/org.eclipse.swt/Eclipse_SWT_PI/common_j2me/org/eclipse/swt/internal/qt/EventLoop.java
changeset 78 71ad690e91f5
equal deleted inserted replaced
72:1f0034e370aa 78:71ad690e91f5
       
     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 }