17#include <asio/awaitable.hpp>
18#include <asio/co_spawn.hpp>
19#include <asio/io_context.hpp>
20#include <asio/use_awaitable.hpp>
31namespace neograph::async {
36namespace neograph::program {
54 bool await_synchronous_stores =
false;
71 static bool active()
noexcept;
88 asio::io_context& get()
noexcept {
return *io_; }
90 void discard() noexcept { reusable_ =
false; }
93 std::unique_ptr<asio::io_context> io_;
94 bool reusable_ =
true;
108template <
typename R,
typename Fn>
110 auto owned = std::make_shared<Fn>(std::move(fn));
111 if constexpr (std::is_void_v<R>) {
115 auto result = std::make_shared<std::optional<R>>();
117 co_return std::move(**result);
124 std::rethrow_exception(std::move(error));
125 if constexpr (!std::is_void_v<R>)
co_return R{};
129asio::awaitable<std::optional<R>> box_store_call(asio::awaitable<R> call) {
130 co_return std::optional<R>(
co_await std::move(call));
133[[noreturn]]
inline void refuse_blocking_store_call(std::string_view operation, std::string reason) {
134 throw ProgramBlockingCallRefused(std::string(operation), {}, std::move(reason));
137inline void check_blocking_store_call(
const BlockingStorePolicy& policy, std::string_view operation) {
139 if (
auto reason = policy.refuse(); !reason.empty()) refuse_blocking_store_call(operation, std::move(reason));
142 if (async::in_host_turn())
143 refuse_blocking_store_call(operation,
144 "it is called inside a turn of a host's event loop, which a blocked thread cannot turn");
164 check_blocking_store_call(policy, operation);
166 std::optional<R> value;
167 std::exception_ptr failure;
168 bool finished =
false;
169 asio::co_spawn(wait.get(), box_store_call<R>(std::move(call)),
170 [&](std::exception_ptr error, std::optional<R> result) {
171 failure = std::move(error);
172 value = std::move(result);
180 refuse_blocking_store_call(operation,
"the store call did not finish on this thread");
182 if (failure) std::rethrow_exception(failure);
183 return std::move(*value);
186inline void block_on_store(asio::awaitable<void> call,
const BlockingStorePolicy& policy, std::string_view operation) {
187 check_blocking_store_call(policy, operation);
188 BlockingIoContext wait;
189 std::exception_ptr failure;
190 bool finished =
false;
191 asio::co_spawn(wait.get(), std::move(call), [&](std::exception_ptr error) {
192 failure = std::move(error);
195 run_blocking_wait(wait);
200 refuse_blocking_store_call(operation,
"the store call did not finish on this thread");
202 if (failure) std::rethrow_exception(failure);
215 std::optional<R> value;
216 std::exception_ptr failure;
217 bool finished =
false;
218 asio::co_spawn(wait.get(), box_store_call<R>(std::move(call)),
219 [&](std::exception_ptr error, std::optional<R> result) {
220 failure = std::move(error);
221 value = std::move(result);
227 refuse_blocking_store_call(operation,
"the call did not finish on this thread");
229 if (failure) std::rethrow_exception(failure);
230 return std::move(*value);
233inline void run_to_completion(asio::awaitable<void> call, std::string_view operation) {
234 InlineStoreCalls inline_calls;
235 BlockingIoContext wait;
236 std::exception_ptr failure;
237 bool finished =
false;
238 asio::co_spawn(wait.get(), std::move(call), [&](std::exception_ptr error) {
239 failure = std::move(error);
242 run_blocking_wait(wait);
245 refuse_blocking_store_call(operation,
"the call did not finish on this thread");
247 if (failure) std::rethrow_exception(failure);
NEOGRAPH_API export/import macro for shared-library builds.
Typed refusals of a Program call that would block a thread that must not block (#557).
The private io_context of one blocking wait.
void discard() noexcept
Call when the wait ended without its coroutine finishing, or with an exception from the loop.
While one is alive on a thread, a Sync...Store completes its awaitable operations on that thread even...
NEOGRAPH_API bool in_host_turn() noexcept
True while the calling thread is running a turn of a HostLoop (#556).
asio::awaitable< R > failed_store_call(std::exception_ptr error)
An awaitable that throws error when it is awaited: an operation the store does not support.
NEOGRAPH_PROGRAM_API bool offload_store_call(bool offloads) noexcept
Whether a Sync...Store whose offloads() is offloads moves its next operation to the pool.
void run_blocking_wait(BlockingIoContext &wait)
Runs the context until its coroutine is done; a context whose loop threw is not reused.
R run_to_completion(asio::awaitable< R > call, std::string_view operation)
Runs call to completion on a private io_context of the calling thread, with InlineStoreCalls in force...
asio::awaitable< R > offload_sync_call(Fn fn)
Runs the synchronous operation fn on a thread of the bounded pool and resumes the awaiting coroutine ...
R block_on_store(asio::awaitable< R > call, const BlockingStorePolicy &policy, std::string_view operation)
Waits for call on a private io_context of the calling thread.
NEOGRAPH_PROGRAM_API asio::awaitable< void > offload_to_blocking_pool(std::function< void()> work)
Runs work on a thread of a bounded pool and resumes the awaiting coroutine on its own executor.
When a Blocking...Store may wait for a store call that does not complete at once.
std::function< std::string()> refuse
Called before the facade blocks its thread on a store that is not synchronous.