!=========================================================================
! Copyright (C) VMware, Inc. 1986-2011.  All Rights Reserved.
!
! $Id: processorscheduler.gs,v 1.14 2008-01-25 22:05:09 bretlb Exp $
!
! Superclass Hierarchy:
!   ProcessorScheduler, Object.
!
!=========================================================================

! Forward references needed
run
Globals at: #Semaphore ifAbsent: [Globals at: #Semaphore put: nil].
Globals at: #Delay ifAbsent: [Globals at: #Delay put: nil].
Globals at: #SharedQueue ifAbsent: [Globals at: #SharedQueue put: nil].
true
%

expectvalue %String
run
^ Object _newKernelSubclass: #ProcessorScheduler
  instVarNames: #(#readyQueue #activeProcess #lastGciProcess
                  #delayQueue #lastTimeMs #readDict #writeDict
                  #suspendedDict #waitingSet #lastGroup #errorEventQueue)
  classVars: #()
  classInstVars: #()
  poolDictionaries: #[]
  inDictionary: Globals
  constraints: #[
	#[#readyQueue, Object],
	#[#activeProcess, GsProcess],
	#[#lastGciProcess, GsProcess],
	#[#delayQueue, SortedCollection],
	#[#lastTimeMs, SmallInteger],      "lastTimeMs is obsolete, no longer used"
	#[#readDict, IdentityKeyValueDictionary],
	#[#writeDict, IdentityKeyValueDictionary],
	#[#suspendedDict, IdentityKeyValueDictionary],
	#[#waitingSet, IdentitySet],
	#[#lastGroup, Integer],
	#[#errorEventQueue, SharedQueue]
	]
  instancesInvariant: false
  isModifiable: false
  reservedOop: 911

%

removeallmethods ProcessorScheduler
removeallclassmethods ProcessorScheduler

category: 'For Documentation Installation only'
classmethod
installDocumentation
| doc txt |
doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'ProcessorScheduler implements a green-threads scheduler for instances of
 GsProcess.  The processes all share one native thread in the virtual
 machine.  There is exactly one instance of ProcessorScheduler in a session;
 this instance is initialized by the virtual machine and may not be 
 committed to disk. ' .

doc documentClassWith: txt.

self description: doc.
%


category: 'Instance Creation'
classmethod: ProcessorScheduler
new
  self shouldNotImplement: #new
%
category: 'Private'
classmethod: ProcessorScheduler
_isInitialized
  ^((System _sessionStateAt: 15) ~~ nil)
%
category: 'Accessing'
classmethod: ProcessorScheduler
scheduler
  "This is equivalent to 'Processor' in the Blue Book.  It returns
   the instance of the receiver that is responsible for scheduling
   ready processes"
  | s |
  ((s := System _sessionStateAt: 15) == nil)
    ifFalse: [ ^s ] 
    ifTrue: [ ^self _halt: 'Attempt to use process scheduler before it has been initialized' ]
%
category: 'Logging'
method: ProcessorScheduler
dbglog: printBlock
  "log a debugging string"
  "GsFile stdout nextPutAll: printBlock value; lf; flush"
%
category: 'Logging'
method: ProcessorScheduler
dbgfatallog: printBlock
  "log a fatal error string"
  GsFile stdout nextPutAll: printBlock value; lf; flush
%
category: 'Priorities'
method: ProcessorScheduler
highestPriority
  "return the highest allowable priority"
  ^40
%
category: 'Priorities'
method: ProcessorScheduler
timingPriority
  "Answer the priority at which the system processes
   keeping track of real time should run. [Blue Book]"
  ^35
%
category: 'Priorities'
method: ProcessorScheduler
highIOPriority
  "Answer the priority at which the most time
   critical input/output processes should run. [Blue Book]"
  ^30
%
category: 'Priorities'
method: ProcessorScheduler
lowIOPriority
  "Answer the priority at which most input/output
   processes should run. [Blue Book]"
  ^25
%
category: 'Priorities'
method: ProcessorScheduler
userInterruptPriority
  "Answer the priority at which processes created
   by the user and desiring immediate service
   should run. [Blue Book]"
  ^20
%
category: 'Priorities'
method: ProcessorScheduler
userSchedulingPriority
  "Answer the priority at which the user inter-
   face processes should run. [Blue Book]"
  ^15
%
category: 'Priorities'
method: ProcessorScheduler
userBackgroundPriority
  "Answer the priority at which background
   processes created by the user should run. [Blue Book]"
  ^10
