'From Squeak3.10.2 of ''5 June 2008'' [latest update: #7179] on 23 March 2009 at 8:13:46 pm'!
"Change Set:		DelayCleanup
Date:			23 March 2009
Author:			Andreas Raab

Cleans up Delay by removing many of the no longer used methods (timerInterruptWatcher etc). It also provides some fixes for methods that got mysteriously broken even though their original versions were perfectly fine, including Delay class>>timeoutSemaphore:after:.

DO NOT REMOVE THE PREAMBLE - we need to make sure that Delay is not running the 'right' version of the code *before* removing the old methods."

Delay startTimerEventLoop.
!

Object subclass: #Delay
	instanceVariableNames: 'delayDuration resumptionTime delaySemaphore beingWaitedOn'
	classVariableNames: 'AccessProtect ActiveDelay ActiveDelayStartTime DelaySuspended FinishedDelay RunTimerEventLoop ScheduledDelay SuspendedDelays TimerEventLoop TimingSemaphore'
	poolDictionaries: ''
	category: 'Kernel-Processes'!

!Delay methodsFor: 'private' stamp: 'jm 9/11/97 14:49'!
adjustResumptionTimeOldBase: oldBaseTime newBase: newBaseTime
	"Private!! Adjust the value of the system's millisecond clock at which this Delay will be awoken. Used to adjust resumption times after a snapshot or clock roll-over."

	resumptionTime := newBaseTime + (resumptionTime - oldBaseTime).
! !

!Delay methodsFor: 'private' stamp: 'ar 3/2/2009 14:41'!
schedule
	"Schedule this delay"
	resumptionTime := Time millisecondClockValue + delayDuration.
	AccessProtect critical:[
		ScheduledDelay := self.
		TimingSemaphore signal.
	].! !

!Delay methodsFor: 'private' stamp: 'jm 9/12/97 08:56'!
setDelay: millisecondCount forSemaphore: aSemaphore
	"Private!! Initialize this delay to signal the given semaphore after the given number of milliseconds."

	delayDuration := millisecondCount.
	delaySemaphore := aSemaphore.
	beingWaitedOn := false.
! !

!Delay methodsFor: 'private' stamp: 'jm 9/11/97 11:54'!
signalWaitingProcess
	"The delay time has elapsed; signal the waiting process."

	beingWaitedOn := false.
	delaySemaphore signal.
! !

!Delay methodsFor: 'private' stamp: 'ar 3/2/2009 14:42'!
unschedule
	AccessProtect critical:[
		FinishedDelay := self.
		TimingSemaphore signal.
	].! !

!Delay methodsFor: 'printing' stamp: 'ar 7/10/2007 22:12'!
printOn: aStream
	super printOn: aStream.
	aStream nextPutAll: '('; print: delayDuration; nextPutAll: ' msecs'.
	beingWaitedOn ifTrue:[
		aStream nextPutAll: '; '; print: resumptionTime - Time millisecondClockValue; nextPutAll: ' msecs remaining'.
	].
	aStream nextPutAll: ')'.! !


!Delay class methodsFor: 'instance creation' stamp: 'dtl 12/11/2004 11:59'!
forMilliseconds: anInteger
	"Return a new Delay for the given number of milliseconds. Sending 'wait' to this Delay will cause the sender's process to be suspended for approximately that length of time."

	anInteger < 0 ifTrue: [self error: 'delay times cannot be negative'].
	^ self new
		setDelay: anInteger asInteger
		forSemaphore: Semaphore new
! !

!Delay class methodsFor: 'instance creation' stamp: 'di 6/16/1999 23:04'!
forSeconds: aNumber
	"Return a new Delay for the given number of seconds. Sending 'wait' to this Delay will cause the sender's process to be suspended for approximately that length of time."

	aNumber < 0 ifTrue: [self error: 'delay times cannot be negative'].
	^ self new
		setDelay: (aNumber * 1000) asInteger
		forSemaphore: Semaphore new
! !

!Delay class methodsFor: 'instance creation' stamp: 'jm 9/15/97 17:10'!
timeoutSemaphore: aSemaphore afterMSecs: anInteger
	"Create and schedule a Delay to signal the given semaphore when the given number of milliseconds has elapsed. Return the scheduled Delay. The timeout can be cancelled by sending 'unschedule' to this Delay."
	"Details: This mechanism is used to provide a timeout when waiting for an external event, such as arrival of data over a network connection, to signal a semaphore. The timeout ensures that the semaphore will be signalled within a reasonable period of time even if the event fails to occur. Typically, the waiting process cancels the timeout request when awoken, then determines if the awaited event has actually occurred."

	anInteger < 0 ifTrue: [self error: 'delay times cannot be negative'].
	^ (self new setDelay: anInteger forSemaphore: aSemaphore) schedule
