C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Forced stopping of RAII compliant execution threads. POSIX threads.

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Sat, 15 Jul 2023 12:35:14 -0400
On Sat, Jul 15, 2023 at 12:08 PM Yuri Petrenko via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> Yes, "throwing an exception into someone else's call-stack" is a good model. "throwing an exception into someone else's call-stack with trigger condition" is a more accurate model. I wrote recently "there are certain intervals at which we can/can't destroy it". Of course, a programmer should be able to mark such unsafe intervals with a flag.

So code is assumed to be safe unless it is marked unsafe? That's...
*horrifyingly* dangerous. Especially for a circumstance that is a
fairly rare occurrence.

One of the reasons why maintaining exception-safety is so difficult is
that C++ makes it really easy to *think* your code is exception safe
when it really isn't. And you're trying to open up a second hole like
that.

And it should be noted that this is even harder than maintaining
exception-safety. Why? Because *any function* can be cancelled. Even
one you didn't write yourself.

If you call a C library function, that function is incapable of
throwing an exception. But it would be capable of being "canceled". So
your code might have been exception safe, but it isn't cancel safe.

Having to proactively, defensively code *everything* around the
possibility of thread cancellation at any time (on top of all of the
other things C++ programmers have to consider) is just not a good
idea. This would make much more sense in a language like Rust, which
had safe and unsafe blocks of code from day one.

> While we are in an unsafe section, we execute the program code normally, as soon as we leave the unsafe section we throw an exception. The second condition that checks our "exception with a trigger condition" is the thread stopping policy. If an immediate, unsafe stop is requested from outside, it will happen like that, some resources will leak (if we got into a dangerous place from the point of view of leaks), but not all of them and this is still better than TerminateThread.
>
> сб, 15 июл. 2023 г. в 16:40, Jason McKesson via Std-Proposals <std-proposals_at_[hidden]>:
>>
>> On Sat, Jul 15, 2023 at 9:27 AM Yuri Petrenko via Std-Proposals
>> <std-proposals_at_[hidden]> wrote:
>> >
>> > It's great that Linux already has something to rely on, but there is a subtle difference. In the case you mentioned, as far as I understand, pthread_cancel will ask the thread to cancel, and this will be possible only when we reach the Cancelation point, this mechanism is much better than a stop token, but very similar to it. In case of RAII thread we can interrupt the execution thread immediately without waiting for Cancelation point or token check, except for a few intervals like prologue and epilogue, destructor (destructors need to be discussed in detail), etc., where special handling is needed.
>> > In case of pthread there are points at which we cancel the thread, in case of RAII thread there are certain intervals at which we can/can't destroy it. The gist of this idea is that if the thread corresponds to RAII as a whole, it is not necessary to wait for Cancellation point.
>>
>> Is it though? Is it really?
>>
>> Even code that follows RAII principles religiously will have places
>> where cancellation at various points will cause breakage. I'm going to
>> model your cancellation feature as "throwing an exception into someone
>> else's call-stack".
>>
>> Consider `make_unique`. Somewhere in this function is code that
>> reduces down to this:
>>
>> unique_ptr<T>(new T(...));
>>
>> In the evaluation of that expression, there is a point where `new T()`
>> has been executed but *not* the constructor of `unique_ptr`. Until
>> that constructor has finished executing, the pointer is not owned by
>> anyone.
>>
>> However, this is fine. `unique_ptr`;s constructor is `noexcept`, the
>> initialization of its parameters are all `noexcept`, and if the `new`
>> expression throws, there is no pointer to manage. So between a
>> successful evaluation of `new T` and the completion of `unique_ptr`'s
>> constructor, no exceptions can possibly be thrown. Therefore, this
>> code is exception-safe.
>>
>> If you can cancel a thread (throw an exception into the call-stack) at
>> any point, then you could cancel it at the precise point where no
>> exceptions could have otherwise been thrown. That's... bad.
>>
>> Code can be exception-safe, but that's only because there are places
>> where it is guaranteed that no exceptions can be thrown. Cancellation
>> *must* work the same way: there must be places where you *cannot*
>> cancel a thread. RAII can encapsulate those into constructors and
>> functions returning prvalues, but that just puts the uncancellable
>> code inside of specific blocks. It still exists and still must be
>> uncancellable.
>>
>> > In general, I would really like to use the handler mechanism you mentioned. I'll be sure to add the relevant part to the discussion, thank you.
>> > --
>> > Std-Proposals mailing list
>> > Std-Proposals_at_[hidden]
>> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2023-07-15 16:35:37