%
category: 'Priorities'
method: ProcessorScheduler
systemBackgroundPriority
  "Answer the priority at which system background
   processes should run. [Blue Book]"
  ^5
%
category: 'Priorities'
method: ProcessorScheduler
lowestPriority
  "return the lowest allowable priority"
  ^1
%
category: 'Delay Access'
method: ProcessorScheduler
_delaySchedule: aDelay
  "Schedule aDelay in the queue."

  (aDelay ~~ nil) ifTrue: [delayQueue add: aDelay.].
%
category: 'Delay Access'
method: ProcessorScheduler
_now
  "Returns the time that should be used by a Delay to represent to
   current time in milliseconds."

<primitive: 651>
^ self _primitiveFailed: #_now
%

category: 'Delay Access'
method: ProcessorScheduler
_delayUnschedule: aDelay
  "Unschedule aDelay in the queue."

  delayQueue removeIdentical: aDelay ifAbsent: [].
%
category: 'Initialization'
method: ProcessorScheduler
_initialize

  "This method is invoked once by the VM during session initialization.
   The single instances of ProcessorScheduler and GsCurrentSession
   are created by the VM before invoking this method.  

   In the following discussion, the single instance of GsCurrentSession
   is 'currSess' . 

   Before invoking this method, the VM has completed these steps :
     1) default timeZone is fetched from (Globals at:#TimeZone) and
     installed in session state.   This wil be either the
     value of the class instance variable 'default' in TimeZone ,
     or the class variable #Default in the ObsoleteTimeZone class
     (if the 2007 upgrade to TimeZone has not been installed). 

     2) current UserProfile's symbolList is searched for #NativeLanguage
     and the result is stored in currSess.nativeLanguage .
 
   Initializes the receiver's process queues.  

   If the environment variable GS_DISABLE_CHARACTER_TABLE_LOAD is not defined,
   loads the extended character set tables.

   Invokes GsCurrentSession >> initialize  to execute any application
   defined initialization.
   "
  | noCTload |

  readyQueue := SortedCollection sortBlock: [:p1:p2 | p1 priority >= p2 priority].
  activeProcess := nil.
  delayQueue := SortedCollection sortBlock: [:d1:d2 | d1 _signalTime <= d2 _signalTime].
  lastTimeMs := nil .  "instvar no longer used"
  readDict := IdentityKeyValueDictionary new.
  writeDict := IdentityKeyValueDictionary new.
  suspendedDict := IdentityKeyValueDictionary new.
  waitingSet := IdentitySet new.
  lastGroup := 0.

  System _sessionStateAt: 15 put: self.

  noCTload := System gemEnvironmentVariable:'GS_DISABLE_CHARACTER_TABLE_LOAD' .
  noCTload == nil ifTrue:[
    Character _loadCharTables.
  ].

  GsCurrentSession currentSession initialize
%

category: 'Private'
method: ProcessorScheduler
_errorEventQueue: aSharedQueue
  "Sets the shared queue that error events will be added to."
  errorEventQueue := aSharedQueue.
%

category: 'Private'
method: ProcessorScheduler
_errorEventQueue
  "Returns the current error event queue or nil."
  ^errorEventQueue
%

category: 'Private'
method: ProcessorScheduler
_raiseErrorEvent: aGsProcess cat: aCategory num: anInteger args: anArray

errorEventQueue nextPut: #[aGsProcess, aCategory, anInteger, anArray].
"Note that the activeProcess needs to go to the debug state."
self _runNextProcess.
%

category 'Private'
method: ProcessorScheduler
_newGroup
  lastGroup := lastGroup + 1.
  ^lastGroup
%

category 'Private'
method: ProcessorScheduler
_switchFrom: oldProcess to: newProcess
  "Suspend the currentProcess and activate the receiver"

<primitive: 492>
"Can't do failure checking because this code gets called when the
 suspended process is reactivated."
%

