This page is part of the web mail archives of SRFI 18 from before July 7th, 2015. The new archives for SRFI 18 contain all messages, not just those from before July 7th, 2015.
While rereading the discussion archive, it occured to me that my mailbox example had a nasty bug which is due to the fact that the mutexes are not used to implement a critical section (of the form: lock, critical-section, unlock). I reproduce the code below. ; an implementation of a mailbox object of depth one (define (make-empty-mailbox) (let ((put-mutex (make-mutex)) ; allow put! operation (get-mutex (make-mutex (current-thread))) ; prevent get! operation (cell #f)) (define (put! obj) (mutex-lock! put-mutex) ; prevent put! operation (set! cell obj) (mutex-unlock! get-mutex)) ; allow get! operation (define (get!) (mutex-lock! get-mutex) ; wait until object in mailbox (let ((result cell)) (set! cell #f) ; prevent space leaks (mutex-unlock! put-mutex) ; allow put! operation result)) (lambda (msg) (case msg ((put!) put!) ((get!) get!) (else (error "unknown message")))))) When a thread T1 "put!"s an object in the mailbox, T1 becomes the owner of the "put-mutex" (due to the call (mutex-lock! put-mutex)). T1 will remain the owner until some thread T2 "get!"s the object from the mailbox (the call (mutex-unlock! put-mutex) will break the link from the mutex to T1). But if after the "put!" T1 decides to terminate and another thread T3 performs a "put!" (before the "get!" by T2) then the put-mutex will be abandoned when T3 does the "put!", which will raise an "abandoned mutex" exception. The problem is that the notion of "mutex ownership" is only meaningful if the owner of a mutex is the one responsible for unlocking the mutex (this is the case when implementing critical sections, but not in the mailbox example). Also, in the context of a real-time multithreading system with priority inheritance, you don't want priority inheritance to occur for mutexes not used in critical sections (because the "priority boost" of the lower priority thread will in no way help the higher priority thread to lock the mutex faster). So I am considering adding a new mutex state that indicates that the mutex is locked but has no owner, and the primitive "mutex-lock-anonymously!" which is like "mutex-lock!" but puts the mutex in this new state. The code above would work properly if both calls of mutex-lock! are replaced by calls to mutex-lock-anonymously!. Below are the relevant changes to the SRFI document: <H4>Mutex</H4> <P> A mutex can be in one of four states: locked (either owned or not owned) and unlocked (either abandoned or not abandoned). An attempt to lock a mutex only succeeds if the mutex is in an unlocked state, otherwise the current thread must wait. A mutex in the locked/owned state has an associated "owner" thread. A mutex becomes locked/owned when a thread locks it using the <CODE>mutex-lock!</CODE> primitive (the thread becomes the mutex's owner) and when the mutex is created with a call to the <CODE>make-mutex</CODE> primitive that specifies an initial owner thread. A mutex becomes locked/not-owned when a thread locks it using the <CODE>mutex-lock-anonymously!</CODE> primitive. A mutex becomes unlocked/abandoned when the owner of a locked/owned mutex terminates. A mutex becomes unlocked/not-abandoned when a thread unlocks it using the <CODE>mutex-unlock!</CODE> and <CODE>condition-variable-wait!</CODE> primitives. Mutexes are not recursive (i.e. if a thread tries to lock a mutex that is currently locked the thread will block even if it is the owner of the mutex). </P> <DT><PRE> (mutex-owner <I>mutex</I>) ;procedure </PRE><DD> Returns information about the state of the <CODE><I>mutex</I></CODE>. The possible results are: <UL> <LI><STRONG>thread T</STRONG>: the <CODE><I>mutex</I></CODE> is in the locked/owned state and thread T is the owner of the <CODE><I>mutex</I></CODE> <LI><STRONG>symbol <CODE>not-owned</CODE></STRONG>: the <CODE><I>mutex</I></CODE> is in the locked/not-owned state <LI><STRONG>symbol <CODE>abandoned</CODE></STRONG>: the <CODE><I>mutex</I></CODE> is in the unlocked/abandoned state <LI><STRONG>symbol <CODE>not-abandoned</CODE></STRONG>: the <CODE><I>mutex</I></CODE> is in the unlocked/not-abandoned state </UL> <PRE> (mutex-owner (make-mutex)) ==> not-abandoned (define (thread-alive? thread) (let* ((mutex (make-mutex thread)) (result (eq? (mutex-owner mutex) thread))) (mutex-unlock! mutex) ; avoid space leak result)) </PRE> <DT><PRE> (mutex-lock! <I>mutex</I> [<I>timeout</I>]) ;procedure </PRE><DD> If the <CODE><I>mutex</I></CODE> is currently locked, the current thread is suspended until the <CODE><I>mutex</I></CODE> is unlocked, or until the timeout is reached if <CODE><I>timeout</I></CODE> is supplied. If the timeout is reached, <CODE><I>#f</I></CODE> is returned. Otherwise the <CODE><I>mutex</I></CODE> becomes locked/owned and the current thread is its owner. An "abandoned mutex exception" is raised after locking the <CODE><I>mutex</I></CODE> if the <CODE><I>mutex</I></CODE> was unlocked/abandoned, otherwise <CODE>mutex-lock!</CODE> returns <CODE>#t</CODE>. It is not an error if the <CODE><I>mutex</I></CODE> is owned by the current thread (but the current thread will block). <DT><PRE> (mutex-lock-anonymously <I>mutex</I> [<I>timeout</I>]) ;procedure </PRE><DD> If the <CODE><I>mutex</I></CODE> is currently locked, the current thread is suspended until the <CODE><I>mutex</I></CODE> is unlocked, or until the timeout is reached if <CODE><I>timeout</I></CODE> is supplied. If the timeout is reached, <CODE><I>#f</I></CODE> is returned. Otherwise the <CODE><I>mutex</I></CODE> becomes locked/not-owned. An "abandoned mutex exception" is raised after locking the <CODE><I>mutex</I></CODE> if the <CODE><I>mutex</I></CODE> was unlocked/abandoned, otherwise <CODE>mutex-lock-anonymously!</CODE> returns <CODE>#t</CODE>. It is not an error if the <CODE><I>mutex</I></CODE> is owned by the current thread (but the current thread will block). <PRE> ; an implementation of a mailbox object of depth one (define (make-empty-mailbox) (let ((put-mutex (make-mutex)) ; allow put! operation (get-mutex (make-mutex (current-thread))) ; prevent get! operation (cell #f)) (define (put! obj) (mutex-lock-anonymously! put-mutex) ; prevent put! operation (set! cell obj) (mutex-unlock! get-mutex)) ; allow get! operation (define (get!) (mutex-lock-anonymously! get-mutex) ; wait until object in mailbox (let ((result cell)) (set! cell #f) ; prevent space leaks (mutex-unlock! put-mutex) ; allow put! operation result)) (lambda (msg) (case msg ((put!) put!) ((get!) get!) (else (error "unknown message")))))) (define (mailbox-put! m obj) ((m 'put!) obj)) (define (mailbox-get! m) ((m 'get!))) ; an alternative implementation of thread-sleep! (define (sleep! timeout) (let ((m (make-mutex))) (mutex-lock-anonymously! m) (mutex-lock-anonymously! m timeout))) </PRE>