Re: [Sbcl-devel] with-timeout sucks

On 29 March 2011 22:08, Alastair Bridgewater
<alastair.bridgewater@...> wrote:
> * WITH-TIMEOUT schedules a timer interrupt (SIGALARM) for some point
> in the future. If the alarm goes off, it unwinds from the body and
> allows execution to continue from somewhere. This is the one to use
> never, or at least not unless you know -exactly- what can go wrong,
> why, and how to avoid it.
That's about the size of it. And given that no-one has risen in its
defence I will deprecate it soonish (signaling a STYLE-WARNING when it
is used) and replace it with a somewhat more manageable
WITH-TIMEOUT-HANDLER (better name suggestions welcome.)
As for why asynchronous unwinds are nasty -- and remain so with
WITH-TIMEOUT-HANDLER -- consider this:
(unwind-protect (foo) (cleanup1) (cleanup2))
If an asynch unwind hits during CLEANUP1, CLEANUP2 is never executed.
OOPS. Similar examples abound. The only places to really sanely use
asynch unwinds is when you know that all code executed during the
period when an asych unwind can happen is asynch-unwind safe--which
pretty much means you must restrict its usage to pretty low-level
functions.
Asynch interrupts are nasty even without unwinding, though: any code
executed during an asynch interrupt should be re-entrant, because you
don't know when and where it might be run.
> * WITH-DEADLINE records a time in the future at which further I/O
> should cause an unwind. "All" I/O that may "block" or otherwise takes
> a timeout argument takes this into account and will unwind if the
> deadline expires. This is the one to use if you have an I/O bound
> process (such as establishing a network connection) and wish to cancel
> it if it takes too long.
That's the right idea, but the details are a bit fuzzier.
1. It deals with blocking reads, not writes (currently.)
2. It also deals with other block operations like WITH-MUTEX.
It is much easier to use safely, since it cannot unwind from a random
point in your program, but only from a limited number of system calls.
You can also use DECODE-TIMEOUT to easily hook your own stuff into the
deadline system.
Cheers,
-- Nikodemus
PS.
To make that UWP asynch unwind safe, you need to disable interrupts
during the cleanups:
(without-interrupts
(unwind-protect
(with-local-interrupts (foo))
(cleanup1)
(cleanup1)))
which of course doesn't help if FOO is not asynch-unwind safe -- and
if something goes badly wrong during the cleanups debugging with
interrupts disabled is much less fun. ...and all that interrupt
allowance fiddling approximately triples (IIRC) the cost of the UWP.
Better, and more realistically, it should really be something like
(let (foo)
(without-interrupts
(unwind-protect
(allow-with-interrupts (setf foo (foo)))
(cleanup foo))))
and have FOO wrap stuff that needs to be interruptible -- eg. blocking
syscalls -- with WITH-INTERRUPTS. ...and you begin to see why assuming
that a random piece of idiomatic Common Lisp code is asynch unwind
safe is just flat out wrong.
As for why UWP doesn't by default disable interrupts for cleanups --
which would help in may cases: it's common enough to have essentially
arbitrary code running under a cleanup form that doing so would not
only make things like WITH-TIMEOUT even *less* useful, and any endless
loops or whatnot in such places would become essentially undebuggable
because you could no longer C-c them to see what's happening. :/