category: 'Private'
method: ProcessorScheduler
_runNextProcess
  "Used when a block thread exits to kick the scheduler so it will run
   another process."
  | newProc oldProc |

  oldProc := activeProcess .
  activeProcess := nil.
  newProc := self _findReadyProcess .
  newProc == nil ifTrue:[
    GsFile gciLogServer:'--- ProcessorScheduler deadlock, oldProcess was: ',
        oldProc printString .
    System signal: (ErrorSymbols at: #rtErrSchedulerDeadlocked)
    args: #[ self , #[ oldProc] ] signalDictionary: GemStoneError.
  ].
  self _switchFrom: nil to: newProc .
  "Should never return"

  self dbgfatallog: ['leaving _runNextProcess!!!!'].
  self _uncontinuableError
%

category: 'Private'
method: ProcessorScheduler
_reschedule
  "Find a process that is ready to run. Once found suspend originalOldProcess
   and activate the new process."
  | oldProcess newProc |

  oldProcess := activeProcess.

  newProc := self _findReadyProcess .
  newProc == nil ifTrue:[
    GsFile gciLogServer:'--- ProcessorScheduler deadlock, oldProcess was: ', 
	oldProcess printString .
    System signal: (ErrorSymbols at: #rtErrSchedulerDeadlocked)
    args: #[ self , #[ oldProcess] ] signalDictionary: GemStoneError.
  ].
  self _switchFrom: oldProcess to: newProc .

  "We only get here once the old process is once again active."
  activeProcess := oldProcess.
%

! _reportDeadlock not currently used
category: 'Private'
method: ProcessorScheduler
_reportDeadlock: anArray

"Invoked from the interpreter to print stacks of processes which
 may be contributing to a deadlock before the intepreter reports 
 error 2366.  Stacks are printed to the topaz -l output or to the
 gem log file for a remote session. "

| names |
names := #[ 'prim492.oldProcess', 'prim492.newProcess', 'iS.lastGci', 
		'iS.procToContinue' ].
GsFile gciLogServer:'---- ProcessorScheduler _reportDeadlock' .
1 to: anArray size do:[:k| | aName aProc |
  aProc := anArray at: k .
  k <= names size ifTrue:[ aName := names at: k ] ifFalse:[ aName := 'extra'].
  GsFile gciLogServer: '---- ', aName , ': ' , aProc printString .
].
GsFile gciLogServer:'---- end of _reportDeadlock '.
%
   
! deleted _rolloverPoint

category: 'Private'
method: ProcessorScheduler
_reapEvents: waitForOne
  "Reap any pending events that will cause a thread to be ready to run.
   If 'waitForOne' is true then this method will wait until at least
   one event has been reaped.

   Returns true if successful, false if deadlocked."
  
  | delays time idx sleep eventCount readyCount checkForEvents emptyArr |

  delays := delayQueue.
  "Set readyCount to zero so we will check at least once for ready events."
  checkForEvents := true.
  emptyArr := Array new .
  [ checkForEvents ] whileTrue: [ 

    time := self _now.

    (waitForOne) ifTrue: [
      readyCount := readyQueue size.
    ] ifFalse: [
      "We do this so that we will not loop"
      readyCount := 1.
    ].
    (delays size > 0) ifTrue: [
      "see if the current time is past the requested delay signal time"
      idx := delays findFirst: [ :e |
	(e _signalTime > time) ifTrue: [
	  true
	] ifFalse: [
	  "self dbglog: [ ('  activating ', e printString, ' time: ',
			  time asString, ', ', e _signalTime asString) ]."
	  e _activate.
	  readyCount := readyCount + 1.
	  false
	].
      ].
      (idx > 1) ifTrue: [
        delays removeFrom: 1 to: (idx - 1).
      ] ifFalse: [(idx == 0) ifTrue: [
        delays size: 0.
      ]].
    ].

    eventCount := delays size + readDict size + writeDict size.
    (eventCount ~~ 0) ifTrue: [
      | result |
      sleep := 0.
      (readyCount == 0) ifTrue: [
        "If nothing is ready we need to sleep for a while."
        (delays size > 0) ifTrue: [
          sleep := (delays first _signalTime) - time.
          (sleep < 0) ifTrue: [ sleep := 0 ].
        ] ifFalse:[
          "non-zero sleep to prevent running hot"
          sleep := 524288000 .
        ].
      ].
      emptyArr size: 0 .
      result := GsSocket _doPoll: emptyArr timeout: sleep.
      (result == nil) ifTrue: [
	self dbgfatallog: [ 'The poll call failed with: ',
			    GsSocket lastErrorString ].
      ] ifFalse: [ (result == true) ifFalse: [
	"result contains an array of GsSockets with new events"
        1 to: result size do:[:k | | sock |
          sock := result at: k .
	  (sock _isReadableOrException) ifTrue: [
	    readDict _reapEventFor: sock.
	  ].
	  (sock _isWritableOrException) ifTrue: [
	    writeDict _reapEventFor: sock.
	  ].
	].
      ] ].
    ] ifFalse: [
      (waitForOne) ifTrue: [
	(readyQueue size == 0) ifTrue: [
	  "We will wait forever! , caller needs to raise a deadlock error."
          (activeProcess ~~ nil) ifTrue: [
            "Before raising the error need to remove the active from
             whatever it is waiting on."
            GsFile gciLogServer:'---about to deadlock, active process was: ',
	      activeProcess printString .
            activeProcess _unscheduleProcess.
          ].
          ^ false .  "would deadlock"
        ].
      ].
    ].
    (waitForOne) ifTrue: [
      checkForEvents := (readyQueue size == 0).
    ] ifFalse: [
      "We do this so that we will not loop"
      checkForEvents := false.
    ].
  ].
  ^ true "success"
