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