It is guaranteed to leave the semaphore in a consistent state, from the 
perspective of the implementation of semaphores. No matter what you do, you 
won’t ever corrupt a semaphore (assuming you’re not using unsafe operations and 
assuming the runtime is not buggy).

But perhaps you mean inconsistent from the point of view of the application, 
not from the point of view of the Racket runtime. In that case, it’s true that 
when using semaphores as locks, using them in a context where breaks are 
enabled is almost certainly wrong. It’s not immediately clear to me that there 
aren’t any valid uses of semaphores where you would want breaks to be enabled, 
but I admit, I have no idea what they are.

Semaphores are low-level primitives, though, so I think it makes some sense for 
them to just do the minimal possible thing. Perhaps a library ought to offer a 
slightly more specialized “critical section” abstraction a la Windows (or 
perhaps something like Haskell’s MVars) that manages disabling interrupts in 
the critical section for you. (Why doesn’t this exist already? My guess is that 
most Racket programmers don’t worry about these details, since they don’t call 
`break-thread` anywhere, and they want SIGINT to just kill their process, 
anyway.)

> On Jan 18, 2020, at 02:54, Jack Firth <jackhfi...@gmail.com> wrote:
> 
> 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 <lexi.lam...@gmail.com 
> <mailto:lexi.lam...@gmail.com>> 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 <jackhfi...@gmail.com 
> > <mailto:jackhfi...@gmail.com>> 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 racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/34CFACC9-D8D7-4D92-8CB9-8AA792688721%40gmail.com.

Reply via email to