1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
8 * A singleton class (one instance per JVM) that implements a queue
9 * for XWT events and threads; this is the main action-scheduling
10 * loop for the engine.
12 * This <i>is</i> the foreground thread -- it
13 * dequeues Messages when they arrive in the queue. Using this
14 * thread ensures that the messages are executed in a synchronous,
17 public class MessageQueue extends Thread {
19 /** a do-nothing message enqueued to trigger Surfaces to refresh themselves */
20 private static Message refreshMessage = new Message() { public void perform() { } };
22 /** enqueues a do-nothing message to get the Surfaces to refresh themselves */
23 public static void refresh() { add(refreshMessage); }
25 /** true iff latency-sensitive UI work is being done; signals the networking code to yield */
26 public static volatile boolean working = false;
28 private MessageQueue() { start(); }
31 private static Queue events = new Queue(50);
33 /** the number of objects in the queue that are not subclasses of ThreadMessage */
34 public static volatile int nonThreadEventsInQueue = 0;
36 /** the message currently being performed */
37 static Message currentlyPerforming = null;
39 private static MessageQueue singleton = new MessageQueue();
40 private static MessageQueueWatcher watcher = new MessageQueueWatcher();
42 // HACK for debugging purposes
43 Object lastfunc = null;
44 Message lastmessage = null;
47 * The message loop. Note that non-ThreadMessage Messages get
48 * priority, and that the queue is emptied in passes -- first we
49 * look at how many events are in the queue, then perform that
50 * many events, then render. This has the effect of throttling
51 * render requests to the appropriate frequency -- when many
52 * messages are in the queue, refreshes happen less frequently.
55 Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
58 int size = events.size();
59 for(int i=0; i<Math.max(1, size); i++) {
60 Message e = (Message)events.remove();
62 // if there are non-ThreadMessage events, perform them first
63 // we check against Thread instead of ThreadMessage so we don't get link failures in the Shoehorn
64 if (!(e instanceof Thread)) nonThreadEventsInQueue--;
65 else if (nonThreadEventsInQueue > 0) {
70 if (!(e instanceof Thread)) working = true;
71 currentlyPerforming = e;
73 // for debugging purposes
75 if (e != null && e instanceof ThreadMessage) lastfunc = ((ThreadMessage)e).f;
78 currentlyPerforming = null;
82 for(int i=0; i<Surface.allSurfaces.size(); i++)
83 ((Surface)Surface.allSurfaces.elementAt(i)).render();
86 } catch (Throwable t) {
87 if (Log.on) Log.log(this, "caught throwable in MessageQueue.run(); this should never happen");
88 if (Log.on) Log.log(this, " currentlyPerforming == " + currentlyPerforming);
89 if (Log.on) Log.log(this, " working == " + working);
90 if (Log.on) Log.log(this, " lastfunc == " + lastfunc);
91 if (Log.on) Log.log(this, " lastmessage == " + lastmessage);
92 if (Log.on) Log.log(this, t);
93 if (Log.on) Log.log(this, "resuming MessageQueue loop");
98 /** Adds an event to the queue */
99 public static void add(Message e) {
101 // Even though we don't synchronize around these two
102 // statements, it is not a race condition. In the worst case,
103 // nonThreadEventsInQueue undercounts -- it never
107 if (!(e instanceof Thread)) nonThreadEventsInQueue++;
110 /** a simple thread that logs a warning if too much time is spent in any given message -- helpful for debugging infinite loops */
111 private static class MessageQueueWatcher extends Thread {
112 long t = System.currentTimeMillis();
114 public MessageQueueWatcher() { start(); }
117 if ((m != null && m == MessageQueue.currentlyPerforming) || MessageQueue.working) {
119 if (m != null && m instanceof ThreadMessage) {
120 where = org.xwt.js.JS.Thread.fromJavaThread((ThreadMessage)m).getSourceName();
121 what = "background thread";
122 } else if (m != null) {
123 where = org.xwt.js.JS.Thread.fromJavaThread(MessageQueue.singleton).getSourceName();
126 where = org.xwt.js.JS.Thread.fromJavaThread(MessageQueue.singleton).getSourceName();
129 long howlong = (System.currentTimeMillis() - t) / 1000;
131 if (Log.on) Log.log(this, "note: executing same " + what + " for " + howlong + "s" + " at " + where);
133 m = MessageQueue.currentlyPerforming;
134 t = System.currentTimeMillis();
136 try { Thread.sleep(1000); } catch (Exception e) { }