EMANE  1.2.1
timer.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2014 - Adjacent Link LLC, Bridgewater, New Jersey
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * * Neither the name of Adjacent Link LLC nor the names of its
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "emane/utils/timer.h"
34 
35 #include <sys/timerfd.h>
36 #include <vector>
37 #include <unistd.h>
38 
40 
41  bRunning_{true},
42  timerId_{},
43  iFd_{}
44 {
45  // create an interval timer with CLOCK_REALTIME
46  if((iFd_ = timerfd_create(CLOCK_REALTIME,0)) < 0)
47  {
48  throw TimerException{};
49  }
50 
51  // spawn the scheduler thread
52  thread_ = std::move(std::thread{&Timer::scheduler,this});
53 };
54 
56 {
57  // synchronization block
58  {
59  std::lock_guard<std::mutex> m(mutex_);
60 
61  bRunning_ = false;
62  }
63 
64  // insert a new timer to unblock
65  // scheduler thread
66  itimerspec spec{{0,0},{0,1}};
67 
68  timerfd_settime(iFd_,0,&spec,nullptr);
69 
70  thread_.join();
71 
72  close(iFd_);
73 }
74 
76 {
77  std::lock_guard<std::mutex> m(mutex_);
78 
79  bool bCancel{};
80 
81  auto iter = timerIdMap_.find(timerId);
82 
83  if(iter != timerIdMap_.end())
84  {
85  bool bReschedule{};
86 
87  // if the timer to cancel is the earliest timer
88  // we need to cancel the timer and reschedule
89  if(std::get<0>(timePointMap_.begin()->second) == timerId)
90  {
91  bReschedule = true;
92  cancel_i();
93  }
94 
95  // clean up the timer stores
96  timePointMap_.erase({iter->second,timerId});
97 
98  timerIdMap_.erase(iter);
99 
100  // if necessary reschedule the new earliest timer
101  if(bReschedule)
102  {
103  schedule_i();
104  }
105 
106  bCancel = true;
107  }
108 
109  return bCancel;
110 }
111 
112 // precondition - mutex is acquired
113 void EMANE::Utils::Timer::cancel_i()
114 {
115  // cancel any existing timer
116  itimerspec spec{{0,0},{0,0}};
117  timerfd_settime(iFd_,0,&spec,nullptr);
118 }
119 
121 EMANE::Utils::Timer::schedule(Callback callback,
122  const TimePoint & timePoint,
123  const Duration & interval)
124 {
125  std::unique_lock<std::mutex> lock(mutex_);
126 
127  bool bReschedule{};
128 
129  // if no timers are present we need to schedule this timer
130  if(timePointMap_.empty())
131  {
132  // need to reschedule
133  bReschedule = true;
134  }
135  else if(timePoint < timePointMap_.begin()->first.first)
136  {
137  // this time is earlier than the current earliest timer
138  // so we need to cancel the current timer and reschedule
139  cancel_i();
140  bReschedule = true;
141  }
142 
143  timerId_+=1;
144 
145  timePointMap_.insert(std::make_pair(std::make_pair(timePoint,timerId_),
146  std::make_tuple(timerId_,
147  timePoint,
148  interval,
149  callback,
150  Clock::now())));
151 
152  timerIdMap_.insert(std::make_pair(timerId_,timePoint));
153 
154  // if necessary rechedule the new earliest timer
155  if(bReschedule)
156  {
157  schedule_i();
158  }
159 
160  return timerId_;
161 }
162 
163 // precondition - mutex is acquired
164 void EMANE::Utils::Timer::schedule_i()
165 {
166  // only schedule the earliest time if one is present
167  if(!timePointMap_.empty())
168  {
169  auto & timePoint = timePointMap_.begin()->first.first;
170 
171  auto timeSinceEpoch = timePoint.time_since_epoch();
172 
173  auto sec = std::chrono::duration_cast<Seconds>(timeSinceEpoch);
174 
175  auto nsec = std::chrono::duration_cast<Nanoseconds>(timeSinceEpoch % Seconds{1});
176 
177  // schedule the interval timer
178  itimerspec spec{{0,0},{sec.count(),nsec.count()}};
179 
180  timerfd_settime(iFd_,TFD_TIMER_ABSTIME,&spec,nullptr);
181  }
182 }
183 
184 
185 void EMANE::Utils::Timer::scheduler()
186 {
187  std::vector<TimerInfo> expired;
188 
189  std::uint64_t u64Expired{};
190 
191  while(1)
192  {
193  // if there are any expired timers execute their
194  // respective callbacks
195  for(const auto & info : expired)
196  {
197  const TimerId & timerId{std::get<0>(info)};
198  const TimePoint & expireTime{std::get<1>(info)};
199  const Callback & callback{std::get<3>(info)};
200  const TimePoint & scheduleTime{std::get<4>(info)};
201 
202  try
203  {
204  callback(timerId,
205  expireTime,
206  scheduleTime,
207  Clock::now());
208  }
209  catch(...)
210  {}
211  }
212 
213  // clear out expired timer list
214  expired.clear();
215 
216  // wait for an interval timer to expire
217  if(read(iFd_,&u64Expired,sizeof(u64Expired)) > 0)
218  {
219  std::unique_lock<std::mutex> lock(mutex_);
220 
221  auto now = Clock::now();
222 
223  if(!bRunning_)
224  {
225  break;
226  }
227 
228  // an interval timer expired - iterate over all scheduled
229  // timers starting with the earliest and expire any that have
230  // expired. Stop iterating once you find a timer that is still
231  // valid (in the future).
232  for(const auto & entry : timePointMap_)
233  {
234  const auto & timePoint = std::get<1>(entry.second);
235 
236  if(now >= timePoint)
237  {
238  expired.push_back(std::move(entry.second));
239  }
240  else
241  {
242  break;
243  }
244  }
245 
246  // remove any expired timers from the timer stores and reschedule
247  // any that have a repeat interval
248  for(const auto & info : expired)
249  {
250  TimerId timerId{};
251  TimePoint expireTime{};
252  Duration interval{};
253  Callback callback{};
254 
255  std::tie(timerId,expireTime,interval,callback,std::ignore) = info;
256 
257  timePointMap_.erase({expireTime,timerId});
258 
259  timerIdMap_.erase(timerId);
260 
261  if(interval != Duration::zero())
262  {
263  expireTime += interval;
264 
265  timePointMap_.insert(std::make_pair(std::make_pair(expireTime,timerId),
266  std::make_tuple(timerId,
267  expireTime,
268  interval,
269  callback,
270  now)));
271 
272  timerIdMap_.insert(std::make_pair(timerId,expireTime));
273  }
274  }
275 
276  // reschedule the new earliest time, if one is present
277  schedule_i();
278  }
279  }
280 }
std::chrono::seconds Seconds
Definition: types.h:43
TimerId schedule(Function fn, const TimePoint &absoluteTimePoint)
Clock::duration Duration
Definition: types.h:49
std::chrono::nanoseconds Nanoseconds
Definition: types.h:46
std::size_t TimerId
Definition: timer.h:57
Clock::time_point TimePoint
Definition: types.h:50
bool cancel(TimerId timerId)
Definition: timer.cc:75