MCPcopy Create free account
hub / github.com/boostorg/cobalt / awaitable_spsc_queue

Class awaitable_spsc_queue

example/spsc.cpp:17–118  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

15/// This is a simple class making a lockfree::spsc_queue awaitable. It's not movable, since the spsc_queue isn't.
16template<typename T, typename ... Options>
17struct awaitable_spsc_queue
18{
19 boost::lockfree::spsc_queue<T, Options...> queue;
20
21 template<typename ... Args>
22 awaitable_spsc_queue(Args && ... args) : queue(std::forward<Args>(args)...) {}
23
24 // if the queue gets destroyed, destroying the awaiters is all we can do.
25 ~awaitable_spsc_queue()
26 {
27 if (auto r = reader.load(); r != nullptr)
28 std::coroutine_handle<void>::from_address(r).destroy();
29
30 if (auto w = writer.load(); w != nullptr)
31 std::coroutine_handle<void>::from_address(w).destroy();
32 }
33 // to avoid locks, put the coroutine handles into atomics.
34 std::atomic<void*> reader{nullptr}, writer{nullptr};
35
36 // capture the read & write executor
37 cobalt::executor read_executor, write_executor;
38
39 // the awaitable to read a value from the queue
40 struct read_op
41 {
42 awaitable_spsc_queue * this_;
43
44 // if reads are available we don't need to suspend
45 bool await_ready() {return this_->queue.read_available();}
46
47 // The suspend implementation. We expect the coroutine promise to have an associated executor.
48 // If it doesn't resumption will end up on the system_executor
49 template<typename Promise>
50 void await_suspend(std::coroutine_handle<Promise> h)
51 {
52 // Capture the read_executor.
53 if constexpr (requires {h.promise().get_executor();})
54 this_->read_executor = h.promise().get_executor();
55 else
56 this_->read_executor = cobalt::this_thread::get_executor();
57
58 // Make sure there's only one coroutine awaiting the read
59 assert(this_->reader == nullptr);
60 // Store the handle of the awaiter.
61 this_->reader.store(h.address());
62 }
63 T await_resume()
64 {
65 T res;
66 // Grab the value from the queue
67 this_->queue.pop(res);
68 // if a writer is waiting post it to complete on it's thread
69 auto w = cobalt::unique_handle<void>::from_address(this_->writer.exchange(nullptr));
70 if (w)
71 boost::asio::post(this_->write_executor, std::move(w));
72
73 return res;
74 }

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected