61e89649ac94b75fb4e09567087f2b9519f1bb46
[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     private volatile static int threadcount = 0;
21
22     /** the JavaScript function that we are executing */
23     volatile JS.Callable 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 synchronized void newthread(JS.Callable 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     /** attempts to put this thread into the background to perform a blocking operation; returns false if unable to do so */
55     public static boolean suspendThread() {
56         // put ourselves in the background
57         Thread thread = Thread.currentThread();
58         if (!(thread instanceof ThreadMessage)) {
59             if (Log.on) Log.logJS(ThreadMessage.class, "attempt to perform background-only operation in a foreground thread");
60             return false;
61         }
62         ThreadMessage mythread = (ThreadMessage)thread;
63         mythread.setPriority(Thread.MIN_PRIORITY);
64         mythread.done.release();
65         return true;
66     }
67
68     /** re-enqueues this thread */
69     public static void resumeThread() {
70         ThreadMessage mythread = (ThreadMessage)Thread.currentThread();
71         MessageQueue.add(mythread);
72         mythread.setPriority(Thread.NORM_PRIORITY);
73         mythread.go.block();        
74     }
75     
76     public void run() {
77         try {
78             threadcount++;
79             while (true) {
80                 try {
81                     go.block();
82                     f.call(new JS.Array());
83                 } catch (JS.Exn e) {
84                     if (Log.on) Log.log(this, "WARNING: uncaught ecmascript exception: " + e);
85                 }
86                 done.release();
87                 synchronized(waiting) {
88                     if (waiting.size() > 0) {
89                         f = (JS.Callable)waiting.remove(false);
90                         MessageQueue.add(this);
91                     } else if (spare.size() < 10) {
92                         spare.append(this);
93                     } else {
94                         threadcount--;
95                         return;
96                     }
97                 }
98             }
99         } catch (Throwable t) {
100             if (Log.on) Log.log(this, "caught throwable at thread entry point; this should never happen");
101             if (Log.on) Log.log(this, t);
102             if (Log.on) Log.log(this, "reaping thread");
103             return;
104         }
105     }
106
107     /** this is invoked in the MessageQueue thread */
108     public void perform() {
109         go.release();
110         done.block();
111     }
112
113 }