Re: [c++-pthreads] Re: FW: RE: Re: I'm Lost
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [c++-pthreads] Re: FW: RE: Re: I'm Lost



Dave Butenhof <david.butenhof@xxxxxx> writes:

>>> Yes, I may have said that backwards. As I've said before, although
>>> I use C++, it's not a "native language" for me, and a lot of this
>>> is based on opinions others have strongly stated     
>>
>> It wouldn't surprise me a bit if others had strongly stated ctors
>> shouldn't throw. It used to be the advice in Stroustrup's books.
>>   
> And often I think it makes sense to construct a viable object even in 
> the face of errors, and restrict the behavior of the object later.

Weak invariants are the result, if you're saying what it sounds like
you're saying.  Weak invariants are almost always worth avoiding.

>> None other than, "prevent exceptions from leaking across language
>> boundaries."  But if you don't do that, your program is broken anyway.
>>   
> Well, non-exception-savvy languages, sure. But (OK, perhaps a VMS bias 
> here) I think all languages on a platform should have a common exception 
> model that interoperates cleanly when stack frames are interleaved. 


That's nice unless someone decides they're going to write in standard
'C.'  And most people do, right?  In general, take an arbitrary Unix
library or program written in 'C', and you're unlikely to find any EH
support in there.

> Certainly if you're interleaved for Java, or Ada, you should be able to 
> have an exception propagate through and unwind, run destructors (or Ada 
> finally clauses), etc., with no problems. And where ISO C is extended 
> for exception semantics (e.g., VMS, Tru64, Windows...), C can join
> the club.

Can, yes.  But speaking broadly, it's not likely to be the case.

>>>> You don't find the idea that exception-safe code implies cancel-safe
>>>> code technically compelling?  I don't think that's an emotional issue.
>>>>       
>>> Well, yes, I do,     
>>
>> find it technically compelling, or think it's an emotional issue
>>   
> What I meant was the former. However the fact that I, or you, find
> it technically compelling doesn't make it right, and certainly
> doesn't mean everyone will agree. 

Goes without saying.

> And historically the disagreement has indeed been emotional. So,
> both are accurate.

Whatever.

>> I haven't even been considering asynchronous cancellation as it's
>> completely untenable to write anything but the most restricted code
>> that could work in the face of async cancellation.
>>   
> Ah, but cancellation is basically asynchronous with respect to the 
> receiving thread. 

Not the kind of cancellation that uses cancellation points.
"Asynchronous exception" means an exception that emanates from code
that is not allowed to throw unless undefined behavior is invoked.
Everything else is a synchronous exception.  If you call some of that
latter stuff "asynchronous," (a)synchronicity of exceptions becomes a
useless distinction.

> Even though we deliver the exception only at defined synchronous
> points, the cancellation request can arrive at any instant.  

But exactly when it arrives is irrelevant with respect to the
receiving thread.

> This is mostly relevant when you talk about blocking behavior --
> that a blocking operation can be interrupted anywhere in the middle
> IS asynchronous.

Huh?  It sounds like you're interested in some detail that I don't yet
have context for.

> "Deferred" cancelability converts that asynchronous interrupt into a 
> synchronous exception, though the definition of the blocking operation 
> as a cancellation point. So the cancelled thread doesn't necessarily see 
> the exception as "asynchronous" (it called a function, and got back an 
> exception); but that doesn't change the fact that it really was 
> asynchronous all the same.

Cancellation can be as asynchronous as you like from that definition.
The resulting exception is still synchronous.

> But I don't mean it in anything like the sense of "asynchronous 
> cancelability mode", where the exception can be raised (cancel 
> delivered) at any arbitrary point. 

Good.  Let's pick different terms that don't lead to this confusion,
please.

