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