ProcessorScheduler.st
changeset 1 a27a279701f8
child 2 6526dde5f3ac
equal deleted inserted replaced
0:aa2498ef6470 1:a27a279701f8
       
     1 "
       
     2  COPYRIGHT (c) 1993 by Claus Gittinger
       
     3               All Rights Reserved
       
     4 
       
     5  This software is furnished under a license and may be used
       
     6  only in accordance with the terms of that license and with the
       
     7  inclusion of the above copyright notice.   This software may not
       
     8  be provided or otherwise made available to, or used by, any
       
     9  other person.  No title to or ownership of the software is
       
    10  hereby transferred.
       
    11 "
       
    12 
       
    13 Object subclass:#ProcessorScheduler
       
    14          instanceVariableNames:'runnable zombie
       
    15                                 currentProcess currentPriority
       
    16                                 fileDescriptors fileHandlers fileSelectors
       
    17                                 timeoutTimes timeHandlers timeSelectors'
       
    18          classVariableNames:'KnownProcesses KnownProcessIds'
       
    19          poolDictionaries:''
       
    20          category:'Kernel-Processes'
       
    21 !
       
    22 
       
    23 ProcessorScheduler comment:'
       
    24 
       
    25 COPYRIGHT (c) 1993 by Claus Gittinger
       
    26              All Rights Reserved
       
    27 
       
    28 %W% %E%
       
    29 '!
       
    30 
       
    31 Smalltalk at:#Processor put:nil!
       
    32 
       
    33 !ProcessorScheduler class methodsFor:'initialization'!
       
    34 
       
    35 initialize
       
    36     KnownProcesses isNil ifTrue:[
       
    37         KnownProcesses := ShadowArray new:5.
       
    38         KnownProcesses watcher:self.
       
    39         KnownProcessIds := OrderedCollection new.
       
    40 
       
    41         "want to get informed when returning from snapshot"
       
    42         ObjectMemory addDependent:self
       
    43     ].
       
    44 
       
    45     "create the one and only processor"
       
    46 
       
    47     Processor := self new
       
    48 !
       
    49 
       
    50 update:something
       
    51     something == #returnFromSnapshot ifTrue:[
       
    52         self reinstallProcesses
       
    53     ]
       
    54 !
       
    55 
       
    56 reinstallProcesses
       
    57     "recreate all processes after a snapShot load"
       
    58 
       
    59    KnownProcesses do:[:p |
       
    60         p notNil ifTrue:[
       
    61             "how, exactly should this be done ?"
       
    62 
       
    63             p id ~~ 0 ifTrue:[
       
    64                 Transcript showCr:'process restart in preparation'
       
    65             ]
       
    66         ]
       
    67     ]
       
    68 ! !
       
    69 
       
    70 !ProcessorScheduler class methodsFor:'private process primitives'!
       
    71 
       
    72 threadCreate:aBlock
       
    73     "physical creation of a process executing aBlock.
       
    74      (warning: low level entry, no administration done)"
       
    75 %{
       
    76     int tid;
       
    77     extern int __threadCreate();
       
    78 
       
    79     tid = __threadCreate(aBlock);
       
    80     if (tid != 0) {
       
    81         RETURN ( _MKSMALLINT(tid));
       
    82     }
       
    83     RETURN (nil);
       
    84 %}
       
    85 !
       
    86 
       
    87 threadDestroy:id
       
    88     "physical destroy other process ...
       
    89      (warning: low level entry, no administration done)"
       
    90 
       
    91 %{
       
    92     if (_isSmallInteger(id)) {
       
    93         __threadDestroy(_intVal(id));
       
    94     }
       
    95 %}
       
    96 .
       
    97     self primitiveFailed
       
    98 ! !
       
    99 
       
   100 !ProcessorScheduler class methodsFor:'instance release'!
       
   101 
       
   102 informDispose
       
   103     "some Process has been collected - terminate the underlying thread"
       
   104 
       
   105     |id
       
   106      index "<SmallInteger>"
       
   107      sz    "<SmallInteger>"|
       
   108 
       
   109     index := 1.
       
   110     sz := KnownProcessIds size.
       
   111     [index <= sz] whileTrue:[
       
   112         (KnownProcesses at:index) isNil ifTrue:[
       
   113             id := KnownProcessIds at:index.
       
   114             id notNil ifTrue:[
       
   115                 Transcript showCr:('terminate thread (no longer refd) ', id printString).
       
   116                 self threadDestroy:id.
       
   117                 KnownProcessIds at:index put:nil.
       
   118             ]
       
   119         ].
       
   120         index := index + 1
       
   121     ]
       
   122 ! !
       
   123 
       
   124 !ProcessorScheduler class methodsFor:'instance creation'!
       
   125 
       
   126 new
       
   127     "there is (currently) only one processor ..."
       
   128 
       
   129     Processor notNil ifTrue:[^ Processor].
       
   130     ^ self basicNew initialize.
       
   131 ! !
       
   132 
       
   133 !ProcessorScheduler methodsFor:'constants'!
       
   134 
       
   135 minPriority
       
   136     ^ 1
       
   137 !
       
   138 
       
   139 maxPriority
       
   140     ^ 31
       
   141 !
       
   142 
       
   143 userInterruptPriority
       
   144     ^ 24
       
   145 !
       
   146 
       
   147 timingPriority
       
   148     ^ 16
       
   149 !
       
   150 
       
   151 userSchedulingPriority
       
   152     ^ 8
       
   153 !
       
   154 
       
   155 userBackgroundPriority
       
   156     ^ 6
       
   157 !
       
   158 
       
   159 systemBackgroundPriority
       
   160     ^ 4
       
   161 ! !
       
   162 
       
   163 !ProcessorScheduler methodsFor:'private initializing'!
       
   164 
       
   165 initialize
       
   166     "initialize the one-and-only ProcessorScheduler"
       
   167 
       
   168     |nPrios l|
       
   169 
       
   170     nPrios := self maxPriority - self minPriority + 1.
       
   171 
       
   172     runnable := Array new:nPrios.
       
   173 
       
   174     "setup the first (init-) process"
       
   175     currentProcess := Process new.
       
   176     currentProcess id:0.
       
   177     currentProcess state:#running.
       
   178     currentPriority := self userSchedulingPriority.
       
   179     currentProcess setPriority:currentPriority.
       
   180 
       
   181     l := LinkedList new.
       
   182     l add:currentProcess.
       
   183 
       
   184     runnable at:currentPriority put:l.
       
   185 
       
   186     IOInterruptHandler := self.
       
   187     OperatingSystem enableIOInterrupts.
       
   188 ! !
       
   189 
       
   190 !ProcessorScheduler methodsFor:'private'!
       
   191 
       
   192 remember:aProcess
       
   193     |newShadow newSize oldSize oldId
       
   194      index "<SmallInteger>"
       
   195      sz    "<SmallInteger>" |
       
   196 
       
   197     index := 1.
       
   198     sz := KnownProcessIds size.
       
   199     [index <= sz] whileTrue:[
       
   200         (KnownProcesses at:index) isNil ifTrue:[
       
   201             oldId := KnownIds at:index.
       
   202             oldId notNil ifTrue:[
       
   203                 self class terminateProcess:oldId
       
   204             ].
       
   205             KnownProcesses at:index put:aProcess.
       
   206             KnownProcessIds at:index put:aProcess id.
       
   207             ^ self
       
   208         ].
       
   209         index := index + 1
       
   210     ].
       
   211 
       
   212     KnownProcessIds grow:index.
       
   213     KnownProcessIds at:index put:aProcess id.
       
   214 
       
   215     oldSize := KnownProcesses size.
       
   216     (index > oldSize) ifTrue:[
       
   217         newShadow := ShadowArray new:(oldSize * 2).
       
   218         newShadow watcher:(KnownProcesses watcher).
       
   219         newShadow replaceFrom:1 with:KnownProcesses.
       
   220         KnownProcesses := newShadow
       
   221     ].
       
   222     KnownProcesses at:index put:aProcess 
       
   223 ! !
       
   224 
       
   225 !ProcessorScheduler methodsFor:'process creation'!
       
   226 
       
   227 newProcessFor:aBlock
       
   228     "create a new process executing aBlock. Return a process (or
       
   229      nil if fail). The new process is not scheduled. To start it 
       
   230      running, it needs a Process>>resume."
       
   231 
       
   232     |id p|
       
   233 
       
   234     id := self class threadCreate:aBlock.
       
   235     id notNil ifTrue:[
       
   236         p := Process new.
       
   237         p id:id.
       
   238         p startBlock:aBlock.
       
   239         p state:#suspended.
       
   240         p setPriority:currentPriority.
       
   241         self remember:p.
       
   242     ].
       
   243     ^ p
       
   244 ! !
       
   245 
       
   246 !ProcessorScheduler methodsFor:'scheduling'!
       
   247 
       
   248 switchTo:aProcess
       
   249     "continue execution in aProcess."
       
   250 
       
   251     |id pri|
       
   252 
       
   253     aProcess isNil ifTrue:[^ self].
       
   254     aProcess == currentProcess ifTrue:[^ self].
       
   255 
       
   256     id := aProcess id.
       
   257     pri := aProcess priority.
       
   258     currentProcess state:#runnable.
       
   259 
       
   260     "no interrupts now ..."
       
   261     currentProcess := aProcess.
       
   262     currentProcess state:#running.
       
   263     currentPriority := pri.
       
   264 %{
       
   265     __threadSwitch(__context, _intVal(id));
       
   266 %}
       
   267 .
       
   268     zombie notNil ifTrue:[
       
   269         self class threadDestroy:zombie.
       
   270         zombie := nil
       
   271     ]
       
   272 !
       
   273 
       
   274 reschedule
       
   275     "switch to the highest prio runnable process"
       
   276 
       
   277     |l|
       
   278 
       
   279     self maxPriority to:self minPriority by:-1 do:[:prio |
       
   280         l := runnable at:prio.
       
   281         l notNil ifTrue:[
       
   282             ^ self switchTo:(l first)
       
   283         ]
       
   284     ].
       
   285     "no process to run - wait to next time event"
       
   286 
       
   287     'wait' printNewline.
       
   288     self waitForNextTimeout
       
   289 !
       
   290 
       
   291 yield
       
   292     "move the currently running process to the end of the currentList
       
   293      and reschedule to the first in the list, thus switching to the 
       
   294      next same-prio-process."
       
   295 
       
   296     |l|
       
   297 
       
   298     l := runnable at:currentPriority.
       
   299     l isNil ifTrue:[
       
   300         'oops - nil runnable list' printNewline.
       
   301         ^ self
       
   302     ].
       
   303     l removeFirst.
       
   304     l addLast:currentProcess.
       
   305     self reschedule
       
   306 !
       
   307 
       
   308 suspend:aProcess
       
   309     "remove the argument, aProcess from the list of runnable processes
       
   310      and put it to the list of suspended ones. If the process is the 
       
   311      currentProcess, reschedule."
       
   312 
       
   313     |pri l|
       
   314 
       
   315     aProcess isNil ifTrue:[self error:'nil suspend'. ^ self].
       
   316     pri := aProcess priority.
       
   317 
       
   318     l := runnable at:pri.
       
   319     l isNil ifTrue:[self error:'bad suspend'. ^ self].
       
   320 
       
   321     aProcess state:#suspended.
       
   322     l remove:aProcess ifAbsent:[self error:'bad suspend'. ^ self].
       
   323 
       
   324     (aProcess == currentProcess) ifTrue:[
       
   325         self reschedule
       
   326     ]
       
   327 !
       
   328 
       
   329 resume:aProcess
       
   330     "set aProcess runnable - if its prio is higher than the currently running prio,
       
   331      reschedule."
       
   332 
       
   333     |l pri|
       
   334 
       
   335     aProcess == currentProcess ifTrue:[^ self].
       
   336     aProcess isNil ifTrue:[^ self].
       
   337     pri := aProcess priority.
       
   338 
       
   339     aProcess state:#runnable.
       
   340     l := runnable at:pri.
       
   341     l isNil ifTrue:[
       
   342         l := LinkedList new.
       
   343         runnable at:pri put:l
       
   344     ].
       
   345     l addLast:aProcess.
       
   346 
       
   347     (pri > currentPriority) ifTrue:[
       
   348         self reschedule
       
   349     ]
       
   350 !
       
   351 
       
   352 processTermination
       
   353     "current process finished its startup block without termination,
       
   354      lay him to rest now"
       
   355 
       
   356     self terminate:currentProcess
       
   357 !
       
   358 
       
   359 terminate:aProcess
       
   360     "terminate aProcess. If its not the current process, its simply
       
   361      removed from its list and destroyed. Otherwise, a switch is forced
       
   362      and the process is destroyed by the next running process."
       
   363 
       
   364     |pri id l|
       
   365 
       
   366     aProcess isNil ifTrue:[^ self].
       
   367     id := aProcess id.
       
   368     id isNil ifTrue:[^ self].   "already dead"
       
   369 
       
   370     pri := aProcess priority.
       
   371 
       
   372     "easy, if currently suspended"
       
   373     ((aProcess state ~~ #runnable) and:[aProcess state ~~ #running]) ifTrue:[
       
   374          aProcess id:nil.
       
   375          aProcess state:#dead.
       
   376          aProcess startBlock:nil.
       
   377          self class threadDestroy:id.
       
   378          ^ self
       
   379     ].
       
   380 
       
   381     (aProcess state ~~ #runnable) ifTrue:[
       
   382         l := runnable at:pri.
       
   383         (l notNil and:[l includes:aProcess]) ifTrue:[
       
   384             l remove:aProcess.
       
   385             aProcess state:#dead.
       
   386             l isEmpty ifTrue:[runnable at:pri put:nil].
       
   387             aProcess == currentProcess ifFalse:[
       
   388                 self class threadDestroy:id.
       
   389             ]
       
   390         ].
       
   391         ^ self
       
   392     ].
       
   393 
       
   394     (aProcess state ~~ #running) ifTrue:[
       
   395         "hard case - its the currently running process
       
   396          we must have the next active process destroy this one
       
   397         "
       
   398         aProcess state:#dead.
       
   399         zombie := id.
       
   400         self reschedule
       
   401     ]
       
   402 !
       
   403 
       
   404 changePriority:newPrio for:aProcess
       
   405     "change the priority of aProcess"
       
   406 
       
   407     |oldList newList oldPrio s|
       
   408 
       
   409     oldPrio := aProcess priority.
       
   410     oldPrio == newPrio ifTrue:[^ self].
       
   411     aProcess setPriority:newPrio.
       
   412     s := aProcess state.
       
   413     s == #runnable ifTrue:[
       
   414         oldList := runnable at:oldPrio.
       
   415         (oldList includes:aProcess) ifTrue:[
       
   416             oldList remove:aProcess
       
   417         ].
       
   418 
       
   419         newList := runnable at:newPrio.
       
   420         newList isNil ifTrue:[
       
   421             newList := LinkedList new.
       
   422             runnable at:newPrio put:newList
       
   423         ].
       
   424         newList addLast:aProcess.
       
   425         (aProcess ~~ currentProcess and:[newPrio > currentPriority]) ifTrue:[
       
   426             self reschedule.
       
   427         ].
       
   428         ^ self
       
   429     ]
       
   430 ! !
       
   431 
       
   432 !ProcessorScheduler class methodsFor:'testing'!
       
   433 
       
   434 test1
       
   435     |scheduler|
       
   436 
       
   437     scheduler := ProcessorScheduler new.
       
   438     scheduler addFileDescriptor:(Stdin fileDescriptor) withHandler:self selector:#inputAvailable.
       
   439     scheduler addFileDescriptor:(Display displayFileDescriptor) withHandler:self selector:#xInputAvailable.
       
   440     scheduler loop
       
   441 
       
   442     "ProcessorScheduler test1"
       
   443 !
       
   444 
       
   445 inputAvailable
       
   446     Transcript showCr:(Stdin nextLine)
       
   447 !
       
   448 
       
   449 xInputAvailable
       
   450     Transcript showCr:'x event'.
       
   451     Display dispatchEvent
       
   452 ! !
       
   453 
       
   454 !ProcessorScheduler methodsFor:'scheduling'!
       
   455 
       
   456 loop
       
   457     |looping nextTime waitTime fd index|
       
   458 
       
   459     looping := true.
       
   460     [looping] whileTrue:[
       
   461         "look if any timeouts are due to be evaluated"
       
   462 
       
   463         nextTime := nil.
       
   464         timeoutTimes notNil ifTrue:[
       
   465             nextTime := self evaluateTimeouts
       
   466         ].
       
   467 
       
   468         nextTime notNil ifTrue:[
       
   469             waitTime := OperatingSystem millisecondTimeDeltaBetween:nextTime
       
   470                                                                 and:OperatingSystem getMillisecondTime
       
   471         ] ifFalse:[
       
   472             waitTime := nil
       
   473         ].
       
   474 
       
   475         (fileDescriptors size == 0) ifTrue:[
       
   476             waitTime isNil ifTrue:[
       
   477                 Transcript showCr:'nothing to schedule'.
       
   478                 ^ self
       
   479             ].
       
   480 
       
   481             "no fd to wait for - hard wait till next timeout has to come"
       
   482             OperatingSystem millisecondDelay:waitTime
       
   483         ] ifFalse:[
       
   484             "wait for any fd to become ready or next timeout has to come"
       
   485             waitTime isNil ifTrue:[waitTime := 10000].
       
   486             fd := OperatingSystem selectOnAnyReadable:fileDescriptors withTimeOut:(waitTime / 1000).
       
   487             fd isNil ifTrue:[
       
   488                 "an interrupt or timeout occured"
       
   489                 Transcript showCr:'interrupt or timeout'
       
   490             ] ifFalse:[
       
   491                 "notify the handler"
       
   492                 index := fileDescriptors identityIndexOf:fd.
       
   493                 (fileHandlers at:index) perform:(fileSelectors at:index)
       
   494             ]
       
   495         ]
       
   496     ]
       
   497 !
       
   498 
       
   499 evaluateTimeouts
       
   500     "evaluate all timeouts that need to be .. and return the time of the
       
   501      next pending timeout"
       
   502 
       
   503     |now thisTime index endIndex handler selector nextTime|
       
   504 
       
   505     nextTime := nil.
       
   506     endIndex := timeoutTimes size.
       
   507     (endIndex ~~ 0) ifTrue:[
       
   508         now := OperatingSystem getMillisecondTime.
       
   509         index := 1.
       
   510         [index <= endIndex] whileTrue:[
       
   511             thisTime := timeoutTimes at:index.
       
   512             (OperatingSystem millisecondTime:thisTime isAfter:now) ifFalse:[
       
   513                 handler := timeHandlers at:index.
       
   514                 selector := timeSelectors at:index.
       
   515                 timeoutTimes at:index put:nil.
       
   516                 timeHandlers at:index put:nil.
       
   517                 timeSelectors at:index put:nil.
       
   518                 handler perform:selector
       
   519             ] ifTrue:[
       
   520                 nextTime isNil ifTrue:[
       
   521                     nextTime := thisTime
       
   522                 ] ifFalse:[
       
   523                     (OperatingSystem millisecondTime:nextTime isAfter:thisTime) ifTrue:[
       
   524                         nextTime := thisTime
       
   525                     ]
       
   526                 ]
       
   527             ].
       
   528             index := index + 1
       
   529         ]
       
   530     ].
       
   531     ^ nextTime
       
   532 ! !
       
   533 
       
   534 !ProcessorScheduler methodsFor:'accessing'!
       
   535 
       
   536 currentPriority
       
   537     ^ currentPriority
       
   538 
       
   539     "Processor currentPriority"
       
   540 !
       
   541 
       
   542 currentProcess
       
   543     ^ currentProcess
       
   544 
       
   545     "Processor currentProcess"
       
   546 !
       
   547 
       
   548 addFileDescriptor:fd withHandler:handler selector:selector
       
   549     |index|
       
   550 
       
   551     fileDescriptors isNil ifTrue:[
       
   552         fileDescriptors := Array with:fd.
       
   553         fileHandlers := Array with:handler.
       
   554         fileSelectors := Array with:selector
       
   555     ] ifFalse:[
       
   556         index := fileDescriptors indexOf:nil.
       
   557         (index ~~ 0) ifTrue:[
       
   558             fileDescriptors at:index put:fd.
       
   559             fileHandlers at:index put:handler.
       
   560             fileSelectors at:index put:selector
       
   561         ] ifFalse:[
       
   562             fileDescriptors := fileDescriptors copyWith:fd.
       
   563             fileHandlers := fileHandlers copyWith:handler.
       
   564             fileSelectors := fileSelectors copyWith:selector
       
   565         ]
       
   566     ]
       
   567 !
       
   568 
       
   569 addTimeoutAfter:millis withHandler:handler selector:selector
       
   570     |index|
       
   571 
       
   572     fileDescriptors isNil ifTrue:[
       
   573         timeoutTimes := Array with:millis.
       
   574         timeHandlers := Array with:handler.
       
   575         timeSelectors := Array with:selector
       
   576     ] ifFalse:[
       
   577         index := timeoutTimes indexOf:nil.
       
   578         (index ~~ 0) ifTrue:[
       
   579             timeoutTimes at:index put:millis.
       
   580             timeHandlers at:index put:handler.
       
   581             timeSelectors at:index put:selector
       
   582         ] ifFalse:[
       
   583             timeoutTimes := fileDescriptors copyWith:millis.
       
   584             timeHandlers := fileHandlers copyWith:handler.
       
   585             timeSelectors := fileSelectors copyWith:selector
       
   586         ]
       
   587     ]
       
   588 !
       
   589 
       
   590 removeFileDescriptor:fd
       
   591     |index|
       
   592 
       
   593     index := fileDescriptors indexOf:nil.
       
   594     (index ~~ 0) ifTrue:[
       
   595         fileDescriptors at:index put:nil.
       
   596         fileHandlers at:index put:nil.
       
   597         fileSelectors at:index put:nil
       
   598     ]
       
   599 ! !