%
category: 'Private'
method: ProcessorScheduler
_findReadyProcess
  "Returns the next process that should be run, or nil if scheduler is
   deadlocked ."

  "NOTE: the method is called from intloopsup.c"
  | proc |
  ( self _reapEvents: true ) ifFalse:[
    ^ nil .
  ].
  proc := readyQueue removeFirst.
  proc _location: nil.
  ^ proc
%
category: 'Private'
method: ProcessorScheduler
_waitUntilMilliseconds: millisecondValue
  "Causes the current thread to suspend until the millisecond clock
   reaches the value millisecondValue.
   Returns true if the timeout expired. Returns false if the thread
   woke up early for some reason."

  millisecondValue _validateClass: Integer.
  millisecondValue < 0 ifTrue:[
    ^ millisecondValue _error: #rtErrArgNotPositive 
  ].

  (self activeProcess) _signalTime: millisecondValue.
  self _delaySchedule: activeProcess.

  self _reschedule.

  "we have woken up."
  (activeProcess _signalTime ~~ nil) ifTrue: [
    "Somehow we woke up without the delay having been activated.
     So clean it up."
    self _delayUnschedule: activeProcess.
    activeProcess _signalTime: nil.
    ^false
  ] ifFalse: [
    ^true
  ].
%

category: 'Private'
method: ProcessorScheduler
_waitUntil: aGsSocket timeout: msToWait type: dict
  "Causes the current thread to suspend until aGsSocket is read/writable,
   has an error, or the specified timeout occurs.
   If msToWait is -1 then this method will not timeout.
   Returns true if the socket is ready otherwise returns false."

  dict _when: aGsSocket signal: (self activeProcess).

  (msToWait > 0) ifTrue: [
    activeProcess _signalTime: (self _now) + msToWait.
    self _delaySchedule: activeProcess.
  ].

  self _reschedule.

  "we have woken up. Figure out if timeout or socket ready"
  (activeProcess _signalTime ~~ nil) ifTrue: [
    self _delayUnschedule: activeProcess.
    activeProcess _signalTime: nil.
    ^true
  ] ifFalse: [
    ^(dict _cancelWhen: aGsSocket signal: activeProcess)
  ].
%
category: 'Private'
method: ProcessorScheduler
_isWhenReadableSet: aGsSocket signal: objToNotify
  "Returns true if read event is still enabled.
   Returns false if read event was never set or has happened and been
   automatically unset."
  ^readDict _isWhenSet: aGsSocket signal: objToNotify.
%
category: 'Private'
method: ProcessorScheduler
_isWhenWritableSet: aGsSocket signal: objToNotify
  "Returns true if write envent is still enabled.
   Returns false if write event was never set or has happened and been
   automatically unset."
  ^writeDict _isWhenSet: aGsSocket signal: objToNotify
%

category: 'Event Scheduling'
method: ProcessorScheduler
whenReadable: aGsSocket signal: objToNotify
  "Causes the receiver to signal objToNotify once when aGsSocket is 
   readable or has an exceptional condition.
   Once the receiver signals the object for this event the event is
   canceled so you need to call this method again to reenable the event.
   objToNotify is signalled by sending it the message '_reapSignal: source'
   with source being the socket that is readable.
   The kernel classes that respond to _reapSignal: are:
     Semaphore, SharedQueue, and GsProcess.
   Returns self."

  aGsSocket _enableRead.
  readDict _when: aGsSocket signal: objToNotify
