Lightweight 0.20261105.1
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#if defined(_MSC_VER)
14 #pragma warning(push)
15 #pragma warning(disable : 4251) // STL types in DLL interface
16#endif
17
18/// @ingroup Async
19/// Serializes work over an underlying executor (an Asio-style "strand").
20///
21/// All work posted to a given strand runs one item at a time, in FIFO order, even though
22/// the items execute on the underlying executor's worker threads. This guarantees that a
23/// single ODBC connection — which is not safe for concurrent use — is touched by only one
24/// thread at a time. A strand does not own a thread; it borrows the underlying executor.
25///
26/// The strand's mutable state lives in a heap @c State held by a @c std::shared_ptr. Each
27/// in-flight drain closure keeps a copy of that pointer, so the state outlives the
28/// @c StrandExecutor wrapper itself until the last drain returns. This makes the strand safe
29/// to destroy (or to replace, e.g. via @c SqlConnection::EnableAsync) while a drain is still
30/// running on a worker thread — the closure only ever touches @c State, never the wrapper.
31class LIGHTWEIGHT_API StrandExecutor final: public IExecutor, public IResumeScheduler
32{
33 public:
34 /// Constructs a strand layered over @p underlying.
35 ///
36 /// @param underlying The executor that actually runs the serialized work.
37 explicit StrandExecutor(IExecutor& underlying);
38
39 StrandExecutor(StrandExecutor const&) = delete;
40 StrandExecutor& operator=(StrandExecutor const&) = delete;
42 StrandExecutor& operator=(StrandExecutor&&) = delete;
43 ~StrandExecutor() override = default;
44
45 void Post(Work work) override;
46 void Resume(std::coroutine_handle<> handle) override;
47
48 private:
49 /// Mutable strand state, heap-allocated so in-flight drain closures can keep it alive
50 /// independently of the @c StrandExecutor wrapper's lifetime. The serialized FIFO and its
51 /// drain-active flag live in @ref detail::SerialDrainQueue, which guards both under one lock.
52 struct State
53 {
54 IExecutor& underlying; ///< Borrowed executor that runs the serialized work.
55 detail::SerialDrainQueue queue;
56
57 explicit State(IExecutor& underlyingExecutor) noexcept:
58 underlying { underlyingExecutor }
59 {
60 }
61 };
62
63 /// Schedules a single drain closure on @p state->underlying that drains @p state->pending
64 /// to completion. The closure captures a copy of @p state, keeping it alive while it runs.
65 static void ScheduleDrain(std::shared_ptr<State> state);
66
67 std::shared_ptr<State> _state;
68};
69
70#if defined(_MSC_VER)
71 #pragma warning(pop)
72#endif
73
74} // namespace Lightweight::Async
StrandExecutor(IExecutor &underlying)
void Resume(std::coroutine_handle<> handle) override
void Post(Work work) override