Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
BroadcastTimeAlarm.hxx
Go to the documentation of this file.
1
35#ifndef _OPENLCB_BROADCASTTIMEALARM_HXX_
36#define _OPENLCB_BROADCASTTIMEALARM_HXX_
37
39
40namespace openlcb
41{
42
44class BroadcastTimeAlarm : public StateFlowBase, protected Atomic
45{
46public:
53 std::function<void(BarrierNotifiable *)> callback)
54 : StateFlowBase(node->iface())
55 , clock_(clock)
56 , wakeup_(this)
57 , callback_(callback)
58 , timer_(this)
59 , bn_()
60 , bnPtr_(nullptr)
61 , expires_(0)
62 , running_(false)
63 , set_(false)
64#if defined(GTEST)
65 , shutdown_(false)
66#endif
67 , updateSubscribeHandle_(clock->update_subscribe_add(
69 {
70 // By ensuring that the alarm runs in the same thread context as the
71 // clock which it uses, we can have much simpler logic for avoiding
72 // race conditions in this implementation.
73 HASSERT(service()->executor() == clock_->service()->executor());
75 }
76
82
88 void set_period(time_t period)
89 {
90 set(clock_->time() + period);
91 }
92
95 void set(time_t time)
96 {
97 bool need_wakeup = false;
98 {
99 AtomicHolder h(this);
100 expires_ = time;
101 running_ = true;
102 if (!set_)
103 {
104 need_wakeup = true;
105 set_ = true;
106 }
107 }
108 if (need_wakeup)
109 {
111 }
112 }
113
115 void clear()
116 {
117 {
118 AtomicHolder h(this);
119 running_ = false;
120 set_ = false;
121 }
122 // rather than waking up the state flow, just let it expire naturally.
123 }
124
125#if defined(GTEST)
126 void shutdown()
127 {
128 shutdown_ = true;
130 }
131
132 bool is_shutdown()
133 {
134 return is_terminated();
135 }
136#endif
137
138protected:
141 virtual Action entry()
142 {
144 }
145
147 virtual void update_notify()
148 {
149 bool need_wakeup = false;
150 {
151 AtomicHolder h(this);
152 if (running_ && !set_)
153 {
154 set_ = true;
155 need_wakeup = true;
156 }
157 }
158 if (need_wakeup)
159 {
161 }
162 }
163
165
166private:
167 // Wakeup helper
168 class Wakeup : public Executable, protected Atomic
169 {
170 public:
174 : alarm_(alarm)
175 , armed(false)
176 {
177 }
178
180 void trigger()
181 {
182 bool add = false;
183 {
184 AtomicHolder h(this);
185 if (!armed)
186 {
187 armed = true;
188 add = true;
189 }
190 }
191 if (add)
192 {
193 alarm_->service()->executor()->add(this);
194 }
195 }
196
197 private:
200 void run() override
201 {
202 armed = false;
203 alarm_->wakeup();
204 }
205
207 bool armed;
208 };
209
215 {
216#if defined(GTEST)
217 if (shutdown_)
218 {
219 return exit();
220 }
221#endif
222
223 AtomicHolder h(this);
224 if (set_)
225 {
226 set_ = false;
227 time_t now = clock_->time();
228
229 if ((now >= expires_ && clock_->get_rate_quarters() > 0) ||
230 (now <= expires_ && clock_->get_rate_quarters() < 0))
231 {
232 // have already met the alarm conditions,
233 // typically won't get here
235 }
236 else if (clock_->is_running())
237 {
238 long long real_expires = 0;
239 bool result =
241 &real_expires);
242 HASSERT(result);
243 return sleep_and_call(&timer_, real_expires, STATE(timeout));
244 }
245 }
246
247 bnPtr_ = bn_.reset(this);
248 return wait_and_call(STATE(setup));
249 }
250
255 {
256 if (timer_.is_triggered())
257 {
258 // this is a wakeup, not a timeout
260 }
261 else
262 {
263 // timeout
265 }
266 }
267
271 {
272 bnPtr_ = bn_.reset(this);
273 if (running_ && clock_->is_running() && callback_)
274 {
275 running_ = false;
277 }
278
279 return wait_and_call(STATE(setup));
280 }
281
283 void wakeup()
284 {
286 if (bnPtr_)
287 {
288 bnPtr_ = nullptr;
289 bn_.notify();
290 }
291 }
292
295 std::function<void(BarrierNotifiable *)> callback_;
299 time_t expires_;
300 uint8_t running_ : 1;
301 uint8_t set_ : 1;
302#if defined(GTEST)
303 uint8_t shutdown_ : 1;
304#endif
307
310
312};
313
316{
317public:
324 std::function<void(BarrierNotifiable *)> callback)
326 node, clock, std::bind(&BroadcastTimeAlarmDate::expired_callback,
327 this, std::placeholders::_1))
328 , callbackUser_(callback)
329 {
330 }
331
336
337private:
340 Action entry() override
341 {
343
345 }
346
349 {
350 const struct tm *tm = clock_->gmtime_recalculate();
351 time_t seconds = clock_->time();
352
353 if (clock_->get_rate_quarters() > 0)
354 {
355 set(seconds + (60 - tm->tm_sec) +
356 (60 * (59 - tm->tm_min)) +
357 (60 * 60 * (23 - tm->tm_hour)));
358 }
359 else if (clock_->get_rate_quarters() < 0)
360 {
361 set(seconds - ((tm->tm_sec + 1) +
362 (60 * (tm->tm_min)) +
363 (60 * 60 * tm->tm_hour)));
364 }
365 }
366
370 {
372 callbackUser_ ? callbackUser_(done) : done->notify();
373 }
374
376 void update_notify() override
377 {
380 }
381
383 std::function<void(BarrierNotifiable *)> callbackUser_;
384
386};
387
390{
391public:
398 std::function<void(BarrierNotifiable *)> callback)
400 node, clock,
402 std::placeholders::_1))
403 , callbackUser_(callback)
404 {
405 }
406
411
412private:
415 Action entry() override
416 {
418
420 }
421
425 void reset_expired_time(bool force_on_match = false)
426 {
427 const struct tm *tm = clock_->gmtime_recalculate();
428 time_t seconds = clock_->time();
429
430 if (force_on_match)
431 {
432 if ((clock_->get_rate_quarters() > 0 && tm->tm_sec == 0) ||
433 (clock_->get_rate_quarters() < 0 && tm->tm_sec == 59))
434 {
435 set(seconds);
436 return;
437 }
438 }
439 if (clock_->get_rate_quarters() > 0)
440 {
441 set(seconds + (60 - tm->tm_sec));
442 }
443 else if (clock_->get_rate_quarters() < 0)
444 {
445 set(seconds - (tm->tm_sec + 1));
446 }
447 }
448
452 {
454 callbackUser_ ? callbackUser_(done) : done->notify();
455 }
456
458 void update_notify() override
459 {
460 reset_expired_time(true);
462 }
463
465 std::function<void(BarrierNotifiable *)> callbackUser_;
466
468};
469
470} // namespace openlcb
471
472#endif // _OPENLCB_BROADCASTTIMEALARM_HXX_
int bind(int socket, const struct sockaddr *address, socklen_t address_len)
Bind a name to a socket.
Definition Socket.cxx:159
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
See OSMutexLock in os/OS.hxx.
Definition Atomic.hxx:153
Lightweight locking class for protecting small critical sections.
Definition Atomic.hxx:130
A BarrierNotifiable allows to create a number of child Notifiable and wait for all of them to finish.
void notify() override
Implementation of the barrier semantics.
BarrierNotifiable * reset(Notifiable *done)
Resets the barrier. Returns &*this. Asserts that is_done().
BarrierNotifiable * new_child()
Call this for each child task.
An object that can be scheduled on an executor to run.
virtual void add(Executable *action, unsigned priority=UINT_MAX)=0
Send a message to this Executor's queue.
ExecutorBase * executor()
Return type for a state flow callback.
Use this timer class to deliver the timeout notification to a stateflow.
Base class for state machines.
Service * service()
Return a pointer to the service I am bound to.
bool is_terminated()
Action exit()
Terminate current StateFlow activity.
void start_flow(Callback c)
Resets the flow to the specified state and starts it.
Action call_immediately(Callback c)
Imediately call the next state upon return.
Action wait_and_call(Callback c)
Wait for resource to become available before proceeding to next state.
Action sleep_and_call(::Timer *timer, long long timeout_nsec, Callback c)
Suspends execution of this control flow for a specified time.
int16_t get_rate_quarters()
Report the clock rate as a 12-bit fixed point number (-512.00 to 511.75).
Definition TimeBase.hxx:170
bool is_running()
Test of the clock is running.
Definition TimeBase.hxx:178
bool real_nsec_until_fast_time_abs(time_t fast_sec, long long *real_nsec)
Convert a fast time to absolute nsec until it will occur.
Definition TimeBase.hxx:249
time_t time()
Get the time as a value of seconds relative to the system epoch.
Definition TimeBase.hxx:87
bool is_triggered()
Definition Timer.hxx:273
void ensure_triggered()
Triggers the timer if it is not expired yet.
Definition Timer.hxx:249
Specialization of BroadcastTimeAlarm meant to expire at each date rollover.
std::function< void(BarrierNotifiable *)> callbackUser_
callback for when alarm expires
void update_notify() override
Called when the clock time has changed.
void reset_expired_time()
Reset the expired time based on what time it is now.
Action entry() override
Entry point of the state machine.
void expired_callback(BarrierNotifiable *done)
callback for when the alarm expires
BroadcastTimeAlarmDate(Node *node, BroadcastTime *clock, std::function< void(BarrierNotifiable *)> callback)
Constructor.
Specialization of BroadcastTimeAlarm meant to expire at each minute.
void update_notify() override
Called when the clock time has changed.
void expired_callback(BarrierNotifiable *done)
callback for when the alarm expires
BroadcastTimeAlarmMinute(Node *node, BroadcastTime *clock, std::function< void(BarrierNotifiable *)> callback)
Constructor.
Action entry() override
Entry point of the state machine.
void reset_expired_time(bool force_on_match=false)
Reset the expired time based on what time it is now.
std::function< void(BarrierNotifiable *)> callbackUser_
callback for when alarm expires
Wakeup(BroadcastTimeAlarm *alarm)
Constructor.
BroadcastTimeAlarm * alarm_
our parent alarm we will wakeup
void trigger()
Trigger the wakeup to run.
Basic alarm type that all other alarms are based off of.
void set_period(time_t period)
Start the alarm to expire at the given period from now.
void clear()
Inactivate the alarm.
Wakeup wakeup_
wakeup helper for scheduling alarms
time_t expires_
time at which the alarm expires
void wakeup()
Wakeup the state machine. Must be called from this service's executor.
void set(time_t time)
Start the alarm to expire at the given fast time.
std::function< void(BarrierNotifiable *)> callback_
callback for when alarm expires
BarrierNotifiable bn_
notifiable for callback callee
BroadcastTimeAlarm(Node *node, BroadcastTime *clock, std::function< void(BarrierNotifiable *)> callback)
Constructor.
BroadcastTime * clock_
clock that our alarm is based off of
Action setup()
Setup, or wait to setup alarm.
virtual Action entry()
Entry point to state flow.
uint8_t set_
true if a start request is pending
Action timeout()
Wait for timeout or early trigger.
BarrierNotifiable * bnPtr_
not null we have an outstanding notification
StateFlowTimer timer_
timer helper
virtual void update_notify()
Called by the clock when time, rate, or running state has changed.
BroadcastTime::UpdateSubscribeHandle updateSubscribeHandle_
handle to the update subscrition used for unsubcribing in the destructor
Action expired()
Handle action on timer expiration.
uint8_t running_
true if running (alarm armed), else false
Implementation of Broadcast Time Protocol.
void update_subscribe_remove(UpdateSubscribeHandle handle)
Unregister a callback for when the time synchronization is updated.
size_t UpdateSubscribeHandle
An opaque data element that is returned from update subscriber registration, and allows unregistering...
const struct tm * gmtime_recalculate()
Recalculate the struct tm representation of time.
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Removes default copy-constructor and assignment added by C++.
Definition macros.h:171