[Openmcl-devel] question on process-allow-schedule and process-wait

Gary Byers gb at clozure.com
Fri Mar 26 01:28:11 UTC 2004



--On Thursday, March 25, 2004 1:15 PM -0500 Gary King <gwking at cs.umass.edu> 
wrote:

> The documentation for both process-allow-schedule and process-wait state
> that "There's almost always a better  alternative (involving waiting for
> some specific event to occur.)" I think I understand what this means in
> theory but I'm not sure how to use it in practice. Is there a simple
> example somewhere?
>
> thanks,
> --
> Gary Warren King, Lab Manager
> EKSL East, University of Massachusetts * 413 577 0176
>


Perhaps the suggestion shouldn't be to wait for "some specific event", but 
to wait for "some event involving an object that the OS can use for thread 
synchronization and scheduling, such as a lock, file descriptor, or 
semaphore."

PROCESS-WAIT winds up having to make scheduling decisions: if it checks the 
predicate too frequently,
it takes scheduling resources away from other threads/programs/the OS (and 
perhaps interferes with whatever would cause the predicate to become true); 
if it doesn't poll often enough, the latency between the time the predicate 
becomes true and the time that it's tested may be unacceptably high.

PROCESS-WAIT was integrated with the lisp scheduler in earlier versions of 
OpenMCL (and it or something like it would likely be integrated with the 
scheduler of other implementations that use cooperatively scheduled 
threads.)   In those implementations (certainly in earlier OpenMCL 
versions), you generally had to avoid the use of blocking primitives (since 
their use typically prevented the lisp scheduler from running).  In 0.14, 
it's not only possible to use blocking primitives but it's preferable to do 
so.

As an example (off the top of my head: beware of typos and other 
brain-damage): suppose that we have two threads - a "producer" and 
"consumer" of something or other - that need to coordinate their 
activities: the consumer can only consume when the producer has produced 
something, and the producer should only produce when the consumer's done 
consuming.  We might have done this via PROCESS-WAIT in a cooperatively 
scheduled lisp via something like:

(defparameter *ready* nil "producer has produced data when true")
(defparameter *done* nil "consumer has consumed data when true")

(process-run-function "producer"
  #'(lambda ()
     (loop
       (produce)
       (without-interrupts (setq *done* nil *ready* t))
       (process-wait "waiting for *done*" #'(lambda () *done*)))))

(process-run-function "consumer"
  #'(lambda ()
      (loop
        (process-wait "waiting for *ready*" #'(lambda () *ready*))
        (consume)
        (without-interrupts (setq *ready* nil *done* t))

We -might- find that that's more-or-less adequate in 0.14 (perhaps we'd 
need to be a little more careful about updating the special variables 
atomically.)  Depending on how complicated PRODUCE and CONSUME are, on what 
other threads are running, and on other factors, it might be better to use 
semaphores and get rid of PROCESS-WAIT:

(defparameter *ready* (make-semaphore))
(defparameter *done* (make-semaphore))

(process-run-function "producer"
   #'(lambda ()
      (loop
        (produce)
        (signal-semaphore *ready*)
        (wait-on-semaphore *done*))))

(process-run-function "consume"
   #'(lambda ()
      (loop
       (wait-on-semaphore *ready*)
       (consume)
       (signal-semaphore *done*)))

In the latter version, both threads will either be:
  a) producing or consuming with little or no resource contention from the 
other thread
  b) signaling a semaphore, which is a constant-time operation
  c) put to sleep by the OS and consuming no scheduling resources at all.

Again, how much those conditions matter depends on what PRODUCE and CONSUME 
are doing and on what else is going on; this may or may not be a compelling 
example.





More information about the Openmcl-devel mailing list