%
category: 'Event Scheduling'
method: ProcessorScheduler
whenWritable: aGsSocket signal: objToNotify
  "Causes the receiver to signal objToNotify once when aGsSocket is 
   writable or has an exceptional condition.
   Once the receiver signals the object for this event the event is
   canceled so you need to call this method again to reenable the event.
   objToNotify is signalled by sending it the message '_reapSignal: source'
   with source being the socket that is writable.
   The kernel classes that respond to _reapSignal: are:
     Semaphore, SharedQueue, and GsProcess.
   Returns self."

  aGsSocket _enableWrite.
  writeDict _when: aGsSocket signal: objToNotify
%
category: 'Private'
method: ProcessorScheduler
_waitUntilReadable: aGsSocket timeout: msToWait
  "Causes the current thread to suspend until aGsSocket is readable,
   has an error, or the specified millisecond timeout occurs.
   If msToWait is -1 then this method will not timeout.
   Returns true if the socket is readable otherwise returns false."

aGsSocket _enableRead.
^ self _waitUntil: aGsSocket timeout: msToWait type: readDict
%
category: 'Private'
method: ProcessorScheduler
_waitUntilWritable: aGsSocket timeout: msToWait
  "Causes the current thread to suspend until aGsSocket is writable,
   has an error, or the specified millisecond timeout occurs.
   If msToWait is -1 then this method will not timeout.
   Returns true if the socket is writable otherwise returns false."

aGsSocket _enableWrite.
^ self _waitUntil: aGsSocket timeout: msToWait type: writeDict
%
category: 'Event Scheduling'
method: ProcessorScheduler
cancelWhenReadable: aGsSocket signal: objToNotify
  "Cancels the event specified by aGsSocket and objToNotify.
   The event is created using the whenReadable:signal: method.
   Returns true if the event has already been cancelled, reaped, or does not exist.
   Returns false if the event is cancelled by this invocation."

^ readDict _cancelWhen: aGsSocket signal: objToNotify
%
category: 'Event Scheduling'
method: ProcessorScheduler
cancelWhenWritable: aGsSocket signal: objToNotify
  "Cancels the event specified by aGsSocket and objToNotify.
   The event is created using the whenWritable:signal: method.
   Returns true if the event has already been cancelled, reaped, or does not exist.
   Returns false if the event is cancelled by this invocation."

^ writeDict _cancelWhen: aGsSocket signal: objToNotify
%

category: 'Private'
method: ProcessorScheduler
_scheduleProcess: aProcess
  "add the given process to the correct ready-queue"

  waitingSet removeIdentical: aProcess ifAbsent: [].
  "Don't add the process to the ready queue twice"
  (aProcess _location ~~ readyQueue) ifTrue: [
    readyQueue add: aProcess.
    aProcess _location: readyQueue.
  ].
%
category: 'Process State Change'
method: ProcessorScheduler
yield
  "schedule a different active process, if any are
   available, and put the current process in the
   appropriate ready queue."

  self _reapEvents: false.
  (readyQueue size > 0) ifTrue: [
    (readyQueue first priority >= (self activePriority)) ifTrue: [
      self _scheduleProcess: (self activeProcess).
      self _reschedule.
    ].
  ].
%
category: 'Accessing'
method: ProcessorScheduler
activeProcess
  "Return the currently active process"

  (activeProcess == nil) ifTrue: [
     activeProcess := GsProcess _new.
     " uncomment for debugging"
     "GsFile gciLogServer:'new activeProcess: ',activeProcess asOop printString . "
  ].
  ^activeProcess
%

category: 'Private'
method: ProcessorScheduler
_suspendedDict
  ^suspendedDict
%
 
category: 'Private'
method: ProcessorScheduler
_unscheduleProcess: aGsProcess
  "remove the given process from any scheduler state it is in."

  waitingSet removeIdentical: aGsProcess ifAbsent: [].
%

category: 'Process Access'
method: ProcessorScheduler
_terminateScheduledProcess: aGsProcess
  "remove the given process from its queues it is in"

  (aGsProcess == activeProcess) ifTrue: [
    activeProcess := nil.
    System signal: 2368"#rtErrGsProcessTerminated" 
         args: #[ ] signalDictionary: GemStoneError.
  ] ifFalse: [
    aGsProcess _unscheduleProcess.
  ].
%
category: 'Private'
method: ProcessorScheduler
_waiting: proc
  "Add proc to the collection of waiting processes."

  waitingSet add: proc.
