[LEAF] The usage of TLS

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

[LEAF] The usage of TLS

Boost - Dev mailing list
Hi Everyone,
I have a remark regarding the LEAF library. But first, I would like to
thank Emil for writing and sharing this library. Handling failures is an
important aspect of writing programs that does not have an ultimate answer
in C++, and is still a subject of exploration. LEAF seems to confirm this.

I would also like to thank Michael for managing this upcoming review and
for making the announcement ahead of time. A ten-day review period is
usually too short (even if later extended), and the early announcement
gives the opportunity to devote more time into reviewing a library and
interact with the author.

The following is not a review, but I wanted to highlight only one aspect of
the library. Namely, the usage of thread-local storage.

There is a number of reasons people do not want to or cannot use the C++
exception handling mechanism. One of them is that the implementation of
exception handling requires the usage of TLS, and TLS is not implementable
on some platforms, like GPUs. If Nvidia's compiler does not implement C++
exception handling mechanism, it is not because it would be slow, but
because it would require TLS support. From this perspective, if I cannot
use C++ exceptions because of TLS and I have to use something else, then
another TLS-based library is not really an alternative. In this case it
does not matter that it is only IDs that are stored in TLS. The sole fact
of even employing TLS is a deal breaker.

Of course, LEAF does not have to satisfy all the people that cannot use C++
exceptions. It can service the environments where TLS is affordable. But
the usage or non-usage of TLS is an important factor in making a decision
what tool/framework/library to use for handling failures, and because of
this it deserves to be mentioned in the beginning of the documentation, so
that potential clients can quickly make a decision if this library is for
them. If your concern is that people would be incorrectly led to think that
the entire error objects are stored in TLS, you can also say explicitly up
front that it is only about int-size IDs. Otherwise, people will be mislead
anyway. A person will ask, "how is it possible that this library does not
return the entire error object in leaf::result<> and it does not take the
pointer to the allocated object as function argument? Is it using TLS?
Let's grep for `thread_local`. Positive: it must be using TLS for storing
error objects."

For a similar reason, I find the comparison with Boost.Outcome, at the end
of documentation, unfair. If the goal of this comparison is to help people
decide which library is best suited for them, LEAF or Outcome, the usage or
non-usage of TLS is an important factor.

Another thing is that I cannot see how a TLS-based implementation can work
with C++20 coroutines. In C++ 20 the following piece of code

{
  A();
  co_await X();
  B();
}

means that when the call to B() will be potentially "packaged" as a task
and scheduled to be executed possibly on a different thread. It means that
B() will be executed on a different thread than A(), which means that any
attempt to communicate between A() and B() using TLS will not be reliable
(e.g. if A() is in fact a call to leaf::preload()). This is the reason why
we still cannot implement the feature known as "on scope failure" (like
destructor call, but only when we are leaving the scope due to exception)
in C++ even though we have `std::uncaught_exceptions()` (which uses TLS
under the hood). This is why a proposal like P0052 (
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0052r10.pdf)
didn't make it to C++20.

Now, LEAF may have a solution to this problem, but I cannot figure it out
from the documentation. So, I do not know if it is safe to use LEAF with
coroutines in multi-threaded environment.

I would really want this to be clarified in the docs before the library
review starts.

Regards,
&rzej;

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [LEAF] The usage of TLS

Boost - Dev mailing list
Thanks for the interest.

On Sun, May 3, 2020 at 4:15 PM Andrzej Krzemienski via Boost <
[hidden email]> wrote:

> It can service the environments where TLS is affordable.


You're assuming that the use of TLS pointers to error objects in LEAF,
moving error objects directly to the scope where they are needed, bypassing
return values and stack frames, is less efficient than transporting them in
return values. Depending on your platform and your error object types, it
could go either way.

Separately, LEAF can transport any number of error objects of arbitrary
types without dynamic allocations. If you need to do this with a
conventional result<T> implementation, you'll be allocating memory. That
may be as much of a showstopper as unavailability of TLS could be for LEAF.

