Custom thread synchronization
=============================

Along with several useful thread synchronization abstraction facilities built-in to Scheme48, there is also a simple and lower-level mechanism for suspending & resuming threads.  The following bindings are exported from the threads-internal structure.

Threads have a field for a Cells, cell that is used when the thread is suspended.  When it is ready to run, it is simply #f. Suspending a thread involves setting its cell to a cell accessible outside, so the thread can later be awoken.  When the thread is awoken, its cell field and the contents of the cell are both set to #f. Often, objects involved in the synchronization of threads will have a Queues, queue of thread cells.  There are two specialized operations on thread cell queues that simplify filtering out cells of threads that have already been awoken.

 -- procedure: maybe-commit-and-block cell returns boolean
 -- procedure: maybe-commit-and-block-on-queue returns boolean
     These attempt to commit the current proposal.  If the commit fails, they immediately return #f.  Otherwise, they suspend the current thread.  Maybe-commit-and-block first sets the current thread's cell to CELL, which should contain the current thread. Maybe-commit-and-block-on-queue adds a cell containing the current thread to QUEUE first.  When the current thread is finally resumed, these return #t.

 -- procedure: maybe-commit-and-make-ready thread-or-queue returns boolean
     Attempts to commit the current proposal.  If the commit fails, this returns #f.  Otherwise, maybe-commit-and-make-ready awakens the specified thread[s] by clearing the thread/each thread's cell and sending a message to the relevant scheduler[s] and returns #t.  If THREAD-OR-QUEUE is a thread, it simply awakens that; if it is a queue, it empties the queue and awakens each thread in it.

 -- procedure: maybe-dequeue-thread! thread-cell-queue returns thread or boolean
 -- procedure: thread-queue-empty? thread-cell-queue returns boolean
     Maybe-dequeue-thread! returns the next thread cell's contents in the queue of thread cells THREAD-CELL-QUEUE.  It removes cells that have been emptied, ie whose threads have already been awoken. Thread-queue-empty? returns #t if there are no cells in THREAD-CELL-QUEUE that contain threads, ie threads that are still suspended.  It too removes cells that have been emptied.

For example, the definition of Higher-level synchronization, placeholders is presented here.  Placeholders contain two fields: the cached value (set when the placeholder is set) & a queue of threads waiting (set to #f when the placeholder is assigned).

     (define-synchronized-record-type placeholder :placeholder
       (really-make-placeholder queue)
       (value queue)                         ; synchronized fields
       placeholder?
       (queue placeholder-queue set-placeholder-queue!)
       (value placeholder-real-value set-placeholder-value!))

     (define (make-placeholder)
       (really-make-placeholder (make-queue)))

     (define (placeholder-value placeholder)
       ;; Set up a new proposal for the transaction.
       (with-new-proposal (lose)
         (cond ((placeholder-queue placeholder)
                ;; There's a queue of waiters.  Attempt to commit the
                ;; proposal and block.  We'll be added to the queue if the
                ;; commit succeeds; if it fails, retry.
                => (lambda (queue)
                     (or (maybe-commit-and-block-on-queue queue)
                         (lose))))))
       ;; Once our thread has been awoken, the placeholder will be set.
       (placeholder-real-value placeholder))

     (define (placeholder-set! placeholder value)
       ;; Set up a new proposal for the transaction.
       (with-new-proposal (lose)
         (cond ((placeholder-queue placeholder)
                => (lambda (queue)
                     ;; Clear the queue, set the value field.
                     (set-placeholder-queue! placeholder #f)
                     (set-placeholder-value! placeholder value)
                     ;; Attempt to commit our changes and awaken all of the
                     ;; waiting threads.  If the commit fails, retry.
                     (if (not (maybe-commit-and-make-ready queue))
                         (lose))))
               (else
                ;; Someone assigned it first.  Since placeholders are
                ;; single-assignment cells, this is an error.
                (error "placeholder is already assigned"
                       placeholder
                       (placeholder-real-value placeholder)))))) lisp