-// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
-package org.xwt;
-
-import java.util.*;
-import org.xwt.util.*;
-
-
-/** A simple interface that must be supported by any object inserted into the MessageQueue. */
-public interface Message {
-
- /** Invoked when the Message is dequeued. */
- public void perform();
-
-
- /**
- * A singleton class (one instance per JVM) that implements a queue
- * for XWT events and threads; this is the main action-scheduling
- * loop for the engine.
- *
- * This <i>is</i> the foreground thread -- it
- * dequeues Messages when they arrive in the queue. Using this
- * thread ensures that the messages are executed in a synchronous,
- * in-order fashion.
- */
- public static class Q extends Thread {
-
- /** a do-nothing message enqueued to trigger Surfaces to refresh themselves */
- private static Message refreshMessage = new Message() { public void perform() { } };
-
- /** enqueues a do-nothing message to get the Surfaces to refresh themselves */
- public static void refresh() { add(refreshMessage); }
-
- /** true iff latency-sensitive UI work is being done; signals the networking code to yield */
- public static volatile boolean working = false;
-
- private Q() { }
-
- /** invoked by Main */
- public static void startQ() { singleton.start(); }
-
- /** pending events */
- private static Queue events = new Queue(50);
-
- /** the number of objects in the queue that are not subclasses of ThreadMessage */
- public static volatile int nonThreadEventsInQueue = 0;
-
- /** the message currently being performed */
- static Message currentlyPerforming = null;
-
- private static Q singleton = new Q();
- private static Watcher watcher = new Watcher();
-
- /**
- * The message loop. Note that non-ThreadMessage Messages get
- * priority, and that the queue is emptied in passes -- first we
- * look at how many events are in the queue, then perform that
- * many events, then render. This has the effect of throttling
- * render requests to the appropriate frequency -- when many
- * messages are in the queue, refreshes happen less frequently.
- */
- public void run() {
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- while(true) {
- try {
- int size = events.size();
- for(int i=0; i<Math.max(1, size); i++) {
- Message e = (Message)events.remove();
-
- // if there are non-ThreadMessage events, perform them first
- // we check against Thread instead of ThreadMessage so we don't get link failures in the Shoehorn
- if (!(e instanceof Thread)) nonThreadEventsInQueue--;
- else if (nonThreadEventsInQueue > 0) {
- add(e);
- i--;
- continue;
- }
- if (!(e instanceof Thread)) working = true;
- currentlyPerforming = e;
- e.perform();
- currentlyPerforming = null;
- working = false;
- }
- working = true;
- for(int i=0; i<Surface.allSurfaces.size(); i++)
- ((Surface)Surface.allSurfaces.elementAt(i)).render();
- working = false;
-
- } catch (Throwable t) {
- if (Log.on) Log.log(this, "caught throwable in Q.run(); this should never happen");
- if (Log.on) Log.log(this, " currentlyPerforming == " + currentlyPerforming);
- if (Log.on) Log.log(this, " working == " + working);
- if (Log.on) Log.log(this, t);
- if (Log.on) Log.log(this, "resuming Q loop");
- }
- }
- }
-
- /** Adds an event to the queue */
- public static void add(Message e) {
- // Even though we don't synchronize around these two statements, it is not a race condition. In the worst case,
- // nonThreadEventsInQueue undercounts -- it never overcounts.
- events.append(e);
- if (!(e instanceof Thread)) nonThreadEventsInQueue++;
- }
-
- /** a simple thread that logs a warning if too much time is spent in any given message -- helpful for debugging infinite loops */
- private static class Watcher extends Thread {
- long t = System.currentTimeMillis();
- Message m = null;
- public Watcher() { start(); }
- public void run() {
- while(true) {
- if ((m != null && m == Q.currentlyPerforming) || Q.working) {
- String what, where;
- if (m != null && m instanceof ThreadMessage) {
- where = org.xwt.js.JS.Thread.fromJavaThread((ThreadMessage)m).getSourceName();
- what = "background thread";
- } else if (m != null) {
- where = org.xwt.js.JS.Thread.fromJavaThread(Q.singleton).getSourceName();
- what = "event trap";
- } else {
- where = org.xwt.js.JS.Thread.fromJavaThread(Q.singleton).getSourceName();
- what = "script";
- }
- long howlong = (System.currentTimeMillis() - t) / 1000;
- if (howlong >= 5)
- if (Log.on) Log.log(this, "note: executing same " + what + " for " + howlong + "s" + " at " + where);
- } else {
- m = Q.currentlyPerforming;
- t = System.currentTimeMillis();
- }
- try { Thread.sleep(1000); } catch (Exception e) { }
- }
- }
- }
-
- }
-
-}
-