ProcSched.st
changeset 699 12f456343eea
parent 645 b9fe149c7ff1
child 750 f4ed622893ce
equal deleted inserted replaced
698:04533375e12c 699:12f456343eea
     9  other person.  No title to or ownership of the software is
     9  other person.  No title to or ownership of the software is
    10  hereby transferred.
    10  hereby transferred.
    11 "
    11 "
    12 
    12 
    13 Object subclass:#ProcessorScheduler
    13 Object subclass:#ProcessorScheduler
    14 	 instanceVariableNames:'quiescentProcessLists scheduler
    14 	 instanceVariableNames:'quiescentProcessLists scheduler zombie activeProcess
    15 				zombie
    15                 currentPriority readFdArray readSemaphoreArray readCheckArray
    16 				activeProcess currentPriority
    16                 writeFdArray writeSemaphoreArray timeoutArray timeoutActionArray
    17 				readFdArray readSemaphoreArray readCheckArray
    17                 timeoutProcessArray timeoutSemaphoreArray idleActions anyTimeouts
    18 				writeFdArray writeSemaphoreArray
    18                 dispatching interruptedProcess useIOInterrupts'
    19 				timeoutArray timeoutActionArray timeoutProcessArray timeoutSemaphoreArray
    19 	 classVariableNames:'KnownProcesses KnownProcessIds PureEventDriven
    20 				idleActions anyTimeouts dispatching interruptedProcess
    20                 UserSchedulingPriority UserInterruptPriority TimingPriority
    21 				useIOInterrupts'
    21                 HighestPriority SchedulingPriority MaxNumberOfProcesses'
    22 	 classVariableNames:'KnownProcesses KnownProcessIds
       
    23 			     PureEventDriven
       
    24 			     UserSchedulingPriority 
       
    25 			     UserInterruptPriority
       
    26 			     TimingPriority
       
    27 			     HighestPriority
       
    28 			     SchedulingPriority
       
    29 			     MaxNumberOfProcesses'
       
    30 	 poolDictionaries:''
    22 	 poolDictionaries:''
    31 	 category:'Kernel-Processes'
    23 	 category:'Kernel-Processes'
    32 !
    24 !
    33 
       
    34 Smalltalk at:#Processor put:nil!
       
    35 
    25 
    36 !ProcessorScheduler class methodsFor:'documentation'!
    26 !ProcessorScheduler class methodsFor:'documentation'!
    37 
    27 
    38 copyright
    28 copyright
    39 "
    29 "
    45  inclusion of the above copyright notice.   This software may not
    35  inclusion of the above copyright notice.   This software may not
    46  be provided or otherwise made available to, or used by, any
    36  be provided or otherwise made available to, or used by, any
    47  other person.  No title to or ownership of the software is
    37  other person.  No title to or ownership of the software is
    48  hereby transferred.
    38  hereby transferred.
    49 "
    39 "
    50 !
       
    51 
       
    52 version
       
    53     ^ '$Header: /cvs/stx/stx/libbasic/Attic/ProcSched.st,v 1.52 1995-11-24 19:19:45 cg Exp $'
       
    54 !
    40 !
    55 
    41 
    56 documentation
    42 documentation
    57 "
    43 "
    58     This class has only one instance, which is bound to the global
    44     This class has only one instance, which is bound to the global
   190 	    ]
   176 	    ]
   191 	]
   177 	]
   192     ]
   178     ]
   193 ! !
   179 ! !
   194 
   180 
   195 !ProcessorScheduler class methodsFor:'queries'!
       
   196 
       
   197 isPureEventDriven
       
   198     "this is temporary - (maybe not :-).
       
   199      you can run ST/X either with or without processes.
       
   200      Without, there is conceptionally a single process handling all
       
   201      outside events and timeouts. This has some negative implications
       
   202      (Debugger is ugly), but allows a fully portable ST/X without any
       
   203      assembler support - i.e. quick portability.
       
   204      The PureEvent flag will automatically be set if the runtime system
       
   205      does not support threads - otherwise, it can be set manually
       
   206      (from rc-file).
       
   207     "
       
   208 
       
   209     ^ PureEventDriven
       
   210 !
       
   211 
       
   212 pureEventDriven
       
   213     "turn on pure-event driven mode - no processes, single dispatch loop"
       
   214 
       
   215     PureEventDriven := true
       
   216 !
       
   217 
       
   218 processDriven
       
   219     "turn on process driven mode"
       
   220 
       
   221     PureEventDriven := false
       
   222 !
       
   223 
       
   224 knownProcesses
       
   225     "return a collection of all (living) processes in the system"
       
   226 
       
   227     ^ KnownProcesses select:[:p | p notNil]
       
   228 !
       
   229 
       
   230 maxNumberOfProcesses
       
   231     "return the limit on the number of processes;
       
   232      the default is nil (i.e. unlimited)."
       
   233 
       
   234     ^ MaxNumberOfProcesses
       
   235 !
       
   236 
       
   237 maxNumberOfProcesses:aNumber
       
   238     "set the limit on the number of processes.
       
   239      This helps if you have a program which (by error) creates countless
       
   240      subprocesses. Without this limit, you may have a hard time to find
       
   241      this error (and repairing it). If nil (the default), the number of
       
   242      processes is unlimited."
       
   243 
       
   244     MaxNumberOfProcesses := aNumber
       
   245 ! !
       
   246 
       
   247 !ProcessorScheduler class methodsFor:'primitive process primitives'!
   181 !ProcessorScheduler class methodsFor:'primitive process primitives'!
   248 
       
   249 threadsAvailable
       
   250     "return true, if the runtime system supports threads (i.e. processes);
       
   251      false otherwise."
       
   252 
       
   253 %{  /* NOCONTEXT */
       
   254     extern OBJ __threadsAvailable();
       
   255 
       
   256     RETURN (__threadsAvailable());
       
   257 %}
       
   258 !
       
   259 
       
   260 threadInterrupt:id
       
   261     "make the process evaluate an interrupt. This sets a flag in the VMs
       
   262      threadSwitcher, to let the process perform a #interrupt when its set to
       
   263      run the next time. The process itself can decide how to react on this 
       
   264      interrupt (currently, it looks for interruptBlocks to evaluate)."
       
   265 
       
   266 %{  /* NOCONTEXT */
       
   267 
       
   268     if (__isSmallInteger(id)) {
       
   269 	__threadInterrupt(_intVal(id));
       
   270     }
       
   271 %}
       
   272 !
       
   273 
   182 
   274 threadCreate:aProcess withId:id
   183 threadCreate:aProcess withId:id
   275     "physical creation of a process.
   184     "physical creation of a process.
   276      (warning: low level entry, no administration done).
   185      (warning: low level entry, no administration done).
   277      This may raise an exception, if a VM process could not be created."
   186      This may raise an exception, if a VM process could not be created."
   320 
   229 
   321     if (__isSmallInteger(id)) {
   230     if (__isSmallInteger(id)) {
   322 	__threadDestroy(_intVal(id));
   231 	__threadDestroy(_intVal(id));
   323     }
   232     }
   324 %}
   233 %}
       
   234 !
       
   235 
       
   236 threadInterrupt:id
       
   237     "make the process evaluate an interrupt. This sets a flag in the VMs
       
   238      threadSwitcher, to let the process perform a #interrupt when its set to
       
   239      run the next time. The process itself can decide how to react on this 
       
   240      interrupt (currently, it looks for interruptBlocks to evaluate)."
       
   241 
       
   242 %{  /* NOCONTEXT */
       
   243 
       
   244     if (__isSmallInteger(id)) {
       
   245 	__threadInterrupt(_intVal(id));
       
   246     }
       
   247 %}
       
   248 !
       
   249 
       
   250 threadsAvailable
       
   251     "return true, if the runtime system supports threads (i.e. processes);
       
   252      false otherwise."
       
   253 
       
   254 %{  /* NOCONTEXT */
       
   255     extern OBJ __threadsAvailable();
       
   256 
       
   257     RETURN (__threadsAvailable());
       
   258 %}
       
   259 ! !
       
   260 
       
   261 !ProcessorScheduler class methodsFor:'queries'!
       
   262 
       
   263 isPureEventDriven
       
   264     "this is temporary - (maybe not :-).
       
   265      you can run ST/X either with or without processes.
       
   266      Without, there is conceptionally a single process handling all
       
   267      outside events and timeouts. This has some negative implications
       
   268      (Debugger is ugly), but allows a fully portable ST/X without any
       
   269      assembler support - i.e. quick portability.
       
   270      The PureEvent flag will automatically be set if the runtime system
       
   271      does not support threads - otherwise, it can be set manually
       
   272      (from rc-file).
       
   273     "
       
   274 
       
   275     ^ PureEventDriven
       
   276 !
       
   277 
       
   278 knownProcesses
       
   279     "return a collection of all (living) processes in the system"
       
   280 
       
   281     ^ KnownProcesses select:[:p | p notNil]
       
   282 !
       
   283 
       
   284 maxNumberOfProcesses
       
   285     "return the limit on the number of processes;
       
   286      the default is nil (i.e. unlimited)."
       
   287 
       
   288     ^ MaxNumberOfProcesses
       
   289 !
       
   290 
       
   291 maxNumberOfProcesses:aNumber
       
   292     "set the limit on the number of processes.
       
   293      This helps if you have a program which (by error) creates countless
       
   294      subprocesses. Without this limit, you may have a hard time to find
       
   295      this error (and repairing it). If nil (the default), the number of
       
   296      processes is unlimited."
       
   297 
       
   298     MaxNumberOfProcesses := aNumber
       
   299 !
       
   300 
       
   301 processDriven
       
   302     "turn on process driven mode"
       
   303 
       
   304     PureEventDriven := false
       
   305 !
       
   306 
       
   307 pureEventDriven
       
   308     "turn on pure-event driven mode - no processes, single dispatch loop"
       
   309 
       
   310     PureEventDriven := true
       
   311 ! !
       
   312 
       
   313 !ProcessorScheduler methodsFor:'I/O event actions'!
       
   314 
       
   315 disableFd:aFileDescriptor
       
   316     "disable block events on aFileDescriptor.
       
   317      This is a leftover support for pure-event systems and may vanish."
       
   318 
       
   319     |idx "{Class: SmallInteger }" 
       
   320      wasBlocked|
       
   321 
       
   322     wasBlocked := OperatingSystem blockInterrupts.
       
   323     idx := readFdArray identityIndexOf:aFileDescriptor startingAt:1.
       
   324     idx ~~ 0 ifTrue:[
       
   325 	readFdArray at:idx put:nil.
       
   326 	readCheckArray at:idx put:nil.
       
   327 	readSemaphoreArray at:idx put:nil
       
   328     ].
       
   329     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   330 !
       
   331 
       
   332 enableIOAction:aBlock onInput:aFileDescriptor
       
   333     "half-obsolete event support: arrange for aBlock to be
       
   334      evaluated when input on aFileDescriptor arrives. 
       
   335      This is a leftover support for pure-event systems and may vanish."
       
   336 
       
   337     |idx "{Class: SmallInteger }"
       
   338      wasBlocked|
       
   339 
       
   340     wasBlocked := OperatingSystem blockInterrupts.
       
   341     (readFdArray identityIndexOf:aFileDescriptor startingAt:1) == 0 ifTrue:[
       
   342 	idx := readFdArray identityIndexOf:nil startingAt:1.
       
   343 	idx ~~ 0 ifTrue:[
       
   344 	    readFdArray at:idx put:aFileDescriptor.
       
   345 	    readCheckArray at:idx put:aBlock.
       
   346 	    readSemaphoreArray at:idx put:nil
       
   347 	] ifFalse:[
       
   348 	    readFdArray := readFdArray copyWith:aFileDescriptor.
       
   349 	    readCheckArray := readCheckArray copyWith:aBlock.
       
   350 	    readSemaphoreArray := readSemaphoreArray copyWith:nil.
       
   351 	]
       
   352     ].
       
   353     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   354 ! !
       
   355 
       
   356 !ProcessorScheduler methodsFor:'accessing'!
       
   357 
       
   358 activePriority
       
   359     "return the priority of the currently running process.
       
   360      GNU-ST & ST-80 compatibility; this is the same as currentPriority"
       
   361 
       
   362     ^ currentPriority
       
   363 !
       
   364 
       
   365 activeProcess
       
   366     "return the currently running process"
       
   367 
       
   368     ^ activeProcess
       
   369 
       
   370     "Processor activeProcess"
       
   371 !
       
   372 
       
   373 currentPriority
       
   374     "return the priority of the currently running process"
       
   375 
       
   376     ^ currentPriority
       
   377 
       
   378     "Processor currentPriority"
       
   379 !
       
   380 
       
   381 interruptedProcess
       
   382     "returns the process which was interrupted by the active one"
       
   383 
       
   384     ^ interruptedProcess
       
   385 ! !
       
   386 
       
   387 !ProcessorScheduler methodsFor:'background processing'!
       
   388 
       
   389 addIdleBlock:aBlock
       
   390     "add the argument, aBlock to the list of idle-actions.
       
   391      Idle blocks are evaluated whenever no other process is runnable,
       
   392      and no events are pending.
       
   393      Use of idle blocks is not recommended, use a low priority processes 
       
   394      instead, which has the same effect. Idle blcoks are still included
       
   395      to support background actions in pure-event systems, where no processes 
       
   396      are available.
       
   397      Support for idle-blocks may vanish."
       
   398 
       
   399     |wasBlocked|
       
   400 
       
   401     wasBlocked := OperatingSystem blockInterrupts.
       
   402     idleActions isNil ifTrue:[
       
   403 	idleActions := OrderedCollection new
       
   404     ].
       
   405     idleActions add:aBlock.
       
   406     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   407 !
       
   408 
       
   409 removeIdleBlock:aBlock
       
   410     "remove the argument, aBlock from the list of idle-blocks.
       
   411      Support for idle-blocks may vanish - use low prio processes instead."
       
   412 
       
   413     |wasBlocked|
       
   414 
       
   415     wasBlocked := OperatingSystem blockInterrupts.
       
   416     idleActions notNil ifTrue:[
       
   417        idleActions remove:aBlock
       
   418     ].
       
   419     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   420 ! !
       
   421 
       
   422 !ProcessorScheduler methodsFor:'constants'!
       
   423 
       
   424 highestPriority
       
   425     "return the highest priority value (normal) processes can have."
       
   426 
       
   427     "must be below schedulingPriority - 
       
   428      otherwise scheduler could be blocked ...
       
   429     "
       
   430     ^ HighestPriority  
       
   431 !
       
   432 
       
   433 lowIOPriority
       
   434     "not currently used - for ST80 compatibility only"
       
   435 
       
   436     ^ 2 "claus: is this ok ?"
       
   437 !
       
   438 
       
   439 lowestPriority
       
   440     "return the lowest priority value"
       
   441 
       
   442     ^ 1   "do not change this - its not variable"
       
   443 !
       
   444 
       
   445 schedulingPriority
       
   446     "return the priority at which the scheduler runs."
       
   447 
       
   448     "must be above highestPriority - 
       
   449      otherwise scheduler could be blocked ...
       
   450     "
       
   451     ^ SchedulingPriority
       
   452 !
       
   453 
       
   454 systemBackgroundPriority
       
   455     "return the priority, at which background system processing
       
   456      should take place.
       
   457      Not currently used - for ST80 compatibility only"
       
   458 
       
   459     ^ 4
       
   460 !
       
   461 
       
   462 timingPriority
       
   463     "return the priority, at which all timing takes place (messageTally,
       
   464      delay etc.)"
       
   465 
       
   466     ^ TimingPriority
       
   467 !
       
   468 
       
   469 userBackgroundPriority
       
   470     "return the priority, at which background user (non-interactive) processing
       
   471      should take place.
       
   472      Not currently used - for ST80 compatibility only"
       
   473 
       
   474     ^ 6
       
   475 !
       
   476 
       
   477 userInterruptPriority
       
   478     "return the priority, at which the event scheduler runs - i.e.
       
   479      all processes running at a lower priority are interruptable by Cntl-C
       
   480      or the timer. Processes running at higher prio will not be interrupted."
       
   481 
       
   482     ^ UserInterruptPriority
       
   483 !
       
   484 
       
   485 userSchedulingPriority
       
   486     "return the priority, at which all normal user (interactive) processing
       
   487      takes place"
       
   488 
       
   489     ^ UserSchedulingPriority
       
   490 ! !
       
   491 
       
   492 !ProcessorScheduler methodsFor:'dispatching'!
       
   493 
       
   494 dispatch
       
   495      "It handles timeouts and switches to the highest prio runnable process"
       
   496 
       
   497     |any millis pri p nActions "{ Class: SmallInteger }" |
       
   498 
       
   499     "
       
   500      handle all timeout actions
       
   501     "
       
   502     anyTimeouts ifTrue:[
       
   503 	self evaluateTimeouts
       
   504     ].
       
   505 
       
   506     "first do a quick check for semaphores using checkActions - this is needed for
       
   507      devices like the X-connection, where some events might be in the event
       
   508      queue. Without these checks, a select might block even though there is work to do
       
   509     "
       
   510     any := false.
       
   511     nActions := readCheckArray size.
       
   512     1 to:nActions do:[:index |
       
   513 	|checkBlock sema|
       
   514 
       
   515 	checkBlock := readCheckArray at:index.
       
   516 	(checkBlock notNil and:[checkBlock value]) ifTrue:[
       
   517 	    sema := readSemaphoreArray at:index.
       
   518 	    sema notNil ifTrue:[
       
   519 		sema signalOnce.
       
   520 	    ].
       
   521 	    any := true.
       
   522 	]
       
   523     ].
       
   524 
       
   525     "now, someone might be runnable ..."
       
   526 
       
   527     p := self highestPriorityRunnableProcess.
       
   528     p isNil ifTrue:[
       
   529 	"no one runnable, hard wait for event or timeout"
       
   530 
       
   531 	self waitForEventOrTimeout.
       
   532 	^ self
       
   533     ].
       
   534 
       
   535     pri := p priority.
       
   536 
       
   537     "
       
   538      want to give control to the process p.
       
   539      If the switched-to processes priority is lower than the
       
   540      userSchedulingPriority, we have to make certain, that the 
       
   541      next input or timer will bring us back for a reschedule.
       
   542      This is done by enabling ioInterrupts for all file descriptors.
       
   543      If ioInterrupts are not available (OS does not support them), 
       
   544      we schedule a timer interrupt to interrupt us after 1/20s of a second
       
   545      - effectively polling the filedescriptors 20 times a second.
       
   546      (which is bad, since low prio processes will be hurt in performance)
       
   547      Therefore, dont let benchmarks run with low prio ...
       
   548 
       
   549      Higher prio processes must be suspended, 
       
   550      same prio ones must yield or suspend to get back control
       
   551     "
       
   552 
       
   553 "
       
   554  uncommenting this will make timeouts interrupt the current process
       
   555  (i.e. as if the interrupt runs at TimingPrio); 
       
   556  if left commented, they are handled at UserSchedulingPrio.
       
   557  this will all change, when timeouts are removed and all is process driven
       
   558  (a future version will have a process running to handle a timeout queue)
       
   559 "
       
   560 
       
   561 "
       
   562     pri < TimingPriority ifTrue:[
       
   563 	anyTimeouts ifTrue:[
       
   564 	    millis := self timeToNextTimeout.
       
   565 	    millis == 0 ifTrue:[^ self].
       
   566 	]
       
   567     ].
       
   568 "
       
   569 
       
   570     "
       
   571      if the process to run has a lower than UserInterruptPriority,
       
   572      arrange for an interrupt to occur on I/O.
       
   573      This is done by enabling IO-signals (if the OS supports them)
       
   574      or by installing a poll-interrupt after 50ms (if the OS does not).
       
   575     "
       
   576     pri < UserInterruptPriority ifTrue:[
       
   577     
       
   578 "comment out this if above is uncommented"
       
   579 	anyTimeouts ifTrue:[
       
   580 	    millis := self timeToNextTimeout.
       
   581 	    millis == 0 ifTrue:[^ self].
       
   582 	].
       
   583 "---"
       
   584 
       
   585 	useIOInterrupts ifTrue:[
       
   586 	    readFdArray do:[:fd |
       
   587 		fd notNil ifTrue:[
       
   588 		    OperatingSystem enableIOInterruptsOn:fd
       
   589 		].
       
   590 	    ].
       
   591 	] ifFalse:[
       
   592 	    millis notNil ifTrue:[
       
   593 		millis := millis min:50
       
   594 	    ] ifFalse:[
       
   595 		millis := 50
       
   596 	    ]
       
   597 	]
       
   598     ].
       
   599 
       
   600     millis notNil ifTrue:[
       
   601 	"schedule a clock interrupt after millis milliseconds"
       
   602 	OperatingSystem enableTimer:millis rounded.
       
   603     ].
       
   604 
       
   605     "
       
   606      now let the process run - will come back here by reschedule
       
   607      from ioInterrupt or timerInterrupt ... (running at max+1)
       
   608     "
       
   609     self threadSwitch:p.
       
   610 
       
   611     "... when we arrive here, we are back on stage"
       
   612 
       
   613     millis notNil ifTrue:[
       
   614 	OperatingSystem disableTimer.
       
   615 	self checkForInputWithTimeout:0.
       
   616     ]
       
   617 !
       
   618 
       
   619 dispatchLoop
       
   620     "central dispatch loop; the scheduler process is always staying in
       
   621      this method, looping forever."
       
   622 
       
   623     "avoid confusion if entered twice"
       
   624 
       
   625     dispatching == true ifTrue:[^ self].
       
   626     dispatching := true.
       
   627 
       
   628     "I made this an extra call to dispatch; this allows recompilation
       
   629      of the dispatch-handling code in the running system.
       
   630     "
       
   631     [true] whileTrue:[
       
   632 	AbortSignal handle:[:ex |
       
   633 	    ex return
       
   634 	] do:[
       
   635 	    self dispatch
       
   636 	]
       
   637     ]
   325 ! !
   638 ! !
   326 
   639 
   327 !ProcessorScheduler methodsFor:'primitive process primitives'!
   640 !ProcessorScheduler methodsFor:'primitive process primitives'!
       
   641 
       
   642 scheduleForInterrupt:aProcess
       
   643     "make aProcess evaluate its pushed interrupt block(s)"
       
   644 
       
   645     |id|
       
   646 
       
   647     aProcess isNil ifTrue:[^ self].
       
   648     aProcess == activeProcess ifTrue:[^ self].
       
   649 
       
   650     id := aProcess id.
       
   651     self class threadInterrupt:id.
       
   652     "
       
   653      and, make the process runnable
       
   654     "
       
   655     aProcess state ~~ #stopped ifTrue:[
       
   656 	"
       
   657 	 and, make the process runnable
       
   658 	"
       
   659 	aProcess resume
       
   660     ]
       
   661 !
   328 
   662 
   329 threadSwitch:aProcess
   663 threadSwitch:aProcess
   330     "continue execution in aProcess.
   664     "continue execution in aProcess.
   331      (warning: low level entry, no administration is done here)"
   665      (warning: low level entry, no administration is done here)"
   332 
   666 
   386     zombie notNil ifTrue:[
   720     zombie notNil ifTrue:[
   387 	self class threadDestroy:zombie.
   721 	self class threadDestroy:zombie.
   388 	zombie := nil
   722 	zombie := nil
   389     ].
   723     ].
   390     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
   724     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
   391 !
       
   392 
       
   393 scheduleForInterrupt:aProcess
       
   394     "make aProcess evaluate its pushed interrupt block(s)"
       
   395 
       
   396     |id|
       
   397 
       
   398     aProcess isNil ifTrue:[^ self].
       
   399     aProcess == activeProcess ifTrue:[^ self].
       
   400 
       
   401     id := aProcess id.
       
   402     self class threadInterrupt:id.
       
   403     "
       
   404      and, make the process runnable
       
   405     "
       
   406     aProcess state ~~ #stopped ifTrue:[
       
   407 	"
       
   408 	 and, make the process runnable
       
   409 	"
       
   410 	aProcess resume
       
   411     ]
       
   412 ! !
       
   413 
       
   414 !ProcessorScheduler methodsFor:'constants'!
       
   415 
       
   416 lowestPriority
       
   417     "return the lowest priority value"
       
   418 
       
   419     ^ 1   "do not change this - its not variable"
       
   420 !
       
   421 
       
   422 highestPriority
       
   423     "return the highest priority value (normal) processes can have."
       
   424 
       
   425     "must be below schedulingPriority - 
       
   426      otherwise scheduler could be blocked ...
       
   427     "
       
   428     ^ HighestPriority  
       
   429 !
       
   430 
       
   431 schedulingPriority
       
   432     "return the priority at which the scheduler runs."
       
   433 
       
   434     "must be above highestPriority - 
       
   435      otherwise scheduler could be blocked ...
       
   436     "
       
   437     ^ SchedulingPriority
       
   438 !
       
   439 
       
   440 userInterruptPriority
       
   441     "return the priority, at which the event scheduler runs - i.e.
       
   442      all processes running at a lower priority are interruptable by Cntl-C
       
   443      or the timer. Processes running at higher prio will not be interrupted."
       
   444 
       
   445     ^ UserInterruptPriority
       
   446 !
       
   447 
       
   448 timingPriority
       
   449     "return the priority, at which all timing takes place (messageTally,
       
   450      delay etc.)"
       
   451 
       
   452     ^ TimingPriority
       
   453 !
       
   454 
       
   455 userSchedulingPriority
       
   456     "return the priority, at which all normal user (interactive) processing
       
   457      takes place"
       
   458 
       
   459     ^ UserSchedulingPriority
       
   460 !
       
   461 
       
   462 userBackgroundPriority
       
   463     "return the priority, at which background user (non-interactive) processing
       
   464      should take place.
       
   465      Not currently used - for ST80 compatibility only"
       
   466 
       
   467     ^ 6
       
   468 !
       
   469 
       
   470 systemBackgroundPriority
       
   471     "return the priority, at which background system processing
       
   472      should take place.
       
   473      Not currently used - for ST80 compatibility only"
       
   474 
       
   475     ^ 4
       
   476 !
       
   477 
       
   478 lowIOPriority
       
   479     "not currently used - for ST80 compatibility only"
       
   480 
       
   481     ^ 2 "claus: is this ok ?"
       
   482 ! !
       
   483 
       
   484 !ProcessorScheduler methodsFor:'private initializing'!
       
   485 
       
   486 initialize
       
   487     "initialize the one-and-only ProcessorScheduler"
       
   488 
       
   489     |nPrios "{ Class: SmallInteger }"
       
   490      l p|
       
   491 
       
   492     KnownProcesses isNil ifTrue:[
       
   493 	KnownProcesses := WeakArray new:10.
       
   494 	KnownProcesses watcher:self class.
       
   495 	KnownProcessIds := OrderedCollection new.
       
   496     ].
       
   497 
       
   498     "
       
   499      create a collection with process lists; accessed using the priority as key
       
   500     "
       
   501     nPrios := SchedulingPriority.
       
   502     quiescentProcessLists := Array new:nPrios.
       
   503     1 to:nPrios do:[:pri |
       
   504 	quiescentProcessLists at:pri put:(LinkedList new)
       
   505     ].
       
   506 
       
   507     readFdArray := Array with:nil.
       
   508     readCheckArray := Array with:nil.
       
   509     readSemaphoreArray := Array with:nil.
       
   510     writeFdArray := Array with:nil.
       
   511     writeSemaphoreArray := Array with:nil.
       
   512     timeoutArray := Array with:nil.
       
   513     timeoutSemaphoreArray := Array with:nil.
       
   514     timeoutActionArray := Array with:nil.
       
   515     timeoutProcessArray := Array with:nil.
       
   516     anyTimeouts := false.
       
   517     dispatching := false.
       
   518     useIOInterrupts := OperatingSystem supportsIOInterrupts.
       
   519 
       
   520     "
       
   521      handcraft the first (dispatcher-) process - this one will never
       
   522      block, but go into a select if there is nothing to do.
       
   523      Also, it has a prio of max+1 - thus, it comes first when looking
       
   524      for a runnable process.
       
   525     "
       
   526     currentPriority := SchedulingPriority.
       
   527     p := Process new.
       
   528     p setId:0 state:#run.
       
   529     p setPriority:currentPriority.
       
   530     p name:'scheduler'.
       
   531 
       
   532     scheduler := activeProcess := p.
       
   533 
       
   534     (quiescentProcessLists at:currentPriority) add:p.
       
   535 
       
   536     "
       
   537      let me handle IO and timer interrupts
       
   538     "
       
   539     ObjectMemory ioInterruptHandler:self.
       
   540     ObjectMemory timerInterruptHandler:self.
       
   541 !
       
   542 
       
   543 reinitialize
       
   544     "all previous processes (except those marked as restartable) are made dead 
       
   545      - each object should reinstall its process(s) upon restart;
       
   546      especially, windowgroups have to.
       
   547      In contrast to ST-80, restartable processes are restarted at the beginning
       
   548      NOT continued where left. This is a consequence of the portable implementation
       
   549      of ST/X, since in order to continue a process, we needed to know the
       
   550      internals of the machines (and C-compilers) stack layout.
       
   551      This was not done, favouring portability for process continuation.
       
   552      In praxis, this is not much of a problem, since in almost every case,
       
   553      the computation state can be saved in some object, and processing be 
       
   554      restarted from scratch, reinitializing things from this saved state."
       
   555 
       
   556     |processesToRestart|
       
   557 
       
   558     "
       
   559      lay all processes to rest, collect restartable ones
       
   560     "
       
   561     processesToRestart := OrderedCollection new.
       
   562     KnownProcesses do:[:p |
       
   563 	p notNil ifTrue:[
       
   564 	    "how, exactly should this be done ?"
       
   565 
       
   566 	    p isRestartable == true ifTrue:[
       
   567 		p nextLink:nil.
       
   568 		processesToRestart add:p
       
   569 	    ] ifFalse:[
       
   570 		p setId:nil state:#dead
       
   571 	    ]
       
   572 	].
       
   573     ].
       
   574     scheduler setId:nil state:#dead. 
       
   575 
       
   576     "
       
   577      now, start from scratch
       
   578     "
       
   579     KnownProcesses := nil.
       
   580     self initialize.
       
   581 
       
   582     "
       
   583      ... and restart those that can be.
       
   584     "
       
   585     processesToRestart do:[:p |
       
   586 "/        'process restart not implemented' errorPrintNL.
       
   587 	p restart
       
   588     ]
       
   589 ! !
   725 ! !
   590 
   726 
   591 !ProcessorScheduler methodsFor:'private'!
   727 !ProcessorScheduler methodsFor:'private'!
   592 
   728 
   593 remember:aProcess
   729 remember:aProcess
   642 	KnownProcesses at:index put:nil.
   778 	KnownProcesses at:index put:nil.
   643     ].
   779     ].
   644     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
   780     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
   645 ! !
   781 ! !
   646 
   782 
       
   783 !ProcessorScheduler methodsFor:'private initializing'!
       
   784 
       
   785 initialize
       
   786     "initialize the one-and-only ProcessorScheduler"
       
   787 
       
   788     |nPrios "{ Class: SmallInteger }"
       
   789      l p|
       
   790 
       
   791     KnownProcesses isNil ifTrue:[
       
   792 	KnownProcesses := WeakArray new:10.
       
   793 	KnownProcesses watcher:self class.
       
   794 	KnownProcessIds := OrderedCollection new.
       
   795     ].
       
   796 
       
   797     "
       
   798      create a collection with process lists; accessed using the priority as key
       
   799     "
       
   800     nPrios := SchedulingPriority.
       
   801     quiescentProcessLists := Array new:nPrios.
       
   802     1 to:nPrios do:[:pri |
       
   803 	quiescentProcessLists at:pri put:(LinkedList new)
       
   804     ].
       
   805 
       
   806     readFdArray := Array with:nil.
       
   807     readCheckArray := Array with:nil.
       
   808     readSemaphoreArray := Array with:nil.
       
   809     writeFdArray := Array with:nil.
       
   810     writeSemaphoreArray := Array with:nil.
       
   811     timeoutArray := Array with:nil.
       
   812     timeoutSemaphoreArray := Array with:nil.
       
   813     timeoutActionArray := Array with:nil.
       
   814     timeoutProcessArray := Array with:nil.
       
   815     anyTimeouts := false.
       
   816     dispatching := false.
       
   817     useIOInterrupts := OperatingSystem supportsIOInterrupts.
       
   818 
       
   819     "
       
   820      handcraft the first (dispatcher-) process - this one will never
       
   821      block, but go into a select if there is nothing to do.
       
   822      Also, it has a prio of max+1 - thus, it comes first when looking
       
   823      for a runnable process.
       
   824     "
       
   825     currentPriority := SchedulingPriority.
       
   826     p := Process new.
       
   827     p setId:0 state:#run.
       
   828     p setPriority:currentPriority.
       
   829     p name:'scheduler'.
       
   830 
       
   831     scheduler := activeProcess := p.
       
   832 
       
   833     (quiescentProcessLists at:currentPriority) add:p.
       
   834 
       
   835     "
       
   836      let me handle IO and timer interrupts
       
   837     "
       
   838     ObjectMemory ioInterruptHandler:self.
       
   839     ObjectMemory timerInterruptHandler:self.
       
   840 !
       
   841 
       
   842 reinitialize
       
   843     "all previous processes (except those marked as restartable) are made dead 
       
   844      - each object should reinstall its process(s) upon restart;
       
   845      especially, windowgroups have to.
       
   846      In contrast to ST-80, restartable processes are restarted at the beginning
       
   847      NOT continued where left. This is a consequence of the portable implementation
       
   848      of ST/X, since in order to continue a process, we needed to know the
       
   849      internals of the machines (and C-compilers) stack layout.
       
   850      This was not done, favouring portability for process continuation.
       
   851      In praxis, this is not much of a problem, since in almost every case,
       
   852      the computation state can be saved in some object, and processing be 
       
   853      restarted from scratch, reinitializing things from this saved state."
       
   854 
       
   855     |processesToRestart|
       
   856 
       
   857     "
       
   858      lay all processes to rest, collect restartable ones
       
   859     "
       
   860     processesToRestart := OrderedCollection new.
       
   861     KnownProcesses do:[:p |
       
   862 	p notNil ifTrue:[
       
   863 	    "how, exactly should this be done ?"
       
   864 
       
   865 	    p isRestartable == true ifTrue:[
       
   866 		p nextLink:nil.
       
   867 		processesToRestart add:p
       
   868 	    ] ifFalse:[
       
   869 		p setId:nil state:#dead
       
   870 	    ]
       
   871 	].
       
   872     ].
       
   873     scheduler setId:nil state:#dead. 
       
   874 
       
   875     "
       
   876      now, start from scratch
       
   877     "
       
   878     KnownProcesses := nil.
       
   879     self initialize.
       
   880 
       
   881     "
       
   882      ... and restart those that can be.
       
   883     "
       
   884     processesToRestart do:[:p |
       
   885 "/        'process restart not implemented' errorPrintNL.
       
   886 	p restart
       
   887     ]
       
   888 ! !
       
   889 
   647 !ProcessorScheduler methodsFor:'process creation'!
   890 !ProcessorScheduler methodsFor:'process creation'!
   648 
       
   649 newProcessFor:aProcess withId:idWant
       
   650     "private entry for Process restart - do not use in your program"
       
   651 
       
   652     (self class threadCreate:aProcess withId:idWant) ~~ idWant ifTrue:[
       
   653 	^ false
       
   654     ].
       
   655 
       
   656     aProcess state:#light.   "meaning: has no stack yet"
       
   657     self remember:aProcess.
       
   658     ^ true
       
   659 ! 
       
   660 
   891 
   661 newProcessFor:aProcess
   892 newProcessFor:aProcess
   662     "create a physical (VM-) process for aProcess.
   893     "create a physical (VM-) process for aProcess.
   663      Return true if ok, false if something went wrong.
   894      Return true if ok, false if something went wrong.
   664      The process is not scheduled; to start it running, 
   895      The process is not scheduled; to start it running, 
   671     id isNil ifTrue:[^ false].
   902     id isNil ifTrue:[^ false].
   672 
   903 
   673     aProcess setId:id state:#light.   "meaning: has no stack yet"
   904     aProcess setId:id state:#light.   "meaning: has no stack yet"
   674     self remember:aProcess.
   905     self remember:aProcess.
   675     ^ true
   906     ^ true
   676 ! !
   907 !
   677 
   908 
   678 !ProcessorScheduler methodsFor:'scheduling'!
   909 newProcessFor:aProcess withId:idWant
   679 
   910     "private entry for Process restart - do not use in your program"
   680 reschedule
   911 
   681     "switch to the highest prio runnable process.
   912     (self class threadCreate:aProcess withId:idWant) ~~ idWant ifTrue:[
   682      The scheduler itself is always runnable, so we can do an unconditional switch
   913 	^ false
   683      to that one. This method is a historical left-over and will vanish."
   914     ].
   684 
   915 
   685     ^ self threadSwitch:scheduler
   916     aProcess state:#light.   "meaning: has no stack yet"
   686 !
   917     self remember:aProcess.
   687 
   918     ^ true
   688 yield
       
   689     "move the currently running process to the end of the currentList
       
   690      and reschedule to the first in the list, thus switching to the 
       
   691      next same-prio-process."
       
   692 
       
   693     |l wasBlocked|
       
   694 
       
   695     wasBlocked := OperatingSystem blockInterrupts.
       
   696 
       
   697     "
       
   698      debugging consistency check - will be removed later
       
   699     "
       
   700     activeProcess priority ~~ currentPriority ifTrue:[
       
   701 	'oops process changed priority' errorPrintNL.
       
   702 	currentPriority := activeProcess priority.
       
   703     ].
       
   704 
       
   705     l := quiescentProcessLists at:currentPriority.
       
   706 
       
   707     "
       
   708      debugging consistency checks - will be removed later
       
   709     "
       
   710     l isEmpty ifTrue:[
       
   711 	wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   712 	'oops - empty runnable list' errorPrintNL.
       
   713 	^ self
       
   714     ].
       
   715 
       
   716     "
       
   717      check if the running process is not the only one
       
   718     "
       
   719     l size ~~ 1 ifTrue:[
       
   720 	"
       
   721 	 bring running process to the end
       
   722 	"
       
   723 	l removeFirst.
       
   724 	l addLast:activeProcess.
       
   725 
       
   726 	"
       
   727 	 and switch to first in the list
       
   728 	"
       
   729 	self threadSwitch:(l first).
       
   730     ].
       
   731     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   732 !
       
   733 
       
   734 suspend:aProcess
       
   735     "remove the argument, aProcess from the list of runnable processes.
       
   736      If the process is the current one, reschedule."
       
   737 
       
   738     |pri l p wasBlocked|
       
   739 
       
   740     "
       
   741      some debugging stuff
       
   742     "
       
   743     aProcess isNil ifTrue:[
       
   744 	MiniDebugger enterWithMessage:'nil suspend'.
       
   745 	^ self
       
   746     ].
       
   747     aProcess id isNil ifTrue:[
       
   748 	MiniDebugger enterWithMessage:'bad suspend: already dead'.
       
   749 	self threadSwitch:scheduler.
       
   750 	^ self
       
   751     ].
       
   752     aProcess == scheduler ifTrue:[
       
   753 	'scheduler should never be suspended' errorPrintNL.
       
   754 	MiniDebugger enterWithMessage:'scheduler should never be suspended'.
       
   755 	^ self
       
   756     ].
       
   757 
       
   758     wasBlocked := OperatingSystem blockInterrupts.
       
   759 
       
   760     pri := aProcess priority.
       
   761     l := quiescentProcessLists at:pri.
       
   762 
       
   763     "notice: this is slightly faster than putting the if-code into
       
   764      the ifAbsent block, because [] is a shared cheap block
       
   765     "
       
   766     (l remove:aProcess ifAbsent:[]) isNil ifTrue:[
       
   767 	wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   768 	'bad suspend: not on run list' errorPrintNL.
       
   769 	"/ MiniDebugger enterWithMessage:'bad suspend: not on run list'.
       
   770 	self threadSwitch:scheduler.
       
   771 	^ self
       
   772     ].
       
   773 
       
   774     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   775 
       
   776     "
       
   777      this is a bit of a kludge: allow someone else to
       
   778      set the state to something like #ioWait etc.
       
   779      In this case, do not set to #suspend.
       
   780      All of this to enhance the output of the process monitor ...
       
   781     "
       
   782     aProcess setStateTo:#suspended if:#active or:#run.
       
   783 
       
   784     (aProcess == activeProcess) ifTrue:[
       
   785 	"we can immediately switch sometimes"
       
   786 	l notEmpty ifTrue:[
       
   787 	    p := l first
       
   788 	] ifFalse:[
       
   789 	    p := scheduler
       
   790 	].
       
   791 	self threadSwitch:p 
       
   792     ].
       
   793 !
       
   794 
       
   795 resume:aProcess
       
   796     "set aProcess runnable - 
       
   797      if its prio is higher than the currently running prio, switch to it."
       
   798 
       
   799     |l pri wasBlocked|
       
   800 
       
   801     (aProcess isNil or:[aProcess == activeProcess]) ifTrue:[^ self].
       
   802 
       
   803     "ignore, if process is already dead"
       
   804     aProcess id isNil ifTrue:[^ self].
       
   805 
       
   806     wasBlocked := OperatingSystem blockInterrupts.
       
   807 
       
   808     pri := aProcess priority.
       
   809 
       
   810     l := quiescentProcessLists at:pri.
       
   811     "if already running, ignore"
       
   812     (l identityIndexOf:aProcess) ~~ 0 ifTrue:[
       
   813 	wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   814 	^ self
       
   815     ].
       
   816     l addLast:aProcess.
       
   817     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   818 
       
   819     (pri > currentPriority) ifTrue:[
       
   820 	"
       
   821 	 its prio is higher; immediately transfer control to it
       
   822 	"
       
   823 	self threadSwitch:aProcess
       
   824     ] ifFalse:[
       
   825 	"
       
   826 	 its prio is lower; it will have to wait for a while ...
       
   827 	"
       
   828 	aProcess state:#run 
       
   829     ]
       
   830 !
       
   831 
       
   832 resumeForSingleSend:aProcess
       
   833     "like resume, but let the process execute a single send only.
       
   834      This will be used by the (new, not yet released) debugger 
       
   835      for single stepping."
       
   836 
       
   837     (aProcess isNil or:[aProcess == activeProcess]) ifTrue:[^ self].
       
   838     aProcess singleStep:true.
       
   839     self resume:aProcess
       
   840 !
       
   841 
       
   842 terminateNoSignal:aProcess
       
   843     "hard terminate aProcess without sending the terminate signal, thus
       
   844      no unwind blocks or exitAction are performed in the process.. 
       
   845      If its not the current process, it is simply removed from its list 
       
   846      and physically destroyed. Otherwise (since we can't take away the chair
       
   847      we are sitting on), a switch is forced and the process 
       
   848      will be physically destroyed by the next running process. 
       
   849      (see zombie handling)"
       
   850 
       
   851     |pri id l wasBlocked|
       
   852 
       
   853     aProcess isNil ifTrue:[^ self].
       
   854     id := aProcess id.
       
   855     id isNil ifTrue:[^ self].   "already dead"
       
   856 
       
   857     aProcess setId:nil state:#dead.
       
   858 
       
   859     wasBlocked := OperatingSystem blockInterrupts.
       
   860 
       
   861     "remove the process from the runnable list"
       
   862 
       
   863     pri := aProcess priority.
       
   864     l := quiescentProcessLists at:pri.
       
   865     (l identityIndexOf:aProcess) ~~ 0 ifTrue:[
       
   866 	l remove:aProcess.
       
   867     ].
       
   868     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   869 
       
   870     aProcess == activeProcess ifTrue:[
       
   871 	"
       
   872 	 hard case - its the currently running process
       
   873 	 we must have the next active process destroy this one
       
   874 	 (we cannot destroy the chair we are sitting on ... :-)
       
   875 	"
       
   876 	zombie := id.
       
   877 	self unRemember:aProcess.
       
   878 	self threadSwitch:scheduler.
       
   879 	"not reached"
       
   880 	^ self
       
   881     ].
       
   882     self class threadDestroy:id.
       
   883     self unRemember:aProcess.
       
   884     ^ self
       
   885 !
       
   886 
       
   887 terminateActiveNoSignal
       
   888     "hard terminate the active process, without sending any
       
   889      terminate signal thus no unwind blocks are evaluated."
       
   890 
       
   891     self terminateNoSignal:activeProcess
       
   892 !
       
   893 
       
   894 processTermination
       
   895     "sent by VM if the current process finished its startup block 
       
   896      without proper process termination. Lay him to rest now. 
       
   897      This can only happen, if something went wrong in Block>>newProcess, 
       
   898      since the block defined there always terminates itself."
       
   899 
       
   900     self terminateNoSignal:activeProcess.
       
   901     self threadSwitch:scheduler
       
   902 !
       
   903 
       
   904 terminate:aProcess
       
   905     "terminate aProcess. This is donen by sending aProcess the terminateSignal,
       
   906      which will evaluate any unwind blocks and finally do a hard terminate."
       
   907 
       
   908     aProcess terminate
       
   909 !
       
   910 
       
   911 terminateActive
       
   912     "terminate the current process (i.e. the running process kills itself).
       
   913      The active process is sent the terminateSignal so it will evaluate any
       
   914      unwind blocks and finally do a hard terminate.
       
   915      This is sent for regular termination and by the VM, if the hard-stack limit
       
   916      is reached. (i.e. a process did not repair things in a recursionInterrupt and
       
   917      continued to grow its stack)"
       
   918 
       
   919     activeProcess terminate
       
   920 !
       
   921 
       
   922 interruptActive
       
   923     "interrupt the current process"
       
   924 
       
   925     activeProcess interrupt
       
   926 !
       
   927 
       
   928 changePriority:prio for:aProcess
       
   929     "change the priority of aProcess"
       
   930 
       
   931     |oldList newList oldPrio newPrio wasBlocked|
       
   932 
       
   933     oldPrio := aProcess priority.
       
   934     oldPrio == prio ifTrue:[^ self].
       
   935 
       
   936     "
       
   937      check for valid argument
       
   938     "
       
   939     newPrio := prio.
       
   940     newPrio < 1 ifTrue:[
       
   941 	newPrio := 1.
       
   942     ] ifFalse:[
       
   943 	aProcess == scheduler ifTrue:[^ self].
       
   944 	newPrio > HighestPriority ifTrue:[
       
   945 	    newPrio := HighestPriority
       
   946 	]
       
   947     ].
       
   948 
       
   949     wasBlocked := OperatingSystem blockInterrupts.
       
   950 
       
   951     aProcess setPriority:newPrio.
       
   952 
       
   953     oldList := quiescentProcessLists at:oldPrio.
       
   954     (oldList identityIndexOf:aProcess) == 0 ifTrue:[
       
   955 	wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   956 	^ self
       
   957     ].
       
   958 
       
   959     oldList remove:aProcess.
       
   960 
       
   961     newList := quiescentProcessLists at:newPrio.
       
   962     newList addLast:aProcess.
       
   963 
       
   964     "if its the current process lowering its prio 
       
   965      or another one raising, we have to reschedule"
       
   966 
       
   967     aProcess == activeProcess ifTrue:[
       
   968 	currentPriority := newPrio.
       
   969 	newPrio < oldPrio ifTrue:[
       
   970 	    self threadSwitch:scheduler.    
       
   971 	]
       
   972     ] ifFalse:[
       
   973 	newPrio > currentPriority ifTrue:[
       
   974 	    self threadSwitch:aProcess.
       
   975 	]
       
   976     ].
       
   977     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
   978 ! !
       
   979 
       
   980 !ProcessorScheduler methodsFor:'accessing'!
       
   981 
       
   982 currentPriority
       
   983     "return the priority of the currently running process"
       
   984 
       
   985     ^ currentPriority
       
   986 
       
   987     "Processor currentPriority"
       
   988 !
       
   989 
       
   990 activePriority
       
   991     "return the priority of the currently running process.
       
   992      GNU-ST & ST-80 compatibility; this is the same as currentPriority"
       
   993 
       
   994     ^ currentPriority
       
   995 !
       
   996 
       
   997 activeProcess
       
   998     "return the currently running process"
       
   999 
       
  1000     ^ activeProcess
       
  1001 
       
  1002     "Processor activeProcess"
       
  1003 !
       
  1004 
       
  1005 interruptedProcess
       
  1006     "returns the process which was interrupted by the active one"
       
  1007 
       
  1008     ^ interruptedProcess
       
  1009 ! !
   919 ! !
  1010 
   920 
  1011 !ProcessorScheduler methodsFor:'queries'!
   921 !ProcessorScheduler methodsFor:'queries'!
       
   922 
       
   923 activeProcessIsSystemProcess
       
   924     "return true if the active process is a system process,
       
   925      which should not be suspended."
       
   926 
       
   927     ^ self isSystemProcess:activeProcess
       
   928 
       
   929     "
       
   930      Processor activeProcessIsSystemProcess
       
   931     "
       
   932 !
  1012 
   933 
  1013 highestPriorityRunnableProcess
   934 highestPriorityRunnableProcess
  1014     "return the highest prio runnable process"
   935     "return the highest prio runnable process"
  1015 
   936 
  1016     |listArray l p prio "{ Class: SmallInteger }" |
   937     |listArray l p prio "{ Class: SmallInteger }" |
  1050     ^ false
   971     ^ false
  1051 
   972 
  1052     "
   973     "
  1053      Processor activeProcessIsSystemProcess
   974      Processor activeProcessIsSystemProcess
  1054     "
   975     "
  1055 !
   976 ! !
  1056 
   977 
  1057 activeProcessIsSystemProcess
   978 !ProcessorScheduler methodsFor:'scheduling'!
  1058     "return true if the active process is a system process,
   979 
  1059      which should not be suspended."
   980 changePriority:prio for:aProcess
  1060 
   981     "change the priority of aProcess"
  1061     ^ self isSystemProcess:activeProcess
   982 
  1062 
   983     |oldList newList oldPrio newPrio wasBlocked|
  1063     "
   984 
  1064      Processor activeProcessIsSystemProcess
   985     oldPrio := aProcess priority.
  1065     "
   986     oldPrio == prio ifTrue:[^ self].
  1066 ! !
   987 
  1067 
   988     "
  1068 !ProcessorScheduler methodsFor:'dispatching'!
   989      check for valid argument
  1069 
   990     "
  1070 dispatchLoop
   991     newPrio := prio.
  1071     "central dispatch loop; the scheduler process is always staying in
   992     newPrio < 1 ifTrue:[
  1072      this method, looping forever."
   993 	newPrio := 1.
  1073 
   994     ] ifFalse:[
  1074     "avoid confusion if entered twice"
   995 	aProcess == scheduler ifTrue:[^ self].
  1075 
   996 	newPrio > HighestPriority ifTrue:[
  1076     dispatching == true ifTrue:[^ self].
   997 	    newPrio := HighestPriority
  1077     dispatching := true.
       
  1078 
       
  1079     "I made this an extra call to dispatch; this allows recompilation
       
  1080      of the dispatch-handling code in the running system.
       
  1081     "
       
  1082     [true] whileTrue:[
       
  1083 	AbortSignal handle:[:ex |
       
  1084 	    ex return
       
  1085 	] do:[
       
  1086 	    self dispatch
       
  1087 	]
   998 	]
       
   999     ].
       
  1000 
       
  1001     wasBlocked := OperatingSystem blockInterrupts.
       
  1002 
       
  1003     aProcess setPriority:newPrio.
       
  1004 
       
  1005     oldList := quiescentProcessLists at:oldPrio.
       
  1006     (oldList identityIndexOf:aProcess) == 0 ifTrue:[
       
  1007 	wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1008 	^ self
       
  1009     ].
       
  1010 
       
  1011     oldList remove:aProcess.
       
  1012 
       
  1013     newList := quiescentProcessLists at:newPrio.
       
  1014     newList addLast:aProcess.
       
  1015 
       
  1016     "if its the current process lowering its prio 
       
  1017      or another one raising, we have to reschedule"
       
  1018 
       
  1019     aProcess == activeProcess ifTrue:[
       
  1020 	currentPriority := newPrio.
       
  1021 	newPrio < oldPrio ifTrue:[
       
  1022 	    self threadSwitch:scheduler.    
       
  1023 	]
       
  1024     ] ifFalse:[
       
  1025 	newPrio > currentPriority ifTrue:[
       
  1026 	    self threadSwitch:aProcess.
       
  1027 	]
       
  1028     ].
       
  1029     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1030 !
       
  1031 
       
  1032 interruptActive
       
  1033     "interrupt the current process"
       
  1034 
       
  1035     activeProcess interrupt
       
  1036 !
       
  1037 
       
  1038 processTermination
       
  1039     "sent by VM if the current process finished its startup block 
       
  1040      without proper process termination. Lay him to rest now. 
       
  1041      This can only happen, if something went wrong in Block>>newProcess, 
       
  1042      since the block defined there always terminates itself."
       
  1043 
       
  1044     self terminateNoSignal:activeProcess.
       
  1045     self threadSwitch:scheduler
       
  1046 !
       
  1047 
       
  1048 reschedule
       
  1049     "switch to the highest prio runnable process.
       
  1050      The scheduler itself is always runnable, so we can do an unconditional switch
       
  1051      to that one. This method is a historical left-over and will vanish."
       
  1052 
       
  1053     ^ self threadSwitch:scheduler
       
  1054 !
       
  1055 
       
  1056 resume:aProcess
       
  1057     "set aProcess runnable - 
       
  1058      if its prio is higher than the currently running prio, switch to it."
       
  1059 
       
  1060     |l pri wasBlocked|
       
  1061 
       
  1062     (aProcess isNil or:[aProcess == activeProcess]) ifTrue:[^ self].
       
  1063 
       
  1064     "ignore, if process is already dead"
       
  1065     aProcess id isNil ifTrue:[^ self].
       
  1066 
       
  1067     wasBlocked := OperatingSystem blockInterrupts.
       
  1068 
       
  1069     pri := aProcess priority.
       
  1070 
       
  1071     l := quiescentProcessLists at:pri.
       
  1072     "if already running, ignore"
       
  1073     (l identityIndexOf:aProcess) ~~ 0 ifTrue:[
       
  1074 	wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1075 	^ self
       
  1076     ].
       
  1077     l addLast:aProcess.
       
  1078     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1079 
       
  1080     (pri > currentPriority) ifTrue:[
       
  1081 	"
       
  1082 	 its prio is higher; immediately transfer control to it
       
  1083 	"
       
  1084 	self threadSwitch:aProcess
       
  1085     ] ifFalse:[
       
  1086 	"
       
  1087 	 its prio is lower; it will have to wait for a while ...
       
  1088 	"
       
  1089 	aProcess state:#run 
  1088     ]
  1090     ]
  1089 !
  1091 !
  1090 
  1092 
  1091 dispatch
  1093 resumeForSingleSend:aProcess
  1092      "It handles timeouts and switches to the highest prio runnable process"
  1094     "like resume, but let the process execute a single send only.
  1093 
  1095      This will be used by the (new, not yet released) debugger 
  1094     |any millis pri p nActions "{ Class: SmallInteger }" |
  1096      for single stepping."
  1095 
  1097 
  1096     "
  1098     (aProcess isNil or:[aProcess == activeProcess]) ifTrue:[^ self].
  1097      handle all timeout actions
  1099     aProcess singleStep:true.
  1098     "
  1100     self resume:aProcess
  1099     anyTimeouts ifTrue:[
  1101 !
  1100 	self evaluateTimeouts
  1102 
  1101     ].
  1103 suspend:aProcess
  1102 
  1104     "remove the argument, aProcess from the list of runnable processes.
  1103     "first do a quick check for semaphores using checkActions - this is needed for
  1105      If the process is the current one, reschedule."
  1104      devices like the X-connection, where some events might be in the event
  1106 
  1105      queue. Without these checks, a select might block even though there is work to do
  1107     |pri l p wasBlocked|
  1106     "
  1108 
  1107     any := false.
  1109     "
  1108     nActions := readCheckArray size.
  1110      some debugging stuff
  1109     1 to:nActions do:[:index |
  1111     "
  1110 	|checkBlock sema|
  1112     aProcess isNil ifTrue:[
  1111 
  1113 	MiniDebugger enterWithMessage:'nil suspend'.
  1112 	checkBlock := readCheckArray at:index.
       
  1113 	(checkBlock notNil and:[checkBlock value]) ifTrue:[
       
  1114 	    sema := readSemaphoreArray at:index.
       
  1115 	    sema notNil ifTrue:[
       
  1116 		sema signalOnce.
       
  1117 	    ].
       
  1118 	    any := true.
       
  1119 	]
       
  1120     ].
       
  1121 
       
  1122     "now, someone might be runnable ..."
       
  1123 
       
  1124     p := self highestPriorityRunnableProcess.
       
  1125     p isNil ifTrue:[
       
  1126 	"no one runnable, hard wait for event or timeout"
       
  1127 
       
  1128 	self waitForEventOrTimeout.
       
  1129 	^ self
  1114 	^ self
  1130     ].
  1115     ].
  1131 
  1116     aProcess id isNil ifTrue:[
  1132     pri := p priority.
  1117 	MiniDebugger enterWithMessage:'bad suspend: already dead'.
  1133 
  1118 	self threadSwitch:scheduler.
  1134     "
  1119 	^ self
  1135      want to give control to the process p.
  1120     ].
  1136      If the switched-to processes priority is lower than the
  1121     aProcess == scheduler ifTrue:[
  1137      userSchedulingPriority, we have to make certain, that the 
  1122 	'scheduler should never be suspended' errorPrintNL.
  1138      next input or timer will bring us back for a reschedule.
  1123 	MiniDebugger enterWithMessage:'scheduler should never be suspended'.
  1139      This is done by enabling ioInterrupts for all file descriptors.
  1124 	^ self
  1140      If ioInterrupts are not available (OS does not support them), 
  1125     ].
  1141      we schedule a timer interrupt to interrupt us after 1/20s of a second
  1126 
  1142      - effectively polling the filedescriptors 20 times a second.
  1127     wasBlocked := OperatingSystem blockInterrupts.
  1143      (which is bad, since low prio processes will be hurt in performance)
  1128 
  1144      Therefore, dont let benchmarks run with low prio ...
  1129     pri := aProcess priority.
  1145 
  1130     l := quiescentProcessLists at:pri.
  1146      Higher prio processes must be suspended, 
  1131 
  1147      same prio ones must yield or suspend to get back control
  1132     "notice: this is slightly faster than putting the if-code into
  1148     "
  1133      the ifAbsent block, because [] is a shared cheap block
  1149 
  1134     "
  1150 "
  1135     (l remove:aProcess ifAbsent:[]) isNil ifTrue:[
  1151  uncommenting this will make timeouts interrupt the current process
  1136 	wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1152  (i.e. as if the interrupt runs at TimingPrio); 
  1137 	'bad suspend: not on run list' errorPrintNL.
  1153  if left commented, they are handled at UserSchedulingPrio.
  1138 	"/ MiniDebugger enterWithMessage:'bad suspend: not on run list'.
  1154  this will all change, when timeouts are removed and all is process driven
  1139 	self threadSwitch:scheduler.
  1155  (a future version will have a process running to handle a timeout queue)
  1140 	^ self
  1156 "
  1141     ].
  1157 
  1142 
  1158 "
  1143     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1159     pri < TimingPriority ifTrue:[
  1144 
  1160 	anyTimeouts ifTrue:[
  1145     "
  1161 	    millis := self timeToNextTimeout.
  1146      this is a bit of a kludge: allow someone else to
  1162 	    millis == 0 ifTrue:[^ self].
  1147      set the state to something like #ioWait etc.
  1163 	]
  1148      In this case, do not set to #suspend.
  1164     ].
  1149      All of this to enhance the output of the process monitor ...
  1165 "
  1150     "
  1166 
  1151     aProcess setStateTo:#suspended if:#active or:#run.
  1167     "
  1152 
  1168      if the process to run has a lower than UserInterruptPriority,
  1153     (aProcess == activeProcess) ifTrue:[
  1169      arrange for an interrupt to occur on I/O.
  1154 	"we can immediately switch sometimes"
  1170      This is done by enabling IO-signals (if the OS supports them)
  1155 	l notEmpty ifTrue:[
  1171      or by installing a poll-interrupt after 50ms (if the OS does not).
  1156 	    p := l first
  1172     "
  1157 	] ifFalse:[
  1173     pri < UserInterruptPriority ifTrue:[
  1158 	    p := scheduler
  1174     
       
  1175 "comment out this if above is uncommented"
       
  1176 	anyTimeouts ifTrue:[
       
  1177 	    millis := self timeToNextTimeout.
       
  1178 	    millis == 0 ifTrue:[^ self].
       
  1179 	].
  1159 	].
  1180 "---"
  1160 	self threadSwitch:p 
  1181 
  1161     ].
  1182 	useIOInterrupts ifTrue:[
  1162 !
  1183 	    readFdArray do:[:fd |
  1163 
  1184 		fd notNil ifTrue:[
  1164 terminate:aProcess
  1185 		    OperatingSystem enableIOInterruptsOn:fd
  1165     "terminate aProcess. This is donen by sending aProcess the terminateSignal,
  1186 		].
  1166      which will evaluate any unwind blocks and finally do a hard terminate."
  1187 	    ].
  1167 
  1188 	] ifFalse:[
  1168     aProcess terminate
  1189 	    millis notNil ifTrue:[
  1169 !
  1190 		millis := millis min:50
  1170 
  1191 	    ] ifFalse:[
  1171 terminateActive
  1192 		millis := 50
  1172     "terminate the current process (i.e. the running process kills itself).
  1193 	    ]
  1173      The active process is sent the terminateSignal so it will evaluate any
  1194 	]
  1174      unwind blocks and finally do a hard terminate.
  1195     ].
  1175      This is sent for regular termination and by the VM, if the hard-stack limit
  1196 
  1176      is reached. (i.e. a process did not repair things in a recursionInterrupt and
  1197     millis notNil ifTrue:[
  1177      continued to grow its stack)"
  1198 	"schedule a clock interrupt after millis milliseconds"
  1178 
  1199 	OperatingSystem enableTimer:millis rounded.
  1179     activeProcess terminate
  1200     ].
  1180 !
  1201 
  1181 
  1202     "
  1182 terminateActiveNoSignal
  1203      now let the process run - will come back here by reschedule
  1183     "hard terminate the active process, without sending any
  1204      from ioInterrupt or timerInterrupt ... (running at max+1)
  1184      terminate signal thus no unwind blocks are evaluated."
  1205     "
  1185 
  1206     self threadSwitch:p.
  1186     self terminateNoSignal:activeProcess
  1207 
  1187 !
  1208     "... when we arrive here, we are back on stage"
  1188 
  1209 
  1189 terminateNoSignal:aProcess
  1210     millis notNil ifTrue:[
  1190     "hard terminate aProcess without sending the terminate signal, thus
  1211 	OperatingSystem disableTimer.
  1191      no unwind blocks or exitAction are performed in the process.. 
  1212 	self checkForInputWithTimeout:0.
  1192      If its not the current process, it is simply removed from its list 
  1213     ]
  1193      and physically destroyed. Otherwise (since we can't take away the chair
  1214 ! !
  1194      we are sitting on), a switch is forced and the process 
  1215 
  1195      will be physically destroyed by the next running process. 
  1216 !ProcessorScheduler methodsFor:'waiting'!
  1196      (see zombie handling)"
  1217 
  1197 
  1218 ioInterrupt
  1198     |pri id l wasBlocked|
  1219     "data arrived while waiting - switch to scheduler process which will decide 
  1199 
  1220      what to do now."
  1200     aProcess isNil ifTrue:[^ self].
  1221 
  1201     id := aProcess id.
  1222     interruptedProcess := activeProcess.
  1202     id isNil ifTrue:[^ self].   "already dead"
  1223     self threadSwitch:scheduler
  1203 
  1224 !
  1204     aProcess setId:nil state:#dead.
  1225 
  1205 
  1226 timerInterrupt
  1206     wasBlocked := OperatingSystem blockInterrupts.
  1227     "timer expired while waiting - switch to scheduler process which will decide 
  1207 
  1228      what to do now."
  1208     "remove the process from the runnable list"
  1229 
  1209 
  1230     interruptedProcess := activeProcess.
  1210     pri := aProcess priority.
  1231     self threadSwitch:scheduler
  1211     l := quiescentProcessLists at:pri.
  1232 !
  1212     (l identityIndexOf:aProcess) ~~ 0 ifTrue:[
  1233 
  1213 	l remove:aProcess.
  1234 timeToNextTimeout
  1214     ].
  1235     "return the delta-T (in millis) to next timeout, or nil if
  1215     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1236      there is none"
  1216 
  1237 
  1217     aProcess == activeProcess ifTrue:[
  1238     |aTime now delta minDelta n "{ Class: SmallInteger }"|
       
  1239 
       
  1240     "find next timeout. since there are usually not many, just search.
       
  1241      If there were many, the list should be kept sorted ... keeping deltas
       
  1242      to next (as in Unix kernel)"
       
  1243 
       
  1244     n := timeoutArray size.
       
  1245     1 to:n do:[:index |
       
  1246 	aTime := timeoutArray at:index.
       
  1247 	aTime notNil ifTrue:[
       
  1248 	    now isNil ifTrue:[
       
  1249 		now := OperatingSystem getMillisecondTime.
       
  1250 	    ].
       
  1251 	    (OperatingSystem millisecondTime:aTime isAfter:now) ifFalse:[^ 0].
       
  1252 	    delta := OperatingSystem millisecondTimeDeltaBetween:aTime and:now.
       
  1253 	    minDelta isNil ifTrue:[
       
  1254 		minDelta := delta
       
  1255 	    ] ifFalse:[
       
  1256 		minDelta := minDelta min:delta
       
  1257 	    ]
       
  1258 	]
       
  1259     ].
       
  1260 
       
  1261     ^ minDelta
       
  1262 !
       
  1263 
       
  1264 waitForEventOrTimeout
       
  1265     "entered when no process is runnable - wait for either input on
       
  1266      any file descriptors to arrive or a timeout to happen.
       
  1267      If it makes sense, do some background garbage collection.
       
  1268      The idle actions are a leftover from previous ST/X releases and will
       
  1269      vanish (installing a low-prio process has the same effect)."
       
  1270 
       
  1271     |millis doingGC|
       
  1272 
       
  1273     doingGC := true.
       
  1274     [doingGC] whileTrue:[
       
  1275 	anyTimeouts ifTrue:[
       
  1276 	    millis := self timeToNextTimeout.
       
  1277 	    (millis notNil and:[millis <= 0]) ifTrue:[
       
  1278 		^ self    "oops - hurry up checking"
       
  1279 	    ].
       
  1280 	].
       
  1281 
       
  1282 	"
  1218 	"
  1283 	 if its worth doing, collect a bit of garbage;
  1219 	 hard case - its the currently running process
  1284 	 but not, if a backgroundCollector is active
  1220 	 we must have the next active process destroy this one
       
  1221 	 (we cannot destroy the chair we are sitting on ... :-)
  1285 	"
  1222 	"
  1286 	ObjectMemory backgroundCollectorRunning ifTrue:[
  1223 	zombie := id.
  1287 	    doingGC := false
  1224 	self unRemember:aProcess.
  1288 	] ifFalse:[
  1225 	self threadSwitch:scheduler.
  1289 	    doingGC := ObjectMemory gcStepIfUseful.
  1226 	"not reached"
  1290 	].
  1227 	^ self
  1291 
  1228     ].
  1292 	"then do idle actions"
  1229     self class threadDestroy:id.
  1293 	(idleActions notNil and:[idleActions size ~~ 0]) ifTrue:[
  1230     self unRemember:aProcess.
  1294 	    idleActions do:[:aBlock |
  1231     ^ self
  1295 		aBlock value.
  1232 !
  1296 	    ].
  1233 
  1297 	    ^ self   "go back checking"
  1234 yield
  1298 	].
  1235     "move the currently running process to the end of the currentList
  1299 
  1236      and reschedule to the first in the list, thus switching to the 
  1300 	doingGC ifTrue:[
  1237      next same-prio-process."
  1301 	    (self checkForInputWithTimeout:0) ifTrue:[
  1238 
  1302 		^ self  "go back checking"
  1239     |l wasBlocked|
  1303 	    ]
  1240 
  1304 	]
  1241     wasBlocked := OperatingSystem blockInterrupts.
  1305     ].
  1242 
  1306 
  1243     "
  1307     (self checkForInputWithTimeout:0) ifTrue:[
  1244      debugging consistency check - will be removed later
  1308 	^ self  "go back checking"
  1245     "
  1309     ].
  1246     activeProcess priority ~~ currentPriority ifTrue:[
  1310 
  1247 	'oops process changed priority' errorPrintNL.
  1311     "absolutely nothing to do - simply wait"
  1248 	currentPriority := activeProcess priority.
  1312 
  1249     ].
  1313     OperatingSystem supportsSelect ifFalse:[
  1250 
  1314 	"SCO instant ShitStation has a bug here,
  1251     l := quiescentProcessLists at:currentPriority.
  1315 	 waiting always 1 sec in the select - therefore we delay a bit and
  1252 
  1316 	 return - effectively polling in 50ms cycles
  1253     "
       
  1254      debugging consistency checks - will be removed later
       
  1255     "
       
  1256     l isEmpty ifTrue:[
       
  1257 	wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1258 	'oops - empty runnable list' errorPrintNL.
       
  1259 	^ self
       
  1260     ].
       
  1261 
       
  1262     "
       
  1263      check if the running process is not the only one
       
  1264     "
       
  1265     l size ~~ 1 ifTrue:[
  1317 	"
  1266 	"
  1318 	OperatingSystem millisecondDelay:50.
  1267 	 bring running process to the end
  1319 	^ self
  1268 	"
  1320     ].
  1269 	l removeFirst.
  1321 
  1270 	l addLast:activeProcess.
  1322     millis isNil ifTrue:[
  1271 
  1323 	millis := 9999.
  1272 	"
  1324     ] ifFalse:[
  1273 	 and switch to first in the list
  1325 	millis := millis rounded
  1274 	"
  1326     ].
  1275 	self threadSwitch:(l first).
  1327     self checkForInputWithTimeout:millis
  1276     ].
  1328 !
  1277     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1329 
       
  1330 checkForInputWithTimeout:millis
       
  1331     "this is called, when there is absolutely nothing to do;
       
  1332      hard wait for either input to arrive or a timeout to occur."
       
  1333 
       
  1334     |fd index sema action|
       
  1335 
       
  1336     fd := OperatingSystem 
       
  1337 	      selectOnAnyReadable:readFdArray 
       
  1338 			 writable:writeFdArray
       
  1339 			exception:nil 
       
  1340 		      withTimeOut:millis.
       
  1341     fd notNil ifTrue:[
       
  1342 	index := readFdArray indexOf:fd.
       
  1343 	index ~~ 0 ifTrue:[
       
  1344 	    sema := readSemaphoreArray at:index.
       
  1345 	    sema notNil ifTrue:[
       
  1346 		sema signalOnce.
       
  1347 		^ true
       
  1348 	    ] ifFalse:[
       
  1349 		action := readCheckArray at:index.
       
  1350 		action notNil ifTrue:[
       
  1351 		    action value.
       
  1352 		     ^ true
       
  1353 		]
       
  1354 	    ]
       
  1355 	]
       
  1356     ].
       
  1357     ^ false
       
  1358 ! !
  1278 ! !
  1359 
  1279 
  1360 !ProcessorScheduler methodsFor:'semaphore signalling'!
  1280 !ProcessorScheduler methodsFor:'semaphore signalling'!
  1361 
  1281 
  1362 signal:aSemaphore onInput:aFileDescriptor
  1282 disableSemaphore:aSemaphore
  1363     "arrange for a semaphore to be triggered when input on aFileDescriptor
  1283     "disable triggering of a semaphore"
  1364      arrives."
       
  1365 
       
  1366     self signal:aSemaphore onInput:aFileDescriptor orCheck:nil
       
  1367 !
       
  1368 
       
  1369 signal:aSemaphore onInput:aFileDescriptor orCheck:aBlock
       
  1370     "arrange for a semaphore to be triggered when input on aFileDescriptor
       
  1371      arrives OR checkblock evaluates to true. 
       
  1372      (checkBlock is used for buffered input, where a select may not detect 
       
  1373       data already read into a buffer - as in Xlib)"
       
  1374 
  1284 
  1375     |idx "{ Class: SmallInteger }"
  1285     |idx "{ Class: SmallInteger }"
  1376      wasBlocked|
  1286      wasBlocked|
  1377 
  1287 
  1378     wasBlocked := OperatingSystem blockInterrupts.
  1288     wasBlocked := OperatingSystem blockInterrupts.
  1379     (readFdArray identityIndexOf:aFileDescriptor startingAt:1) == 0 ifTrue:[
  1289     idx := readSemaphoreArray identityIndexOf:aSemaphore startingAt:1.
  1380 	idx := readFdArray identityIndexOf:nil startingAt:1.
  1290     [idx ~~ 0] whileTrue:[
  1381 	idx ~~ 0 ifTrue:[
  1291 	readFdArray at:idx put:nil.
  1382 	    readFdArray at:idx put:aFileDescriptor.
  1292 	readSemaphoreArray at:idx put:nil.
  1383 	    readSemaphoreArray at:idx put:aSemaphore.
  1293 	readCheckArray at:idx put:nil.
  1384 	    readCheckArray at:idx put:aBlock
  1294 	idx := readSemaphoreArray identityIndexOf:aSemaphore startingAt:idx.
  1385 	] ifFalse:[
  1295     ].
  1386 	    readFdArray := readFdArray copyWith:aFileDescriptor.
  1296     idx := writeSemaphoreArray identityIndexOf:aSemaphore startingAt:1.
  1387 	    readSemaphoreArray := readSemaphoreArray copyWith:aSemaphore.
  1297     [idx ~~ 0] whileTrue:[
  1388 	    readCheckArray := readCheckArray copyWith:aBlock.
  1298 	writeFdArray at:idx put:nil.
  1389 	]
  1299 	writeSemaphoreArray at:idx put:nil.
  1390     ].
  1300 	idx := writeSemaphoreArray identityIndexOf:aSemaphore startingAt:idx.
  1391     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1301     ].
  1392 !
  1302     idx := timeoutSemaphoreArray identityIndexOf:aSemaphore startingAt:1.
  1393 
  1303     [idx ~~ 0] whileTrue:[
  1394 signal:aSemaphore onOutput:aFileDescriptor
  1304 	timeoutArray at:idx put:nil.
  1395     "arrange for a semaphore to be triggered when output on aFileDescriptor
  1305 	timeoutSemaphoreArray at:idx put:nil.
  1396      is possible. (i.e. can be written without blocking)"
  1306 	timeoutActionArray at:idx put:nil.
  1397 
  1307 	timeoutProcessArray at:idx put:nil.
  1398     |idx "{ Class: SmallInteger }"
  1308 	idx := timeoutSemaphoreArray identityIndexOf:aSemaphore startingAt:idx.
  1399      wasBlocked|
  1309     ].
  1400 
  1310     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1401     wasBlocked := OperatingSystem blockInterrupts.
       
  1402     (writeFdArray identityIndexOf:aFileDescriptor startingAt:1) == 0 ifTrue:[
       
  1403 	idx := writeFdArray identityIndexOf:nil startingAt:1.
       
  1404 	idx ~~ 0 ifTrue:[
       
  1405 	    writeFdArray at:idx put:aFileDescriptor.
       
  1406 	    writeSemaphoreArray at:idx put:aSemaphore.
       
  1407 	] ifFalse:[
       
  1408 	    writeFdArray := writeFdArray copyWith:aFileDescriptor.
       
  1409 	    writeSemaphoreArray := writeSemaphoreArray copyWith:aSemaphore.
       
  1410 	]
       
  1411     ].
       
  1412     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1413 !
       
  1414 
       
  1415 signal:aSemaphore afterSeconds:seconds
       
  1416     "arrange for a semaphore to be triggered after some seconds"
       
  1417 
       
  1418     self signal:aSemaphore afterMilliseconds:(seconds * 1000)
       
  1419 !
  1311 !
  1420 
  1312 
  1421 signal:aSemaphore afterMilliseconds:millis
  1313 signal:aSemaphore afterMilliseconds:millis
  1422     "arrange for a semaphore to be triggered after some milliseconds"
  1314     "arrange for a semaphore to be triggered after some milliseconds"
  1423 
  1315 
  1426     wasBlocked := OperatingSystem blockInterrupts.
  1318     wasBlocked := OperatingSystem blockInterrupts.
  1427     now := OperatingSystem getMillisecondTime.
  1319     now := OperatingSystem getMillisecondTime.
  1428     then := OperatingSystem millisecondTimeAdd:now and:millis rounded.
  1320     then := OperatingSystem millisecondTimeAdd:now and:millis rounded.
  1429     self signal:aSemaphore atMilliseconds:then.
  1321     self signal:aSemaphore atMilliseconds:then.
  1430     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1322     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1323 !
       
  1324 
       
  1325 signal:aSemaphore afterSeconds:seconds
       
  1326     "arrange for a semaphore to be triggered after some seconds"
       
  1327 
       
  1328     self signal:aSemaphore afterMilliseconds:(seconds * 1000)
  1431 !
  1329 !
  1432 
  1330 
  1433 signal:aSemaphore atMilliseconds:aMillisecondTime
  1331 signal:aSemaphore atMilliseconds:aMillisecondTime
  1434     "arrange for a semaphore to be triggered at a specific millisecond time.
  1332     "arrange for a semaphore to be triggered at a specific millisecond time.
  1435      If there is already a pending trigger time, the time is changed."
  1333      If there is already a pending trigger time, the time is changed."
  1458 
  1356 
  1459     anyTimeouts := true.
  1357     anyTimeouts := true.
  1460     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1358     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1461 !
  1359 !
  1462 
  1360 
  1463 disableSemaphore:aSemaphore
  1361 signal:aSemaphore onInput:aFileDescriptor
  1464     "disable triggering of a semaphore"
  1362     "arrange for a semaphore to be triggered when input on aFileDescriptor
       
  1363      arrives."
       
  1364 
       
  1365     self signal:aSemaphore onInput:aFileDescriptor orCheck:nil
       
  1366 !
       
  1367 
       
  1368 signal:aSemaphore onInput:aFileDescriptor orCheck:aBlock
       
  1369     "arrange for a semaphore to be triggered when input on aFileDescriptor
       
  1370      arrives OR checkblock evaluates to true. 
       
  1371      (checkBlock is used for buffered input, where a select may not detect 
       
  1372       data already read into a buffer - as in Xlib)"
  1465 
  1373 
  1466     |idx "{ Class: SmallInteger }"
  1374     |idx "{ Class: SmallInteger }"
  1467      wasBlocked|
       
  1468 
       
  1469     wasBlocked := OperatingSystem blockInterrupts.
       
  1470     idx := readSemaphoreArray identityIndexOf:aSemaphore startingAt:1.
       
  1471     [idx ~~ 0] whileTrue:[
       
  1472 	readFdArray at:idx put:nil.
       
  1473 	readSemaphoreArray at:idx put:nil.
       
  1474 	readCheckArray at:idx put:nil.
       
  1475 	idx := readSemaphoreArray identityIndexOf:aSemaphore startingAt:idx.
       
  1476     ].
       
  1477     idx := writeSemaphoreArray identityIndexOf:aSemaphore startingAt:1.
       
  1478     [idx ~~ 0] whileTrue:[
       
  1479 	writeFdArray at:idx put:nil.
       
  1480 	writeSemaphoreArray at:idx put:nil.
       
  1481 	idx := writeSemaphoreArray identityIndexOf:aSemaphore startingAt:idx.
       
  1482     ].
       
  1483     idx := timeoutSemaphoreArray identityIndexOf:aSemaphore startingAt:1.
       
  1484     [idx ~~ 0] whileTrue:[
       
  1485 	timeoutArray at:idx put:nil.
       
  1486 	timeoutSemaphoreArray at:idx put:nil.
       
  1487 	timeoutActionArray at:idx put:nil.
       
  1488 	timeoutProcessArray at:idx put:nil.
       
  1489 	idx := timeoutSemaphoreArray identityIndexOf:aSemaphore startingAt:idx.
       
  1490     ].
       
  1491     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1492 ! !
       
  1493 
       
  1494 !ProcessorScheduler methodsFor:'background processing'!
       
  1495 
       
  1496 addIdleBlock:aBlock
       
  1497     "add the argument, aBlock to the list of idle-actions.
       
  1498      Idle blocks are evaluated whenever no other process is runnable,
       
  1499      and no events are pending.
       
  1500      Use of idle blocks is not recommended, use a low priority processes 
       
  1501      instead, which has the same effect. Idle blcoks are still included
       
  1502      to support background actions in pure-event systems, where no processes 
       
  1503      are available.
       
  1504      Support for idle-blocks may vanish."
       
  1505 
       
  1506     |wasBlocked|
       
  1507 
       
  1508     wasBlocked := OperatingSystem blockInterrupts.
       
  1509     idleActions isNil ifTrue:[
       
  1510 	idleActions := OrderedCollection new
       
  1511     ].
       
  1512     idleActions add:aBlock.
       
  1513     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1514 !
       
  1515 
       
  1516 removeIdleBlock:aBlock
       
  1517     "remove the argument, aBlock from the list of idle-blocks.
       
  1518      Support for idle-blocks may vanish - use low prio processes instead."
       
  1519 
       
  1520     |wasBlocked|
       
  1521 
       
  1522     wasBlocked := OperatingSystem blockInterrupts.
       
  1523     idleActions notNil ifTrue:[
       
  1524        idleActions remove:aBlock
       
  1525     ].
       
  1526     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1527 ! !
       
  1528 
       
  1529 !ProcessorScheduler methodsFor:'I/O event actions'!
       
  1530 
       
  1531 enableIOAction:aBlock onInput:aFileDescriptor
       
  1532     "half-obsolete event support: arrange for aBlock to be
       
  1533      evaluated when input on aFileDescriptor arrives. 
       
  1534      This is a leftover support for pure-event systems and may vanish."
       
  1535 
       
  1536     |idx "{Class: SmallInteger }"
       
  1537      wasBlocked|
  1375      wasBlocked|
  1538 
  1376 
  1539     wasBlocked := OperatingSystem blockInterrupts.
  1377     wasBlocked := OperatingSystem blockInterrupts.
  1540     (readFdArray identityIndexOf:aFileDescriptor startingAt:1) == 0 ifTrue:[
  1378     (readFdArray identityIndexOf:aFileDescriptor startingAt:1) == 0 ifTrue:[
  1541 	idx := readFdArray identityIndexOf:nil startingAt:1.
  1379 	idx := readFdArray identityIndexOf:nil startingAt:1.
  1542 	idx ~~ 0 ifTrue:[
  1380 	idx ~~ 0 ifTrue:[
  1543 	    readFdArray at:idx put:aFileDescriptor.
  1381 	    readFdArray at:idx put:aFileDescriptor.
  1544 	    readCheckArray at:idx put:aBlock.
  1382 	    readSemaphoreArray at:idx put:aSemaphore.
  1545 	    readSemaphoreArray at:idx put:nil
  1383 	    readCheckArray at:idx put:aBlock
  1546 	] ifFalse:[
  1384 	] ifFalse:[
  1547 	    readFdArray := readFdArray copyWith:aFileDescriptor.
  1385 	    readFdArray := readFdArray copyWith:aFileDescriptor.
       
  1386 	    readSemaphoreArray := readSemaphoreArray copyWith:aSemaphore.
  1548 	    readCheckArray := readCheckArray copyWith:aBlock.
  1387 	    readCheckArray := readCheckArray copyWith:aBlock.
  1549 	    readSemaphoreArray := readSemaphoreArray copyWith:nil.
       
  1550 	]
  1388 	]
  1551     ].
  1389     ].
  1552     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1390     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1553 !
  1391 !
  1554 
  1392 
  1555 disableFd:aFileDescriptor
  1393 signal:aSemaphore onOutput:aFileDescriptor
  1556     "disable block events on aFileDescriptor.
  1394     "arrange for a semaphore to be triggered when output on aFileDescriptor
  1557      This is a leftover support for pure-event systems and may vanish."
  1395      is possible. (i.e. can be written without blocking)"
  1558 
  1396 
  1559     |idx "{Class: SmallInteger }" 
  1397     |idx "{ Class: SmallInteger }"
  1560      wasBlocked|
  1398      wasBlocked|
  1561 
  1399 
  1562     wasBlocked := OperatingSystem blockInterrupts.
  1400     wasBlocked := OperatingSystem blockInterrupts.
  1563     idx := readFdArray identityIndexOf:aFileDescriptor startingAt:1.
  1401     (writeFdArray identityIndexOf:aFileDescriptor startingAt:1) == 0 ifTrue:[
  1564     idx ~~ 0 ifTrue:[
  1402 	idx := writeFdArray identityIndexOf:nil startingAt:1.
  1565 	readFdArray at:idx put:nil.
  1403 	idx ~~ 0 ifTrue:[
  1566 	readCheckArray at:idx put:nil.
  1404 	    writeFdArray at:idx put:aFileDescriptor.
  1567 	readSemaphoreArray at:idx put:nil
  1405 	    writeSemaphoreArray at:idx put:aSemaphore.
       
  1406 	] ifFalse:[
       
  1407 	    writeFdArray := writeFdArray copyWith:aFileDescriptor.
       
  1408 	    writeSemaphoreArray := writeSemaphoreArray copyWith:aSemaphore.
       
  1409 	]
  1568     ].
  1410     ].
  1569     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1411     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1570 ! !
  1412 ! !
  1571 
  1413 
  1572 !ProcessorScheduler methodsFor:'timeout handling'!
  1414 !ProcessorScheduler methodsFor:'timeout handling'!
       
  1415 
       
  1416 addTimedBlock:aBlock afterMilliseconds:delta
       
  1417     "add the argument, aBlock to the list of time-scheduled-blocks; to be
       
  1418      evaluated after delta milliseconds. The process which installs this timed 
       
  1419      block will be interrupted for execution of the block.
       
  1420      (if it is running, the interrupt will occur in whatever method it is
       
  1421       executing; if it is suspended, it will be resumed).
       
  1422      The block will be removed from the timed-block list after evaluation 
       
  1423      (i.e. it will trigger only once)."
       
  1424 
       
  1425     ^ self addTimedBlock:aBlock for:activeProcess afterMilliseconds:delta
       
  1426 !
  1573 
  1427 
  1574 addTimedBlock:aBlock afterSeconds:delta
  1428 addTimedBlock:aBlock afterSeconds:delta
  1575     "add the argument, aBlock to the list of time-scheduled-blocks.
  1429     "add the argument, aBlock to the list of time-scheduled-blocks.
  1576      to be evaluated after delta seconds. The process which installs this timed 
  1430      to be evaluated after delta seconds. The process which installs this timed 
  1577      block will be interrupted for execution of the block.
  1431      block will be interrupted for execution of the block.
  1579       executing; if it is suspended, it will be resumed).
  1433       executing; if it is suspended, it will be resumed).
  1580      The block will be removed from the timed-block list after evaluation 
  1434      The block will be removed from the timed-block list after evaluation 
  1581      (i.e. it will trigger only once)."
  1435      (i.e. it will trigger only once)."
  1582 
  1436 
  1583     self addTimedBlock:aBlock for:activeProcess afterMilliseconds:(delta * 1000) rounded
  1437     self addTimedBlock:aBlock for:activeProcess afterMilliseconds:(delta * 1000) rounded
       
  1438 !
       
  1439 
       
  1440 addTimedBlock:aBlock atMilliseconds:aMillisecondTime
       
  1441     "add the argument, aBlock to the list of time-scheduled-blocks; to be
       
  1442      evaluated when the millisecondClock value passes aMillisecondTime.
       
  1443      The process which installs this timed block will be interrupted for 
       
  1444      execution of the block.
       
  1445      (if it is running, the interrupt will occur in whatever method it is
       
  1446       executing; if it is suspended, it will be resumed).
       
  1447      The block will be removed from the timed-block list after evaluation 
       
  1448      (i.e. it will trigger only once)."     
       
  1449 
       
  1450     self addTimedBlock:aBlock for:activeProcess atMilliseconds:aMillisecondTime
       
  1451 !
       
  1452 
       
  1453 addTimedBlock:aBlock for:aProcess afterMilliseconds:delta
       
  1454     "add the argument, aBlock to the list of time-scheduled-blocks; to be
       
  1455      evaluated after delta milliseconds. The process specified by the argument,
       
  1456      aProcess will be interrupted for execution of the block. 
       
  1457      (if it is running, the interrupt will occur in whatever method it is
       
  1458       executing; if it is suspended, it will be resumed).
       
  1459      If aProcess is nil, the block will be evaluated by the scheduler itself
       
  1460      (which is dangerous - the block should not raise any error conditions).
       
  1461      The block will be removed from the timed-block list after evaluation 
       
  1462      (i.e. it will trigger only once)."
       
  1463 
       
  1464     |now then wasBlocked|
       
  1465 
       
  1466     wasBlocked := OperatingSystem blockInterrupts.
       
  1467     now := OperatingSystem getMillisecondTime.
       
  1468     then := OperatingSystem millisecondTimeAdd:now and:delta.
       
  1469     self addTimedBlock:aBlock for:aProcess atMilliseconds:then.
       
  1470     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1584 !
  1471 !
  1585 
  1472 
  1586 addTimedBlock:aBlock for:aProcess afterSeconds:delta
  1473 addTimedBlock:aBlock for:aProcess afterSeconds:delta
  1587     "add the argument, aBlock to the list of time-scheduled-blocks.
  1474     "add the argument, aBlock to the list of time-scheduled-blocks.
  1588      to be evaluated after delta seconds. aProcess will be interrupted for 
  1475      to be evaluated after delta seconds. aProcess will be interrupted for 
  1593      (which is dangerous - the block should not raise any error conditions).
  1480      (which is dangerous - the block should not raise any error conditions).
  1594      The block will be removed from the timed-block list after evaluation 
  1481      The block will be removed from the timed-block list after evaluation 
  1595      (i.e. it will trigger only once)."
  1482      (i.e. it will trigger only once)."
  1596 
  1483 
  1597     self addTimedBlock:aBlock for:aProcess afterMilliseconds:(delta * 1000) rounded
  1484     self addTimedBlock:aBlock for:aProcess afterMilliseconds:(delta * 1000) rounded
  1598 !
       
  1599 
       
  1600 addTimedBlock:aBlock afterMilliseconds:delta
       
  1601     "add the argument, aBlock to the list of time-scheduled-blocks; to be
       
  1602      evaluated after delta milliseconds. The process which installs this timed 
       
  1603      block will be interrupted for execution of the block.
       
  1604      (if it is running, the interrupt will occur in whatever method it is
       
  1605       executing; if it is suspended, it will be resumed).
       
  1606      The block will be removed from the timed-block list after evaluation 
       
  1607      (i.e. it will trigger only once)."
       
  1608 
       
  1609     ^ self addTimedBlock:aBlock for:activeProcess afterMilliseconds:delta
       
  1610 !
       
  1611 
       
  1612 addTimedBlock:aBlock for:aProcess afterMilliseconds:delta
       
  1613     "add the argument, aBlock to the list of time-scheduled-blocks; to be
       
  1614      evaluated after delta milliseconds. The process specified by the argument,
       
  1615      aProcess will be interrupted for execution of the block. 
       
  1616      (if it is running, the interrupt will occur in whatever method it is
       
  1617       executing; if it is suspended, it will be resumed).
       
  1618      If aProcess is nil, the block will be evaluated by the scheduler itself
       
  1619      (which is dangerous - the block should not raise any error conditions).
       
  1620      The block will be removed from the timed-block list after evaluation 
       
  1621      (i.e. it will trigger only once)."
       
  1622 
       
  1623     |now then wasBlocked|
       
  1624 
       
  1625     wasBlocked := OperatingSystem blockInterrupts.
       
  1626     now := OperatingSystem getMillisecondTime.
       
  1627     then := OperatingSystem millisecondTimeAdd:now and:delta.
       
  1628     self addTimedBlock:aBlock for:aProcess atMilliseconds:then.
       
  1629     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1630 !
       
  1631 
       
  1632 addTimedBlock:aBlock atMilliseconds:aMillisecondTime
       
  1633     "add the argument, aBlock to the list of time-scheduled-blocks; to be
       
  1634      evaluated when the millisecondClock value passes aMillisecondTime.
       
  1635      The process which installs this timed block will be interrupted for 
       
  1636      execution of the block.
       
  1637      (if it is running, the interrupt will occur in whatever method it is
       
  1638       executing; if it is suspended, it will be resumed).
       
  1639      The block will be removed from the timed-block list after evaluation 
       
  1640      (i.e. it will trigger only once)."     
       
  1641 
       
  1642     self addTimedBlock:aBlock for:activeProcess atMilliseconds:aMillisecondTime
       
  1643 !
  1485 !
  1644 
  1486 
  1645 addTimedBlock:aBlock for:aProcess atMilliseconds:aMillisecondTime
  1487 addTimedBlock:aBlock for:aProcess atMilliseconds:aMillisecondTime
  1646     "add the argument, aBlock to the list of time-scheduled-blocks; to be
  1488     "add the argument, aBlock to the list of time-scheduled-blocks; to be
  1647      evaluated by aProcess when the millisecondClock value passes 
  1489      evaluated by aProcess when the millisecondClock value passes 
  1679 	    timeoutProcessArray := timeoutProcessArray copyWith:aProcess.
  1521 	    timeoutProcessArray := timeoutProcessArray copyWith:aProcess.
  1680 	].
  1522 	].
  1681     ].
  1523     ].
  1682 
  1524 
  1683     anyTimeouts := true.
  1525     anyTimeouts := true.
  1684     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1685 !
       
  1686 
       
  1687 removeTimedBlock:aBlock
       
  1688     "remove the argument, aBlock from the list of time-sceduled-blocks."
       
  1689 
       
  1690     |index "{ Class: SmallInteger }"
       
  1691      wasBlocked|
       
  1692 
       
  1693     wasBlocked := OperatingSystem blockInterrupts.
       
  1694     index := timeoutActionArray identityIndexOf:aBlock startingAt:1.
       
  1695     (index ~~ 0) ifTrue:[
       
  1696 	timeoutArray at:index put:nil.
       
  1697 	timeoutActionArray at:index put:nil. 
       
  1698 	timeoutSemaphoreArray at:index put:nil.
       
  1699 	timeoutProcessArray at:index put:nil.
       
  1700     ].
       
  1701     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1526     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
  1702 !
  1527 !
  1703 
  1528 
  1704 evaluateTimeouts
  1529 evaluateTimeouts
  1705     "walk through timeouts and evaluate blocks or signal semas that need to be .."
  1530     "walk through timeouts and evaluate blocks or signal semas that need to be .."
  1757 	    ] ifFalse:[
  1582 	    ] ifFalse:[
  1758 		p interruptWith:block
  1583 		p interruptWith:block
  1759 	    ]
  1584 	    ]
  1760 	]
  1585 	]
  1761     ]
  1586     ]
  1762 ! !
  1587 !
       
  1588 
       
  1589 removeTimedBlock:aBlock
       
  1590     "remove the argument, aBlock from the list of time-sceduled-blocks."
       
  1591 
       
  1592     |index "{ Class: SmallInteger }"
       
  1593      wasBlocked|
       
  1594 
       
  1595     wasBlocked := OperatingSystem blockInterrupts.
       
  1596     index := timeoutActionArray identityIndexOf:aBlock startingAt:1.
       
  1597     (index ~~ 0) ifTrue:[
       
  1598 	timeoutArray at:index put:nil.
       
  1599 	timeoutActionArray at:index put:nil. 
       
  1600 	timeoutSemaphoreArray at:index put:nil.
       
  1601 	timeoutProcessArray at:index put:nil.
       
  1602     ].
       
  1603     wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
       
  1604 ! !
       
  1605 
       
  1606 !ProcessorScheduler methodsFor:'waiting'!
       
  1607 
       
  1608 checkForInputWithTimeout:millis
       
  1609     "this is called, when there is absolutely nothing to do;
       
  1610      hard wait for either input to arrive or a timeout to occur."
       
  1611 
       
  1612     |fd index sema action|
       
  1613 
       
  1614     fd := OperatingSystem 
       
  1615 	      selectOnAnyReadable:readFdArray 
       
  1616 			 writable:writeFdArray
       
  1617 			exception:nil 
       
  1618 		      withTimeOut:millis.
       
  1619     fd notNil ifTrue:[
       
  1620 	index := readFdArray indexOf:fd.
       
  1621 	index ~~ 0 ifTrue:[
       
  1622 	    sema := readSemaphoreArray at:index.
       
  1623 	    sema notNil ifTrue:[
       
  1624 		sema signalOnce.
       
  1625 		^ true
       
  1626 	    ] ifFalse:[
       
  1627 		action := readCheckArray at:index.
       
  1628 		action notNil ifTrue:[
       
  1629 		    action value.
       
  1630 		     ^ true
       
  1631 		]
       
  1632 	    ]
       
  1633 	]
       
  1634     ].
       
  1635     ^ false
       
  1636 !
       
  1637 
       
  1638 ioInterrupt
       
  1639     "data arrived while waiting - switch to scheduler process which will decide 
       
  1640      what to do now."
       
  1641 
       
  1642     interruptedProcess := activeProcess.
       
  1643     self threadSwitch:scheduler
       
  1644 !
       
  1645 
       
  1646 timeToNextTimeout
       
  1647     "return the delta-T (in millis) to next timeout, or nil if
       
  1648      there is none"
       
  1649 
       
  1650     |aTime now delta minDelta n "{ Class: SmallInteger }"|
       
  1651 
       
  1652     "find next timeout. since there are usually not many, just search.
       
  1653      If there were many, the list should be kept sorted ... keeping deltas
       
  1654      to next (as in Unix kernel)"
       
  1655 
       
  1656     n := timeoutArray size.
       
  1657     1 to:n do:[:index |
       
  1658 	aTime := timeoutArray at:index.
       
  1659 	aTime notNil ifTrue:[
       
  1660 	    now isNil ifTrue:[
       
  1661 		now := OperatingSystem getMillisecondTime.
       
  1662 	    ].
       
  1663 	    (OperatingSystem millisecondTime:aTime isAfter:now) ifFalse:[^ 0].
       
  1664 	    delta := OperatingSystem millisecondTimeDeltaBetween:aTime and:now.
       
  1665 	    minDelta isNil ifTrue:[
       
  1666 		minDelta := delta
       
  1667 	    ] ifFalse:[
       
  1668 		minDelta := minDelta min:delta
       
  1669 	    ]
       
  1670 	]
       
  1671     ].
       
  1672 
       
  1673     ^ minDelta
       
  1674 !
       
  1675 
       
  1676 timerInterrupt
       
  1677     "timer expired while waiting - switch to scheduler process which will decide 
       
  1678      what to do now."
       
  1679 
       
  1680     interruptedProcess := activeProcess.
       
  1681     self threadSwitch:scheduler
       
  1682 !
       
  1683 
       
  1684 waitForEventOrTimeout
       
  1685     "entered when no process is runnable - wait for either input on
       
  1686      any file descriptors to arrive or a timeout to happen.
       
  1687      If it makes sense, do some background garbage collection.
       
  1688      The idle actions are a leftover from previous ST/X releases and will
       
  1689      vanish (installing a low-prio process has the same effect)."
       
  1690 
       
  1691     |millis doingGC|
       
  1692 
       
  1693     doingGC := true.
       
  1694     [doingGC] whileTrue:[
       
  1695 	anyTimeouts ifTrue:[
       
  1696 	    millis := self timeToNextTimeout.
       
  1697 	    (millis notNil and:[millis <= 0]) ifTrue:[
       
  1698 		^ self    "oops - hurry up checking"
       
  1699 	    ].
       
  1700 	].
       
  1701 
       
  1702 	"
       
  1703 	 if its worth doing, collect a bit of garbage;
       
  1704 	 but not, if a backgroundCollector is active
       
  1705 	"
       
  1706 	ObjectMemory backgroundCollectorRunning ifTrue:[
       
  1707 	    doingGC := false
       
  1708 	] ifFalse:[
       
  1709 	    doingGC := ObjectMemory gcStepIfUseful.
       
  1710 	].
       
  1711 
       
  1712 	"then do idle actions"
       
  1713 	(idleActions notNil and:[idleActions size ~~ 0]) ifTrue:[
       
  1714 	    idleActions do:[:aBlock |
       
  1715 		aBlock value.
       
  1716 	    ].
       
  1717 	    ^ self   "go back checking"
       
  1718 	].
       
  1719 
       
  1720 	doingGC ifTrue:[
       
  1721 	    (self checkForInputWithTimeout:0) ifTrue:[
       
  1722 		^ self  "go back checking"
       
  1723 	    ]
       
  1724 	]
       
  1725     ].
       
  1726 
       
  1727     (self checkForInputWithTimeout:0) ifTrue:[
       
  1728 	^ self  "go back checking"
       
  1729     ].
       
  1730 
       
  1731     "absolutely nothing to do - simply wait"
       
  1732 
       
  1733     OperatingSystem supportsSelect ifFalse:[
       
  1734 	"SCO instant ShitStation has a bug here,
       
  1735 	 waiting always 1 sec in the select - therefore we delay a bit and
       
  1736 	 return - effectively polling in 50ms cycles
       
  1737 	"
       
  1738 	OperatingSystem millisecondDelay:50.
       
  1739 	^ self
       
  1740     ].
       
  1741 
       
  1742     millis isNil ifTrue:[
       
  1743 	millis := 9999.
       
  1744     ] ifFalse:[
       
  1745 	millis := millis rounded
       
  1746     ].
       
  1747     self checkForInputWithTimeout:millis
       
  1748 ! !
       
  1749 
       
  1750 !ProcessorScheduler class methodsFor:'documentation'!
       
  1751 
       
  1752 version
       
  1753     ^ '$Header: /cvs/stx/stx/libbasic/Attic/ProcSched.st,v 1.53 1995-12-07 21:29:55 cg Exp $'
       
  1754 ! !
       
  1755 ProcessorScheduler initialize!