001    /*
002    * $Id: AnimatorThread.java,v 1.5 2003/09/23 15:31:11 gus Exp $
003    *
004    * Developed for "Rethinking CS101", a project of Lynn Andrea Stein's AP Group.
005    * For more information, see <a href="http://www.ai.mit.edu/projects/cs101/">the
006    * CS101 homepage</a> or email <las@ai.mit.edu>.
007    *
008    * Copyright (C) 1999 Massachusetts Institute of Technology.
009    * Please do not redistribute without obtaining permission.
010    */
011    
012    package cs101.lang;
013    
014    /** 
015     * This class replaces Thread as a way to animate autonomous objects.
016     * An AnimatorThread can be passed any object that implements the
017     * Animate interface.  The AnimatorThread (when started) begins
018     * executing at the Animate's act() method.
019     *
020     * Instances of this class provide safe ways to start, stop, suspend,
021     * and resume execution through the use of startExecution(),
022     * stopExecution(), suspendExecution() and resumeExecution() methods.
023     *
024     * Unlike java.lang.Thread, this class cannot be extended.
025     * 
026     * Copyright 1999 Massachusetts Institute of Technology
027     *
028     * @see Thread
029     * @see cs101.lang.Animate
030     * @see #startExecution()
031     * @see #stopExecution()
032     * @see #suspendExecution()
033     * @see #resumeExecution()
034     *
035     * @author Lynn Andrea Stein, las@ai.mit.edu
036     * @version $Id: AnimatorThread.java,v 1.5 2003/09/23 15:31:11 gus Exp $
037     *
038     */
039    public final class AnimatorThread extends Thread
040    {
041      /* CONSTANTS */
042    
043      /**
044       * These constants allow mnemonic access to AnimateObject's final
045       * constructor argument, i.e., should the object start running on
046       * construction or on (a subsequent) call to a separate start()
047       * method?
048       *
049       * @see #AnimatorThread( Animate, boolean ) 
050       */
051      public static final boolean START_IMMEDIATELY = true,
052                                  DONT_START_YET = false;
053    
054      /**
055       * Default values for sleepMinInterval, sleepRange.  Static so
056       * they're available to constructors.
057       *
058       * These default values are chosen to allow GUI events to be 
059       * easily perceived.
060       *
061       */
062      private static final long DEFAULT_SLEEP_MIN_INTERVAL = 200,
063                                DEFAULT_SLEEP_RANGE = 400;
064    
065      /* FIELDS */
066    
067      /**
068       * These private fields control the state of execution of this
069       * object's animacy. They supercede the now-deprecated
070       * Thread.stop()/Thread.suspend() regime of Java 1.0. <p>
071       *
072       * Note that they must be volatile in order to ensure correct
073       * access by the animating Thread. 
074       *
075       * @see #stopExecution()
076       * @see #suspendExecution()
077       * @see #resumeExecution()
078       */
079      private volatile boolean isStopped = false,
080                               isSuspended = false;
081      /**
082       * This object exists only to be a unique privately held object
083       * on which wait() and notify() can be invoked in cases of ersatz
084       * suspension. This is a part of the solution to handle the
085       * now-deprecated Thread.suspend() regime of Java 1.0. <p>
086       *
087       * @see #stopExecution()
088       * @see #suspendExecution()
089       * @see #resumeExecution()
090       */
091      private final Object suspendLock = new Object();
092      /**
093       * These values control the sleep interval between calls to the
094       * Animate's act() method: the smallest interval for which this
095       * AnimatorThread will sleep and the variance above this interval.
096       * In no case will the minimum sleep time be less than
097       * this.sleepMinInterval; it could be as long as
098       * this.sleepMinInterval + this.sleepRange.
099       *
100       * The default values are set by
101       * AnimatorThread.DEFAULT_SLEEP_MIN_INTERVAL and
102       * AnimatorThread.DEFAULT_SLEEP_RANGE.  These values can be
103       * overriden either by the constructor or by means of a public
104       * setter method.
105       *
106       * @see #AnimatorThread( Animate, long, long )
107       * @see #setSleepMinInterval(long)
108       * @see #setSleepRange( long )
109       * @see #DEFAULT_SLEEP_MIN_INTERVAL
110       * @see #DEFAULT_SLEEP_RANGE
111       */
112      private long sleepMinInterval, 
113                   sleepRange;
114    
115      /**
116       * This is the object that this AnimatorThread will Animate.
117       * 
118       * @see Animate
119       */
120      private final Animate what;
121    
122    
123      /* CONSTRUCTORS */
124    
125      /**
126       * This constructor requires the Animate that is to be animated.
127       * Once the AnimatorThread being constructed is started (using its
128       * start() method), the Animate's act() method will be called
129       * periodically, with the interval between calls controlled by
130       * sleepMinInterval and sleepRange.
131       *
132       * @param a  the Animate to be animated.
133       *
134       * @see Animate
135       * @see #start()
136       * @see #setSleepMinInterval(long)
137       * @see #setSleepRange( long )
138       */
139      public AnimatorThread ( Animate a )
140      {
141        this( a, AnimatorThread.DONT_START_YET );
142      }
143      
144      /**
145       * This constructor requires the Animate that is to be animated and
146       * a boolean value (expected to be one of
147       * AnimatorThread.START_IMMEDIATELY or
148       * AnimatorThread.DONT_START_YET) that determines whether this
149       * AnimatorThread should start running as the last step of its
150       * construction.  If this boolean's value is START_IMMEDIATELY,
151       * execution will begin as soon as this constructor exits.
152       * Otherwise, a subsequent call to the AnimatorThread's start()
153       * method is required.
154       *
155       * It is expected that this boolean will generally be
156       * AnimatorThread.START_IMMEDIATELY as the default value of the
157       * one-arg constructor is AnimatorThread.DONT_START_YET.
158       *
159       * @param a                 the Animate to be animated.
160       * @param startImmediately  one of AnimatorThread.START_IMMEDIATELY
161       *                          or AnimatorThread.DONT_START_YET
162       *
163       * @see Animate
164       * @see #AnimatorThread( Animate )
165       * @see #start()
166       * @see #setSleepMinInterval(long)
167       * @see #setSleepRange( long )
168       */
169      public AnimatorThread ( Animate a, boolean startImmediately )
170      {
171        this( a, startImmediately, 
172              AnimatorThread.DEFAULT_SLEEP_MIN_INTERVAL,
173              AnimatorThread.DEFAULT_SLEEP_RANGE );
174      }
175      
176      /**
177       * This constructor requires the Animate that is to be animated and
178       * a long representing the desired variance in sleep times between
179       * calls to the Animate's act() method.  This constructor requires
180       * an additional call to the AnimatorThread's start() method.
181       *
182       * The sleep time between calls to the Animate's act() will vary
183       * between this.sleepMinInterval and this.sleepMinInterval +
184       * sleepRange.
185       *
186       * @param a           the Animate to be animated.
187       * @param sleepRange  the desired variance in sleep times above and 
188       *                    beyond sleepMinInterval 
189       *
190       * @see Animate
191       * @see #AnimatorThread( Animate )
192       * @see #start()
193       * @see #setSleepMinInterval(long)
194       * @see #setSleepRange( long )
195       */
196      public AnimatorThread ( Animate a, long sleepRange )
197      {
198        this( a, AnimatorThread.DONT_START_YET, sleepRange );
199      }
200      
201      /**
202       * This constructor requires the Animate that is to be animated, a
203       * boolean reflecting whether execution should begin immediately,
204       * and a long representing the desired variance in sleep times
205       * between calls to the Animate's act() method.
206       *
207       * @param a                 the Animate to be animated.
208       * @param startImmediately  one of AnimatorThread.START_IMMEDIATELY
209       *                          or AnimatorThread.DONT_START_YET
210       * @param sleepRange        the desired variance in sleep times above and 
211       *                          beyond sleepMinInterval 
212       *
213       * @see Animate
214       * @see #AnimatorThread( Animate, boolean )
215       * @see #AnimatorThread( Animate, long )
216       * @see #start()
217       * @see #setSleepMinInterval(long)
218       * @see #setSleepRange( long )
219       */
220      public AnimatorThread ( Animate a, 
221                              boolean startImmediately, 
222                              long sleepRange )
223      {
224        this( a, startImmediately, sleepRange, 
225              AnimatorThread.DEFAULT_SLEEP_RANGE );
226      }
227      
228      /**
229       * This constructor requires the Animate that is to be animated and
230       * two longs representing the desired variance in sleep times and
231       * the desired minimum sleep interval between calls to the Animate's
232       * act() method.  This constructor requires an additional call to
233       * the AnimatorThread's start() method.
234       *
235       * The sleep time between calls to the Animate's act() will vary
236       * between sleepMinInterval and sleepMinInterval + sleepRange.
237       *
238       * @param a                 the Animate to be animated.
239       * @param sleepRange        the desired variance in sleep times above and 
240       *                          beyond sleepMinInterval 
241       * @param sleepMinInterval  the minimum interval between calls to 
242       *                          the Animate's act() method
243       *
244       * @see Animate
245       * @see #AnimatorThread( Animate )
246       * @see #start()
247       * @see #setSleepMinInterval(long)
248       * @see #setSleepRange( long )
249       */
250      public AnimatorThread ( Animate a, 
251                              long sleepRange,
252                              long sleepMinInterval )
253      {
254        this( a, AnimatorThread.DONT_START_YET, sleepRange, sleepMinInterval );
255      }
256      
257      /**
258       * This constructor requires the Animate that is to be animated, a
259       * boolean reflecting whether execution should begin immediately,
260       * and two longs representing the desired variance in sleep times
261       * and the desired minimum sleep interval between calls to the
262       * Animate's act() method.
263       *
264       * @param a                 the Animate to be animated.
265       * @param startImmediately  one of AnimatorThread.START_IMMEDIATELY
266       *                          or AnimatorThread.DONT_START_YET
267       * @param sleepRange        the desired variance in sleep times above and 
268       *                          beyond sleepMinInterval 
269       * @param sleepMinInterval  the minimum interval between calls to 
270       *                          the Animate's act() method
271       *
272       * @see Animate
273       * @see #AnimatorThread( Animate, boolean )
274       * @see #AnimatorThread( Animate, long, long )
275       * @see #start()
276       * @see #setSleepMinInterval(long)
277       * @see #setSleepRange( long )
278       */
279      public AnimatorThread ( Animate a, 
280                              boolean startImmediately, 
281                              long sleepRange,
282                              long sleepMinInterval )
283      {
284        super();
285        this.what = a;
286        if ( this.what == null )
287        {
288          throw new IllegalArgumentException("Cannot start an AnimatorThread "
289                                             + "without supplying an Animate!");
290        }
291        this.sleepRange = sleepRange;
292        this.sleepMinInterval = sleepMinInterval;
293        if ( startImmediately )
294          {
295            this.start();
296          }
297      }
298      
299      /**
300    
301       * Repeatedly invoke your Animate's act() method, sleeping between
302       * invocations.
303       *
304       * This method contains an amazing amount of hair to deal with
305       * suspension, resumption, and stopping of AnimatorThreads.  See <a
306       * href="http://java.sun.com/products/jdk/1.2/docs/guide/misc/threadPrimitiveDeprecation.html">
307       * theJavaSoft statement on Thread primitive deprication.  </a>
308       *
309       * @see Animate
310       * @see #setSleepMinInterval(long)
311       * @see #setSleepRange( long )
312       */
313      public void run() 
314      {
315      RUN_LOOP:
316        while ( ! this.isStopped ) 
317          {
318            if ( this.isSuspended )
319              {
320                synchronized ( this.suspendLock )
321                  {
322                    while ( this.isSuspended )
323                      {
324                        try
325                          {
326                            this.suspendLock.wait();
327                          }
328                        catch ( InterruptedException e )
329                          {
330                          }
331                        finally
332                          {
333                            if ( this.isStopped )
334                              {
335                                break RUN_LOOP;
336                              }
337                          }
338                      }
339                  } 
340              }
341            try
342              {
343                Thread.sleep( Math.round( Math.random() * this.sleepRange ) 
344                              + this.sleepMinInterval );
345                this.what.act();
346              }
347            catch ( InterruptedException e )
348              {
349              }
350          }
351      }
352    
353      /**
354       * Begin execution.  This causes the AnimatorThread to periodically
355       * call its Animate's act() method.  (Same as this.startExecution().)
356       *
357       * @see Animate
358       * @see #AnimatorThread(Animate)
359       * @see Thread#start()
360       */
361      public void start()
362      {
363        this.startExecution();
364      }
365    
366      /**
367       * Begin execution.  This causes the AnimatorThread to periodically
368       * call its Animate's act() method.
369       *
370       * @see Animate
371       * @see #AnimatorThread(Animate)
372       * @see Thread#start()
373       */
374      public void startExecution()
375      {
376        if (( ! this.isStopped ) && ( ! this.isAlive() ))
377          {
378            super.start();
379          }
380      }
381    
382      /**
383       * Terminates execution.  This causes the AnimatorThread to cease
384       * execution immediately.  Once stopped, an AnimatorThread cannot 
385       * be restarted. 
386       *
387       * Safely replaces Thread's (deprecated) stop() method.  
388       *
389       * @see Thread#stop()
390       */
391      public void stopExecution()
392      {
393        this.isStopped = true;      // Set state
394        this.interrupt();           // Wake if sleeping, waiting, etc.
395      }
396    
397      /**
398       * Temporarily suspends execution.  This causes the AnimatorThread
399       * to suspend execution following the next act() of its Animate.
400       * Periodic execution of the Animate's act() method can be restarted
401       * using resumeExecution().
402       *
403       * Safely replaces Thread's (deprecated) suspend() method.  
404       *
405       * @see #resumeExecution()
406       * @see Thread#suspend()
407       *
408       */
409      public void suspendExecution()
410      {
411        synchronized ( this.suspendLock ) // I suspect this
412          {                               // synchronization is
413            this.isSuspended = true;      // unnecessary.... 
414          }
415      }
416      
417      /**
418       * Resumes execution after a temporary suspension (using 
419       * suspendExecution()). 
420       *
421       * Safely replaces Thread's (deprecated) resume() method.  
422       *
423       * @see #suspendExecution()
424       * @see Thread#resume()
425       *
426       */
427      public void resumeExecution()
428      {
429        synchronized ( this.suspendLock )
430          {
431            this.isSuspended = false;
432            this.suspendLock.notify();
433          }
434      }
435    
436      /**
437       * Gives access to this AnimatorThread's sleep minimum.
438       * This controls the smallest interval for which this AnimatorThread
439       * will sleep, i.e., the minimum time between actions for the
440       * Animate that it animates.
441       *
442       * If sleepMinInterval is set to 0, it is possible that this
443       * AnimatorThread will prevent execution by other Threads by fully
444       * occupying the CPU.
445       *
446       * @see #AnimatorThread( Animate, boolean, long, long )
447       * @see #setSleepRange( long )
448       */
449      public void setSleepMinInterval( long minInterval )
450      {
451        this.sleepMinInterval = minInterval;
452      }
453    
454      /**
455       * This controls the possible range of durations for AnimatorThread
456       * to sleep, i.e., the possible time between actions for the Animate
457       * that it animates.  In no case will the minimum sleep time be less
458       * than this.sleepMinInterval; it could be as long as
459       * this.sleepMinInterval + this.sleepRange.
460       *
461       * Gives access to this AnimatorThread's sleep variance.  If 0, this
462       * AnimatorThread will sleep for the same amount of time between each
463       * invocation of the Animate's act() method.
464       *
465       * @see #AnimatorThread( Animate, boolean, long, long )
466       * @see #setSleepRange( long )
467       */
468      public void setSleepRange( long range )
469      {
470        this.sleepRange = range;
471      }
472    
473    }
474    
475    /*
476     * $Log: AnimatorThread.java,v $
477     * Revision 1.5  2003/09/23 15:31:11  gus
478     * more javadoc fixes
479     *
480     * Revision 1.4  2003/09/23 15:02:58  gus
481     * Lots of javadoc fixes
482     *
483     * Revision 1.3  2003/09/23 14:49:18  gus
484     * javadoc fix
485     *
486     * Revision 1.2  2002/11/25 16:16:43  gus
487     * fixing javadoc errors
488     *
489     * Revision 1.1.1.1  2002/06/05 21:56:32  root
490     * CS101 comes to Olin finally.
491     *
492     * Revision 1.4  1999/08/16 16:57:02  jsmthng
493     * Updated to make JavaDoc happy.
494     *
495     * Revision 1.3  1999/07/19 21:32:44  jsmthng
496     * Fix previous version, which had included a "this.what = a" in all
497     * constructors in order to placate Linux-Java, but which was confusing
498     * Win-NT Java2.
499     *
500     * Revision 1.2  1999/06/18 23:12:47  las
501     * Fixed embarrassingly idiotic bug in constructor (null test prior to
502     * assignment).
503     *
504     * Revision 1.1  1999/06/18 21:11:16  las
505     * Created cs101.lang package to house core additions such as Animate and
506     * AnimatorThread.  Added those two Java files (interface Animate and
507     * class AnimatorThread) to the package.  This will ultimately allow
508     * revision of all self-animating cs101 code.
509     *
510     */