blob: 9cf23575dbadaf878d5da96add76fb610b2ed401 [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Alexander Afanasyevf6468892014-01-29 01:04:14 -08002/**
Alexander Afanasyevc169a812014-05-20 20:37:29 -04003 * Copyright (c) 2013-2014 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
Alexander Afanasyevf6468892014-01-29 01:04:14 -080020 */
21
Alexander Afanasyeve2dcdfd2014-02-07 15:53:28 -080022#include "common.hpp"
23
Alexander Afanasyevf6468892014-01-29 01:04:14 -080024#include "scheduler.hpp"
25
26namespace ndn {
27
28struct EventIdImpl
29{
30 EventIdImpl(const Scheduler::EventQueue::iterator& event)
31 : m_event(event)
32 , m_isValid(true)
33 {
34 }
35
36 void
37 invalidate()
38 {
39 m_isValid = false;
40 }
41
42 bool
43 isValid() const
44 {
45 return m_isValid;
46 }
47
48 operator const Scheduler::EventQueue::iterator&() const
49 {
50 return m_event;
51 }
52
53 void
54 reset(const Scheduler::EventQueue::iterator& newIterator)
55 {
56 m_event = newIterator;
57 m_isValid = true;
58 }
59
60private:
61 Scheduler::EventQueue::iterator m_event;
62 bool m_isValid;
63};
64
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070065Scheduler::EventInfo::EventInfo(const time::nanoseconds& after,
66 const time::nanoseconds& period,
67 const Event& event)
68 : m_scheduledTime(time::steady_clock::now() + after)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080069 , m_period(period)
70 , m_event(event)
71{
72}
73
Alexander Afanasyevb67090a2014-04-29 22:31:01 -070074Scheduler::EventInfo::EventInfo(const time::steady_clock::TimePoint& when,
75 const EventInfo& previousEvent)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080076 : m_scheduledTime(when)
77 , m_period(previousEvent.m_period)
78 , m_event(previousEvent.m_event)
79 , m_eventId(previousEvent.m_eventId)
80{
81}
82
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070083time::nanoseconds
Alexander Afanasyevf6468892014-01-29 01:04:14 -080084Scheduler::EventInfo::expiresFromNow() const
85{
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070086 time::steady_clock::TimePoint now = time::steady_clock::now();
Alexander Afanasyevf6468892014-01-29 01:04:14 -080087 if (now > m_scheduledTime)
88 return time::seconds(0); // event should be scheduled ASAP
89 else
90 return m_scheduledTime - now;
91}
92
93
94Scheduler::Scheduler(boost::asio::io_service& ioService)
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070095 : m_scheduledEvent(m_events.end())
Alexander Afanasyevf6468892014-01-29 01:04:14 -080096 , m_deadlineTimer(ioService)
Yingdi Yuf2a82092014-02-03 16:49:15 -080097 , m_isEventExecuting(false)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080098{
99}
100
101EventId
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700102Scheduler::scheduleEvent(const time::nanoseconds& after,
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800103 const Event& event)
104{
105 return schedulePeriodicEvent(after, time::nanoseconds(-1), event);
106}
107
108EventId
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700109Scheduler::schedulePeriodicEvent(const time::nanoseconds& after,
110 const time::nanoseconds& period,
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800111 const Event& event)
112{
113 EventQueue::iterator i = m_events.insert(EventInfo(after, period, event));
Alexander Afanasyevf73f0632014-05-12 18:02:37 -0700114
115 // On OSX 10.9, boost, and C++03 the following doesn't work without ndn::
116 // because the argument-dependent lookup prefers STL to boost
117 i->m_eventId = ndn::make_shared<EventIdImpl>(i);
Yingdi Yuf2a82092014-02-03 16:49:15 -0800118
119 if (!m_isEventExecuting)
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800120 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800121 if (m_scheduledEvent == m_events.end() ||
122 *i < *m_scheduledEvent)
123 {
124 m_deadlineTimer.expires_from_now(after);
125 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
126 m_scheduledEvent = i;
127 }
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800128 }
129
130 return i->m_eventId;
131}
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700132
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800133void
134Scheduler::cancelEvent(const EventId& eventId)
135{
Yingdi Yuab136552014-02-03 14:31:02 -0800136 if (!static_cast<bool>(eventId) || !eventId->isValid())
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800137 return; // event already fired or cancelled
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700138
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800139 if (static_cast<EventQueue::iterator>(*eventId) != m_scheduledEvent) {
140 m_events.erase(*eventId);
141 eventId->invalidate();
142 return;
143 }
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700144
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800145 m_deadlineTimer.cancel();
146 m_events.erase(static_cast<EventQueue::iterator>(*eventId));
147 eventId->invalidate();
148
Yingdi Yuf2a82092014-02-03 16:49:15 -0800149 if (!m_isEventExecuting)
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800150 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800151 if (!m_events.empty())
152 {
153 m_deadlineTimer.expires_from_now(m_events.begin()->expiresFromNow());
154 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
155 m_scheduledEvent = m_events.begin();
156 }
157 else
158 {
159 m_scheduledEvent = m_events.end();
160 }
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800161 }
162}
163
164void
165Scheduler::onEvent(const boost::system::error_code& error)
166{
167 if (error) // e.g., cancelled
168 {
169 return;
170 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800171
172 m_isEventExecuting = true;
173
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800174 // process all expired events
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700175 time::steady_clock::TimePoint now = time::steady_clock::now();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800176 while(!m_events.empty() && m_events.begin()->m_scheduledTime <= now)
177 {
178 EventQueue::iterator head = m_events.begin();
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700179
Yingdi Yuf2a82092014-02-03 16:49:15 -0800180 Event event = head->m_event;
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700181 if (head->m_period < time::nanoseconds::zero())
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800182 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800183 head->m_eventId->invalidate();
184 m_events.erase(head);
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800185 }
186 else
187 {
188 // "reschedule" and update EventId data of the event
189 EventInfo event(now + head->m_period, *head);
190 EventQueue::iterator i = m_events.insert(event);
191 i->m_eventId->reset(i);
Yingdi Yuf2a82092014-02-03 16:49:15 -0800192 m_events.erase(head);
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800193 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800194
195 event();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800196 }
197
198 if (!m_events.empty())
199 {
200 m_deadlineTimer.expires_from_now(m_events.begin()->m_scheduledTime - now);
201 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
Yingdi Yuf2a82092014-02-03 16:49:15 -0800202 m_scheduledEvent = m_events.begin();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800203 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800204 else
205 {
206 m_scheduledEvent = m_events.end();
207 }
208
209 m_isEventExecuting = false;
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800210}
211
212
213} // namespace ndn