1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
6 import org.mozilla.javascript.*;
9 * A singleton class (one instance per JVM) that implements a queue
10 * for XWT events and threads; this is the main action-scheduling
11 * loop for the engine.
13 * This <i>is</i> the foreground thread -- it
14 * dequeues Messages when they arrive in the queue. Using this
15 * thread ensures that the messages are executed in a synchronous,
18 public class MessageQueue extends Thread {
20 /** a do-nothing message enqueued to trigger Surfaces to refresh themselves */
21 private static Message refreshMessage = new Message() { public void perform() { } };
23 /** enqueues a do-nothing message to get the Surfaces to refresh themselves */
24 public static void refresh() { add(refreshMessage); }
26 /** true iff latency-sensitive UI work is being done; signals the networking code to yield */
27 public static volatile boolean working = false;
29 private MessageQueue() { start(); }
32 private static Queue events = new Queue(50);
34 /** the number of objects in the queue that are not subclasses of ThreadMessage */
35 private static volatile int nonThreadEventsInQueue = 0;
37 /** the message currently being performed */
38 static Message currentlyPerforming = null;
40 private static MessageQueue singleton = new MessageQueue();
41 private static MessageQueueWatcher watcher = new MessageQueueWatcher();
43 // HACK for debugging purposes
44 Function lastfunc = null;
45 Message lastmessage = null;
48 * The message loop. Note that non-ThreadMessage Messages get
49 * priority, and that the queue is emptied in passes -- first we
50 * look at how many events are in the queue, then perform that
51 * many events, then render. This has the effect of throttling
52 * render requests to the appropriate frequency -- when many
53 * messages are in the queue, refreshes happen less frequently.
56 Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
59 int size = events.size();
60 for(int i=0; i<Math.max(1, size); i++) {
61 Message e = (Message)events.remove();
63 // if there are non-ThreadMessage events, perform them first
64 // we check against Thread instead of ThreadMessage so we don't get link failures in the Shoehorn
65 if (!(e instanceof Thread)) nonThreadEventsInQueue--;
66 else if (nonThreadEventsInQueue > 0) {
71 if (!(e instanceof Thread)) working = true;
72 currentlyPerforming = e;
74 // for debugging purposes
76 if (e != null && e instanceof ThreadMessage) lastfunc = ((ThreadMessage)e).f;
79 currentlyPerforming = null;
83 for(int i=0; i<Surface.allSurfaces.size(); i++)
84 ((Surface)Surface.allSurfaces.elementAt(i)).render();
87 } catch (Throwable t) {
88 if (Log.on) Log.log(this, "caught throwable in MessageQueue.run(); this should never happen");
89 if (Log.on) Log.log(this, " currentlyPerforming == " + currentlyPerforming);
90 if (Log.on) Log.log(this, " working == " + working);
91 if (Log.on) Log.log(this, " lastfunc == " + lastfunc);
92 if (Log.on) Log.log(this, " lastmessage == " + lastmessage);
93 if (Log.on) Log.log(this, t);
94 if (Log.on) Log.log(this, "resuming MessageQueue loop");
99 /** Adds an event to the queue */
100 public static void add(Message e) {
102 // Even though we don't synchronize around these two
103 // statements, it is not a race condition. In the worst case,
104 // nonThreadEventsInQueue undercounts -- it never
108 if (!(e instanceof Thread)) nonThreadEventsInQueue++;
111 /** a simple thread that logs a warning if too much time is spent in any given message -- helpful for debugging infinite loops */
112 private static class MessageQueueWatcher extends Thread {
113 long t = System.currentTimeMillis();
115 public MessageQueueWatcher() { start(); }
118 if (m != null && m == MessageQueue.currentlyPerforming) {
121 if (m instanceof ThreadMessage) {
122 ThreadMessage tm = (ThreadMessage)m;
123 cx = Context.getContextForThread(tm);
124 what = "background thread";
126 cx = Context.getContextForThread(MessageQueue.singleton);
129 if (Log.on) Log.log(this, "note: executing same " + what + " for " + (System.currentTimeMillis() - t) / 1000 + "s" +
130 " at " + cx.interpreterSourceFile + ":" + cx.interpreterLine);
132 m = MessageQueue.currentlyPerforming;
133 t = System.currentTimeMillis();
135 try { Thread.sleep(1000); } catch (Exception e) { }