Marcin 'Qrczak' Kowalczyk <qrczak@knm.org.pl> wrote:
+---------------
| While supporting green threads at all does constrain implementation
| choices and is non-trivial to add to an existing implementation,
| making such threads preemptive is quite easy. Especially if the
| runtime already supports interruption by Unix signals or ^C. If a Lisp
| implementation requires explicit yields, it seems that it should be
| easy to be improved.
+---------------
My understanding [and I invite anyone who knows better to update me!]
is that CMUCL's signal handling is not *quite* 100% reliable yet,
and that there are rare, tiny windows wherein a signal interrupt
might not be continuable. While that is (barely) tolerable when the
only signals being used are manual ^C to check on progress or kill
runaway code, even a very small probability of failure is unacceptable
when using frequent SIGALRM (timer) interrupts for pre-emptive
scheduling. So even though there *is* already code in CMUCL to
provide that, it is not enabled by default.
Aha! Here it is! Look starting at line 1488 in "src/code/multi-proc.lisp":
;;; Start-Sigalrm-Yield -- Internal
;;;
;;; Start a regular interrupt to switch processes. This may not be a
;;; good idea yet as the CMUCL code is not too interrupt safe.
;;;
(defun start-sigalrm-yield (&optional (sec 0) (usec 500000))
"Start a regular SIGALRM interrupt which calls process-yield. An optional
time in seconds and micro seconds may be provided. Note that CMUCL code
base is not too interrupt safe so this may cause problems."
... )
So you could always call (START-SIGALRM-YIELD 0 100000) [for 0.1s slices]
and take your chances on hitting a bad window...
A very brief test suggests that it does work as expected [when it works]:
cmu> (mp::startup-idle-and-top-level-loops) ; not necessary, but helps
cmu> (mp::start-sigalrm-yield 0 100000) ; use pre-emptive scheduling
cmu> (defun busywait () (dotimes (i 1000000))) ; note: *NOT* compiled!
; takes ~4s on my laptop
BUSYWAIT
cmu> (defun start (name count)
(mp:make-process
(lambda ()
(loop for i below count do
(busywait)
(format t "~&~s: step ~d~%" name i)))))
START
cmu> (start :first 10)
#<Process Anonymous {4838A01D}>
cmu> (start :second 10)
#<Process Anonymous {48781735}>
cmu> (start :third 10)
:FIRST: step 0
#<Process Anonymous {482D2E25}>
cmu>
:SECOND: step 0
:FIRST: step 1
:THIRD: step 0
:SECOND: step 1
:FIRST: step 2
:THIRD: step 1
:SECOND: step 2
(mp:all-processes) ; <== hand-typed
(#<Process Anonymous {482D2E25}> #<Process Anonymous {48781735}>
#<Process Anonymous {4838A01D}> #<Process Top Level Loop {4814E9F5}>
#<Process Idle Loop {48007C1D}>)
cmu>
:FIRST: step 3
:THIRD: step 2
:SECOND: step 3
...[and so on]...
:SECOND: step 8
:THIRD: step 8
:FIRST: step 9
:SECOND: step 9
:THIRD: step 9
(mp:all-processes) ; <== hand-typed
(#<Process Top Level Loop {4814E9F5}> #<Process Idle Loop {48007C1D}>)
cmu>
-Rob
-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607