[comp.lang.smalltalk] Smalltalk/V for PM V1.0 bug fixes & enhancements Part 6

cowan@marob.masa.com (John Cowan) (07/27/90)

"

Purpose of GENERATR.ST:

Icon-style generators.  A Generator is essentially a
resumable subroutine.  The implementation here uses a
Process to support the generator and a pair of Semaphores
to enforce synchronous call and return.

"

Object subclass: #Generator
  instanceVariableNames: 
    'value process callSemaphore returnSemaphore safe failed '
  classVariableNames: ''
  poolDictionaries: ''  !


!Generator class methods !
   
from: aTwoArgumentBlock
        "Answer a new instance of Generator which runs the
        code in aTwoArgumentBlock.  The first argument
        will be the Generator, and the second argument will
        be the object passed by the value: method."
    ^super new initialize: aTwoArgumentBlock! !



!Generator methods !
  
all
        "Answer an OrderedCollection containing all of the
        results returned by the receiver."
    | answer |
    answer := OrderedCollection new.
    self do: [:object | answer add: object].
    ^answer!
   
do: aBlock
        "Invoke the receiver repeatedly, and evaluate aBlock
        for each result returned, passing the result to aBlock."
    | result |
    [result := self value.
    self hasReturned] whileFalse:
        [aBlock value: result].!
 
fail
        "Terminate the receiver, returning nil to the caller
        and setting the failure flag."
    failed := true.
    self return.!
  
hasFailed
        "Answer true if receiver has failed."
    ^failed!
  
hasReturned
        "Answer true if receiver has returned."
    ^process isNil!
   
initialize: aTwoArgumentBlock
        "Private - Initialize instance variables."
    callSemaphore := Semaphore new.
    failed := false.
    returnSemaphore := Semaphore new.
    value := nil.
    Processor fork:
        [process := CurrentProcess.
        self wait.
        aTwoArgumentBlock value: self value: value.
        self fail.]!
 
mustBeSafe
        "Private - Signal an error if receiver is not safe."
    safe ifFalse:
        [^self error: 'Cannot call a running or failed generator']!

mustBeSelfProcess
        "Private - Signal an error if current process is not the
        generator process."
    CurrentProcess == process ifFalse:
        [^self error: 'Cannot invoke generator from outside generator process']!
  
passBack: anObject
        "Private - Pass back anObject to caller and signal him."
    self mustBeSelfProcess.
    value := anObject.
    callSemaphore signal.!
   
printOn: aStream
        "Display the receiver's name to aStream."
    aStream nextPutAll: 'Generator:'.
    self hash printOn: aStream.!

return
        "Terminate the receiver and return nil to the caller."
    self return: nil!
   
return: anObject
        "Terminate the receiver and return anObject to the caller."
    self passBack: anObject.
    self shutdown.!

shutdown
        "Private - Terminate receiver's process."
    self mustBeSelfProcess.
    process := nil.
    returnSemaphore := nil.
    callSemaphore signal.
    Processor schedule.!
 
suspend
        "Suspend the receiver, returning nil to the caller."
    ^self suspend: nil.!
 
suspend: anObject
        "Suspend the receiver, returning anObject to the caller."
    self passBack: anObject.
    self wait.
    ^value!
 
value
        "Invoke or resume the receiver, passing nil."
    ^self value: nil!
 
value: anObject
        "Invoke or resume the receiver, passing anObject."
    self mustBeSafe.
    value := anObject.
    returnSemaphore signal.
    callSemaphore wait.
    ^value!

wait
        "Private - Wait for caller to resume receiver."
    self mustBeSelfProcess.
    safe := true.
    returnSemaphore wait.
    safe := false.! !
-- 
cowan@marob.masa.com			(aka ...!hombre!marob!cowan)
			e'osai ko sarji la lojban