001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx;
021 
022 import js.ArrayLike;
023 import js.Disposable;
024 import js.Js;
025 import js.Vars;
026 import jsx.core.ArrayLikes;
027 
028 /**
029  * <p>Facilitates multitasking features by queuing the submitted {@link Runnable} tasks 
030  * and running them in the submitted order.</p>
031  * <p>An instance of this class keeps a FIFO queue of {@link Runnable} objects. The task 
032  * manager keeps popping a task from the head of the queue and runs it in the same thread. 
033  * It falls into sleep when the queue becomes empty, taking up no CPU time and wakes up again 
034  * when a new task is submitted. A call to the method {@link TaskManager#submit(Runnable)} 
035  * on the task manager pushes the argument task to the tail of the queue in the calling thread.</p>
036  * <p>This class provides a simple but efficient mechanism for multitasking needs, which is 
037  * used by the {@link Source} class to implement a high level event model. Note that, the 
038  * re-compiled JavaScript code is to be run in a single thread. However, in JS Simulation 
039  * mode, the scenario is quite different: the tasks submitted to the same task manager are 
040  * being run in the single thread, but the tasks submitted to different task managers are 
041  * being run in different threads. This is because this class creates an {@link Interval} 
042  * object for its periodic executions. {@link Interval} calls the {@link jsx.client.Global#setInterval(js.core.JsFunction, Number)} 
043  * method which calls the {@link js.Js#setInterval(js.core.JsFunction, Number)} method which 
044  * is simulating JavaScript in Java multi-thread environment and creates a new thread whenever 
045  * called. The whole thing is designed deliberately for the efficiency for JS Simulation.</p>
046  * <p>Note that, this class is <tt>final</tt> and not extensible.</p>
047  * 
048  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
049  * @see Source
050  */
051 public final class TaskManager extends Disposable
052 {
053     private Number interval;
054     private ArrayLike<Runnable> queue = new Vars<Runnable>().var();
055     private int start = 0;
056     private final Interval timer;
057 
058     /**
059      * <p>The default constructor.</p>
060      * @see #TaskManager(Number)
061      * @since 1.0
062      */
063     public TaskManager() {
064         timer = new Interval() {
065             @Override
066             public void run() {
067                 Runnable r = poll();
068                 if (Js.be(r)) {
069                     r.run();
070                 }
071             }
072         };
073     }
074 
075     /**
076      * <p>The typical constructor.</p>
077      * @param interval The interval in milliseconds to run the submitted tasks.
078      * @see #TaskManager()
079      * @since 1.0
080      */
081     public TaskManager(Number interval) {
082         this();
083         this.interval = interval;
084     }
085 
086     /**
087      * <p>Checks if the task manager is idle.</p>
088      * @return <tt>true</tt> if the task manager is idle; <tt>false</tt> otherwise.
089      * @since 1.0
090      */
091     public final synchronized boolean idle() {
092         return !timer.isRunning();
093     }
094 
095     /**
096      * <p>Returns the number of the pending tasks for this task manager to run.</p>
097      * @return The size of the pending tasks to run.
098      * @since 1.0
099      */
100     public final synchronized int size() {
101         return ArrayLikes.length(queue) - start;
102     }
103 
104     /**
105      * <p>Polls the first pending task.</p>
106      * <p>If there is no pending tasks, this method returns <tt>null</tt>. If there is no 
107      * pending tasks left after popping the last one, this method put the task manager 
108      * into sleep before it returns the last task.</p>
109      * @return The first pending task.
110      * @since 1.0
111      */
112     public final synchronized Runnable poll() {
113         if (size() > 0) {
114             return queue.get(start++);
115         } else {
116             if (start > 0) {
117                 queue = new Vars<Runnable>().var();
118                 start = 0;
119             }
120             timer.clear();
121         }
122         return null;
123     }
124 
125     /**
126      * <p>Peeks the last pending task.</p>
127      * <p>If there is no pending tasks, this method returns <tt>null</tt>.</p>
128      * @return The last pending task.
129      * @since 1.0
130      */
131     public final synchronized Runnable peek() {
132         return size() > 0 ? queue.get(ArrayLikes.length(queue) - 1) : null;
133     }
134 
135     /**
136      * <p>Pops the last pending task.</p>
137      * <p>If there is no pending tasks, this method returns <tt>null</tt>.</p>
138      * @return The last pending task.
139      * @since 1.0
140      */
141     public final synchronized Runnable pop() {
142         return size() > 0 ? ArrayLikes.pop(queue) : null;
143     }
144 
145     /**
146      * <p>Submits a task to this task manager.</p>
147      * <p>If the task manager is sleeping, this method wakes it up after pushing the specified 
148      * task in the end of its task queue.</p>
149      * @since 1.0
150      */
151     public final synchronized void submit(Runnable r) {
152         ArrayLikes.push(queue, r);
153         if (idle()) {
154             if (Js.be(interval)) {
155                 timer.set(interval);
156             } else {
157                 timer.set();
158             }
159         }
160     }
161 }