thread: fix place-channel ephemeron problem

Retains a strong link to a place-channel write end when there's at
least one waiting thread. This is symmetic to keeping a strong link to
the read end when the place-channel queue is non-empty. The change
repairs a problem building documentation with places in `racocs
setup`.
This commit is contained in:
Matthew Flatt 2018-10-16 09:06:55 -06:00
parent c78787a259
commit a7ae05a414
2 changed files with 74 additions and 11 deletions

View File

@ -0,0 +1,52 @@
#lang racket/base
(require racket/place)
;; Make sure multiple places can compete for a lock that is
;; implemented by a place channel. This test turns out to also make
;; sure that the link to the read end of a message queue is not too
;; weak when a suspend thread is waiting on the channel.
(define (lock-via-channel lock-ch)
(let ([saved-ch #f])
(lambda (mode)
(case mode
[(lock)
(define ch (sync lock-ch))
(place-channel-put ch 'lock)
(set! saved-ch ch)]
[(unlock)
(place-channel-put saved-ch 'done)
(set! saved-ch #f)]))))
(define (go)
(place pch
#;(printf "start\n")
(define lock-ch (place-channel-get pch))
(define lock (lock-via-channel lock-ch))
(for ([i (in-range 100)])
#;(printf "~s\n" i)
(lock 'lock)
(lock 'unlock))
#;(printf "done\n")))
(module+ main
(define-values (lock-ch lock-ch-in) (place-channel))
(thread (lambda ()
(define-values (ch ch-in) (place-channel))
(let loop ()
(place-channel-put lock-ch-in ch)
(unless (eq? (place-channel-get ch-in) 'lock)
(error "bad lock"))
(unless (eq? (place-channel-get ch-in) 'done)
(error "bad unlock"))
(loop))))
(define ps
(for/list ([i (in-range 4)])
(define p (go))
(place-channel-put p lock-ch)
p))
(map place-wait ps))
(module+ test (require (submod ".." main)))

View File

@ -233,8 +233,9 @@
(struct message-queue (lock (struct message-queue (lock
[q #:mutable] [q #:mutable]
[rev-q #:mutable] [rev-q #:mutable]
key-box ; holds write key when non-empty out-key-box ; holds write key when non-empty
[waiters #:mutable]) ; hash of waiting place -> semaphore [waiters #:mutable] ; hash of waiting place -> semaphore
in-key-box) ; holds read key when waiters
#:authentic) #:authentic)
(define (make-message-queue) (define (make-message-queue)
@ -242,7 +243,8 @@
'() '()
'() '()
(box #f) (box #f)
#hash())) #hash()
(box #f)))
(define (enqueue! mq msg wk) (define (enqueue! mq msg wk)
(define lock (message-queue-lock mq)) (define lock (message-queue-lock mq))
@ -251,8 +253,11 @@
(set-message-queue-rev-q! mq (cons msg (message-queue-rev-q mq))) (set-message-queue-rev-q! mq (cons msg (message-queue-rev-q mq)))
(define waiters (message-queue-waiters mq)) (define waiters (message-queue-waiters mq))
(set-message-queue-waiters! mq '#hash()) (set-message-queue-waiters! mq '#hash())
(set-box! (message-queue-key-box mq) wk) (set-box! (message-queue-out-key-box mq) wk)
(set-box! (message-queue-in-key-box mq) #f)
(host:mutex-release lock) (host:mutex-release lock)
;; Waking all waiters is not great, but we don't know which of
;; them is still waiting and can reliably succeed
(for ([(pl s) (in-hash waiters)]) (for ([(pl s) (in-hash waiters)])
(host:mutex-acquire (place-lock pl)) (host:mutex-acquire (place-lock pl))
(set-place-dequeue-semas! pl (cons s (place-dequeue-semas pl))) (set-place-dequeue-semas! pl (cons s (place-dequeue-semas pl)))
@ -266,7 +271,7 @@
;; a message. Note that if the message queue becomes ;; a message. Note that if the message queue becomes
;; inaccessible (so no writers), then the semaphores ;; inaccessible (so no writers), then the semaphores
;; become inaccessible. ;; become inaccessible.
(define (dequeue! mq success-k fail-k) (define (dequeue! mq rk success-k fail-k)
(ensure-wakeup-handle!) (ensure-wakeup-handle!)
(define lock (message-queue-lock mq)) (define lock (message-queue-lock mq))
(host:mutex-acquire lock) (host:mutex-acquire lock)
@ -286,13 +291,14 @@
[else [else
(define s (make-semaphore)) (define s (make-semaphore))
(set-message-queue-waiters! mq (hash-set waiters current-place s)) (set-message-queue-waiters! mq (hash-set waiters current-place s))
(set-box! (message-queue-in-key-box mq) rk)
(host:mutex-release lock) (host:mutex-release lock)
(fail-k s)])] (fail-k s)])]
[else [else
(define new-q (cdr q)) (define new-q (cdr q))
(set-message-queue-q! mq new-q) (set-message-queue-q! mq new-q)
(when (null? new-q) (when (null? new-q)
(set-box! (message-queue-key-box mq) #f)) (set-box! (message-queue-out-key-box mq) #f))
(host:mutex-release lock) (host:mutex-release lock)
(success-k (car q))])) (success-k (car q))]))
@ -302,12 +308,14 @@
out-mq-e ; ephemeron of reader key and message-queue out-mq-e ; ephemeron of reader key and message-queue
reader-key reader-key
writer-key writer-key
in-key-box) ; causes in-mq-e to be retained when non-empty in-key-box ; causes in-mq-e value to be retained when non-empty
out-key-box) ; causes out-mq-e value to be retained when waiters
#:reflection-name 'place-channel #:reflection-name 'place-channel
#:property prop:evt (poller (lambda (self poll-ctx) #:property prop:evt (poller (lambda (self poll-ctx)
(define in-mq (ephemeron-value (pchannel-in-mq-e self))) (define in-mq (ephemeron-value (pchannel-in-mq-e self)))
(if in-mq (if in-mq
(dequeue! in-mq (dequeue! in-mq
(pchannel-reader-key self)
(lambda (v) (lambda (v)
(values #f (values #f
(wrap-evt (wrap-evt
@ -336,8 +344,10 @@
(define wk1 (gensym 'write)) (define wk1 (gensym 'write))
(define rk2 (gensym 'read)) (define rk2 (gensym 'read))
(define wk2 (gensym 'write)) (define wk2 (gensym 'write))
(values (pchannel (make-ephemeron wk1 mq1) (make-ephemeron rk2 mq2) rk1 wk2 (message-queue-key-box mq1)) (values (pchannel (make-ephemeron wk1 mq1) (make-ephemeron rk2 mq2) rk1 wk2
(pchannel (make-ephemeron wk2 mq2) (make-ephemeron rk1 mq1) rk2 wk1 (message-queue-key-box mq2)))) (message-queue-out-key-box mq1) (message-queue-in-key-box mq2))
(pchannel (make-ephemeron wk2 mq2) (make-ephemeron rk1 mq1) rk2 wk1
(message-queue-out-key-box mq2) (message-queue-in-key-box mq1))))
(define/who (place-channel-get in-pch) (define/who (place-channel-get in-pch)
(check who place-channel? in-pch) (check who place-channel? in-pch)
@ -347,6 +357,7 @@
(begin (begin
(start-atomic) (start-atomic)
(dequeue! in-mq (dequeue! in-mq
(pchannel-reader-key pch)
(lambda (v) (lambda (v)
(end-atomic) (end-atomic)
(un-message-ize v)) (un-message-ize v))