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()
38#include <stlab/concurrency/task.hpp>
43STLAB_VERSION_NAMESPACE_BEGIN()
70class serial_instance_t :
public std::enable_shared_from_this<serial_instance_t> {
71 using executor_t = std::function<void(task<
void()
noexcept>&&)>;
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, and coroutine integration.
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:1955
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
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).