Re: [c++-pthreads] pthread_cancel and EH: let's try this again
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [c++-pthreads] pthread_cancel and EH: let's try this again



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, but not strongly enough to try to stand in the way of progress. 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.

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.)

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.

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.

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.

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.
  }

Your auto-recancel semantics are probably good enough in this kind of situation.

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.

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.

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.

--
Mark Mitchell
CodeSourcery, LLC
mark@xxxxxxxxxxxxxxxx
(916) 791-8304