LeechCraft 0.6.70-19214-g121e5318a7
Modular cross-platform feature rich live environment.
Loading...
Searching...
No Matches
channel.h
Go to the documentation of this file.
1/**********************************************************************
2 * LeechCraft - modular cross-platform feature rich internet client.
3 * Copyright (C) 2006-2014 Georg Rudoy
4 *
5 * Distributed under the Boost Software License, Version 1.0.
6 * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7 **********************************************************************/
8
9#pragma once
10
11#include <coroutine>
12#include <deque>
13#include <mutex>
14#include <optional>
15#include <stdexcept>
16#include <utility>
17#include "channelfwd.h"
18
19namespace LC::Util
20{
21 template<typename T>
22 class Channel
23 {
24 struct ReceiveAwaiter
25 {
26 QObject Ctx_;
27
28 Channel& Ch_;
29 std::optional<T> Slot_;
30 std::coroutine_handle<> Handle_;
31 bool Registered_ = false;
32
33 explicit ReceiveAwaiter (Channel& ch)
34 : Ch_ { ch }
35 {
36 }
37
38 ~ReceiveAwaiter ()
39 {
40 if (Registered_)
41 {
42 std::lock_guard guard { Ch_.Lock_ };
43 std::erase (Ch_.Awaiters_, this);
44 }
45
46 // channel consumer died while scheduled but without consuming the slot
47 if (Slot_)
48 Ch_.SendImpl (std::move (*Slot_), SendClosedBehavior::Ignore);
49 }
50
51 bool await_ready () const noexcept
52 {
53 return false;
54 }
55
56 bool await_suspend (std::coroutine_handle<> handle)
57 {
58 std::lock_guard guard { Ch_.Lock_ };
59 if (!Ch_.Elems_.empty ())
60 {
61 Slot_ = std::move (Ch_.Elems_.front ());
62 Ch_.Elems_.pop_front ();
63 return false;
64 }
65
66 if (Ch_.Closed_)
67 return false;
68
69 Ch_.Awaiters_.push_back (this);
70 Handle_ = handle;
71 Registered_ = true;
72 return true;
73 }
74
75 std::optional<T> await_resume ()
76 {
77 if (Slot_)
78 return std::exchange (Slot_, {});
79
80 std::lock_guard guard { Ch_.Lock_ };
81 if (Ch_.Elems_.empty ())
82 return {};
83 auto value = std::move (Ch_.Elems_.front ());
84 Ch_.Elems_.pop_front ();
85 return value;
86 }
87 };
88
89 mutable std::mutex Lock_;
90 std::deque<T> Elems_;
91 std::deque<ReceiveAwaiter*> Awaiters_;
92
93 bool Closed_ = false;
94 public:
95 using ItemType_t = T;
96
97 Channel () = default;
98
99 Channel (const Channel&) = delete;
100 Channel (Channel&&) = delete;
101 Channel& operator= (const Channel&) = delete;
103
104 void Close ()
105 {
106 {
107 std::lock_guard guard { Lock_ };
108 if (std::exchange (Closed_, true))
109 return;
110 }
111
112 while (const auto next = PopNextAwaiter ())
113 Invoke (*next);
114 }
115
116 template<typename U = T>
117 void Send (U&& value)
118 {
119 SendImpl (std::forward<U> (value), SendClosedBehavior::Throw);
120 }
121
122 bool IsEmpty () const
123 {
124 std::lock_guard guard { Lock_ };
125 return Elems_.empty ();
126 }
127
128 ReceiveAwaiter Receive ()
129 {
130 return ReceiveAwaiter { *this };
131 }
132
133 auto operator co_await ()
134 {
135 return Receive ();
136 }
137 private:
138 enum class SendClosedBehavior : std::uint8_t { Ignore, Throw };
139
140 template<typename U = T>
141 void SendImpl (U&& value, SendClosedBehavior closedBehavior)
142 {
143 const auto next = [&]
144 {
145 std::lock_guard guard { Lock_ };
146 if (Closed_ && closedBehavior == SendClosedBehavior::Throw)
147 throw std::runtime_error { "sending into a closed channel" };
148
149 const auto awaiter = PopNextAwaiterUnlocked ();
150 if (!awaiter)
151 Elems_.emplace_back (std::forward<U> (value));
152 return awaiter;
153 } ();
154
155 if (next)
156 {
157 next->Slot_.emplace (std::forward<U> (value));
158 Invoke (*next);
159 }
160 }
161
162 ReceiveAwaiter* PopNextAwaiter ()
163 {
164 std::lock_guard guard { Lock_ };
165 return PopNextAwaiterUnlocked ();
166 }
167
168 ReceiveAwaiter* PopNextAwaiterUnlocked ()
169 {
170 if (Awaiters_.empty ())
171 return nullptr;
172 auto next = Awaiters_.front ();
173 Awaiters_.pop_front ();
174 next->Registered_ = false;
175 return next;
176 }
177
178 void Invoke (ReceiveAwaiter& awaiter)
179 {
180 QMetaObject::invokeMethod (&awaiter.Ctx_,
181 [&awaiter] { awaiter.Handle_ (); },
182 Qt::QueuedConnection);
183 }
184 };
185}
Channel(Channel &&)=delete
Channel & operator=(const Channel &)=delete
Channel(const Channel &)=delete
ReceiveAwaiter Receive()
Definition channel.h:128
void Send(U &&value)
Definition channel.h:117
bool IsEmpty() const
Definition channel.h:122