Re: non critical-section uses of mutexes oleg@xxxxxx 26 Apr 2000 21:58 UTC
Marc, I'm afraid there is another problem with your mailbox solution. Consider the corrected code (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")))))) Let thread T1 execute (set! global-mbox (make-empty-mailbox)) and yield control to T2, which will do ((global-mbox 'get!)) This will immediately block T2. The control reverts to T1. Suppose that T1 terminates right away. global-mbox::get-mutex was locked by T1 upon mailbox creation. After T1 is gone, the mutex becomes abandoned, so T2 all of the sudden gets "abandoned mutex exception". It gets worse. Suppose T1 runs (set! global-mbox (make-empty-mailbox)) ((global-mbox 'put!) 'value) ((global-mbox 'get!)) However, in the middle of ((global-mbox 'get!)), right after executing (set! cell #f) but _before_ (mutex-unlock! put-mutex), thread T1 exhausts its quantum and is kicked off the CPU. Suppose thread T3 gets control, and forcibly terminates T1. T1 is not running, so it dies instantly. However, the global-mbox::get-mutex does not become abandoned, because it was locked anonymously. So, global-mbox::get-mutex remains locked, and so does global-mbox::put-mutex (because T1 lost CPU control before it could unlock the mutex). Thus both mailbox mutexes are locked, and no one can put to or take anything from the mailbox. If some threads were waiting on put!, they will wait forever. Wasn't mutex ownership introduced to specifically prevent this kind of problem?