1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
8 /** A simple interface that must be supported by any object inserted into the MessageQueue. */
9 public interface Message {
11 /** Invoked when the Message is dequeued. */
12 public void perform();
16 * A singleton class (one instance per JVM) that implements a queue
17 * for XWT events and threads; this is the main action-scheduling
18 * loop for the engine.
20 * This <i>is</i> the foreground thread -- it
21 * dequeues Messages when they arrive in the queue. Using this
22 * thread ensures that the messages are executed in a synchronous,
25 public static class Q extends Thread {
27 /** a do-nothing message enqueued to trigger Surfaces to refresh themselves */
28 private static Message refreshMessage = new Message() { public void perform() { } };
30 /** enqueues a do-nothing message to get the Surfaces to refresh themselves */
31 public static void refresh() { add(refreshMessage); }
33 /** true iff latency-sensitive UI work is being done; signals the networking code to yield */
34 public static volatile boolean working = false;
36 private Q() { start(); }
39 private static Queue events = new Queue(50);
41 /** the number of objects in the queue that are not subclasses of ThreadMessage */
42 public static volatile int nonThreadEventsInQueue = 0;
44 /** the message currently being performed */
45 static Message currentlyPerforming = null;
47 private static Q singleton = new Q();
48 private static Watcher watcher = new Watcher();
51 * The message loop. Note that non-ThreadMessage Messages get
52 * priority, and that the queue is emptied in passes -- first we
53 * look at how many events are in the queue, then perform that
54 * many events, then render. This has the effect of throttling
55 * render requests to the appropriate frequency -- when many
56 * messages are in the queue, refreshes happen less frequently.
59 Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
62 int size = events.size();
63 for(int i=0; i<Math.max(1, size); i++) {
64 Message e = (Message)events.remove();
66 // if there are non-ThreadMessage events, perform them first
67 // we check against Thread instead of ThreadMessage so we don't get link failures in the Shoehorn
68 if (!(e instanceof Thread)) nonThreadEventsInQueue--;
69 else if (nonThreadEventsInQueue > 0) {
74 if (!(e instanceof Thread)) working = true;
75 currentlyPerforming = e;
77 currentlyPerforming = null;
81 for(int i=0; i<Surface.allSurfaces.size(); i++)
82 ((Surface)Surface.allSurfaces.elementAt(i)).render();
85 } catch (Throwable t) {
86 if (Log.on) Log.log(this, "caught throwable in Q.run(); this should never happen");
87 if (Log.on) Log.log(this, " currentlyPerforming == " + currentlyPerforming);
88 if (Log.on) Log.log(this, " working == " + working);
89 // FIXME - this currently calls compiledfunction.toString which gives more info than we need
90 if (Log.on) Log.log(this, t);
91 if (Log.on) Log.log(this, "resuming Q loop");
96 /** Adds an event to the queue */
97 public static void add(Message e) {
98 // Even though we don't synchronize around these two statements, it is not a race condition. In the worst case,
99 // nonThreadEventsInQueue undercounts -- it never overcounts.
101 if (!(e instanceof Thread)) nonThreadEventsInQueue++;
104 /** a simple thread that logs a warning if too much time is spent in any given message -- helpful for debugging infinite loops */
105 private static class Watcher extends Thread {
106 long t = System.currentTimeMillis();
108 public Watcher() { start(); }
111 if ((m != null && m == Q.currentlyPerforming) || Q.working) {
113 if (m != null && m instanceof ThreadMessage) {
114 where = org.xwt.js.JS.Thread.fromJavaThread((ThreadMessage)m).getSourceName();
115 what = "background thread";
116 } else if (m != null) {
117 where = org.xwt.js.JS.Thread.fromJavaThread(Q.singleton).getSourceName();
120 where = org.xwt.js.JS.Thread.fromJavaThread(Q.singleton).getSourceName();
123 long howlong = (System.currentTimeMillis() - t) / 1000;
125 if (Log.on) Log.log(this, "note: executing same " + what + " for " + howlong + "s" + " at " + where);
127 m = Q.currentlyPerforming;
128 t = System.currentTimeMillis();
130 try { Thread.sleep(1000); } catch (Exception e) { }