pthread_cancel and EH: let's try this again
David Abrahams
dave at boost-consulting.com
Mon Jul 11 22:29:25 UTC 2005
Mark Mitchell <mark at codesourcery.com> writes:
> Jason Merrill wrote:
>> Previous discussion on this topic ended in something of a stalemate.
>
> Thank you for re-starting the discussion. I wasn't happy with the
> stalemate, but I didn't have the fortitude to try again!
>
>> In previous discussions, my favored solution was:
>> 1a) Cancellation is disabled during stack unwinding, to avoid trying to
>> throw out of a destructor.
>> 1b) Make cancellation a normal exception that can be caught and discarded,
>> but have the destructor for the exception re-assert cancellation so that
>> the process will begin again at the next cancellation point.
>
> I think this is a reasonable solution.
>
> I think I'd still prefer just to have the handler catch the exception
> and discard it,
Me too. I don't see any need to make this exception special.
> but not strongly enough to try to stand in the way of progress.
Me too. I haven't thought through the arguments about whether this
re-cancel behavior is a good idea. In fact, I don't know what they
are, so if someone could summarize that would be great.
> If you can build consensus around this option, I'll support
> it fully.
>
> Both your model, and the variant I suggest, preserve the pleasant
> property that code which already handles "random" exceptions (like that
> in libraries designed to plug into applications) continues to behave
> reasonably well in the presence of cancellation.
Yep, that's important to me.
> Code that doesn't handle random exceptions probably doesn't handle
> thread-cancellation either; if it's relying on certain functions
> throwing only certain exceptions, then it's probably written to work in
> a relatively controlled environment. (Of course, if it were compiled
> with headers that say that cancellation point functions throw no
> exceptions, then it would be completely hosed, under any of these models.)
Unless the code doesn't call any of those cancellation point
functions.
>> Then there's the Ada-equivalent model:
>> 2a) Cancellation is disabled during destructors and empty exception specs.
>> 2b) Cancellation exceptions are not caught by (...).
>
> I think this ought to be considered a non-starter. Ignoring "catch
> (...)" blocks in C++ is worse than just killing the thread. Style
> considerations aside, there's a ton of code that relies on that to clean
> up resources, or to otherwise restore state; and running destructors
> after skipping "catch (...)" blocks is just plain wrong, in my opinion.
Agree.
> Maybe in workstation/server applications this makes sense to some
> people, but I don't think it makes any sense at all on an embedded
> system, where the system is often set up to handle complete process
> death, but not weird inconsistencies. You'd be breaking people's
> program verification regimes, as well.
The platform doesn't matter. Destructors have to be able to rely on
program invariants being maintained, and as you imply, skipping a
catch(...) block in the middle of unwinding is likely to break those
invariants.
>> Thread robustness (catch and retry). A thread could have a catch (...)
>> at the top level to try to recover from errors, on the principle that
>> limping along is better than total failure. Previous discussion seemed
>> to assume that the users would want this to catch cancellation as well,
>> but I think that's wrong; if someone specifically told the thread to go
>> away, they don't want it to recover, they want it to go away.
>
> I agree. In fact, despite my oft-stated opinion that it should be
> possible to catch cancellation exceptions, I agree that actually doing
> that -- and never restarting the cancellation process -- would generally
> be a bug in user code.
Almost. If the thread (optionally issues some kind of report and)
exits -- an entirely plausible response to an exception ending up in
catch(...) because you didn't recognize it -- I think that's not
quite bad enough to be a bug, and technically it does not amount to
restarting the cancellation process. That would entail re-throwing a
cancellation exception IMO.
> The reason I'm OK with catching the exception is to do things like:
>
> try { ... } catch (Cancellation) {
> cancelled = 1;
> }
> /* Other stuff here. We have no finally clauses in C++. */
> if (cancelled) {
> throw Cancellation; // Or, maybe a different exception type, that
> // tells the top level to cancel the thread.
> }
As Peter D. suggested, something similar happens when C++
interoperates with Python (and I'm sure many other systems written in
'C'). Usually "Other stuff here" is actually a few stack frames of
'C' code.
> Your auto-recancel semantics are probably good enough in this kind of
> situation.
I'd like more detail on how it helps (and of course any arguments
against it too).
>> Ulrich Drepper insists that #1 is impossible, because pthread cancellation
>> is an irreversible state change. But I'm not sure why you can't just flip
>> various flags back to where they were before.
Agree.
> Yes, I've asked Ulrich about this several times, and have never gotten
> an explanation.
>
>> My current inclination is to go with model #2; backwards compatibility
>> with code written to work with pthread_cleanup_push/pop seems like a
>> powerful argument in its favor. People who want model #1 can use a
>> different threading library, such as Boost.Threads.
>
> Oh, no... I thought up until this paragraph that we were going to be on
> the same page.
Whoa! Me too!
> I think that if #1 really is impossible, #2 might be second-best. But,
> I'd very much like an explanation of why #1 is impossible. While I have
> very high confidence in Ulrich's technical abilities, I don't think we
> should have to take his opinions on faith.
Yah. Number 2 breaks too many programs and libraries that ought to be
correct in the presence of threads.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
More information about the c++-pthreads
mailing list