> Asynchronous cancelability was invented for use in tight
> compute-bound loops, and intended to be unusable anywhere else. It's
> one of the things we thought reasonable at the time but that I've
> since become convinced should have been on the other side of the
> "too unsafe and rarely useful to be standardized" line.
>
>>> C++ exceptions are synchronous and non-interrupting. (The latter a
>>> consequence of the former, really.)  One of the main advantages of
>>> cancellation is that it can break through an extended blocking
>>> operation;
>>
>> If you make all blocking operations cancellation points you can do
>> that anyway.  No?
>>   
> That's the intent...
>
>>> but that's unavoidably an extra condition over "exception-safety".
>>> Cancel-safe has to mean something more unless we drop
>>> interruptibility. If we drop it, then cancel-safety is just
>>> exception-safety but loses much of its value in controlling
>>> application responsiveness.
>>>     
>> You lost me.  I think async cancel safety should be thought of as a
>> separate level of design.
>>   
> Um, OK; I'm not sure where I lost you. I'm not talking about 
> asynchronous cancelability. I personally don't think C++ should even 
> briefly entertain the notion of any support for that.

Great.  Sounds like there's no disagreement, then.

> However, when cancellation is enabled, any blocking call (or any
> method/operator that makes or might make a blocking call, like
> "cout<<", might raise an exception. Not all code will be prepared to
> handle that, and much shouldn't be; it's important to be able to
> disable cancelability dynamically over critical scopes. It's not
> like most exceptions where the conditions for an exception are
> generally static; it could happen at any time for reasons the
> current thread cannot possibly anticipate.

That's exactly like most exceptions (c.f. out-of-memory).  Usually
conditions whose reasons can be anticipated can be effectively tested
and become preconditions or simply should be reported by other means.

> It's asynchronous simply because it's external and
> independent. Also, where a normal exception means "something's wrong
> and I can't continue on this code path", cancellation means sometime
> subtly different -- "I've been asked not to" rather than "I can't";
> but if you must, you may. ;-)

Once the exception is thrown, it amounts to exactly the same thing.

> Therefore we have cancelability state to managed scoped local control 
> over when the thread can respond to cancellation requests. (An obvious 
> candidate, in C++, for guard objects.)

Okay.  Not sure what your point is, but I don't disagree.

>>> In any case, though, I wasn't suggesting that you need to convince
>>> me.  I'm saying there are diverse and strongly held positions that
>>> somehow need to be unified in order to get consensus on any
>>> proposal. I think that I'm the least of your worries. ;-)
>>>     
>> Not that you have any obligation to do so, but it might be easier if
>> you would recognize the weight your opinion carries.  That might mean
>> learning enough about C++ to form a definite opinion.  That's, at
>> least, what I've tried to do with threading.
>>   
> I'm not ignorant of C++, 

I didn't think so.

> and I'm much less ignorant than I was 2 years ago when I started
> working with C++ and STL on a regular basis. Still, I am not steeped
> in the history and tradition of C++ as I am in threads, and probably
> never will be. More than that, while I have an authoritative voice
> on the POSIX working group and in the community, I'm not involved
> with the C++ committee and have no time or management support to get
> involved; and I won't put myself in the position of being an outside
> expert in some other area pretending to tell the C++ committee what
> it must (or even should) do.

I don't think you should.  There are other areas where you could make
a big difference, though, like the ISO committee for C++/POSIX binding
Mr. Drepper is now running.

> I will happily say that as a thread expert and C++ dabbler, this is
> what seems to make sense to me; but I reject any aura of authority
> in the C++ side of semantics and syntax.
>
> However my statement above wasn't in any way related to my tradition
> of C++ deference. I was merely stating that I've seen many opinions
> (other than mine) that will need to be resolved or accommodated to
> make a standard.

Understood.  That's normal.