! !

!Delay class methodsFor: 'primitives' stamp: 'ar 3/2/2009 14:43'!
primSignal: aSemaphore atMilliseconds: aSmallInteger
	"Signal the semaphore when the millisecond clock reaches the value of the second argument. Fail if the first argument is neither a Semaphore nor nil. Essential. See Object documentation whatIsAPrimitive."
	<primitive: 136>
	^self primitiveFailed! !

!Delay class methodsFor: 'snapshotting' stamp: 'ar 3/2/2009 14:44'!
restoreResumptionTimes
	"Private!! Restore the resumption times of all scheduled Delays after a snapshot or clock roll-over. This method should be called only while the AccessProtect semaphore is held."

	| newBaseTime |
	newBaseTime := Time millisecondClockValue.
	SuspendedDelays do: [:d | d adjustResumptionTimeOldBase: 0 newBase: newBaseTime].
	ActiveDelay == nil ifFalse: [
		ActiveDelay adjustResumptionTimeOldBase: 0 newBase: newBaseTime.
	].
! !

!Delay class methodsFor: 'snapshotting' stamp: 'jm 9/11/97 15:15'!
saveResumptionTimes
	"Private!! Record the resumption times of all Delays relative to a base time of zero. This is done prior to snapshotting or adjusting the resumption times after a clock roll-over. This method should be called only while the AccessProtect semaphore is held."

	| oldBaseTime |
	oldBaseTime := Time millisecondClockValue.
	ActiveDelay == nil
		ifFalse: [
			oldBaseTime < ActiveDelayStartTime
				ifTrue: [oldBaseTime := ActiveDelayStartTime].  "clock rolled over"
			ActiveDelay adjustResumptionTimeOldBase: oldBaseTime newBase: 0].
	SuspendedDelays do:
		[:d | d adjustResumptionTimeOldBase: oldBaseTime newBase: 0].
! !

!Delay class methodsFor: 'snapshotting' stamp: 'ar 9/30/2007 12:46'!
shutDown
	"Suspend the active delay, if any, before snapshotting. It will be reactived when the snapshot is resumed."
	"Details: This prevents a timer interrupt from waking up the active delay in the midst snapshoting, since the active delay will be restarted when resuming the snapshot and we don't want to process the delay twice."

	AccessProtect wait.
	self primSignal: nil atMilliseconds: 0.
	self saveResumptionTimes.
	DelaySuspended := true.! !

!Delay class methodsFor: 'snapshotting' stamp: 'ar 3/2/2009 14:44'!
startUp
	"Restart active delay, if any, when resuming a snapshot."

	DelaySuspended ifFalse:[^self error: 'Trying to activate Delay twice'].
	DelaySuspended := false.
	self restoreResumptionTimes.
	AccessProtect signal.
! !

!Delay class methodsFor: 'timer process' stamp: 'ar 3/2/2009 14:40'!
runTimerEventLoop
	"Run the timer event loop."
	[RunTimerEventLoop] whileTrue: [self handleTimerEvent]! !

!Delay class methodsFor: 'timer process' stamp: 'ar 3/2/2009 14:40'!
startTimerEventLoop
	"Start the timer event loop"
	"Delay startTimerEventLoop"
	self stopTimerEventLoop.
	AccessProtect := Semaphore forMutualExclusion.
	ActiveDelayStartTime := Time millisecondClockValue.
	SuspendedDelays := 
		Heap withAll: (SuspendedDelays ifNil:[#()])
			sortBlock: [:d1 :d2 | d1 resumptionTime <= d2 resumptionTime].
	TimingSemaphore := Semaphore new.
	RunTimerEventLoop := true.
	TimerEventLoop := [self runTimerEventLoop] newProcess.
	TimerEventLoop priority: Processor timingPriority.
	TimerEventLoop resume.
	TimingSemaphore signal. "get going"
! !

!Delay class methodsFor: 'class initialization' stamp: 'bgf 2/25/2009 18:36'!
initialize
	"Delay initialize."
	self startTimerEventLoop.! !

Delay class removeSelector: #guardianDelay!
Delay initialize!
Delay class removeSelector: #startTimerInterruptWatcher!
Delay class removeSelector: #stopTimerInterruptWatcher!
Delay class removeSelector: #timerInterruptWatcher!
Delay removeSelector: #activate!
Delay removeSelector: #beGuardianDelay!
Delay removeSelector: #scheduleEvent!
Delay removeSelector: #unscheduleEvent!
