blob: d51f83db52443685ab968de127c991c867fc61db [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,
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070066 const Event& event)
67 : m_scheduledTime(time::steady_clock::now() + after)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080068 , m_event(event)
69{
70}
71
Alexander Afanasyevb67090a2014-04-29 22:31:01 -070072Scheduler::EventInfo::EventInfo(const time::steady_clock::TimePoint& when,
73 const EventInfo& previousEvent)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080074 : m_scheduledTime(when)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080075 , m_event(previousEvent.m_event)
76 , m_eventId(previousEvent.m_eventId)
77{
78}
79
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070080time::nanoseconds
Alexander Afanasyevf6468892014-01-29 01:04:14 -080081Scheduler::EventInfo::expiresFromNow() const
82{
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070083 time::steady_clock::TimePoint now = time::steady_clock::now();
Alexander Afanasyevf6468892014-01-29 01:04:14 -080084 if (now > m_scheduledTime)
85 return time::seconds(0); // event should be scheduled ASAP
86 else
87 return m_scheduledTime - now;
88}
89
90
91Scheduler::Scheduler(boost::asio::io_service& ioService)
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070092 : m_scheduledEvent(m_events.end())
Alexander Afanasyevf6468892014-01-29 01:04:14 -080093 , m_deadlineTimer(ioService)
Yingdi Yuf2a82092014-02-03 16:49:15 -080094 , m_isEventExecuting(false)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080095{
96}
97
98EventId
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070099Scheduler::scheduleEvent(const time::nanoseconds& after,
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800100 const Event& event)
101{
Alexander Afanasyev72a11782014-06-19 13:46:57 -0700102 EventQueue::iterator i = m_events.insert(EventInfo(after, event));
Alexander Afanasyevf73f0632014-05-12 18:02:37 -0700103
104 // On OSX 10.9, boost, and C++03 the following doesn't work without ndn::
105 // because the argument-dependent lookup prefers STL to boost
106 i->m_eventId = ndn::make_shared<EventIdImpl>(i);
Yingdi Yuf2a82092014-02-03 16:49:15 -0800107
108 if (!m_isEventExecuting)
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800109 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800110 if (m_scheduledEvent == m_events.end() ||
111 *i < *m_scheduledEvent)
112 {
113 m_deadlineTimer.expires_from_now(after);
114 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
115 m_scheduledEvent = i;
116 }
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800117 }
118
119 return i->m_eventId;
120}
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700121
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800122void
123Scheduler::cancelEvent(const EventId& eventId)
124{
Yingdi Yuab136552014-02-03 14:31:02 -0800125 if (!static_cast<bool>(eventId) || !eventId->isValid())
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800126 return; // event already fired or cancelled
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700127
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800128 if (static_cast<EventQueue::iterator>(*eventId) != m_scheduledEvent) {
129 m_events.erase(*eventId);
130 eventId->invalidate();
131 return;
132 }
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700133
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800134 m_deadlineTimer.cancel();
135 m_events.erase(static_cast<EventQueue::iterator>(*eventId));
136 eventId->invalidate();
137
Yingdi Yuf2a82092014-02-03 16:49:15 -0800138 if (!m_isEventExecuting)
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800139 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800140 if (!m_events.empty())
141 {
142 m_deadlineTimer.expires_from_now(m_events.begin()->expiresFromNow());
143 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
144 m_scheduledEvent = m_events.begin();
145 }
146 else
147 {
148 m_scheduledEvent = m_events.end();
149 }
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800150 }
151}
152
153void
154Scheduler::onEvent(const boost::system::error_code& error)
155{
156 if (error) // e.g., cancelled
157 {
158 return;
159 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800160
161 m_isEventExecuting = true;
162
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800163 // process all expired events
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700164 time::steady_clock::TimePoint now = time::steady_clock::now();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800165 while(!m_events.empty() && m_events.begin()->m_scheduledTime <= now)
166 {
167 EventQueue::iterator head = m_events.begin();
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700168
Yingdi Yuf2a82092014-02-03 16:49:15 -0800169 Event event = head->m_event;
Alexander Afanasyev72a11782014-06-19 13:46:57 -0700170 head->m_eventId->invalidate();
171 m_events.erase(head);
Yingdi Yuf2a82092014-02-03 16:49:15 -0800172
173 event();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800174 }
175
176 if (!m_events.empty())
177 {
178 m_deadlineTimer.expires_from_now(m_events.begin()->m_scheduledTime - now);
179 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
Yingdi Yuf2a82092014-02-03 16:49:15 -0800180 m_scheduledEvent = m_events.begin();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800181 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800182 else
183 {
184 m_scheduledEvent = m_events.end();
185 }
186
187 m_isEventExecuting = false;
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800188}
189
190
191} // namespace ndn