558c49d66466814138bd2c4294985dd57acb127d
[org.ibex.core.git] / src / org / xwt / ThreadMessage.java
1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt;
3
4 import org.xwt.util.*;
5 import java.util.*;
6 import org.mozilla.javascript.*;
7
8 /**
9  *  A background thread. All threads created with <tt>xwt.thread</tt>
10  *  are instances of this class. ThreadMessage objects can be enqueued
11  *  in MessageQueue.
12  *
13  *  INVARIANT: only one thread can be executing JavaScript scripts or
14  *             accessing Box instances at any given time. For
15  *             performance reasons, no locks are employed to enforce
16  *             this invariant.
17  */
18 public class ThreadMessage extends Thread implements Message {
19     
20     private volatile static int threadcount = 0;
21
22     /** the JavaScript function that we are executing */
23     volatile Function f;
24
25     /** the ThreadMessage thread blocks on this before executing any JavaScript */
26     Semaphore go = new Semaphore();
27
28     /** The MessageQueue (main) thread blocks on this while the ThreadMessage thread is running JavaScript code */
29     Semaphore done = new Semaphore();
30
31     /** used to pool ThreadMessages that are not in use */
32     private static Queue spare = new Queue(50);
33
34     /** queue of functions waiting to be spawned; used if threadcount == Platform.maxThreads() */
35     private static Queue waiting = new Queue(50);
36
37     private ThreadMessage() { start(); }
38     private static Object[] emptyobj = new Object[] { };
39
40     /** creates a new thread to execute function <tt>f</tt> */
41     public static void newthread(Function f) {
42         ThreadMessage ret = (ThreadMessage)spare.remove(false);
43         if (ret == null) {
44             if (threadcount < Platform.maxThreads()) ret = new ThreadMessage();
45             else {
46                 waiting.append(f);
47                 return;
48             }
49         }
50         ret.f = f;
51         MessageQueue.add(ret);
52     }
53     
54     public void run() {
55         try {
56             threadcount++;
57             Context cx = Context.enter();
58             while (true) {
59                 try {
60                     go.block();
61                     f.call(cx, f.getParentScope(), f.getParentScope(), emptyobj);
62                 } catch (EcmaError e) {
63                     if (Log.on) Log.log(this, "WARNING: uncaught interpreter exception: " + e.getMessage());
64                     if (Log.on) Log.log(this, "         thrown from background thread at " + e.getSourceName() + ":" + e.getLineNumber());
65                 } catch (JavaScriptException e) {
66                     if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e.getMessage());
67                     if (Log.on) Log.log(this, "         thrown from background thread at " + e.sourceFile + ":" + e.line);
68                 }
69                 done.release();
70                 synchronized(waiting) {
71                     if (waiting.size() > 0) {
72                         f = (Function)waiting.remove(false);
73                         MessageQueue.add(this);
74                     } else if (spare.size() < 10) {
75                         spare.append(this);
76                     } else {
77                         threadcount--;
78                         return;
79                     }
80                 }
81             }
82         } catch (Throwable t) {
83             if (Log.on) Log.log(this, "caught throwable at thread entry point; this should never happen");
84             if (Log.on) Log.log(this, t);
85             if (Log.on) Log.log(this, "reaping thread");
86             return;
87         }
88     }
89
90     /** this is invoked in the MessageQueue thread */
91     public void perform() {
92         go.release();
93         done.block();
94     }
95
96 }