1 // Copyright 2019 The Marl Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef marl_event_h
16 #define marl_event_h
17
18 #include "conditionvariable.h"
19 #include "containers.h"
20 #include "memory.h"
21
22 #include <chrono>
23
24 namespace marl {
25
26 // Event is a synchronization primitive used to block until a signal is raised.
27 class Event {
28 public:
29 enum class Mode {
30 // The event signal will be automatically reset when a call to wait()
31 // returns.
32 // A single call to signal() will only unblock a single (possibly
33 // future) call to wait().
34 Auto,
35
36 // While the event is in the signaled state, any calls to wait() will
37 // unblock without automatically reseting the signaled state.
38 // The signaled state can be reset with a call to clear().
39 Manual
40 };
41
42 inline Event(Mode mode = Mode::Auto,
43 bool initialState = false,
44 Allocator* allocator = Allocator::Default);
45
46 // signal() signals the event, possibly unblocking a call to wait().
47 inline void signal() const;
48
49 // clear() clears the signaled state.
50 inline void clear() const;
51
52 // wait() blocks until the event is signaled.
53 // If the event was constructed with the Auto Mode, then only one
54 // call to wait() will unblock before returning, upon which the signalled
55 // state will be automatically cleared.
56 inline void wait() const;
57
58 // wait_for() blocks until the event is signaled, or the timeout has been
59 // reached.
60 // If the timeout was reached, then wait_for() return false.
61 // If the event is signalled and event was constructed with the Auto Mode,
62 // then only one call to wait() will unblock before returning, upon which the
63 // signalled state will be automatically cleared.
64 template <typename Rep, typename Period>
65 inline bool wait_for(
66 const std::chrono::duration<Rep, Period>& duration) const;
67
68 // wait_until() blocks until the event is signaled, or the timeout has been
69 // reached.
70 // If the timeout was reached, then wait_for() return false.
71 // If the event is signalled and event was constructed with the Auto Mode,
72 // then only one call to wait() will unblock before returning, upon which the
73 // signalled state will be automatically cleared.
74 template <typename Clock, typename Duration>
75 inline bool wait_until(
76 const std::chrono::time_point<Clock, Duration>& timeout) const;
77
78 // test() returns true if the event is signaled, otherwise false.
79 // If the event is signalled and was constructed with the Auto Mode
80 // then the signalled state will be automatically cleared upon returning.
81 inline bool test() const;
82
83 // isSignalled() returns true if the event is signaled, otherwise false.
84 // Unlike test() the signal is not automatically cleared when the event was
85 // constructed with the Auto Mode.
86 // Note: No lock is held after bool() returns, so the event state may
87 // immediately change after returning. Use with caution.
88 inline bool isSignalled() const;
89
90 // any returns an event that is automatically signalled whenever any of the
91 // events in the list are signalled.
92 template <typename Iterator>
93 inline static Event any(Mode mode,
94 const Iterator& begin,
95 const Iterator& end);
96
97 // any returns an event that is automatically signalled whenever any of the
98 // events in the list are signalled.
99 // This overload defaults to using the Auto mode.
100 template <typename Iterator>
101 inline static Event any(const Iterator& begin, const Iterator& end);
102
103 private:
104 struct Shared {
105 inline Shared(Mode mode, bool initialState);
106 inline void signal();
107 inline void wait();
108
109 template <typename Rep, typename Period>
110 inline bool wait_for(const std::chrono::duration<Rep, Period>& duration);
111
112 template <typename Clock, typename Duration>
113 inline bool wait_until(
114 const std::chrono::time_point<Clock, Duration>& timeout);
115
116 std::mutex mutex;
117 ConditionVariable cv;
118 const Mode mode;
119 bool signalled;
120 containers::vector<std::shared_ptr<Shared>, 2> deps;
121 };
122
123 const std::shared_ptr<Shared> shared;
124 };
125
Shared(Mode mode,bool initialState)126 Event::Shared::Shared(Mode mode, bool initialState)
127 : mode(mode), signalled(initialState) {}
128
signal()129 void Event::Shared::signal() {
130 std::unique_lock<std::mutex> lock(mutex);
131 if (signalled) {
132 return;
133 }
134 signalled = true;
135 if (mode == Mode::Auto) {
136 cv.notify_one();
137 } else {
138 cv.notify_all();
139 }
140 for (auto dep : deps) {
141 dep->signal();
142 }
143 }
144
wait()145 void Event::Shared::wait() {
146 std::unique_lock<std::mutex> lock(mutex);
147 cv.wait(lock, [&] { return signalled; });
148 if (mode == Mode::Auto) {
149 signalled = false;
150 }
151 }
152
153 template <typename Rep, typename Period>
wait_for(const std::chrono::duration<Rep,Period> & duration)154 bool Event::Shared::wait_for(
155 const std::chrono::duration<Rep, Period>& duration) {
156 std::unique_lock<std::mutex> lock(mutex);
157 if (!cv.wait_for(lock, duration, [&] { return signalled; })) {
158 return false;
159 }
160 if (mode == Mode::Auto) {
161 signalled = false;
162 }
163 return true;
164 }
165
166 template <typename Clock, typename Duration>
wait_until(const std::chrono::time_point<Clock,Duration> & timeout)167 bool Event::Shared::wait_until(
168 const std::chrono::time_point<Clock, Duration>& timeout) {
169 std::unique_lock<std::mutex> lock(mutex);
170 if (!cv.wait_until(lock, timeout, [&] { return signalled; })) {
171 return false;
172 }
173 if (mode == Mode::Auto) {
174 signalled = false;
175 }
176 return true;
177 }
178
Event(Mode mode,bool initialState,Allocator * allocator)179 Event::Event(Mode mode /* = Mode::Auto */,
180 bool initialState /* = false */,
181 Allocator* allocator /* = Allocator::Default */)
182 : shared(allocator->make_shared<Shared>(mode, initialState)) {}
183
signal()184 void Event::signal() const {
185 shared->signal();
186 }
187
clear()188 void Event::clear() const {
189 std::unique_lock<std::mutex> lock(shared->mutex);
190 shared->signalled = false;
191 }
192
wait()193 void Event::wait() const {
194 shared->wait();
195 }
196
197 template <typename Rep, typename Period>
wait_for(const std::chrono::duration<Rep,Period> & duration)198 bool Event::wait_for(const std::chrono::duration<Rep, Period>& duration) const {
199 return shared->wait_for(duration);
200 }
201
202 template <typename Clock, typename Duration>
wait_until(const std::chrono::time_point<Clock,Duration> & timeout)203 bool Event::wait_until(
204 const std::chrono::time_point<Clock, Duration>& timeout) const {
205 return shared->wait_until(timeout);
206 }
207
test()208 bool Event::test() const {
209 std::unique_lock<std::mutex> lock(shared->mutex);
210 if (!shared->signalled) {
211 return false;
212 }
213 if (shared->mode == Mode::Auto) {
214 shared->signalled = false;
215 }
216 return true;
217 }
218
isSignalled()219 bool Event::isSignalled() const {
220 std::unique_lock<std::mutex> lock(shared->mutex);
221 return shared->signalled;
222 }
223
224 template <typename Iterator>
any(Mode mode,const Iterator & begin,const Iterator & end)225 Event Event::any(Mode mode, const Iterator& begin, const Iterator& end) {
226 Event any(mode, false);
227 for (auto it = begin; it != end; it++) {
228 auto s = it->shared;
229 std::unique_lock<std::mutex> lock(s->mutex);
230 if (s->signalled) {
231 any.signal();
232 }
233 s->deps.push_back(any.shared);
234 }
235 return any;
236 }
237
238 template <typename Iterator>
any(const Iterator & begin,const Iterator & end)239 Event Event::any(const Iterator& begin, const Iterator& end) {
240 return any(Mode::Auto, begin, end);
241 }
242
243 } // namespace marl
244
245 #endif // marl_event_h
246