allocation aware futures asynchronous programming with ... · Long history in JavaScript: q,...
Transcript of allocation aware futures asynchronous programming with ... · Long history in JavaScript: q,...
![Page 1: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/1.jpg)
Continuableasynchronous programming with
allocation aware futures
/Naios/continuable
Denis Blank <[email protected]>
Meeting C++ 2018
![Page 2: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/2.jpg)
2
IntroductionAbout me
Denis Blank
● Master’s student @Technical University of Munich
● GSoC participant in 2017 @STEllAR-GROUP/hpx
● Author of the continuable and function2 libraries
● Interested in: compiler engineering, asynchronous
programming and metaprogramming
![Page 3: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/3.jpg)
3
IntroductionTable of contents
1. The future pattern (and its disadvantages)
2. Rethinking futures○ Continuable implementation
○ Usage examples of continuable
3. Connections○ Traversals for arbitrarily nested packs
○ Expressing connections with continuable
4. Coroutines
The continuable library talk:
/Naios/continuable
![Page 4: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/4.jpg)
The future pattern
4
![Page 5: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/5.jpg)
The future pattern
5
promises and futures
Future result
std::future<int>
Resolver
std::promise<int>
Creates
Resolves
![Page 6: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/6.jpg)
std::promise<int> promise;
std::future<int> future = promise.get_future();
promise.set_value(42);
int result = future.get();
The future pattern
6
In C++17 we can only pool or wait for the result synchronously
Synchronous wait
![Page 7: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/7.jpg)
future<std::string> other = future
.then([](future<int> future) {
return std::to_string(future.get());
});
Resolve the next future
Asynchronous return types
The future pattern
The Concurrency TS proposed a then method for
adding a continuation handler, now reworked in the
“A Unified Futures” and executors proposal.
7
Asynchronous continuation chaining
![Page 8: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/8.jpg)
The future pattern
8
The shared state
std::promise<int>std::future<int>
Shared stateon the heap
![Page 9: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/9.jpg)
The future pattern
9
template<typename T>
class shared_state {
std::variant<
std::monostate, T, std::exception_ptr
> result_;
std::function<void(future<T>)> then_;
std::mutex lock_;
};
Shared state implementation
Simplified version
The shared state contains a result storage,
continuation storage and synchronization primitives.
![Page 10: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/10.jpg)
The future pattern
10
● std::future
● boost::future
● folly::Future
● hpx::future
● stlab::future
● ...
Implementations with a shared state
![Page 11: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/11.jpg)
11
Future disadvantagesShared state overhead
● Attaching a continuation (then) creates a new future
and shared state every time (allocation overhead)!
● Maybe allocation for the continuation as well
● Result read/write not wait free○ Lock acquisition or spinlock
○ Can be optimized to an atomic wait free state read/write in the
single producer and consumer case (non shared future/promise).
● If futures are shared across multiple cores:
Shared-nothing futures can be zero cost (Seastar).
![Page 12: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/12.jpg)
12
Future disadvantagesShared state overhead
● Attaching a continuation (then) creates a new future
and shared state every time (allocation overhead)!
● Maybe allocation for the continuation as well
● Result read/write not wait free○ Lock acquisition or spinlock
○ Can be optimized to an atomic wait free state read/write in the
single producer and consumer case (non shared future/promise).
● If futures are shared across multiple cores:
Shared-nothing futures can be zero cost (Seastar).
![Page 13: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/13.jpg)
13
Future disadvantagesStrict eager evaluation
std::future<std::string> future = std::async([] {
return "Hello Meeting C++!"s;
});
● Futures represent the asynchronous result of an
already running operation!
● Impossible not to request it
● Execution is non deterministic:○ Leads to unintended side effects!
○ No ensured execution order!
● Possible: Wrapping into a lambda to achieve laziness.
![Page 14: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/14.jpg)
14
Future disadvantagesStrict eager evaluation
● Futures represent the asynchronous result of an
already running operation!
● Impossible not to request it
● Execution is non deterministic:○ Leads to unintended side effects!
○ No ensured execution order!
● Possible: Wrapping into a lambda to achieve laziness.
std::future<std::string> future = std::async([] {
return "Hello Meeting C++!"s;
});
![Page 15: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/15.jpg)
15
Future disadvantagesUnwrapping and R-value correctness
● future::then L-value callable although consuming ○ Should be R-value callable only (for detecting misuse)
● Always required to call future::get○ But: Fine grained exception control possible (not needed)
● Repetition of type○ Becomes worse in compound futures (connections)
future.then([] (future<std::tuple<future<int>,
future<int>>> future) {
int a = std::get<0>(future.get()).get();
int b = std::get<1>(future.get()).get();
return a + b;
});
![Page 16: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/16.jpg)
16
Future disadvantagesUnwrapping and R-value correctness
● future::then L-value callable although consuming ○ Should be R-value callable only (for detecting misuse)
● Always required to call future::get○ But: Fine grained exception control possible (not needed)
● Repetition of type○ Becomes worse in compound futures (connections)
future.then([] (future<std::tuple<future<int>,
future<int>>> future) {
int a = std::get<0>(future.get()).get();
int b = std::get<1>(future.get()).get();
return a + b;
});
![Page 17: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/17.jpg)
17
Future disadvantagesException propagation
● Propagation overhead through rethrowing on get
● No error codes as exception type possible
make_exceptional_future<int>(std::exception{})
.then([] (future<int> future) {
int result = future.get();
return result;
})
.then([] (future<int> future) {
int result = future.get();
return result;
})
.then([] (future<int> future) {
try {
int result = future.get();
} catch (std::exception const& e) {
// Handle the exception
}
});
![Page 18: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/18.jpg)
18
Future disadvantagesAvailability
● std::future::experimental::then will change heavily:○ Standardization date unknown○ “A Unified Future” proposal maybe C++23
● Other implementations require a large framework, runtime or are difficult to build
Now C++20 C++23 Future
![Page 19: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/19.jpg)
Rethinking futures
19
![Page 20: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/20.jpg)
20
Rethinking futures
● Usable in a broad case of usage scenarios (boost, Qt)
● Portable, platform independent and simple to use
● Agnostic to user provided executors and runtimes
● Should resolve the previously mentioned disadvantages:
○ Shared state overhead
○ Strict eager evaluation
○ Unwrapping and R-value correctness
○ Exception propagation
○ Availability
Designing goals
![Page 21: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/21.jpg)
21
Rethinking futures
● Usable in a broad case of usage scenarios (boost, Qt)
● Portable, platform independent and simple to use
● Agnostic to user provided executors and runtimes
● Should resolve the previously mentioned disadvantages:
○ Shared state overhead
○ Strict eager evaluation
○ Unwrapping and R-value correctness
○ Exception propagation
○ Availability
Designing goals
![Page 22: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/22.jpg)
22
Rethinking futuresWhy we don’t use callbacks
● Difficult to express complicated chains
● But: Simple and performant to express an
asynchronous continuation.
● But: Work nicely with existing libraries
signal_set.async_wait([](auto error, int slot) {
signal_set.async_wait([](auto error, int slot) {
signal_set.async_wait([](auto error, int slot) {
signal_set.async_wait([](auto error, int slot) {
// handle the result here
});
});
});
});
Callback hell
![Page 23: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/23.jpg)
23
Rethinking futuresHow we could use callbacks
● Idea: Transform the callbacks into something
easier to use without the callback hell○ Long history in JavaScript: q, bluebird
○ Much more complicated in C++ because of static typing,
requires heavy metaprogramming.
● Mix this with syntactic sugar and C++ candies
like operator overloading.
And finished is the continuable
Not trivial...
![Page 24: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/24.jpg)
24
Rethinking futuresHow we could use callbacks
● Idea: Transform the callbacks into something
easier to use without the callback hell○ Long history in JavaScript: q, bluebird
○ Much more complicated in C++ because of static typing,
requires heavy metaprogramming.
● Mix this with syntactic sugar and C++ candies
like operator overloading.
And finished is the continuable
Not trivial...
![Page 25: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/25.jpg)
auto continuable = make_continuable<int>([](auto&& promise) {
// Resolve the promise immediately or store
// it for later resolution.
promise.set_value(42);
});
Resolve the promise,set_value alias for operator()
Arbitrary asynchronous return types
Creating continuables
A continuable_base is creatable through
make_continuable, which requires its types trough
template arguments and accepts a callable type
The promise might be moved or stored
25
![Page 26: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/26.jpg)
auto continuable = make_continuable<int>([](auto&& promise) {
// Resolve the promise immediately or store
// it for later resolution.
promise.set_value(42);
});
Resolve the promise,set_value alias for operator()
Arbitrary asynchronous return types
Creating continuables
A continuable_base is creatable through
make_continuable, which requires its types trough
template arguments and accepts a callable type
The promise might be moved or stored
26
![Page 27: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/27.jpg)
make_ready_continuable(42)
.then([] (int value) {
// return something
});
Chaining continuables
A continuable_base is chainable through its then
method, which accepts a continuation handler.
We work on values directly rather than continuables.
This ready continuable resolves the given result instantly
27
Continuation chaining
Optional return value:● Plain object● Tuple of objects● The next continuable
to resolve
![Page 28: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/28.jpg)
http_request("example.com")
.then([] (int status, std::string body) {
return mysql_query("SELECT * FROM `users` LIMIT 1");
})
.then(do_delete_caches())
.then(do_shutdown());
Chaining continuables
then may also return plain objects, a tuple of
objects or the next continuable_base to resolve.
Return the next continuable_base
to resolve
Just a dummy function which returns a continuable_base of int, std::string
28
Continue from callbacks
Ignore previous results
![Page 29: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/29.jpg)
http_request("example.com")
.then([] (int status, std::string body) {
return mysql_query("SELECT * FROM `users` LIMIT 1");
})
.then(do_delete_caches())
.then(do_shutdown());
Chaining continuables
then may also return plain objects, a tuple of
objects or the next continuable_base to resolve.
Return the next continuable_base
to resolve
Just a dummy function which returns a continuable_base of int, std::string
29
Continue from callbacks
Ignore previous results
![Page 30: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/30.jpg)
make_ready_continuable(‘a’, 2, 3)
.then([] (char a) {
return std::make_tuple(‘d’, 5);
})
.then([] (char c, int d) {
// ...
});
Chaining continuables
The continuation passed to then may also accept the
result partially, and may pass multiple objects
wrapped inside a std::tuple to the next handler.
Return multiple objects that are passed to the
next continuation directly
Use the asynchronous arguments partially
30
Continuation chaining sugar
![Page 31: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/31.jpg)
make_ready_continuable(‘a’, 2, 3)
.then([] (char a) {
return std::make_tuple(‘d’, 5);
})
.then([] (char c, int d) {
// ...
});
Chaining continuables
The continuation passed to then may also accept the
result partially, and may pass multiple objects
wrapped inside a std::tuple to the next handler.
Use the asynchronous arguments partially
31
Continuation chaining sugar
Return multiple objects that are passed to the
next continuation directly
![Page 32: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/32.jpg)
Continuable implementation
32
![Page 33: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/33.jpg)
33
Continuable implementationCreating ready continuables
make_ready_continuable(0, 1)
make_continuable<int, int>([] (auto&& promise) {
promise.set_value(0, 1);
});
The implementation stores the arguments into a
std::tuple first and sets the promise with the
content of the tuple upon request (std::apply).
![Page 34: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/34.jpg)
34
Continuable implementationDecorating the continuation result
.then([] (auto result) {
return;
})
.then([] (auto result) {
return make_ready_continuable();
})
Transform the continuation result such that it is
always a continuable_base of the corresponding
result.
.then([] (auto result) {
return std::make_tuple(0, 1);
})
.then([] (auto result) {
return make_ready_continuable(0, 1);
})
![Page 35: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/35.jpg)
35
Continuable implementationDecorating the continuation result
.then([] (auto result) {
return;
})
.then([] (auto result) {
return make_ready_continuable();
})
Transform the continuation result such that it is
always a continuable_base of the corresponding
result.
.then([] (auto result) {
return std::make_tuple(0, 1);
})
.then([] (auto result) {
return make_ready_continuable(0, 1);
})
![Page 36: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/36.jpg)
36
Continuable implementationInvoker selection through tag dispatching
using result_t = std::invoke_result_t<Callback, Args...>;
// ^ std::tuple<int, int> for example
auto invoker = invoker_of(identity<result_t>{});
// void
auto invoker_of(identity<void>);
// T
template<typename T>
auto invoker_of(identity<T>);
// std::tuple<T...>
template<typename... T>
auto invoker_of(identity<std::tuple<T...>>);
3
2
1
![Page 37: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/37.jpg)
37
Continuable implementationAttaching a continuation
auto continuation =
[=](auto promise) {
promise(1);
};
auto callback =
[] (int result) {
return make_ready_continuable();
};
Attaching a callback to a continuation yields a new
continuation with new argument types.
auto new_continuation =
[](auto next_callback) {
auto proxy = decorate(callback,
next_callback)
continuation(proxy);
};
![Page 38: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/38.jpg)
38
Continuable implementationDecorating the callback
auto proxy = [ callback,
next_callback ] (auto&&... args) {
auto next_continuation = callback(std::forward<decltype(args)>(args)...);
next_continuation(next_callback);
};
The proxy callback passed to the previous
continuation invokes the next continuation with the
next callback.
![Page 39: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/39.jpg)
39
Continuable implementationSeeing the big picture
Yield result ofouter continuation
continuationcallback
continuationcallback
continuationcallback
continuationcallbackcontinuationcallback
Invocation
![Page 40: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/40.jpg)
40
Continuable implementationSeeing the big picture
continuationcallback
continuationcallback
continuationcallback
continuationcallbackcontinuationcallback
Invocation
Yield result ofouter continuation
callback passed to this continuation
through then!
![Page 41: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/41.jpg)
Russian Matryoshka doll
41
Continuable implementationSeeing the big picture
![Page 42: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/42.jpg)
42
Continuable implementationException handling
When the promise is resolved with an exception an
exception_ptr is passed to the next available
failure handler.
read_file("entries.csv")
.then([] (std::string content) {
// ...
})
.fail([] (std::exception_ptr exception) {
// handle the exception
})
promise.set_exception(...)
On exceptions skip the result handlers between.
![Page 43: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/43.jpg)
43
Continuable implementationSplit asynchronous control flows
Results
Exceptions
Others
throw recover
![Page 44: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/44.jpg)
44
Continuable implementationSplit asynchronous control flows
template<typename... Args>
struct callback {
auto operator() (Args&&... args);
auto operator() (dispatch_error_tag, std::exception_ptr);
// dispatch_error_tag is exception_arg_t in the
// "Unified Futures" standard proposal.
};
Or any other error type
![Page 45: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/45.jpg)
45
Continuable implementationException propagation
template<typename... Args>
struct proxy {
Callback failure_callback_;
NextCallback next_callback_
void operator() (Args&&... args) {
// The next callback has the same signature
next_callback_(std::forward<Args>(args)...);
}
void operator() (dispatch_error_tag, std::exception_ptr exception) {
failure_callback_(exception);
}
};
On a valid result forward it to the next available result handler
![Page 46: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/46.jpg)
46
Continuable implementationResult handler conversion
template<typename... Args>
struct proxy {
Callback callback_;
NextCallback next_callback_
void operator() (Args&&... args) {
auto continuation = callback_(std::forward<Args>(args)...);
continuation(next_callback);
}
void operator() (dispatch_error_tag, std::exception_ptr exception) {
next_callback_(dispatch_error_tag{}, exception);
}
};
Forward the exception to the next available handler
![Page 47: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/47.jpg)
47
The continuable_baseThe wrapper
The continuable_base is convertible when the types
of Continuation are convertible to each other.
template<typename Continuation, typename Strategy>
class continuable_base {
Continuation continuation_;
ownership ownership_;
template<typename C,
typename E = this_thread_executor>
auto then(C&& callback,
E&& executor = this_thread_executor{}) &&;
};
● void (until now)● strategy_all_tag● strategy_seq_tag● strategy_any_tag
consuming = R-valuestd::move(continuable).then(...);
![Page 48: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/48.jpg)
48
The continuable_baseThe wrapper
The continuable_base is convertible when the types
of Continuation are convertible to each other.
template<typename Continuation, typename Strategy>
class continuable_base {
Continuation continuation_;
ownership ownership_;
template<typename C,
typename E = this_thread_executor>
auto then(C&& callback,
E&& executor = this_thread_executor{}) &&;
};
● void (until now)● strategy_all_tag● strategy_seq_tag● strategy_any_tag
consuming = R-valuestd::move(continuable).then(...);
![Page 49: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/49.jpg)
49
The continuable_baseThe ownership model
The continuation is invoked when the
continuable_base is still valid and being destroyed
(race condition free continuation chaining).
npc->talk("Greetings traveller, how is your name?")
.then([log, player] {
log->info("Player {} asked for name.", player->name());
return player->ask_for_name();
})
.then([](std::string name) {
// ...
});Invoke the
continuation here
![Page 50: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/50.jpg)
50
Memory allocationThe continuable_base
● Increases the amount of types the compiler has to generate ⇒ slower compilation
● Better for compiler optimization● Increases the executable size● ⇒ We require a concrete type for APIs where we
don’t want to expose our implementation
Until now: no memory allocation involved!then always returns an object of an unknown type
![Page 51: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/51.jpg)
51
Concrete typesThe continuable_base
continuable<int, std::string> http_request(std::string url) {
return [=](promise<int, std::string> promise) {
// Resolve the promise later
promise.set_value(200, "<html> ... </html>");
};
}
Preserve unknown types across the continuation chaining, convert it to concrete types in APIs on request
![Page 52: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/52.jpg)
52
The continuable_baseType erasure
For the callable type erasure my function2 library
is used that provides move only and multi signature
capable type erasures + small functor optimization.
using callback_t = function<void(Args...),
void(dispatch_error_tag,
std::exception_ptr)>;
using continuation_t = function<void(callback_t)>;
Erased callable for promise<Args…>
Erased callable for continuable<Args…>
![Page 53: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/53.jpg)
53
The continuable_baseType erasure
For the callable type erasure my function2 library
is used that provides move only and multi signature
capable type erasures + small functor optimization.
using callback_t = function<void(Args...),
void(dispatch_error_tag,
std::exception_ptr)>;
using continuation_t = function<void(callback_t)>;
Erased callable for promise<Args…>
Erased callable for continuable<Args…>
![Page 54: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/54.jpg)
54
The continuable_baseType erasure aliases
template<typename... Args>
using promise = promise_base<callback_t<Args...>>;
template<typename... Args>
using continuable = continuable_base<
function<void(promise<Args...>)>,
void
>;
template<typename... Args>
using callback_t = function<void(Args...),
void(dispatch_error_tag,
std::exception_ptr)>;
continuable_base type erasure works implicitly and
with any type erasure wrapper out of the box.
![Page 55: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/55.jpg)
55
The continuable_baseApply type erasure when needed
futures requires a minimum of two fixed allocations
per then whereas continuable requires a maximum of
two allocations per type erasure.
// auto do_sth();
continuable<> cont = do_sth()
.then([] {
return do_sth();
})
.then([] {
return do_sth();
});
// future<void> do_sth();
future<void> cont = do_sth()
.then([] (future<void>) {
return do_sth();
})
.then([] (future<void>) {
return do_sth();
});
Max 2 allocs on need
2*2 fixed + 2*1 maybe continuationsallocs
![Page 56: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/56.jpg)
56
The continuable_baseApply type erasure when needed
futures requires a minimum of two fixed allocations
per then whereas continuable requires a maximum of
two allocations per type erasure.
// auto do_sth();
continuable<> cont = do_sth()
.then([] {
return do_sth();
})
.then([] {
return do_sth();
});
// future<void> do_sth();
future<void> cont = do_sth()
.then([] (future<void>) {
return do_sth();
})
.then([] (future<void>) {
return do_sth();
});
Max 2 allocs on need
2*2 fixed + 2*1 maybe continuationsallocs
![Page 57: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/57.jpg)
Executor support
57
![Page 58: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/58.jpg)
58
Usage casesExecutor support
mysql_query("SELECT `id`, `name` FROM `users` WHERE `id` = 123")
.then([](ResultSet result) {
// On which thread this continuation runs?
});
promise.set_value(result);
● On which thread the continuation runs:○ Resolving thread? (default)○ Thread which created the continuation?
● When does the continuation run:○ Immediately on resolving? (default)○ Later?
● Can we cancel the continuation chain?
That should be up to you!
![Page 59: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/59.jpg)
59
Using an executorExecutor support
struct my_executor_proxy {
template<typename T>
void operator()(T&& work) {
std::forward<T>(work)();
}
};
mysql_query("SELECT `id`, `name` FROM `users` WHERE `id` = 123")
.then([](ResultSet result) {
// Pass this continuation to my_executor
}, my_executor_proxy{});
second argument of then
● Invoke the work● Drop the work● Move the work to another
thread or executor
![Page 60: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/60.jpg)
60
No executor propagationExecutor support
The executor isn’t propagated to the next handler
and has to be passed again to avoid unnecessary
type erasure (we could make it a type parameter).
continuable<> next = do_sth().then([] {
// Do sth.
}, my_executor_proxy{});
std::move(next).then([] {
// No ensured propagation!
});
Propagation would lead to type erasure although it isn’t requested here!
![Page 61: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/61.jpg)
Context of executionExecutor support
⇒ We can neglect executor propagation when moving heavy tasks to a continuation,
except in case of data races!
do_sth().then([] {
// Do something short
});
continuable<> do_sth() {
return [] (auto&& promise) {
// Do something long
promise.set_value();
};
}
ContinuationCallback
![Page 62: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/62.jpg)
62
Check design goals
● No shared state overhead
● No strict eager evaluation
● No unwrapping and R-value correctness
● Exception propagation
● Availability
○ C++14
○ Header-only (depends on function2)
○ GCC / Clang / MSVC
![Page 63: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/63.jpg)
63
Check design goals
● No Shared state overhead
● No strict eager evaluation
● No Unwrapping and R-value correctness
● Exception propagation
● Availability
○ C++14
○ Header-only (depends on function2)
○ GCC / Clang / MSVC
![Page 64: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/64.jpg)
Connections
64
![Page 65: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/65.jpg)
65
ConnectionsThe call graph
when_all, when_any usable to express relations
between multiple continuables.
⇒ Guided/graph based execution requires a shared
state (not available)
when_all
then
when_any
Ready when any dependent continuable is ready
Ready when all dependent continuables are ready
![Page 66: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/66.jpg)
66
ConnectionsLazy evaluation advantages
Using lazy (on request) evaluation over an eager one
makes it possible to choose the evaluation strategy.
⇒ Moves this responsibility from the executor to
the evaluator!
when_seq
Invokes all dependent continuables in sequential order
Thoughts(not implemented)
● when_pooled - pooling● when_every - request all● when_first_succeeded /
when_first_failed - exception strategies
![Page 67: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/67.jpg)
67
ConnectionsSimplification over std::experimental::when_all
std::when_all introduces code overhead because of
unnecessary fine grained exception handling.
// continuable<int> do_sth();
when_all(do_sth(),
do_sth())
.then([] (int a, int b) {
return a == b;
});
// std::future<int> do_sth();
std::experimental::when_all(do_sth(),
do_sth())
.then([] (std::tuple<std::future<int>,
std::future<int>> res) {
return std::get<0>(res).get()
== std::get<1>(res).get();
});
![Page 68: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/68.jpg)
68
ConnectionsSimplification over std::experimental::when_all
std::when_all introduces code overhead because of
unnecessary fine grained exception handling.
// continuable<int> do_sth();
when_all(do_sth(),
do_sth())
.then([] (int a, int b) {
return a == b;
});
// std::future<int> do_sth();
std::experimental::when_all(do_sth(),
do_sth())
.then([] (std::tuple<std::future<int>,
std::future<int>> res) {
return std::get<0>(res).get()
== std::get<1>(res).get();
});
![Page 69: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/69.jpg)
69
Connections implementationBased on GSoC @STEllAR-GROUP/hpx
The map_pack, traverse_pack and traverse_pack_async
API helps to apply an arbitrary connection between
continuables contained in a variadic pack.
hpx::when_all
hpx::dataflowhpx::unwrapped
hpx::when_any
hpx::when_somehpx::wait_any
hpx::wait_some
hpx::wait_all
synchronous traversal (map_pack)
synchronous mapping (traverse_pack)
asynchronous traversal(traverse_pack_async)
Basically the same implementation
![Page 70: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/70.jpg)
70
Connections implementationBased on GSoC @STEllAR-GROUP/hpx
The map_pack, traverse_pack and traverse_pack_async
API helps to apply an arbitrary connection between
continuables contained in a variadic pack.
hpx::when_all
hpx::dataflowhpx::unwrapped
hpx::when_any
hpx::when_somehpx::wait_any
hpx::wait_some
hpx::wait_all
synchronous traversal (map_pack)
synchronous mapping (traverse_pack)
asynchronous traversal(traverse_pack_async)
Basically the same implementation
![Page 71: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/71.jpg)
71
Connections implementationIndexer example (map_pack)
Because when_any returns the first ready result of
a common denominator, map_pack could be used to
apply an index to the continuables.
index_continuables(do_sth(),
do_sth(),
do_sth());
// Shall return:
std::tuple<
continuable<size_t /*= 0*/, int>,
continuable<size_t /*= 1*/, int>,
continuable<size_t /*= 2*/, int>
>
// continuable<int> do_sth();
when_any(do_sth(), do_sth(),
do_sth());
.then([] (int a) {
// ?: We don’t know which
// continuable became ready
});
![Page 72: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/72.jpg)
72
Connections implementationIndexer example (map_pack)
Because when_any returns the first ready result of
a common denominator, map_pack could be used to
apply an index to the continuables.
index_continuables(do_sth(),
do_sth(),
do_sth());
// Shall return:
std::tuple<
continuable<size_t /*= 0*/, int>,
continuable<size_t /*= 1*/, int>,
continuable<size_t /*= 2*/, int>
>
// continuable<int> do_sth();
when_any(do_sth(), do_sth(),
do_sth());
.then([] (int a) {
// ?: We don’t know which
// continuable became ready
});
![Page 73: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/73.jpg)
73
Connections implementationIndexer example (map_pack)
map_pack(indexer{}, do_sth(), do_sth(), do_sth());
struct indexer {
size_t index = 0;
template <typename T,
std::enable_if_t<is_continuable<std::decay_t<T>>::value>* = nullptr>
auto operator()(T&& continuable) {
auto current = ++index;
return std::forward<T>(continuable).then([=] (auto&&... args) {
return std::make_tuple(current,
std::forward<decltype(args)>(args)...);
});
}
};
map_pack transforms an arbitrary argument pack
through a callable mapper.
![Page 74: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/74.jpg)
74
Connections implementationIndexer example (map_pack)
map_pack(indexer{}, do_sth(), do_sth(), do_sth());
struct indexer {
size_t index = 0;
template <typename T,
std::enable_if_t<is_continuable<std::decay_t<T>>::value>* = nullptr>
auto operator()(T&& continuable) {
auto current = ++index;
return std::forward<T>(continuable).then([=] (auto&&... args) {
return std::make_tuple(current,
std::forward<decltype(args)>(args)...);
});
}
};
map_pack transforms an arbitrary argument pack
through a callable mapper.
![Page 75: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/75.jpg)
75
Connections implementationArbitrary and nested arguments
map_pack and friends can work with plain values and
nested packs too and so can when_all.
continuable<int> aggregate(std::tuple<int,
continuable<int>,
std::vector<continuable<int>>> all) {
return when_all(std::move(all))
.then([] (std::tuple<int, int, std::vector<int>> result) {
int aggregated = 0;
traverse_pack([&] (int current) {
aggregated += current;
}, std::move(result));
return aggregated;
});
}
![Page 76: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/76.jpg)
76
Connections implementationArbitrary and nested arguments
map_pack and friends can work with plain values and
nested packs too and so can when_all.
continuable<int> aggregate(std::tuple<int,
continuable<int>,
std::vector<continuable<int>>> all) {
return when_all(std::move(all))
.then([] (std::tuple<int, int, std::vector<int>> result) {
int aggregated = 0;
traverse_pack([&] (int current) {
aggregated += current;
}, std::move(result));
return aggregated;
});
}
![Page 77: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/77.jpg)
77
Connections implementationwhen_all/when_seq
Connections require a shared state by design,
concurrent writes to the same box never happen.
int,continuable<int>, std::vector<continuable<int>>
when_all/seq
map_pack(boxify{}, ...)
Ready
traverse_pack(resolve{}, ...)
Continue
map_pack(unwrap{}, ...)
Counter
int,box<expected<int>, continuable<int>>, std::vector<box<expected<int>, continuable<int>>>
![Page 78: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/78.jpg)
78
Connections implementationwhen_all/when_seq
Connections require a shared state by design,
concurrent writes to the same box never happen.
int,continuable<int>, std::vector<continuable<int>>
int,box<expected<int>, continuable<int>>, std::vector<box<expected<int>, continuable<int>>>
when_all/seq
map_pack(boxify{}, ...)
Ready
traverse_pack(resolve{}, ...)
Continue
map_pack(unwrap{}, ...)
Counter
![Page 79: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/79.jpg)
79
Connections implementationwhen_all/when_seq
Connections require a shared state by design,
concurrent writes to the same box never happen.
int,continuable<int>, std::vector<continuable<int>>
when_all/seq
map_pack(boxify{}, ...)
Ready
traverse_pack(resolve{}, ...)
Continue
map_pack(unwrap{}, ...)
Counter
int,box<expected<int>, continuable<int>>, std::vector<box<expected<int>, continuable<int>>>
![Page 80: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/80.jpg)
80
Operator overloadingExpress connections
Operator overloading allows expressive connections
between continuables.
(http_request("example.com/a") && http_request("example.com/b"))
.then([] (http_response a, http_response b) {
// ...
return wait_until(20s)
|| wait_key_pressed(KEY_SPACE)
|| wait_key_pressed(KEY_ENTER);
});
when_any: operator||
when_all: operator&&
![Page 81: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/81.jpg)
81
Operator overloadingDifficulties
A naive operator overloading approach where we
instantly connect 2 continuables would lead to
unintended evaluations and thus requires linearization.
return wait_until(20s)
|| wait_key_pressed(KEY_SPACE)
|| wait_key_pressed(KEY_ENTER); when_any
when_any
wait_key_pressed(KEY_SPACE)
wait_until(20s)
wait_key_pressed(KEY_ENTER)
![Page 82: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/82.jpg)
82
Operator overloadingCorrect operator evaluation required
when_any
wait_key_pressed(KEY_SPACE)
wait_until(20s) wait_key_pressed(KEY_ENTER)
![Page 83: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/83.jpg)
83
Operator overloadingImplementation
Set the continuable_base into an intermediate state
(strategy), materialize the connection on use or when
the strategy changes (expression template).
wait_until(20s)|| wait_key_pressed(KEY_SPACE)
... || wait_key_pressed(KEY_SPACE)
.then(...)
continuable_base<std::tuple<..., ...>, strategy_any_tag>
continuable_base<std::tuple<..., ..., ...>, strategy_any_tag>
continuable_base<..., void>Materialization
![Page 84: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/84.jpg)
Continuable & Coroutines TS
84
![Page 85: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/85.jpg)
85
Continuable & Coroutines TSInteroperability
continuable_base implements operator co_await() and
specializes coroutine_traits and thus is compatible to
the Coroutines TS.
continuable<int> interoperability_check() {
try {
auto response = co_await http_request("example.com/c");
} catch (std::exception const& e) {
co_return 0;
}
auto other = cti::make_ready_continuable(0, 1);
auto [ first, second ] = co_await std::move(other);
co_return first + second;
}
![Page 86: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/86.jpg)
86
Continuable & Coroutines TSInteroperability
continuable_base implements operator co_await() and
specializes coroutine_traits and thus is compatible to
the Coroutines TS.
continuable<int> interoperability_check() {
try {
auto response = co_await http_request("example.com/c");
} catch (std::exception const& e) {
co_return 0;
}
auto other = cti::make_ready_continuable(0, 1);
auto [ first, second ] = co_await std::move(other);
co_return first + second;
}
![Page 87: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/87.jpg)
87
Continuable & Coroutines TSDo Coroutines deprecate Continuables?
● A coroutine isn’t necessarily allocation free○ Recursive coroutines frames○ Depends on compiler optimization
● Connections
● Executors (difficult to do with plain coroutines)
● Takes time until Coroutines are widely supported○ Libraries that work with plain callbacks (legacy codebases)
● But: Coroutines have much better Call Stacks!
Probably not!There are many things a plain coroutine doesn’t provide
![Page 88: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/88.jpg)
88
Continuable & Coroutines TSDo Coroutines deprecate Continuables?
● A coroutine isn’t necessarily allocation free○ Recursive coroutines frames○ Depends on compiler optimization
● Connections
● Executors (difficult to do with plain coroutines)
● Takes time until Coroutines are widely supported○ Libraries that work with plain callbacks (legacy codebases)
● But: Coroutines have much better Call Stacks!
Probably not!There are many things a plain coroutine doesn’t provide
![Page 89: allocation aware futures asynchronous programming with ... · Long history in JavaScript: q, bluebird Much more complicated in C++ because of static typing, requires heavy metaprogramming.](https://reader035.fdocuments.us/reader035/viewer/2022081613/5fbce638801aa24f0b2a7a35/html5/thumbnails/89.jpg)
89
Questions?Thank you for your attention
/Naios/continuableDenis Blank <[email protected]>
MIT Licensed
/Naios/talks
slides code
me