Lightweight 0.20260617.0
Loading...
Searching...
No Matches
ManualExecutor.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 <cstddef>
9#include <stop_token>
10
11namespace Lightweight::Async
12{
13
14/// An executor that runs work only when explicitly pumped by the owning thread.
15///
16/// This is the "app thread" / event-loop resume target for the single-threaded model:
17/// blocking ODBC work is offloaded to a @ref ThreadPoolExecutor, and the coroutine's
18/// continuation is posted back here so that all user-visible coroutine logic resumes on the
19/// one thread that drives this executor (via @ref Run, @ref Drain, @ref RunOne or
20/// @ref RunUntil). All members are thread-safe to call; the pumping members
21/// (@ref Run / @ref Drain / @ref RunOne / @ref RunUntil) are intended for a single
22/// consumer thread.
23class LIGHTWEIGHT_API ManualExecutor final: public IExecutor, public IResumeScheduler
24{
25 public:
26 ManualExecutor() = default;
27 ManualExecutor(ManualExecutor const&) = delete;
28 ManualExecutor& operator=(ManualExecutor const&) = delete;
30 ManualExecutor& operator=(ManualExecutor&&) = delete;
31 ~ManualExecutor() override = default;
32
33 void Post(Work work) override;
34 void Resume(std::coroutine_handle<> handle) override;
35
36 /// Runs at most one queued work item without blocking.
37 /// @return true if an item was run, false if the queue was empty.
38 bool RunOne();
39
40 /// Runs all currently-runnable work until the queue is empty, without blocking.
41 /// @return the number of work items executed.
42 std::size_t Drain();
43
44 /// Blocks pumping work until @ref Stop is called and the queue has drained.
45 void Run();
46
47 /// Requests @ref Run to return once the queue is empty.
48 void Stop();
49
50 /// @return the number of currently-queued work items.
51 [[nodiscard]] std::size_t PendingCount() const;
52
53 /// Pumps work until @p predicate returns true.
54 ///
55 /// Blocks the calling thread between work items, waking when new work is posted. The
56 /// predicate must be cheap and must not acquire this executor's internal lock. This is
57 /// the driver used by @c SyncWaitPumping for the single-threaded model.
58 ///
59 /// @tparam Predicate A callable returning something contextually convertible to bool.
60 /// @param predicate Stop condition, re-checked between work items.
61 template <typename Predicate>
62 void RunUntil(Predicate predicate)
63 {
64 // NOTE: RunUntil deliberately ignores the stop request — it must pump strictly until the
65 // predicate is satisfied (the awaited task completes). Honoring Stop() here would return early
66 // with the predicate still false, causing the caller (SyncWaitPumping) to read an unfinished
67 // result. The stop request governs Run() (the event-loop pump), not RunUntil. The wake
68 // condition is the predicate alone; wakeups are delivered by the work posted to the queue.
69 while (!predicate())
70 {
71 Work work;
72 if (!_queue.WaitAndPop(work, predicate))
73 return;
74 work();
75 }
76 }
77
78 private:
79 detail::PumpQueue _queue;
80 std::stop_source _stopSource; ///< Requested by Stop(); read by Run()'s wake condition.
81};
82
83} // namespace Lightweight::Async
void Post(Work work) override
std::size_t PendingCount() const
void Run()
Blocks pumping work until Stop is called and the queue has drained.
void Stop()
Requests Run to return once the queue is empty.
void Resume(std::coroutine_handle<> handle) override
void RunUntil(Predicate predicate)