For a similar reason, I find the comparison with Boost.Outcome, at the end
> of documentation, unfair. If the goal of this comparison is to help people
> decide which library is best suited for them, LEAF or Outcome, the usage or
> non-usage of TLS is an important factor.
>

It's important but not in terms of efficiency, only in terms of
availability and suitability. LEAF is not suitable for coroutines or GPUs
(though GPU code doesn't need much in terms of error handling anyway).

LEAF works well with async code based on threads, see:

https://github.com/zajo/leaf/blob/master/examples/capture_in_result.cpp?ts=4
(without
exceptions)

https://github.com/zajo/leaf/blob/master/examples/capture_in_exception.cpp?ts=4
(using
exceptions)

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [LEAF] The usage of TLS

Boost - Dev mailing list
pon., 4 maj 2020 o 02:41 Emil Dotchevski via Boost <[hidden email]>
napisał(a):

> Thanks for the interest.
>
> On Sun, May 3, 2020 at 4:15 PM Andrzej Krzemienski via Boost <
> [hidden email]> wrote:
>
> > It can service the environments where TLS is affordable.
>
>
> You're assuming that the use of TLS pointers to error objects in LEAF,
> moving error objects directly to the scope where they are needed, bypassing
> return values and stack frames, is less efficient than transporting them in
> return values. Depending on your platform and your error object types, it
> could go either way.
>


I am sorry. It was a bad choice of words on my side. I did not mean to
imply that the usage of TLS would make the library go slower compared to
alternatives. What I was trying to say is that on some platforms LEAF would
not compile because of the missing TLS support, and that I would only
discover it after having invested a significant amount of time, because the
documentation does not declare it up front.

These are the guidelines that I learned from Robert Ramey's talk on
documenting a library (https://www.youtube.com/watch?v=ACeNgqBKL7E). As a
potential user I need to learn in no more than 5 minutes: what the library
does, how I will use it (LEAF documentation does it perfectly), and what
are the requirements. For the last point I would expect to see: "your
platform needs to support TLS, and your program cannot use coroutines in
asynchronous manner." IOW: high-level design trade-offs should be
documented up front.


>
> Separately, LEAF can transport any number of error objects of arbitrary
> types without dynamic allocations. If you need to do this with a
> conventional result<T> implementation, you'll be allocating memory. That
> may be as much of a showstopper as unavailability of TLS could be for LEAF.
>

I acknowledge this. And I think this part is nicely outlined in the
documentation. My critique was not about the trade-offs that the library
does. It was about not documenting it clearly.

As a side note, the choice of providing efficient stack storage for
arbitrary number and kind of error objects does not necessarily require the
usage of STL. An alternative described in
https://www.research.ed.ac.uk/portal/files/78829292/low_cost_deterministic_C_exceptions_for_embedded_systems.pdf
creates a similar stack storage, but instead passes a pointer to it to
every function that needs to report a failure using this mechanism. But the
authors are implementing it as a compiler extension, and this choice to
pass the pointer to the storage to all function may not be suitable for a
library implementation. So the trade-off here is: TLS in order to preserve
the normal set and type of function inputs.


> For a similar reason, I find the comparison with Boost.Outcome, at the end
> > of documentation, unfair. If the goal of this comparison is to help
> people
> > decide which library is best suited for them, LEAF or Outcome, the usage
> or
> > non-usage of TLS is an important factor.
> >
>
> It's important but not in terms of efficiency, only in terms of
> availability and suitability. LEAF is not suitable for coroutines or GPUs
> (though GPU code doesn't need much in terms of error handling anyway).
>

Regarding platforms that do not support TLS, I will learn about it the hard
way when I try to compile the library, which is enough to avoid any bugs.

However, not being suitable for coroutines is more serious, because the
library will compile and maybe even execute as expected in simple test
cases. And it may give false impression that this just works. I would
recommend putting a visible note in the documentation saying that the
library is using TLS and therefore is not suitable for programs that use
coroutines for asynchronous calls.


> LEAF works well with async code based on threads, see:
>
>
> https://github.com/zajo/leaf/blob/master/examples/capture_in_result.cpp?ts=4
> (without
> exceptions)
>
>
> https://github.com/zajo/leaf/blob/master/examples/capture_in_exception.cpp?ts=4
> (using
> exceptions)
>

Acknowledged.

Regards,
&rzej;


> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [LEAF] The usage of TLS

Boost - Dev mailing list

> On 4. May 2020, at 10:29, Andrzej Krzemienski via Boost <[hidden email]> wrote:
>
> These are the guidelines that I learned from Robert Ramey's talk on
> documenting a library (https://www.youtube.com/watch?v=ACeNgqBKL7E). As a
> potential user I need to learn in no more than 5 minutes: what the library
> does, how I will use it (LEAF documentation does it perfectly), and what
> are the requirements. For the last point I would expect to see: "your
> platform needs to support TLS, and your program cannot use coroutines in
> asynchronous manner." IOW: high-level design trade-offs should be
> documented up front.

On that note, we have a documentation guideline which says exactly that:
https://www.boost.org/development/requirements.html#Documentation

"General introduction to the library. The introduction particularly needs to include: A very high-level overview of what the library is good for, and perhaps what it isn't good for, understandable even by those with no prior knowledge of the problem domain."

Best regards,
Hans

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [LEAF] The usage of TLS

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
On 4/05/2020 20:29, Andrzej Krzemienski wrote:
> These are the guidelines that I learned from Robert Ramey's talk on
> documenting a library (https://www.youtube.com/watch?v=ACeNgqBKL7E). As a
> potential user I need to learn in no more than 5 minutes: what the library
> does, how I will use it (LEAF documentation does it perfectly), and what
> are the requirements. For the last point I would expect to see: "your
> platform needs to support TLS, and your program cannot use coroutines in
> asynchronous manner." IOW: high-level design trade-offs should be
> documented up front.

Coroutines can be used asynchronously without using threads.  You just
need to be careful to schedule them on an executor that is backed by
only one thread, rather than a threadpool.

In fact at least all the times that I have personally used coroutines,
it's been in this manner -- when you're just managing multiple parallel
tasks with a known-external delay (eg. slow network I/O), you don't
really need a threadpool.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [LEAF] The usage of TLS

Boost - Dev mailing list
wt., 5 maj 2020 o 04:39 Gavin Lambert via Boost <[hidden email]>
napisał(a):

> On 4/05/2020 20:29, Andrzej Krzemienski wrote:
> > These are the guidelines that I learned from Robert Ramey's talk on
> > documenting a library (https://www.youtube.com/watch?v=ACeNgqBKL7E). As
> a
> > potential user I need to learn in no more than 5 minutes: what the
> library
> > does, how I will use it (LEAF documentation does it perfectly), and what
> > are the requirements. For the last point I would expect to see: "your
> > platform needs to support TLS, and your program cannot use coroutines in
> > asynchronous manner." IOW: high-level design trade-offs should be
> > documented up front.
>
> Coroutines can be used asynchronously without using threads.  You just
> need to be careful to schedule them on an executor that is backed by
> only one thread, rather than a threadpool.
>
> In fact at least all the times that I have personally used coroutines,
> it's been in this manner -- when you're just managing multiple parallel
> tasks with a known-external delay (eg. slow network I/O), you don't
> really need a threadpool.
>

You are correct. Not every asynchronous use of coroutines is broken. Only
those that can potentially execute the continuation part on a different
thread.

Regards,
&rzej;

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [LEAF] The usage of TLS

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
On 04/05/2020 01:40, Emil Dotchevski via Boost wrote:

> LEAF is not suitable for coroutines

I cannot see any good reason why LEAF could not transport the current
tree of LEAF state within a C++ Coroutine across kernel threads.

Rough implementation:

1. Traverse the current LEAF state in the current C++ Coroutine.

2. Suspend the current C++ coroutine, resume an internal checking C++
Coroutine.

3. Traverse the current LEAF state from within the internal checking C++
Coroutine.

4. Compare this LEAF state with the previous one. If some of the LEAF
state "escapes" the previous Coroutine, resume the original C++
Coroutine and report failure, as this LEAF state cannot be transported
between kernel threads.

5. Otherwise transporting LEAF state with the current C++ coroutine
ought to be as simple as restamping the head TLS pointer to the current
LEAF state when one resumes Coroutine execution in another kernel thread
(i.e. squirrel away the head TLS pointer into the C++ coroutine frame,
restore it upon resumption)

6. For convenience, wrap up all of the above into C++ coroutine
awaitables which self-specialise when they see a T which is a LEAF
result e.g. boost::leaf::awaitables::lazy<T>. You may find inspiration
in the implementation code for outcome::awaitables::lazy<T>, as Outcome
provides Outcome-specialising awaitable types.


As an aside, Outcome did not support C++ Coroutines originally as I did
not expect much demand for such support. I assumed that given the poor
QoI of C++ Coroutines in all the major implementations nobody would
deploy production software using them.

It turns out I was wrong on that. Outcome + C++ Coroutines is
surprisingly popular. And even I have ended up using it personally in
test code (I still think Coroutines are not production ready).

I would expect that if LEAF is accepted, you will see the same.

Niall

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [LEAF] The usage of TLS

Boost - Dev mailing list
The mechanism in LEAF for dealing with this sort of thing is to manually
activate/deactivate a leaf::context<>.

The general architecture of LEAF is this: there are one or more
leaf::context<E1, E2, ...> objects (think something similar to
std::tuple<E1, E2, ...>) at different error-handling scopes in the call
stack. When a context<E1, E2, ...> is activated, TLS pointers of the E1,
E2, ... types are pointed to the E1, E2, ... objects in that context. This
binds the context<> object to the calling thread and, from this point on,
the TLS pointers are used to transport the error objects when reported.
When control returns to the error-handling scope -- where the
leaf::context<> resides -- it is deactivated before errors are handled

Normally, users don't deal with leaf::context<> directly, but it is part of
the API and you can just make a context<>, activate it as needed to collect
error objects, then deactivate it and, if there was an error, move it
elsewhere to handle errors (if no error, you can just activate it again the
next time).

So, when I say it doesn't work well with coroutines, what I mean is it
won't "just work", not that you can't use it.

On Tue, May 5, 2020 at 6:59 AM Niall Douglas via Boost <
[hidden email]> wrote:

> On 04/05/2020 01:40, Emil Dotchevski via Boost wrote:
>
> > LEAF is not suitable for coroutines
>
> I cannot see any good reason why LEAF could not transport the current
> tree of LEAF state within a C++ Coroutine across kernel threads.
>
> Rough implementation:
>
> 1. Traverse the current LEAF state in the current C++ Coroutine.
>
> 2. Suspend the current C++ coroutine, resume an internal checking C++
> Coroutine.
>
> 3. Traverse the current LEAF state from within the internal checking C++
> Coroutine.
>
> 4. Compare this LEAF state with the previous one. If some of the LEAF
> state "escapes" the previous Coroutine, resume the original C++
> Coroutine and report failure, as this LEAF state cannot be transported
> between kernel threads.
>
> 5. Otherwise transporting LEAF state with the current C++ coroutine
> ought to be as simple as restamping the head TLS pointer to the current
> LEAF state when one resumes Coroutine execution in another kernel thread
> (i.e. squirrel away the head TLS pointer into the C++ coroutine frame,
> restore it upon resumption)
>
> 6. For convenience, wrap up all of the above into C++ coroutine
> awaitables which self-specialise when they see a T which is a LEAF
> result e.g. boost::leaf::awaitables::lazy<T>. You may find inspiration
> in the implementation code for outcome::awaitables::lazy<T>, as Outcome
> provides Outcome-specialising awaitable types.
>
>
> As an aside, Outcome did not support C++ Coroutines originally as I did
> not expect much demand for such support. I assumed that given the poor
> QoI of C++ Coroutines in all the major implementations nobody would
> deploy production software using them.
>
> It turns out I was wrong on that. Outcome + C++ Coroutines is
> surprisingly popular. And even I have ended up using it personally in
> test code (I still think Coroutines are not production ready).
>
> I would expect that if LEAF is accepted, you will see the same.
>
> Niall
>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost