(* Copyright (C) 1992, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* *) (* Last modified on Thu Dec 31 09:31:54 PST 1992 by mhb *) (* modified on Tue Aug 4 17:18:26 PDT 1992 by meehan *) (* modified on Tue Jun 16 12:55:10 PDT 1992 by muller *) (* modified on Fri Feb 28 00:14:11 1992 by steveg *) (* modified on Wed Mar 6 11:16:56 PST 1991 by brooks *) (* modified on Tue Feb 12 13:44:22 PST 1991 by chan *) (* modified on Mon Sep 24 12:08:26 PDT 1990 by mcjones *) <* PRAGMA LL *> MODULE AutoRepeat; IMPORT Time, Thread; (* This code needs an overhaul. It could be simplified to take advantage of two features of the current interface: 1) The repeat method can be called by the timer thread rather than from a thread forked by the timer thread. 2) The Start and Continue are no-ops if ar is already running. (The code was written when Start and Continue tried to change the timing of the existing timer thread.) *) TYPE Microseconds = CARDINAL; REVEAL T = Public BRANDED OBJECT firstWait: Microseconds; period : Microseconds; timer: Thread.T := NIL; (* auto-repeat timer thread *) repeatPeriod: Microseconds; (* period of repetition *) timerMutex : MUTEX; <* LL = timerMutex *> timerSet := FALSE; (* whether timer is active *) timerSeqNo: CARDINAL; (* seq no of current timer setting *) signalSeqNo: CARDINAL := 0; (* seq no of current timer event *) timerWait: Microseconds; (* current setting of timer *) repeatMutex: MUTEX; (* prevents new repeat events while one is in progress. *) OVERRIDES init := Init; apply := Timer END; REVEAL Private = Thread.Closure BRANDED OBJECT END; TYPE RepeatClosure = Thread.Closure OBJECT cl: T OVERRIDES apply := Repeater END; PROCEDURE Init (cl : T; firstWait := DefaultFirstWait; period := DefaultPeriod ): T = BEGIN cl.timerMutex := NEW (MUTEX); LOCK cl.timerMutex DO cl.firstWait := 1000 * firstWait; (* milli -> micro *) cl.period := 1000 * period; cl.repeatMutex := NEW (MUTEX); END; RETURN cl END Init; PROCEDURE Start (cl: T) = BEGIN LOCK cl.timerMutex DO IF cl.timerSet THEN RETURN END; cl.timerWait := cl.firstWait; cl.repeatPeriod := cl.period; ContinueWithTimerLocked(cl) END END Start; PROCEDURE Stop (cl: T) = BEGIN LOCK cl.timerMutex DO IF NOT cl.timerSet THEN RETURN END; cl.timerSet := FALSE; IF cl.timer # NIL THEN Thread.Alert(cl.timer) END END END Stop; PROCEDURE Continue (cl: T) = BEGIN LOCK cl.timerMutex DO IF cl.timerSet THEN RETURN END; ContinueWithTimerLocked(cl); END END Continue; PROCEDURE ContinueWithTimerLocked (cl: T) = BEGIN cl.timerSet := TRUE; IF cl.timer = NIL THEN cl.timerSeqNo := 1; cl.timer := Thread.Fork (cl) ELSE INC (cl.timerSeqNo); Thread.Alert (cl.timer) END END ContinueWithTimerLocked; PROCEDURE Timer (cl: T): REFANY = VAR seqNo, wait: CARDINAL; set : BOOLEAN; BEGIN LOOP TRY LOCK cl.timerMutex DO seqNo := cl.timerSeqNo; wait := cl.timerWait; set := cl.timerSet; END; IF set THEN Time.AlertPause (wait) ELSE Time.AlertLongPause (10); (* kill thread if unused for 10 sec. *) LOCK cl.timerMutex DO IF NOT cl.timerSet THEN cl.timer := NIL; EXIT END END END; LOCK cl.repeatMutex DO LOCK cl.timerMutex DO IF cl.timerSet AND cl.timerSeqNo = seqNo THEN cl.signalSeqNo := cl.timerSeqNo; EVAL Thread.Fork (NEW (RepeatClosure, cl := cl)) END END END EXCEPT | Thread.Alerted => (* good, the wait was broken *) END END; RETURN NIL END Timer; PROCEDURE Repeater (rcl: RepeatClosure): REFANY = <* LL = 0 *> VAR doit: BOOLEAN; cl : T := rcl.cl; BEGIN LOCK cl.timerMutex DO doit := cl.timerSet AND cl.signalSeqNo = cl.timerSeqNo; IF doit THEN IF cl.timerWait # cl.repeatPeriod THEN cl.timerWait := cl.repeatPeriod; IF cl.timer # NIL THEN Thread.Alert (cl.timer) END; (* changeover from initial wait to repetition period *) END END END; LOCK cl.repeatMutex DO IF doit THEN cl.repeat () END; RETURN NIL END END Repeater; BEGIN END AutoRepeat.