I do understand all of that, and you're right that "kill-safe" isn't what I
meant.

What I'm confused about is why, if it's inherently not guaranteed to leave
the semaphore in a consistent state, semaphore-wait attempts to work at all
if breaks are enabled. Why not raise some helpful error like "it's unsafe
to wait on a semaphore while breaks are enabled, did you forget to disable
breaks?". What's the actual *use case* for calling semaphore-wait (and *not*
semaphore-wait/enable-break) while breaks are enabled?

On Sat, Jan 18, 2020 at 12:47 AM Alexis King <[email protected]> wrote:

> Killing a thread is different from breaking a thread. Killing a thread
> kills the thread unrecoverably, and no cleanup actions are run. This
> usually isn’t what you want, but there’s always a tension between these
> kinds of things: defensive programmers ask “How do I make myself unkillable
> so I can safely clean up?” but then implementors of a dynamic environment
> (like, say, DrRacket) find themselves asking “How do I kill a runaway
> thread?” Assuming you’re not DrRacket, you usually want `break-thread`, not
> `kill-thread`.
>
> But perhaps you know that already, and your question is just about
> breaking, so by “kill-safe” you mean “break-safe.” You ask why
> `semaphore-break` doesn’t just disable breaking, but that wouldn’t help
> with the problem the documentation alludes to. The problem is that there’s
> fundamentally a race condition in code like this:
>
>     (semaphore-wait sem)
>     ; do something important
>     (semaphore-post sem)
>
> If this code is executed in a context where breaks are enabled, it’s not
> break-safe whether or not `semaphore-wait` were to disable breaks while
> waiting on the semaphore. As soon as `semaphore-wait` returns, the queued
> break would be delivered, the stack would unwind, and the matching
> `semaphore-post` call would never execute, potentially holding a lock
> forever. So the issue isn’t that the semaphore’s internal state gets
> somehow corrupted, but that the state no longer reflects the value you want.
>
> The right way to write that code is to disable breaks in the critical
> section:
>
>     (parameterize-break #f
>       (semaphore-wait sem)
>       ; do something important
>       (semaphore-post sem))
>
> This eliminates the race condition, since a break cannot be delivered
> until the `semaphore-post` executes (and synchronous, non-break exceptions
> can be protected against via `dynamic-wind` or an exception handler). But
> this creates a new problem, since if a break is delivered while the code is
> blocked on the semaphore, it won’t be delivered until the semaphore is
> posted/unlocked, which may be a very long time. You’d really rather just
> break the thread, since it hasn’t entered the critical section yet, anyway.
>
> This is what `semaphore-wait/enable-break` is for. You can think of it as
> a version of `semaphore-wait` that re-enables breaks internally, inside its
> implementation, and it installs an exception handler to ensure that if a
> break is delivered at the worst possible moment (after the count has been
> decremented but before breaks are disabled again), it reverses the change
> and re-raises the break exception. (I have no idea if this is how it’s
> actually implemented, but I think it’s an accurate model of its behavior.)
> This does exactly what we want, since it ensures that if we do enter the
> critical section, breaks are disabled until we exit it, but we can still be
> interrupted if we’re blocked waiting to enter it.
>
> So it’s not so much that there’s anything really special going on here,
> but more that break safety is inherently anti-modular where state is
> involved, and you can’t implement `semaphore-wait/enable-break`-like
> constructs if you only have access to the `semaphore-wait`-like sibling.
>
> > On Jan 17, 2020, at 22:37, Jack Firth <[email protected]> wrote:
> >
> > The docs for semaphores say this:
> >
> > In general, it is impossible using only semaphore-wait to implement the
> guarantee that either the semaphore is decremented or an exception is
> raised, but not both. Racket therefore supplies semaphore-wait/enable-break
> (see Semaphores), which does permit the implementation of such an exclusive
> guarantee.
> >
> > I understand the purpose of semaphore-wait/enable-break, but there's
> something about semaphore-wait that confuses me: why does it allow breaking
> at all? My understanding is that if breaks are enabled, semaphore-wait
> still tries to block and decrement the counter, even though a break at any
> time could destroy the integrity of the semaphore. Does that mean it's not
> kill-safe to use a semaphore as a lock? Wouldn't it be safer if
> semaphore-wait automatically disabled breaks while waiting?
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/CAAXAoJU2wUR2cCqXgsenSL%2B_Ca_3qSu9SKZO7ko-AMNGjBCC_w%40mail.gmail.com.

Reply via email to