Here is a summary of the real-time extensions that were added to
SRFI 18.
1) The concept of "base priority", "boosted priority", and "effective
priority". All of these priorities are represented with real
numbers (the maximum and minimum are plus and minus infinity).
This allows deadlines to be expressed by setting the base priority
to the negation of the deadline time, i.e.
(thread-base-priority-set!
thread
(- (+ (time->seconds (current-time)) 10)))
The "priority boost" (which should be 0 when deadlines are used) is
added to the base priority to get the boosted priority, when the
thread blocks. By carefully choosing the base priority and
priority boost it is possible to set up an interactive thread so
that it has good I/O response time without being a CPU hog when it
performs long computations.
The effective priority of a thread T is equal to the maximum of T's
boosted priority and the effective priority of all the threads that
are blocked on a mutex owned by T. This "priority inheritance"
avoids priority inversion problems that would prevent a high
priority thread blocked at the entry of a critical section to
progress because a low priority thread inside the critical section
is preempted for an arbitrary long time by a medium priority
thread.
2) A precise definition of scheduling constraints based on priorities
and blocking time, and a notion of time ordering of events based on
a clock with a finite resolution.
The following are other changes which I also intend to add to SRFI 18:
3) Threads have a "specific" field which can be used to store thread
specific data (i.e. the "thread local storage" of other thread
systems).
4) Mutexes now have 4 states: locked (either owned or not owned) and
unlocked (either abandoned or not abandoned). A locked/not-owned
mutex is not linked to a particular thread, which means no priority
inheritance will occur if a thread blocks on that mutex.
5) "mutex-owner" has been renamed "mutex-state".
6) "mutex-lock!" can be passed a third argument to specify how the
ownership of the mutex is changed. If the argument is #f the mutex
will become locked/not-owned, if the argument is a thread the mutex
will become locked/owned and owned by that thread, and if the
argument is not supplied the mutex will become locked/owned by the
current thread.
7) "make-mutex" can no longer create a mutex that is initially owned
by a thread. This is so the only places a mutex is locked and
unlocked is when "mutex-lock!" and "mutex-unlock!" are called,
making it easier to reason about locks.
8) The functionality of "condition-variable-wait!" has been folded
into "mutex-unlock!", for the reason given in 7) and also because
there are two ways to lock a mutex and extra parameters would have
to be passed to condition-variable-wait! to specify the type of
locking to use once the wait is over. Note that this means an
explicit call to mutex-lock! is needed, for example:
(define (semaphore-wait! sema)
(mutex-lock! (vector-ref sema 1))
(let ((n (vector-ref sema 0)))
(if (> n 0)
(begin
(vector-set! sema 0 (- n 1))
(mutex-unlock! (vector-ref sema 1)))
(begin
(mutex-unlock! (vector-ref sema 1) (vector-ref sema 2))
(semaphore-wait! sema))))
Marc