Lightweight 0.20260617.0
Loading...
Searching...
No Matches
StrandExecutor.hpp
1// SPDX-License-Identifier: Apache-2.0
2#pragma once
3
4#include "../Api.hpp"
5#include "Executor.hpp"
6#include "detail/ExecutorQueues.hpp"
7
8#include <memory>
9
10namespace Lightweight::Async
11{
12
13/// Serializes work over an underlying executor (an Asio-style "strand").
14///
15/// All work posted to a given strand runs one item at a time, in FIFO order, even though
16/// the items execute on the underlying executor's worker threads. This guarantees that a
17/// single ODBC connection — which is not safe for concurrent use — is touched by only one
18/// thread at a time. A strand does not own a thread; it borrows the underlying executor.
19///
20/// The strand's mutable state lives in a heap @c State held by a @c std::shared_ptr. Each
21/// in-flight drain closure keeps a copy of that pointer, so the state outlives the
22/// @c StrandExecutor wrapper itself until the last drain returns. This makes the strand safe
23/// to destroy (or to replace, e.g. via @c SqlConnection::EnableAsync) while a drain is still
24/// running on a worker thread — the closure only ever touches @c State, never the wrapper.
25class LIGHTWEIGHT_API StrandExecutor final: public IExecutor, public IResumeScheduler
26{
27 public:
28 /// Constructs a strand layered over @p underlying.
29 ///
30 /// @param underlying The executor that actually runs the serialized work.
31 explicit StrandExecutor(IExecutor& underlying);
32
33 StrandExecutor(StrandExecutor const&) = delete;
34 StrandExecutor& operator=(StrandExecutor const&) = delete;
36 StrandExecutor& operator=(StrandExecutor&&) = delete;
37 ~StrandExecutor() override = default;
38
39 void Post(Work work) override;
40 void Resume(std::coroutine_handle<> handle) override;
41
42 private:
43 /// Mutable strand state, heap-allocated so in-flight drain closures can keep it alive
44 /// independently of the @c StrandExecutor wrapper's lifetime. The serialized FIFO and its
45 /// drain-active flag live in @ref detail::SerialDrainQueue, which guards both under one lock.
46 struct State
47 {
48 IExecutor& underlying; ///< Borrowed executor that runs the serialized work.
49 detail::SerialDrainQueue queue;
50
51 explicit State(IExecutor& underlyingExecutor) noexcept:
52 underlying { underlyingExecutor }
53 {
54 }
55 };
56
57 /// Schedules a single drain closure on @p state->underlying that drains @p state->pending
58 /// to completion. The closure captures a copy of @p state, keeping it alive while it runs.
59 static void ScheduleDrain(std::shared_ptr<State> state);
60
61 std::shared_ptr<State> _state;
62};
63
64} // namespace Lightweight::Async
StrandExecutor(IExecutor &underlying)
void Resume(std::coroutine_handle<> handle) override
void Post(Work work) override