"======================================================================
|
|   Delay Method Definitions
|
|   $Revision: 1.7.5$
|   $Date: 2000/05/28 16:56:52$
|   $Author: pb$
|
 ======================================================================"


"======================================================================
|
| Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc.
| Written by Steve Byrne.
|
| This file is part of the GNU Smalltalk class library.
|
| The GNU Smalltalk class library is free software; you can redistribute it
| and/or modify it under the terms of the GNU Lesser General Public License
| as published by the Free Software Foundation; either version 2.1, or (at
| your option) any later version.
| 
| The GNU Smalltalk class library is distributed in the hope that it will be
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
| General Public License for more details.
| 
| You should have received a copy of the GNU Lesser General Public License
| along with the GNU Smalltalk class library; see the file COPYING.LESSER.
| If not, write to the Free Software Foundation, 59 Temple Place - Suite
| 330, Boston, MA 02111-1307, USA.  
|
 ======================================================================"


Object subclass: #Delay
       instanceVariableNames: 'resumptionTime isRelative'
       classVariableNames: 'Queue TimeoutSem MutexSem DelayProcess IdleProcess'
       poolDictionaries: ''
       category: 'Language-Processes'
!

Delay comment: 
'I am the ultimate agent for frustration in the world.  I cause things to wait 
(typically much more than is appropriate, but it is those losing operating
systems'' fault).  When a process sends one of my instances a wait message,
that process goes to sleep for the interval specified when the instance was
created.'
!
	   


!Delay class methodsFor: 'instance creation'!

forMilliseconds: millisecondCount
    "Answer a Delay waiting for millisecondCount milliseconds"
    ^self new init: millisecondCount isRelative: true
!

forSeconds: secondCount
    "Answer a Delay waiting for secondCount seconds"
    ^self forMilliseconds: secondCount * 1000
!

untilMilliseconds: millisecondCount
    "Answer a Delay waiting for millisecondCount milliseconds after midnight"
    ^self new init: millisecondCount isRelative: false
! !



!Delay class methodsFor: 'general inquiries'!

millisecondClockValue
    "Private - Answer the number of milliseconds since midnight"
    ^Time primMillisecondClock
! !



!Delay class methodsFor: 'initialization'!

initialize
    "Private - Initialize the receiver and the associated process"
    "'initalizing Delays' printNl."
    Queue := SortedCollection sortBlock:
	[ :a :b | (a key) <= (b key) ].

    MutexSem := Semaphore forMutualExclusion.
    TimeoutSem := Semaphore new.

    IdleProcess := [
	[ Processor idle; yield ] repeat
    ] newProcess.

    IdleProcess
	name: 'idle';
	priority: Processor systemBackgroundPriority.
! !


!Delay class methodsFor: 'private'!

startDelayLoop
    "Private - Start the processes for Delays"

    DelayProcess := [
        | empty |
	[ 
	    IdleProcess resume.
	    TimeoutSem wait.
	    IdleProcess suspend.
	    MutexSem critical: [
		Queue removeFirst value signal.
		empty := Queue isEmpty.
		empty ifFalse: [ self timeout: Queue first key ]
	    ].
	    empty
	] whileFalse
    ]   forkAt: Processor timingPriority.

    DelayProcess name: 'timeout'.
!

timeout: milliseconds
    "Private - Signal the TimeoutSem after the given number of milliseconds.
    Delays across midnight are gracefully handled."
    | resumeMillis |
    resumeMillis := milliseconds - Delay millisecondClockValue.
    resumeMillis <= 0 ifTrue: [ TimeoutSem signal. ^self ].

    resumeMillis := (resumeMillis \\ Time millisecondsPerDay) asInteger.
    Processor
	signal: TimeoutSem
	atMilliseconds: resumeMillis
! !



!Delay methodsFor: 'accessing'!

resumptionTime
    "Answer the time when a process waiting on a Delay will resume"
    isRelative
	ifTrue: [ ^Delay millisecondClockValue + resumptionTime ] 
	ifFalse: [ ^resumptionTime ] 
! !



!Delay methodsFor: 'process delay'!

wait
    "Wait until the amount of time represented by the instance of Delay
     elapses"
    | elt sem |
    IdleProcess isNil ifTrue: [ Delay initialize ].
    MutexSem critical: [
	Processor isTimeoutProgrammed
	    ifFalse: [
		DelayProcess isNil ifFalse: [ DelayProcess terminate ].
		Delay startDelayLoop ].

	sem := Semaphore new.
	elt := Association key: self resumptionTime value: sem.
	Queue add: elt.
	"If we've become the head of the list, we need to
	 alter the interrupt time"
	Queue first == elt ifTrue: [ Delay timeout: elt key ]
    ].
    sem wait
! !



!Delay methodsFor: 'comparing'!

= aDelay
    "Answer whether the receiver and aDelay denote the same delay"
    self class == aDelay class ifFalse: [ ^false ].

    ^(isRelative = aDelay isRelative)
	and: [ resumptionTime = aDelay basicResumptionTime ]
!

hash
    "Answer an hash value for the receiver"
    ^resumptionTime
! !



!Delay methodsFor: 'private'!

basicResumptionTime
    ^resumptionTime
!

init: milliseconds isRelative: aBoolean
    isRelative := aBoolean.
    resumptionTime := milliseconds
!

isRelative
    ^isRelative
! !
