A safer API for V4 #18
ConorWilliams
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
The problems
With the V3 API, the exception-safe Fibonacci looks like:
Key things I want to improve in future versions of libfork are:
fork[&a, fib](n - 1) -> fork(&a, fib, n - 1)
resume(...)
requires a context then associate the call with a context!Implications of some of these changes are discussed below.
Templates
This will require storing the root/call/fork information dynamically in the promise. This is expected to be a small runtime slowdown for big compile-time gains as well as removing the need for the first argument which will simplify the API. The first argument as a y-combinator is not required in C++23 as lambdas can be recursive.
We may expect runtime gains if the instruction cache can be better re-used.
Finally, this removes our opportunity to specify the return address type. Some flexibility could be returned with this API:
Exceptions and joining
Exceptions are unsafe because:
join
can easily be missed on the exception pathAn example of (2):
If the continuation was stolen then the child task may write to the memory location that stored
a
aftera
has been destructed, this would be a big-bad for non-trivial types.Solutions:
a
on the heap and store a pointer.-- x --
via exception.fork
in "safe" library function?Ideal (language level):
Scope based
Exploring solution 2, we could introduce a scope:
If the destructor of
scope
is called before a join we can issue a terminate, the compiler should be able to omit the check on the non-exceptional path. Furthermore the above can be macro'd:and written:
Alternatively, if we introduced a helper that stack allocated some return variables we could achieve full safety:
Helping to prevent writing:
Nesting scopes
Scopes would allow us to have fork-join regions inside fork-join regions:
Cancellation
With the scope based API each scope could be cancelled with APIs like:
Implementing V4
In this section we use
void*
for all pointer-size types.Promise defined such that the synthesized frame looks like:
A task handle would be a pointer to a scope.
Early testing
I have mocked up some of this API in the experimental branch, initial testing suggests serial performance is on par with the fully templated version.
Beta Was this translation helpful? Give feedback.
All reactions