Subject: Re: cmucl mp: where is simultaneousness?
From: rpw3@rpw3.org (Rob Warnock)
Date: Sun, 28 May 2006 18:49:25 -0500
Newsgroups: comp.lang.lisp
Message-ID: <ZdCdnTWor_2YqufZnZ2dnUVZ_s-dnZ2d@speakeasy.net>
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