9#ifndef STLAB_CONCURRENCY_SERIAL_QUEUE_HPP
10#define STLAB_CONCURRENCY_SERIAL_QUEUE_HPP
23#include <stlab/config.hpp>
33#ifndef STLAB_DISABLE_FUTURE_COROUTINES
34#define STLAB_DISABLE_FUTURE_COROUTINES()
43STLAB_VERSION_NAMESPACE_BEGIN()
70class serial_instance_t :
public std::enable_shared_from_this<serial_instance_t> {
72 using queue_t = std::deque<
task<void()
noexcept>>;
73 using lock_t = std::scoped_lock<std::mutex>;
79 void (serial_instance_t::*_kickstart)();
81 static auto pop_front_unsafe(queue_t& q) {
82 auto f = std::move(q.front());
87 auto empty() ->
bool {
91 empty = _queue.empty();
104 scope<lock_t>(_m, [&]() { std::swap(local_queue, _queue); });
106 while (!local_queue.empty()) {
107 pop_front_unsafe(local_queue)();
110 if (!empty()) _executor([_this(shared_from_this())]()
noexcept { _this->all(); });
120 if (!empty()) _executor([_this(shared_from_this())]()
noexcept { _this->single(); });
126 void kickstart() { (this->*_kickstart)(); }
131 return &serial_instance_t::single;
133 return &serial_instance_t::all;
137 return &serial_instance_t::single;
141 template <
typename F>
142 void enqueue(F&& f) {
146 _queue.emplace_back(std::forward<F>(f));
151 std::swap(running, _running);
155 _executor([_this(shared_from_this())]()
noexcept { _this->kickstart(); });
160 _executor(std::move(executor)), _kickstart(kickstarter(mode)) {}
171 std::shared_ptr<detail::serial_instance_t> _impl;
175 template <
typename Executor>
177 _impl(std::make_shared<detail::serial_instance_t>(
178 [_e = std::
move(e)](auto&& f) { _e(std::forward<
decltype(f)>(f)); }, mode)) {}
187 _impl](
auto&& f) -> std::enable_if_t<std::is_nothrow_invocable_v<
decltype(f)>> {
188 _impl->enqueue(std::forward<
decltype(f)>(f));
197 template <
typename F,
typename... Args>
199 return async(
executor(), std::forward<F>(f), std::forward<Args>(args)...);
207STLAB_VERSION_NAMESPACE_END()
serial_queue_t(Executor e, schedule_mode mode=schedule_mode::single)
Constructs a serial queue using underlying executor e and drain mode mode.
Definition serial_queue.hpp:176
auto executor() const
Returns an executor that enqueues void() noexcept tasks on this queue.
Definition serial_queue.hpp:185
auto operator()(F &&f, Args &&... args) const
Schedules f(args...) on this queue via async and returns the resulting future.
Definition serial_queue.hpp:198
Futures, packaged tasks, channels, and coroutine integration.
std::function< void(stlab::task< void() noexcept >)> executor_t
Type-erased executor: accepts a void() noexcept task.
Definition executor_base.hpp:44
auto async(const E &executor, F &&f, Args &&... args) -> detail::reduced_t< detail::result_t< std::decay_t< F >, std::decay_t< Args >... > >
Runs f with args on executor and returns a future for the result.
Definition future.hpp:1937
schedule_mode
How the serial queue drains its task deque when kicked.
Definition serial_queue.hpp:57
@ all
Swap out the entire queue and run it to completion before accepting the next batch.
Definition serial_queue.hpp:61
@ single
Run one task, then reschedule; fair interleaving with newly enqueued work.
Definition serial_queue.hpp:59
typename noexcept_deducer< task_, F >::type task
task_ with noexcept deduced from the function type F (e.g. void() vs void() noexcept).
Definition task.hpp:324
auto scope(Args &&... args)
Scopes the lifetime of an instance of T. All but the last arguments construct T; the last argument is...
Definition scope.hpp:71
constexpr auto move(T &&t) noexcept -> std::remove_reference_t< T > &&
A standard move implementation but with a compile-time check for const types.
Definition utility.hpp:154
Definition reverse.hpp:28
Bind an object’s lifetime to a callable’s execution (scope).
Move-only callable wrapper for executor scheduling (task<Signature>).