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 Message.Q (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           Message.Q.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           Message.Q.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                           Message.Q.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 Message.Q thread */
108      public void perform() {
109          go.release();
110          done.block();
111      }
112  
113  }
114