capture
[rrq/gorite.git] / com / intendico / gorite / Executor.java
1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
3
4 This file is part of GORITE.
5
6 GORITE is free software: you can redistribute it and/or modify it
7 under the terms of the Lesser GNU General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 GORITE is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14 License for more details.
15
16 You should have received a copy of the Lesser GNU General Public
17 License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
18 **********************************************************************/
19
20 package com.intendico.gorite;
21
22 import java.util.Vector;
23 import java.util.Iterator;
24 import com.intendico.data.Ref;
25
26 /**
27  * The Executor class implements functions for managing goal execution
28  * for a collection of performers. More precisely, it implements the
29  * dispatch thread control to the {@link Performer.TodoGroup} goal
30  * executions.
31  *
32  * <p> System execution may be controlled by invoking the {@link
33  * Performer#performGoal} method for the top level team. This will
34  * lead the execution thread into the Executor for progressing all
35  * {@link Performer.TodoGroup} executions.
36  *
37  * <p> The Executor class also includes a control loop, the {@link
38  * #run} method, for using a separate thread to run performers. This
39  * is an alternative execution principle to the use of {@link
40  * Performer#performGoal}. It's not a good idea to use both.
41  *
42  * <p> Ordinarily the use of the Executor class is transparent to an
43  * application developer. A single Executor object is created so as to
44  * provide thread control dispatch to all performers, and the top
45  * level {@link Performer#performGoal} utilizes this as needed.
46  *
47  * <p> An application may split the performers amongst several
48  * executors, and create background threads for {@link
49  * Performer.TodoGroup} executions. In that case, the developer must
50  * take precaution against multi-threading interferences, and in
51  * particular refrain from using {@link Performer#performGoal}.
52  *
53  * <p> The transfer of control between goal executions uses the {@link
54  * #steps} variable, which tells how many goal steps an execution may
55  * advance before being stopped to allow the thread to pursue another
56  * goal execution.
57  */
58 public class Executor implements Runnable {
59
60     /**
61      * This is a callback interface for monitoring the executor.
62      */
63     public interface Listener {
64         /**
65          * This method is invoked when the Executor is going idle.
66          */
67         public void goingIdle(Executor e);
68         /**
69          * This method is invoked when the Executor is waking up.
70          */
71         public void wakingUp(Executor e);
72     }
73
74
75     /**
76      * Holds the current default executor that performers add
77      * themselves to by default. Initially <em>null</em>, but one is
78      * set up on demand if needed by {@link #getDefaultExecutor()}.
79      */
80     public static Executor default_executor = null;
81
82     /**
83      * Returns the default executor, and creates one if needed.
84      */
85     public static Executor getDefaultExecutor() {
86         if ( default_executor == null )
87             default_executor = new Executor( "[gorite]" );
88         return default_executor;
89     }
90
91     /**
92      * A name for the executor. This is also used as thread name for
93      * the executor's dedicated thread.
94      */
95     public String name;
96
97     /**
98      * Configuration value for how many {@link #changeFocus()}
99      * invocations to count between it returning <em>true</em>.
100      */
101     public int steps = 40;
102
103     /**
104      * Dynamic counter of how many {@link #changeFocus()} invocations
105      * remain until it will return <em>true</em>.
106      */
107     public int count = steps;
108
109     /**
110      * The {@link Performer} objects managed by this executor.
111      */
112     public Vector/*<Performer>*/ performers = new Vector/*<Performer>*/();
113
114     /**
115      * A dynamic state variable.
116      */
117     public boolean running = true;
118
119     /**
120      * A dynamic state variable. Used by the {link #stop} method for
121      * telling the {@link #run} method to stop and exit when
122      * convenient. Value 1 is a "hard stop", that takes effect even
123      * while performers have things to do, and value 2 is a "soft
124      * stop" that takes effect when all performers are blocking.
125      */
126     public int stopping = 0;
127
128     /**
129      * A dynamic state variable. Index of the performer to re-run
130      * next.
131      */
132     public int index = 0;
133
134     /**
135      * The collection of listeners, which get notify about the
136      * thread going to sleep and waking up.
137      */
138     public Vector/*<Listener>*/ listeners = new Vector/*<Listener>*/();
139
140     /**
141      * Utility method to notify all listeners
142      */
143     public void notifyGoingIdle() {
144         Vector/*<Listener>*/ handlers = new Vector/*<Listener>*/();
145         synchronized ( this ) {
146             handlers.addAll( listeners );
147         }
148         for ( Iterator/*<Listener>*/ i = handlers.iterator();
149               i.hasNext(); ) {
150             Listener x = (Listener) i.next();
151             x.goingIdle( this );
152         }
153     }
154
155     /**
156      * Utility method to notify all listeners
157      */
158     public void notifyWakingUp() {
159         Vector/*<Listener>*/ handlers = new Vector/*<Listener>*/();
160         synchronized ( this ) {
161             handlers.addAll( listeners );
162         }
163         for ( Iterator/*<Listener>*/ i = handlers.iterator();
164               i.hasNext(); ) {
165             Listener x = (Listener) i.next();
166             x.wakingUp( this );
167         }
168     }
169
170     /**
171      * Adds a listener to the Executor to be notified when the
172      * Executor goes idle and wakes up from idle
173      * @param listener
174      */
175     synchronized public void addListener(Listener listener) {
176         listeners.add( listener );
177     }
178
179     /**
180      * Removes a listener from the executor.
181      * @param listener
182      */
183     synchronized public void removeListener(Listener listener) {
184         listeners.remove( listener );
185     }
186
187     /**
188      * Constructor. The given name is also used as thread name for the
189      * thread that is started.
190      */
191     public Executor(String n) {
192         name = n;
193     }
194
195     /**
196      * Returns the executor's name.
197      */
198     public String toString() {
199         return name;
200     }
201
202     /**
203      * Tells whether it is time to change focus or not. This is used
204      * by the {@link Goal.Instance#perform} method to possibly stop
205      * the current computation in order to let the executor shift
206      * focus to another computation. Whenever the {@link #count}
207      * arrives at 0, then it it is time to shift focus, and to reset
208      * the count from {@link #steps}. Though, if the latter is 0 or
209      * negative, then a change of focus will never be signalled from
210      * here.
211      */
212     public boolean changeFocus() {
213         if ( steps <= 0 )
214             return false;
215         if ( count-- > 0 )
216             return false;
217         if ( Goal.isTracing() )
218             System.err.println( "CHANGE FOCUS" );
219         count = steps;
220         return true;
221     }
222
223     /**
224      * Utility method to "preempt" current intention by forcing {@link
225      * #count} to zero. This only has effect if {@link #steps} is
226      * non-zero, i.e., when using intention interleaving.
227      */
228     public void preempt() {
229         count = 0;
230     }
231
232     /**
233      * Utility method to add a {@link Performer} to be managed by this
234      * executor. If the performer already belongs to an executor, it
235      * is first removed from that one.
236      */
237     synchronized public void addPerformer(Performer performer) {
238         if ( performer.executor != null ) {
239             // The performer already belongs to an executor
240             if ( performer.executor != this )
241                 performer.executor.removePerformer( performer );
242         }
243         if ( ! performers.contains( performer ) )
244             performers.add( performer );
245         performer.executor = this;
246     }
247
248     /**
249      * Utility method to remove a {@link Performer} from the
250      * collection of performers managed by this executor. This does
251      * not interrupt an ongoing computation, but means that the
252      * executor will not re-run the performer.
253      */
254     synchronized public void removePerformer(Performer performer) {
255         if ( performer.executor == this ) {
256             performers.remove( performer );
257             performer.executor = null;
258         }
259     }
260
261     /**
262      * Utility method to localize the first {@link Performer} by its
263      * name.
264      */
265     synchronized public Performer findPerformer(String name)
266     {
267         for ( Iterator/*<Performer>*/ i =
268                   performers.iterator(); i.hasNext(); ) {
269             Performer performer = (Performer) i.next();
270             if ( performer.name.equals( name ) )
271                 return performer;
272         }
273         return null;
274     }
275
276     /**
277      * Utility method to "tickle" the executor with, just in case it
278      * is idle.
279      */
280     synchronized public void signal() {
281         running = true;
282         notifyAll();
283     }
284
285     /**
286      * A utility method for obtaining the next performer to re-run, or
287      * <em>null</em>, in between having told about them all.
288      */
289     synchronized public Performer getNext() {
290         if ( index < performers.size() )
291             return (Performer) performers.get( index++ );
292         index = 0;
293         return null;
294     }
295
296     /**
297      * A utility method for going idle when there doesn't seem to be
298      * anything to do.
299      */
300     public void waitRunning() {
301         boolean idle = false;
302         synchronized ( this ) {
303             if ( ! running && stopping == 0 )
304                 idle = true;
305         }
306         if ( idle )
307             notifyGoingIdle();
308         synchronized ( this ) {
309             if ( idle ) {
310                 while ( ! running && stopping == 0 ) {
311                     if ( Goal.isTracing() )
312                         System.err.println(
313                             "** executor " + this + " idle..." );
314                     try {
315                         wait();
316                     } catch (InterruptedException e) {
317                     }
318                 }
319             }
320             running = false;
321         }
322         if ( idle ) {
323             if ( Goal.isTracing() )
324                 System.err.println( "** executor " + this + " active..." );
325             notifyWakingUp();
326         }
327     }
328
329     /**
330      * Utility method to run all {@link Performer Performers} once,
331      * and then report if they all reported blocking or not.
332      */
333     public Goal.States runPerformersOnce() {
334         if ( Goal.isTracing() )
335             System.err.println( "** runPerformersOnce" );
336         boolean stopped = false;
337         Performer performer = getNext();
338         while ( performer != null ) {
339             if ( performer.runPerformer() != Goal.States.BLOCKED )
340                 stopped = true;
341             performer = getNext();
342         }
343         if ( Goal.isTracing() )
344             System.err.println( "** runPerformersOnce stopped " + stopped );
345         return stopped? Goal.States.STOPPED : Goal.States.BLOCKED ;
346     }
347
348     /**
349      * Utility method to keep running {@link Performer Performers}
350      * while not all are blocking. This method invokes {@link
351      * #runPerformersOnce} repeatedly until it reports that all
352      * performers are blocked.
353      */
354     public boolean runPerformersBlocked() {
355         if ( Goal.isTracing() )
356             System.err.println( "** runPerformersBlocked" );
357         boolean done_something = false;
358         while ( runPerformersOnce() != Goal.States.BLOCKED ) {
359             done_something = true;
360             if ( stopping > 0 )
361                 break;
362         }
363         if ( Goal.isTracing() )
364             System.err.println(
365                 "** runPerformersBlocked done something " + done_something );
366         return done_something;
367     }
368
369     /**
370      * This is the main execution loop of the executor. It keeps
371      * running its performers until they are all blocked, in which
372      * case it waits for a signal to re-run them.
373      */
374     public void run() {
375         if ( Goal.isTracing() )
376             System.err.println( "** starting executor " + this );
377         try {
378             index = 0;
379             for (;;) {
380                 if (runPerformersBlocked()) {
381                     running = true;
382                 }
383                 if (running) {
384                     if (stopping == 1) {
385                         break;
386                     }
387                     running = false;
388                 } else {
389                     waitRunning();
390                     if (stopping > 0) {
391                         break;
392                     }
393                 }
394             }
395             
396         } catch (Throwable t) {
397             t.printStackTrace();
398             System.err.println( "** terminating executor " + this );
399         }
400         stopping = 0;
401     }
402
403     /**
404      * Tell the {@link #run} method to stop.
405      */
406     public void stop(boolean hard) {
407         stopping = hard? 1 : 2;
408         signal();
409     }
410
411     /**
412      * Service interface to request a goal to be performed by a named
413      * performer, with thread synchronisation on this Executor.
414      */
415     synchronized public boolean performGoal(
416         String performer,Goal goal,String head,Data d) {
417         return findPerformer( performer ).performGoal( goal, head, d );
418     }
419     
420     /**
421      * Service interface to request a goal to be performed by a named
422      * performer, with thread synchronisation on this Executor.
423      */
424     synchronized public boolean performGoal(
425         String performer,Goal goal,String head,
426         Vector/*<Ref>*/ ins,Vector/*<Ref>*/ outs) {
427         return findPerformer( performer ).performGoal( goal, head, ins, outs );
428     }
429
430     /**
431      * Service interface to request a goal to be performed by a named
432      * performer.
433      */
434     public boolean performGoal(
435         String performer,String goal,String head,
436         Vector/*<Ref>*/ ins,Vector/*<Ref>*/ outs) {
437         return findPerformer( performer ).performGoal( goal, head, ins, outs );
438     }
439 }
440