2003/10/28 10:10:17
[org.ibex.core.git] / src / org / xwt / ThreadMessage.java
1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt;
3
4 import java.util.*;
5 import org.xwt.util.*;
6 import org.xwt.js.*;
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     public static boolean fakeBackground = false;
21     
22     private volatile static int threadcount = 0;
23
24     /** the JavaScript function that we are executing */
25     volatile JS.Callable f;
26
27     /** the ThreadMessage thread blocks on this before executing any JavaScript */
28     Semaphore go = new Semaphore();
29
30     /** The Message.Q (main) thread blocks on this while the ThreadMessage thread is running JavaScript code */
31     Semaphore done = new Semaphore();
32
33     /** used to pool ThreadMessages that are not in use */
34     private static Queue spare = new Queue(50);
35
36     /** queue of functions waiting to be spawned; used if threadcount == Platform.maxThreads() */
37     private static Queue waiting = new Queue(50);
38
39     private ThreadMessage() { start(); }
40     private static Object[] emptyobj = new Object[] { };
41
42     /** creates a new thread to execute function <tt>f</tt> */
43     public static synchronized void newthread(JS.Callable f) {
44         ThreadMessage ret = (ThreadMessage)spare.remove(false);
45         if (ret == null) {
46             if (threadcount < Platform.maxThreads()) ret = new ThreadMessage();
47             else {
48                 waiting.append(f);
49                 return;
50             }
51         }
52         ret.f = f;
53         Message.Q.add(ret);
54     }
55
56     /** attempts to put this thread into the background to perform a blocking operation; returns false if unable to do so */
57     public static boolean suspendThread() {
58         if (fakeBackground) return true;
59         // put ourselves in the background
60         Thread thread = Thread.currentThread();
61         if (!(thread instanceof ThreadMessage)) {
62             if (Log.on) Log.logJS(ThreadMessage.class, "attempt to perform background-only operation in a foreground thread");
63             return false;
64         }
65         ThreadMessage mythread = (ThreadMessage)thread;
66         mythread.setPriority(Thread.MIN_PRIORITY);
67         mythread.done.release();
68         return true;
69     }
70
71     /** re-enqueues this thread */
72     public static void resumeThread() {
73         if (fakeBackground) return;
74         ThreadMessage mythread = (ThreadMessage)Thread.currentThread();
75         Message.Q.add(mythread);
76         mythread.setPriority(Thread.NORM_PRIORITY);
77         mythread.go.block();        
78     }
79     
80     public void run() {
81         try {
82             threadcount++;
83             while (true) {
84                 try {
85                     go.block();
86                     f.call(new JS.Array());
87                 } catch (JS.Exn e) {
88                     if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e);
89                 }
90                 done.release();
91                 synchronized(waiting) {
92                     if (waiting.size() > 0) {
93                         f = (JS.Callable)waiting.remove(false);
94                         Message.Q.add(this);
95                     } else if (spare.size() < 10) {
96                         spare.append(this);
97                     } else {
98                         threadcount--;
99                         return;
100                     }
101                 }
102             }
103         } catch (Throwable t) {
104             if (Log.on) Log.log(this, "caught throwable at thread entry point; this should never happen");
105             if (Log.on) Log.log(this, t);
106             if (Log.on) Log.log(this, "reaping thread");
107             return;
108         }
109     }
110
111     /** this is invoked in the Message.Q thread */
112     public void perform() {
113         go.release();
114         done.block();
115     }
116
117 }