forked from chriskohlhoff/asio
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The experimental::promise type allows eager execution and synchronisation of async operations. auto promise = async_read( stream, asio::buffer(my_buffer), asio::experimental::use_promise); ... do other stuff while the read is going on ... promise.async_wait( // completion the operation [](error_code ec, std::size_t bytes_read) { ... }); Promises can be safely disregarded if the result is no longer required. Different operations can be combined to either wait for all to complete or for one to complete (and cancel the rest). For example, to wait for one to complete: auto timeout_promise = timer.async_wait( asio::experimental::use_promise); auto read_promise = async_read( stream, asio::buffer(my_buffer), asio::experimental::use_promise); auto promise = asio::experimental::promise<>::race( timeout_promise, read_promise); promise.async_wait( [](std::variant<error_code, std::tuple<error_code, std::size_t>> v) { if (v.index() == 0) {} //timed out else if (v.index() == 1) // completed in time }); or to wait for all to complete: auto write_promise = async_write( stream, asio::buffer(my_write_buffer), asio::experimental::use_promise); auto read_promise = async_read( stream, asio::buffer(my_buffer), asio::experimental::use_promise); auto promise = asio::experimental::promise<>::all( write_promise, read_promise); promise.async_wait( [](std::tuple<error_code, std::size_t> write_result, std::tuple<error_code, std::size_t> read_result) { });
- Loading branch information
1 parent
903c9fa
commit 7e3d996
Showing
7 changed files
with
1,183 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
158 changes: 158 additions & 0 deletions
158
asio/include/asio/experimental/detail/completion_handler_erasure.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
// | ||
// experimental/detail/completion_handler_erasure.hpp | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// | ||
// Copyright (c) 2021 Klemens D. Morgenstern | ||
// (klemens dot morgenstern at gmx dot net) | ||
// | ||
// Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
// | ||
|
||
#ifndef ASIO_EXPERIMENTAL_DETAIL_COMPLETION_HANDLER_ERASURE_HPP | ||
#define ASIO_EXPERIMENTAL_DETAIL_COMPLETION_HANDLER_ERASURE_HPP | ||
|
||
#include <asio/associated_allocator.hpp> | ||
#include <asio/dispatch.hpp> | ||
|
||
namespace asio { | ||
|
||
class any_io_executor; | ||
|
||
namespace experimental { | ||
namespace detail { | ||
|
||
template<typename Signature, typename Executor> | ||
struct completion_handler_erasure_base; | ||
|
||
template<typename Func, typename Signature, typename Executor> | ||
struct completion_handler_erasure_impl; | ||
|
||
template<typename Return, typename ... Args, typename Executor> | ||
struct completion_handler_erasure_base<Return(Args...), Executor> | ||
{ | ||
Executor executor; | ||
|
||
completion_handler_erasure_base(Executor&& executor) | ||
: executor(std::move(executor)) | ||
{ | ||
} | ||
|
||
virtual Return call(Args ...args) = 0; | ||
virtual ~completion_handler_erasure_base() = default; | ||
}; | ||
|
||
template<typename Func, typename Return, typename ... Args, typename Executor> | ||
struct completion_handler_erasure_impl<Func, Return(Args...), Executor> final | ||
: completion_handler_erasure_base<Return(Args...), Executor> | ||
{ | ||
completion_handler_erasure_impl(Executor&& exec, Func&& func) | ||
: completion_handler_erasure_base<Return(Args...), Executor>( | ||
std::move(exec)), func(std::move(func)) | ||
{ | ||
} | ||
|
||
virtual Return call(Args ...args) override | ||
{ | ||
std::move(func)(std::move(args)...); | ||
} | ||
|
||
Func func; | ||
}; | ||
|
||
template<typename Signature, typename Executor = any_io_executor> | ||
struct completion_handler_erasure; | ||
|
||
template<typename Return, typename ... Args, typename Executor> | ||
struct completion_handler_erasure<Return(Args...), Executor> | ||
{ | ||
struct deleter_t | ||
{ | ||
using allocator_base = typename associated_allocator<Executor>::type; | ||
using allocator_type = | ||
typename std::allocator_traits<allocator_base>::template rebind_alloc< | ||
completion_handler_erasure_base<Return(Args...), Executor>>; | ||
|
||
allocator_type allocator; | ||
std::size_t size; | ||
|
||
template<typename Func> | ||
static std::unique_ptr< | ||
completion_handler_erasure_base<Return(Args...), Executor>, deleter_t> | ||
make(Executor exec, Func&& func) | ||
{ | ||
using type = completion_handler_erasure_impl< | ||
std::remove_reference_t<Func>, Return(Args...), Executor>; | ||
using alloc_type = typename std::allocator_traits< | ||
allocator_base>::template rebind_alloc<type>; | ||
auto alloc = alloc_type(get_associated_allocator(exec)); | ||
auto size = sizeof(type); | ||
auto p = std::allocator_traits<alloc_type>::allocate(alloc, size); | ||
auto res = std::unique_ptr<type, deleter_t>( | ||
p, deleter_t{allocator_type(alloc), size}); | ||
std::allocator_traits<alloc_type>::construct(alloc, | ||
p, std::move(exec), std::forward<Func>(func)); | ||
return res; | ||
} | ||
|
||
void operator()( | ||
completion_handler_erasure_base<Return(Args...), Executor> * p) | ||
{ | ||
std::allocator_traits<allocator_type>::destroy(allocator, p); | ||
std::allocator_traits<allocator_type>::deallocate(allocator, p, size); | ||
} | ||
}; | ||
|
||
completion_handler_erasure(const completion_handler_erasure&) = delete; | ||
completion_handler_erasure(completion_handler_erasure&&) = default; | ||
completion_handler_erasure& operator=( | ||
const completion_handler_erasure&) = delete; | ||
completion_handler_erasure& operator=( | ||
completion_handler_erasure&&) = default; | ||
|
||
constexpr completion_handler_erasure() = default; | ||
|
||
constexpr completion_handler_erasure(nullptr_t) | ||
: completion_handler_erasure() | ||
{ | ||
} | ||
|
||
template<typename Func> | ||
completion_handler_erasure(Executor exec, Func&& func) | ||
: impl_(deleter_t::make(std::move(exec), std::forward<Func>(func))) | ||
{ | ||
} | ||
|
||
~completion_handler_erasure() | ||
{ | ||
if (auto f = std::exchange(impl_, nullptr); f != nullptr) | ||
{ | ||
asio::dispatch(f->executor, | ||
[f = std::move(f)]() mutable | ||
{ | ||
std::move(f)->call(Args{}...); | ||
}); | ||
} | ||
} | ||
|
||
Return operator()(Args ... args) | ||
{ | ||
if (auto f = std::exchange(impl_, nullptr); f != nullptr) | ||
f->call(std::move(args)...); | ||
} | ||
|
||
constexpr bool operator==(nullptr_t) const noexcept {return impl_ == nullptr;} | ||
constexpr bool operator!=(nullptr_t) const noexcept {return impl_ != nullptr;} | ||
constexpr bool operator!() const noexcept {return impl_ == nullptr;} | ||
|
||
private: | ||
std::unique_ptr< | ||
completion_handler_erasure_base<Return(Args...), Executor>, deleter_t> | ||
impl_; | ||
}; | ||
|
||
} // namespace detail | ||
} // namespace experimental | ||
} // namespace asio | ||
|
||
#endif // ASIO_EXPERIMENTAL_DETAIL_COMPLETION_HANDLER_ERASURE_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// | ||
// experimental/impl/promise.hpp | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// | ||
// Copyright (c) 2021 Klemens D. Morgenstern | ||
// (klemens dot morgenstern at gmx dot net) | ||
// | ||
// Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
// | ||
#ifndef ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP | ||
#define ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP | ||
|
||
#include "asio/cancellation_signal.hpp" | ||
#include "asio/experimental/detail/completion_handler_erasure.hpp" | ||
#include <tuple> | ||
#include <optional> | ||
|
||
namespace asio { | ||
namespace experimental { | ||
|
||
template<typename Signature = void(), typename Executor = any_io_executor> | ||
struct promise; | ||
|
||
namespace detail { | ||
|
||
template<typename Signature, typename Executor> | ||
struct promise_impl; | ||
|
||
template<typename ... Ts, typename Executor> | ||
struct promise_impl<void(Ts...), Executor> | ||
{ | ||
using result_type = std::tuple<Ts...>; | ||
|
||
promise_impl(Executor executor = {}) | ||
: executor(std::move(executor)) | ||
{ | ||
} | ||
|
||
std::optional<result_type> result; | ||
bool done{false}; | ||
detail::completion_handler_erasure<void(Ts...), Executor> completion; | ||
cancellation_signal cancel; | ||
Executor executor; | ||
}; | ||
|
||
template<typename Signature = void(), typename Executor = any_io_executor> | ||
struct promise_handler; | ||
|
||
template<typename Signature, typename Executor> | ||
struct promise_handler; | ||
|
||
template<typename ... Ts, typename Executor> | ||
struct promise_handler<void(Ts...), Executor> | ||
{ | ||
using promise_type = promise<void(Ts...), Executor>; | ||
|
||
promise_handler(Executor executor) // get_associated_allocator(exec) | ||
: impl_{ | ||
std::allocate_shared<promise_impl<void(Ts...), Executor>>( | ||
get_associated_allocator(executor))} | ||
{ | ||
impl_->executor = std::move(executor); | ||
} | ||
|
||
std::shared_ptr<promise_impl<void(Ts...), Executor>> impl_; | ||
|
||
using cancellation_slot_type = cancellation_slot; | ||
|
||
cancellation_slot_type get_cancellation_slot() const noexcept | ||
{ | ||
return impl_->cancel.slot(); | ||
} | ||
|
||
auto make_promise() -> promise<void(Ts...), Executor> | ||
{ | ||
return {impl_}; | ||
} | ||
|
||
void operator()(std::remove_reference_t<Ts>... ts) | ||
{ | ||
assert(impl_); | ||
impl_->result.emplace(std::move(ts)...); | ||
impl_->done = true; | ||
if (auto f = std::exchange(impl_->completion, nullptr); f != nullptr) | ||
std::apply(std::move(f), std::move(*impl_->result)); | ||
} | ||
}; | ||
|
||
} // namespace detail | ||
} // namespace experimental | ||
} // namespace asio | ||
|
||
#endif // ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP |
Oops, something went wrong.