[plt-scheme] threads, breaks, and races
At Sat, 30 Jun 2007 02:54:51 -0400 (EDT), Dimitris Vyzovitis wrote:
> I would expect this code to die silently:
> (let ((thr (thread (lambda ()
> (with-handlers ((void void))
> (sync never-evt))))))
> (break-thread thr))
>
> Instead, I get this:
> (let ((thr (thread (lambda ()
> (with-handlers ((void void))
> (sync never-evt))))))
> (break-thread thr))
> > user break
Right. The break is likely to be received before evaluation reaches
`with-handlers'. As your subject line says, there's a race condition.
> In fact, it seems that the break is not delivered to the
> target thread at all. Instead, it is captured by the main thread (but
> the continuation in the main thread is not broken!):
> > (let ((thr (thread (lambda ()
> (with-handlers ((void void))
> (sync never-evt))))))
> (break-thread thr)
> (thread-dead? thr))
> #f
> > user break
Same deal. The `thread-dead?' expression is very likely to be evaluated
before the new thread has run at all, so that it hasn't had a chance to
handle a break signal. When it does run, it handles the break before
getting into the `with-handlers' form. The break is definitely
delivered to the right thread.
> Now, if I add a sleep before breaking the thread, the break is delivered
> normally:
>
> > (let ((thr (thread (lambda ()
> (with-handlers ((void void))
> (sync never-evt) 'boo)))))
> (sleep 1)
> (break-thread thr)
> (sleep 1)
> (thread-dead? thr))
> #t
Yes (unless your machine is extremely busy and you're unlucky with
thread scheduling).
Consider
(let* ((ready (make-semaphore))
(thr (thread
(lambda ()
(with-handlers ((exn:break? void))
(semaphore-post ready)
(sync never-evt))))))
(semaphore-wait ready)
(break-thread thr))
This kind of pattern is very common in programs that use breaks
properly. It reliably waits until the new thread is ready before
sending it a break.
Then again, there are very few programs that use breaks correctly for
something like cooperative termination. Unless you've done it a lot, I
suggest that it's even more difficult than you think. :) Certainly,
it's too difficult for me in all but the simplest cases. In contrast, I
find that non-cooperative termination (using custodians) is something I
can reliably understand.
Matthew