11#include <QtConcurrentRun>
23 void CoroChannelTest::testSingleRecv ()
25 using namespace std::chrono_literals;
27 constexpr auto producersCount = 32;
28 constexpr auto repCount = 100;
29 constexpr auto sleepLength = 1ms;
33 std::vector<std::thread> threads;
34 std::atomic_int expected;
35 for (
int i = 0; i < producersCount; ++i)
36 threads.emplace_back ([&, i]
38 for (int j = 0; j < repCount; ++j)
40 const auto val = j * producersCount + i;
42 expected.fetch_add (val, std::memory_order::relaxed);
43 std::this_thread::sleep_for (sleepLength);
47 auto mainThread = std::this_thread::get_id ();
51 while (
auto next =
co_await ch->Receive ())
55 auto thisThread = std::this_thread::get_id ();
56 QCOMPARE (thisThread, mainThread);
64 for (
auto& thread : threads)
70 QCOMPARE (result, expected);
73 void CoroChannelTest::testManyRecvs ()
75 using namespace std::chrono_literals;
77 constexpr auto producersCount = 32;
78 constexpr auto consumersCount = 8;
79 constexpr auto repCount = 100;
80 constexpr auto sleepLength = 1ms;
84 std::vector<std::thread> producers;
85 std::atomic_int expected;
86 for (
int i = 0; i < producersCount; ++i)
87 producers.emplace_back ([&, i]
89 for (int j = 0; j < repCount; ++j)
91 const auto val = j * producersCount + i;
93 expected.fetch_add (val, std::memory_order::relaxed);
94 std::this_thread::sleep_for (sleepLength);
99 std::vector<std::thread> consumers;
100 for (
int i = 0; i < consumersCount; ++i)
101 consumers.emplace_back ([&]
103 auto reader = [] (Channel<int> *ch) -> Task<int, ThreadSafetyExtension>
106 while (auto next = co_await ch->Receive ())
113 for (
auto& producer : producers)
118 for (
auto& consumer : consumers)
121 QCOMPARE (sum, expected);
124 void CoroChannelTest::testSingleThreaded ()
126 constexpr auto iterations = 1000;
130 auto reader = [] (Channel<int> *ch) -> Task<int, ThreadSafetyExtension>
133 while (
auto next =
co_await ch->Receive ())
139 for (
int i = 0; i < iterations; ++i)
148 QCOMPARE (result, expected);
151 void CoroChannelTest::testSingleThreadedTimered ()
153 using namespace std::chrono_literals;
155 constexpr auto iterations = 100;
156 constexpr auto interval = 1ms;
160 auto reader = [] (Channel<int> *ch) -> Task<int, ThreadSafetyExtension>
163 while (
auto next =
co_await ch->Receive ())
171 timer.callOnTimeout ([&, i = 0]
mutable
175 if (++i == iterations)
181 timer.start (interval);
184 QCOMPARE (result, expected);
187 void CoroChannelTest::testMerge ()
189 using namespace std::chrono_literals;
191 constexpr auto numChannels = 100;
192 constexpr auto iterations = 100;
193 constexpr auto interval = 1ms;
195 QVector<Channel_ptr<int>> channels;
196 std::generate_n (std::back_inserter (channels), numChannels, [] {
return std::make_shared<Channel<int>> (); });
200 auto reader = [] (Channel<int> *ch) -> Task<int, ThreadSafetyExtension>
203 while (
auto next =
co_await ch->Receive ())
211 timer.callOnTimeout ([&, i = 0]
mutable
213 for (
int j = 0; j < numChannels; ++j)
215 auto value = i * numChannels + j;
217 channels [j]->Send (value);
219 if (++i == iterations)
222 for (
auto chan : channels)
226 timer.start (interval);
229 QCOMPARE (result, expected);
234 using Event = CoroChannelTest::Event;
236 Task<void>
Receiver (Channel<int>& ch, QList<Event>& log)
238 log << (
co_await ch ? Event::Received : Event::ReceivedEnd);
242 void CoroChannelTest::testSendDefersWakeup ()
250 QCOMPARE (log, (QList { Event::Sent, Event::Received }));
253 void CoroChannelTest::testCloseDefersWakeup ()
259 log << Event::Closed;
261 QCOMPARE (log, (QList { Event::Closed, Event::ReceivedEnd }));
266 ContextTask<void> Doomed (CoroContext& context, Channel<int>& ch, QList<int>& received)
268 co_await AddContext { context };
269 if (
const auto value =
co_await ch)
273 Task<void> Survivor (Channel<int>& ch, QList<int>& received)
275 while (
const auto value =
co_await ch)
280 void CoroChannelTest::testCancelledReceiverDoesntTakeValue ()
283 auto context = std::make_unique<CoroContext> ();
284 QList<int> doomedReceived;
285 auto doomed = Doomed (*context, ch, doomedReceived);
289 QList<int> survivorReceived;
290 auto survivor = Survivor (ch, survivorReceived);
293 QVERIFY_THROWS_EXCEPTION (ContextDeadException, GetTaskResult (doomed));
295 QVERIFY (doomedReceived.isEmpty ());
296 QCOMPARE (survivorReceived, (QList { 42 }));
299 void CoroChannelTest::testCancelledReceiverDoesntStrandValueBehindClose ()
302 auto context = std::make_unique<CoroContext> ();
303 QList<int> doomedReceived;
304 QList<int> survivorReceived;
305 auto doomed = Doomed (*context, ch, doomedReceived);
306 auto survivor = Survivor (ch, survivorReceived);
311 QVERIFY_THROWS_EXCEPTION (ContextDeadException, GetTaskResult (doomed));
313 QVERIFY (doomedReceived.isEmpty ());
314 QCOMPARE (survivorReceived, (QList { 42 }));
319 struct Producer : QObject
322 bool InCall_ =
false;
324 ContextTask<void> SendOne ()
326 co_await AddContext { CoroContext_ };
332 ContextTask<void> CloseChannel ()
334 co_await AddContext { CoroContext_ };
343 Task<bool> DestroyingReceiver (std::unique_ptr<Producer>& producer)
345 co_await producer->Ch_;
346 const auto inCall = producer->InCall_;
353 void CoroChannelTest::testReceiverMayDestroyProducerOnSend ()
355 auto producer = std::make_unique<Producer> ();
356 auto reader = DestroyingReceiver (producer);
357 producer->SendOne ();
358 QVERIFY (!GetTaskResult (reader));
362 void CoroChannelTest::testReceiverMayDestroyProducerOnClose ()
364 auto producer = std::make_unique<Producer> ();
365 auto reader = DestroyingReceiver (producer);
366 producer->CloseChannel ();
367 QVERIFY (!GetTaskResult (reader));
#define LC_CORO_CONTEXT
Declares the coroutine context of the enclosing QObject-derived class.
Q_DECL_IMPORT const QString Receiver
T GetTaskResult(Task< T, Extensions... > task)
Channel_ptr< T > MergeChannels(QVector< Channel_ptr< T > > channels)