>>>> A simpler approach might be to have two kinds of exception: "forced"
>>>> and finalizable.  At least then we can say that exception-safe code
>>>> implies finalizable cancellation safety.  Then "forced" synchronous
>>>> cancellation can do whatever people desire.  I personally think it
>>>> will become a useless appendage sort of like C++ exception
>>>> specifications, but at least evolution will take care of it.  And if
>>>> I'm wrong, evolution will wilt my finalizable cancellations.
>>>>       
>>> Is this the "unwind" vs "exception" idea? (Where "unwind" is like a
>>> new sort of 'throw' that triggers dtors but can't be
>>> caught/finalized.) Or something different...?
>>>     
>> No, I wasn't suggesting anything that couldn't be caught.  I was just
>> suggesting an exception that couldn't be stopped.  It could throw
>> itself in its dtor (not that I'm advocating it, but it might satisfy
>> the "other side"), for example.
>>   
> The POSIX model where cancel propagates inexorably to thread termination 
> is an inherently flawed compromise; but simply the best we could do 
> within the context of ISO C and POSIX APIs. OUR implementation always 
> allowed finalization, via C++ catch(...), our ISO C "CATCH_ALL" 
> extensions, or whatever other language syntax might fit.
>
> I really wouldn't want to propagate this restriction to C++.

Be clear, I'm not talking about a restriction.  If you ask it to throw
something normal, it's finalizable in the normal way.  This is a way
for the _cancelling_ thread to say, "I know what I'm doing; the author
of the thread I'm cancelling doesn't.  Force it to be killed at the
next cancellation point."

>> In fact, a general mechanism like:
>>
>>    cancel( thread_id, exception_object );
>>
>> is possible, where "cancel" really means throw a copy of the given
>> exception object when the specified thread reaches the next
>> cancellation point.
>>
>> We could call it 
>>
>>    throw_synchronously( thread_id, exception_object );
>>    instead, if "cancel" really means forced execution to too many
>> people.
>>   
> That's actually where we started out in CMA. Resolving down to a
> single pre-defined exception was partly a matter of simplicity, but
> also represented a basic thread model that "a thread is simple; it's
> an asynchronous procedure call within the context of an application,
> not an independent application". In any case where you would need to
> distinguish between two separate interrupt conditions, the functions
> stimulated by those separate interrupts should have been assigned to
> separate threads and therefore only one exception is needed.
>
> While this made a lot of sense at the time, we were in a very
> academic and theoretical phase, and there was not that great a body
> of threaded code in 1987 -- and none using anything closely
> resembling the thread model we were inventing and that became the
> principal influence for POSIX. SRC's Firefly/Modula-3 had "alert",
> but it was so much simpler as to be a distinct variety of beast.
>
> One advantage, though, of the single cancel exception, is that it's 
> universal. When you asynchronously issue a cancel request for a thread, 
> you can't really know what code is executing: your's, STL, some other 
> shared library, etc. Cancel means the same to all of them, and either is 
> supported with commonly agreed semantics or will be ignored (by 
> disabling cancellation in critical scopes). Once you start firing off 
> your own arbitrary exceptions, though, anything might happen because 
> half the time the exceptions won't belong anywhere in the call tree 
> that's active at the time they arrive.

That's not the way most exception-safe code works.  It goes to the
reason that exception-specifications are a failure: the particular
type of exception that propagates out of a throwing function makes
almost no difference to anyone.  The type only becomes important where
errors are reported, or where exceptions are translated -- either to
other exception types or, for example, to error return codes that can
propagate through other languages.  So the danger of injecting an
arbitrary exception type into existing code (especially libraries,
which are very often exception-neutral) is very very low.

> Which brings us back to the "academic" resolution: if an exception
> means distinct things in different call trees, those call trees
> should be distinct threads and only one universal exception is
> necessary. ;-)

I think you might be missing the point.  I am proposing the generalized 

  thread_throw( thread_id, exception_object )

function so that those who wish to hang themselves with homegrown
unstoppable exception types can do so without forcing the standard to
sanction the use of unstoppable exceptions by providing any kind of
"forced cancellation."  If "the other side" has A WAY to force
cancellation, maybe they won't insist it has to be THE WAY.  I know,
wishful thinking :)


-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com