• 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 "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