[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: multiprocessing scheduling



Your message:

    
    hello:

    could someone who has experience with extended common lisp multiprocessing
    facilities please explain how to properly cause a suspended process to
    be restarted.

    i am attempting to construct an event processing loop to handle CLX events
    by making a new process, running it and having it suspend until there are
    events in the queue.

    the method i have used (see below) causes the event loop to resume only aft
   er
    there has been input to terminal io but before the listener process has run
   .
    i had thought that both of these processes would be suspended due to calls
    to process-wait, and that the first one with input would be resumed.

    the event loop is started as follows:

      (when (not (mp:process-lock-locker *clx-process-lock*))
        (mp:process-run-function
         "CLX event processor"
         #'(lambda ()
    	 (setf (mp:process-priority mp:*current-process*) 1)
    	 (mp:process-lock *clx-process-lock*)
    	 (loop
    	  (mp:process-wait
    	   :no-input
    	   #'(lambda ()
    	       (if (and (xlib:display-p *the-%display*)
    			(not (xlib::buffer-dead *the-%display*)))
    		 (xlib:event-listen *the-%display* 0)
    		 t)))
    	  #|(mp:process-wait :no-input
                                 #'xlib::listen-fd
                                 (xlib::display-input-stream *the-%display*)) |
   #
      	  (if (not (xlib:display-p *the-%display*)) (return))
    	  (event-dispatch))
    	 (mp:process-unlock *clx-process-lock*)
    	 (mp:process-kill mp:*current-process*))))

    in which the commented code - as suggested by j.irwin - shows the same
    behavior.

    whats missing?
    ...

--------

This is a consequence of the way the Allegro scheduler is implemented.  When
there is nothing to do (no processes are runnable) the scheduler sits in a
select(), waiting for either input to become available or for an interrupt
to happen.  Unix however does not allow us to select() on all possible file
descriptors -- it gives an error if you try to do so.  So we must select() on
only the 'interesting' file descriptors.  Here 'interesting' means "those
file descriptors that have a process blocked on input from them."

So what you need to do is to tell the scheduler that the CLX input stream is
'interesting'.  In our current Lisp there is an inelegant way to do this, but
I have included the source to a better way here:

(in-package :mp)

(defun wait-for-input-available
    (stream-or-fd &key (wait-function #'listen)
		       (whostate "waiting for input"))
  (let ((fd (if* (fixnump stream-or-fd) then stream-or-fd
	     elseif (streamp stream-or-fd)
	       then (excl::stream-input-fn stream-or-fd)
	       else (error "wait-for-input-available expects a stream or file descriptor: ~s" stream-or-fd))))
    ;; At this point fd could be nil, since stream-input-fn returns nil for
    ;; streams that are output only, or for certain special purpose streams.
    (unwind-protect
	(progn
	  (if fd
	      (mp::mpwatchfor fd))
	  (mp::process-wait whostate wait-function stream-or-fd))
      (if fd
	  (mp::mpunwatchfor fd)))))
	    
This defines a new function mp::wait-for-input-available, whose first argument
is either a stream or a raw file descriptor (a small integer), which is the
input "stream" of interest.  The default wait function is just #'listen,
but you can choose another one (your example wants #'xlib::listen-fd).
It should be a function that takes one argument that is the stream or fd
in question, and immediately returns true if input is available, or nil if
it is not.

If you need a timeout, you can wrap the call to wait-for-input-available in
a with-timeout form.

The center of your code thus becomes:
    	 (loop
	   ;; May want to detect buffer-deadness here also.
	   (mp::wait-for-input-available
	    (xlib::display-input-stream *the-%display*)
	    :whostate :no-input		; Optional, of course
	    :wait-function
	    #'(lambda (stream)
		(if (and (xlib:display-p *the-%display*)
			 (not (xlib::buffer-dead *the-%display*)))
		    (xlib::listen-fd (xlib::display-input-stream
				      *the-%display*))
		  t)))
	   ;; Now input is available or the stream is dead
	   (if (not (xlib:display-p *the-%display*)) (return))
	   (event-dispatch))

;; Note that R4CLX'ers should use fd-char-avail-p instead of listen-fd.

I have added wait-for-input-available to our list of RFE's for our next
release of Allegro CL, so you should see it or something like it in a future
release.  I've also rewritten CLX to use this new function, and included it
in excldep.cl.  (I don't control R4CLX however, so this change may not make
it in since R4 is almost frozen)

As always, please let us know how this works out for you.  Thanks.

	-- John Irwin