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 |
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. |
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! |