forked from andreasbuhr/cppcoro
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathround_robin_scheduler.hpp
More file actions
124 lines (107 loc) · 2.99 KB
/
Copy pathround_robin_scheduler.hpp
File metadata and controls
124 lines (107 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Lewis Baker
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPCORO_ROUND_ROBIN_SCHEDULER_HPP_INCLUDED
#define CPPCORO_ROUND_ROBIN_SCHEDULER_HPP_INCLUDED
#include <cppcoro/config.hpp>
#include <cppcoro/coroutine.hpp>
#include <array>
#include <cassert>
#include <algorithm>
#include <utility>
namespace cppcoro
{
#if CPPCORO_COMPILER_SUPPORTS_SYMMETRIC_TRANSFER
/// This is a scheduler class that schedules coroutines in a round-robin
/// fashion once N coroutines have been scheduled to it.
///
/// Only supports access from a single thread at a time so
///
/// This implementation was inspired by Gor Nishanov's CppCon 2018 talk
/// about nano-coroutines.
///
/// The implementation relies on symmetric transfer and noop_coroutine()
/// and so only works with a relatively recent version of Clang and does
/// not yet work with MSVC.
template<size_t N>
class round_robin_scheduler
{
static_assert(
N >= 2,
"Round robin scheduler must be configured to support at least two coroutines");
class schedule_operation
{
public:
explicit schedule_operation(round_robin_scheduler& s) noexcept : m_scheduler(s) {}
bool await_ready() noexcept
{
return false;
}
cppcoro::coroutine_handle<> await_suspend(
cppcoro::coroutine_handle<> awaitingCoroutine) noexcept
{
return m_scheduler.exchange_next(awaitingCoroutine);
}
void await_resume() noexcept {}
private:
round_robin_scheduler& m_scheduler;
};
friend class schedule_operation;
public:
round_robin_scheduler() noexcept
: m_index(0)
, m_noop(cppcoro::noop_coroutine())
{
for (size_t i = 0; i < N - 1; ++i)
{
m_coroutines[i] = m_noop();
}
}
~round_robin_scheduler()
{
// All tasks should have been joined before calling destructor.
assert(std::all_of(
m_coroutines.begin(),
m_coroutines.end(),
[&](auto h) { return h == m_noop; }));
}
schedule_operation schedule() noexcept
{
return schedule_operation{ *this };
}
/// Resume any queued coroutines until there are no more coroutines.
void drain() noexcept
{
size_t countRemaining = N - 1;
do
{
auto nextToResume = exchange_next(m_noop);
if (nextToResume != m_noop)
{
nextToResume.resume();
countRemaining = N - 1;
}
else
{
--countRemaining;
}
} while (countRemaining > 0);
}
private:
cppcoro::coroutine_handle exchange_next(
cppcoro::coroutine_handle<> coroutine) noexcept
{
auto coroutineToResume = std::exchange(
m_scheduler.m_coroutines[m_scheduler.m_index],
awaitingCoroutine);
m_scheduler.m_index = m_scheduler.m_index < (N - 2) ? m_scheduler.m_index + 1 : 0;
return coroutineToResume;
}
size_t m_index;
const cppcoro::coroutine_handle<> m_noop;
std::array<cppcoro::coroutine_handle<>, N - 1> m_coroutines;
};
#endif
}
#endif