The Scheduler Will Fitzgerald (11 Apr 2000 18:12 UTC)
Re: The Scheduler Marc Feeley (13 Apr 2000 14:16 UTC)
RE: The Scheduler Will Fitzgerald (13 Apr 2000 15:27 UTC)
Re: The Scheduler Marc Feeley (13 Apr 2000 16:56 UTC)
RE: The Scheduler Will Fitzgerald (13 Apr 2000 17:29 UTC)
Re: The Scheduler David Rush (14 Apr 2000 10:34 UTC)
RE: The Scheduler Will Fitzgerald (14 Apr 2000 13:42 UTC)
Re: The Scheduler Marc Feeley (14 Apr 2000 13:47 UTC)
Re: The Scheduler David Rush (14 Apr 2000 15:14 UTC)
Re: The Scheduler Mark K. Gardner (14 Apr 2000 16:09 UTC)
Re: The Scheduler David Rush (14 Apr 2000 16:31 UTC)
Re: The Scheduler Marc Feeley (15 Apr 2000 02:26 UTC)

Re: The Scheduler Marc Feeley 13 Apr 2000 16:56 UTC

> If the THREAD-PRIORITY>? approach were adopted, then the 'fairness
> constraints' section remains the same, the 'priority aging' issue goes
> away (user x, want to implement priority aging? Just change
> THREAD-PRIORITY>? to reflect this), I could implement deadlines, etc.
> What do you think? Does this mess up the semantics too much? Make things
> too inefficient?

This has some coupling problems that are difficult to resolve.

For efficiency the scheduler maintains sets of threads (such as the
set of runnable threads, and the set of threads blocked on a given
mutex, and the set of threads blocked with a timeout) in a
data-structure ordered by priority (and insertion time and timeout)
where the highest priority thread can be accessed quickly.  My
implementation uses red-black trees, but leftist trees, plain binary
trees or a "heap" could be used.

When some scheduling information (such as the priority field of a
thread, or it's "deadline") is changed, it is important that the
red-black tree(s) containing the thread be reordered.  So at a minimum
you need another primitive such as (thread-reorder!  <thread>) to
notify the scheduler of a change of priority so it can reorder the
appropriate red-black trees, because it has no knowledge of the user's
implementation of priorities.  The user would use this primitive like
this, where t is a thread:

     (thread-deadline-set! t (foo x)) ; user defined deadline procedure
     (thread-reorder! t)

and define

     (define (thread-priority>? t1 t2)
       (< (thread-deadline t1)   ; note: bogus code... I don't fully
          (thread-deadline t2))) ;       understand what "deadline" means

Note that the scheduler has to be blocked out when the scheduler calls
thread-priority>? during a thread reordering (for example, no
preemption timer interrupts are allowed because it would require
modifying the red-black tree of runnable threads which might be in an
inconsistent state).  Let's assume the preemption timer interrupt is
not a problem because the scheduler disables these interrupts on entry
to the scheduler.  Nevertheless the procedure thread-priority>? has to
be written very specially.  It can't use scheduler related operations,
such as on mutexes.  It probably can't use I/O because mutexes are
used to protect the I/O buffers.  It probably can't allocate memory
(often multiprocessor memory allocators need to protect the memory
pool with a mutex) or do anything that might raise an exception.
Remember also that calling a procedure needs to allocate memory (for
the continuation frame and/or the rest parameter list).  Finally,
what is the value of (current-thread) when thread-priority>? is
called (note that thread reordering occurs at other points than
when the user calls thread-reorder!, for example when a timeout
occurs).  The value of (current-thread) is important to give
meaning to (current-exception-handler), (current-output-port), etc

Moreover, thread reordering has to be done promptly because the
red-black tree search/insertion/deletion operations assume that the
tree is in an ordered state.  But note that the preemption timer may
interrupt the computation just between the calls to
thread-deadline-set! and thread-reorder!.  So these operations must be
performed in a critical section that blocks out the scheduler, along
these lines:

     (thread-deadline-set! t (foo x))
     (thread-reorder-and-enable-scheduler! t)

This bottleneck is going to be a problem in a multiprocessor because
all scheduler related operations, including on mutexes, are going
to have to busy wait for the duration of this critical section before
they can proceed.

Moreover, to be complete the code needs to prevent exceptions (raised
in (foo x)) from exiting the critical-section prematurely.  So you
really have to do something like this:

     (let ((eh (current-exception-handler)))
         (lambda (exc)
           (thread-reorder-and-enable-scheduler! t)
           (eh exc))
         (lambda ()
           (thread-deadline-set! t (foo x))
           (thread-reorder-and-enable-scheduler! t))))

If this is not done then a missed thread-reorder-and-enable-scheduler!
will completely hang the system.

As you see I'm not very warm to your proposal!