blob: 21106e0d3bed63ad533df0a35c30d5eb7b84e633 [file] [log] [blame]
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
Alexander Afanasyevf6468892014-01-29 01:04:14 -08002/**
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07003 * Copyright (c) 2013-2014, Regents of the University of California.
4 * All rights reserved.
5 *
6 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
7 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
8 *
9 * This file licensed under New BSD License. See COPYING for detailed information about
10 * ndn-cxx library copyright, permissions, and redistribution restrictions.
Alexander Afanasyevf6468892014-01-29 01:04:14 -080011 */
12
Alexander Afanasyeve2dcdfd2014-02-07 15:53:28 -080013#include "common.hpp"
14
Alexander Afanasyevf6468892014-01-29 01:04:14 -080015#include "scheduler.hpp"
16
17namespace ndn {
18
19struct EventIdImpl
20{
21 EventIdImpl(const Scheduler::EventQueue::iterator& event)
22 : m_event(event)
23 , m_isValid(true)
24 {
25 }
26
27 void
28 invalidate()
29 {
30 m_isValid = false;
31 }
32
33 bool
34 isValid() const
35 {
36 return m_isValid;
37 }
38
39 operator const Scheduler::EventQueue::iterator&() const
40 {
41 return m_event;
42 }
43
44 void
45 reset(const Scheduler::EventQueue::iterator& newIterator)
46 {
47 m_event = newIterator;
48 m_isValid = true;
49 }
50
51private:
52 Scheduler::EventQueue::iterator m_event;
53 bool m_isValid;
54};
55
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070056Scheduler::EventInfo::EventInfo(const time::nanoseconds& after,
57 const time::nanoseconds& period,
58 const Event& event)
59 : m_scheduledTime(time::steady_clock::now() + after)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080060 , m_period(period)
61 , m_event(event)
62{
63}
64
Alexander Afanasyevb67090a2014-04-29 22:31:01 -070065Scheduler::EventInfo::EventInfo(const time::steady_clock::TimePoint& when,
66 const EventInfo& previousEvent)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080067 : m_scheduledTime(when)
68 , m_period(previousEvent.m_period)
69 , m_event(previousEvent.m_event)
70 , m_eventId(previousEvent.m_eventId)
71{
72}
73
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070074time::nanoseconds
Alexander Afanasyevf6468892014-01-29 01:04:14 -080075Scheduler::EventInfo::expiresFromNow() const
76{
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070077 time::steady_clock::TimePoint now = time::steady_clock::now();
Alexander Afanasyevf6468892014-01-29 01:04:14 -080078 if (now > m_scheduledTime)
79 return time::seconds(0); // event should be scheduled ASAP
80 else
81 return m_scheduledTime - now;
82}
83
84
85Scheduler::Scheduler(boost::asio::io_service& ioService)
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -070086 : m_scheduledEvent(m_events.end())
Alexander Afanasyevf6468892014-01-29 01:04:14 -080087 , m_deadlineTimer(ioService)
Yingdi Yuf2a82092014-02-03 16:49:15 -080088 , m_isEventExecuting(false)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080089{
90}
91
92EventId
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -070093Scheduler::scheduleEvent(const time::nanoseconds& after,
Alexander Afanasyevf6468892014-01-29 01:04:14 -080094 const Event& event)
95{
96 return schedulePeriodicEvent(after, time::nanoseconds(-1), event);
97}
98
99EventId
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700100Scheduler::schedulePeriodicEvent(const time::nanoseconds& after,
101 const time::nanoseconds& period,
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800102 const Event& event)
103{
104 EventQueue::iterator i = m_events.insert(EventInfo(after, period, event));
Alexander Afanasyevf73f0632014-05-12 18:02:37 -0700105
106 // On OSX 10.9, boost, and C++03 the following doesn't work without ndn::
107 // because the argument-dependent lookup prefers STL to boost
108 i->m_eventId = ndn::make_shared<EventIdImpl>(i);
Yingdi Yuf2a82092014-02-03 16:49:15 -0800109
110 if (!m_isEventExecuting)
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800111 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800112 if (m_scheduledEvent == m_events.end() ||
113 *i < *m_scheduledEvent)
114 {
115 m_deadlineTimer.expires_from_now(after);
116 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
117 m_scheduledEvent = i;
118 }
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800119 }
120
121 return i->m_eventId;
122}
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700123
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800124void
125Scheduler::cancelEvent(const EventId& eventId)
126{
Yingdi Yuab136552014-02-03 14:31:02 -0800127 if (!static_cast<bool>(eventId) || !eventId->isValid())
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800128 return; // event already fired or cancelled
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700129
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800130 if (static_cast<EventQueue::iterator>(*eventId) != m_scheduledEvent) {
131 m_events.erase(*eventId);
132 eventId->invalidate();
133 return;
134 }
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700135
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800136 m_deadlineTimer.cancel();
137 m_events.erase(static_cast<EventQueue::iterator>(*eventId));
138 eventId->invalidate();
139
Yingdi Yuf2a82092014-02-03 16:49:15 -0800140 if (!m_isEventExecuting)
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800141 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800142 if (!m_events.empty())
143 {
144 m_deadlineTimer.expires_from_now(m_events.begin()->expiresFromNow());
145 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
146 m_scheduledEvent = m_events.begin();
147 }
148 else
149 {
150 m_scheduledEvent = m_events.end();
151 }
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800152 }
153}
154
155void
156Scheduler::onEvent(const boost::system::error_code& error)
157{
158 if (error) // e.g., cancelled
159 {
160 return;
161 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800162
163 m_isEventExecuting = true;
164
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800165 // process all expired events
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700166 time::steady_clock::TimePoint now = time::steady_clock::now();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800167 while(!m_events.empty() && m_events.begin()->m_scheduledTime <= now)
168 {
169 EventQueue::iterator head = m_events.begin();
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -0700170
Yingdi Yuf2a82092014-02-03 16:49:15 -0800171 Event event = head->m_event;
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700172 if (head->m_period < time::nanoseconds::zero())
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800173 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800174 head->m_eventId->invalidate();
175 m_events.erase(head);
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800176 }
177 else
178 {
179 // "reschedule" and update EventId data of the event
180 EventInfo event(now + head->m_period, *head);
181 EventQueue::iterator i = m_events.insert(event);
182 i->m_eventId->reset(i);
Yingdi Yuf2a82092014-02-03 16:49:15 -0800183 m_events.erase(head);
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800184 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800185
186 event();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800187 }
188
189 if (!m_events.empty())
190 {
191 m_deadlineTimer.expires_from_now(m_events.begin()->m_scheduledTime - now);
192 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
Yingdi Yuf2a82092014-02-03 16:49:15 -0800193 m_scheduledEvent = m_events.begin();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800194 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800195 else
196 {
197 m_scheduledEvent = m_events.end();
198 }
199
200 m_isEventExecuting = false;
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800201}
202
203
204} // namespace ndn