%

category: 'Private'
method: ProcessorScheduler
_suspendProcess: proc location: oldLocation
  "suspend the process and do not schedule it for further processing"

  (oldLocation ~~ suspendedDict) ifTrue: [
    suspendedDict at: proc put: oldLocation.
    proc _location: suspendedDict.
  ].
  (proc == activeProcess) ifTrue: [
    self _reschedule.
  ] ifFalse: [
    readyQueue removeIdentical: proc ifAbsent: [].
    waitingSet removeIdentical: proc ifAbsent: [].
  ].
%

category: 'Private'
method: ProcessorScheduler
_resumeProcess: proc
  "Put a suspended process back to the state it was in when it was
   suspended."

  | oldLocation |
  oldLocation := suspendedDict removeKey: proc ifAbsent: [
    "proc was not suspended so do nothing"
    ^self
  ].
  (oldLocation == nil) ifTrue: [
    (proc _signalTime ~~ nil) ifTrue: [
      "waiting for a timer"
      proc _location: oldLocation.
    ] ifFalse: [  
      "Ready to run"
      self _scheduleProcess: proc.
    ].
  ] ifFalse: [
    (oldLocation == readyQueue) ifTrue: [
      self _scheduleProcess: proc.
    ] ifFalse: [
      "Must have been waiting for something"
      proc _location: oldLocation.
      self _waiting: proc.
    ].
  ].
%

category: 'Private'
method: ProcessorScheduler
_statusString: proc
  "Returns a string that describes proc's status."

  | result location waiting |

  (proc == self activeProcess) ifTrue: [
     ^ 'active'.
  ].
  location := proc _location.
  (location == readyQueue) ifTrue: [
    ^ 'ready'.
  ].
  (location == suspendedDict) ifTrue: [
    ^ 'suspended'.
  ].

  waiting := false.
  result := String new.
  result addLast: 'waiting on '.
  (proc _signalTime ~~ nil) ifTrue: [
    waiting := true.
    result addLast: 'a timeout'.
    (location ~~ nil) ifTrue: [
      result addLast: ' and '.
    ].
  ].
  (location ~~ nil) ifTrue: [
    waiting := true.
    (location isKindOf: Semaphore) ifTrue: [
      result addLast: 'a Semaphore'.
    ] ifFalse: [
      (location isKindOf: Delay) ifTrue: [
        result addLast: 'a Delay'.
      ] ifFalse: [
	(location == readDict) ifTrue: [
	  result addLast: 'a socket read event'.
	] ifFalse: [
	  (location == writeDict) ifTrue: [
	    result addLast: 'a socket write event'.
	  ] ifFalse: [
	    result addLast: 'an unknown location'.
	  ].
        ].
      ].
    ].
  ].
  (waiting) ifFalse: [
    (proc _isTerminated) ifTrue: [
      result := 'terminated'.
    ] ifFalse: [
      "application must have it in the debug state"
      result := 'debug'.
    ].
  ].
  ^ result
%

category: 'Accessing'
method: ProcessorScheduler
activePriority
  "return the priority of the active process"
  ^(activeProcess == nil) ifTrue:  [ self userSchedulingPriority ]
                          ifFalse: [ activeProcess priority ]
%

category: 'Accessing'
method: ProcessorScheduler
allProcesses
  "Return all of the instances of GsProcess that are not terminated."

  | result |
  result := (self readyProcesses + self waitingProcesses +
             self suspendedProcesses).
  result add: self activeProcess.
  ^result
%
category: 'Accessing'
method: ProcessorScheduler
readyProcesses
  "Return all of the instances of GsProcess that are ready to run but
   are not active."

  ^ readyQueue asIdentitySet
%
category: 'Accessing'
method: ProcessorScheduler
waitingProcesses
  "Return all of the instances of GsProcess that are waiting for an event."

  | result |

  result := IdentitySet withAll: waitingSet.
  delayQueue do: [ :e | | t |
      t := e _targetProcess.
      (t ~~ nil) ifTrue: [result add: t]
  ].
  ^ result
%
category: 'Accessing'
method: ProcessorScheduler
suspendedProcesses
  "Return all of the instances of GsProcess that were suspended using
   the GsProcess>>suspend method
   or created with the ExecutableBlock>>newProcess method and not yet
   sent resume."

  ^ suspendedDict keys
%
