blob: a1759ca2465c16c022b820b70df370f344058009 [file] [log] [blame]
Alexander Afanasyev920af2f2014-01-25 22:56:11 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (C) 2014 Named Data Networking Project
4 * See COPYING for copyright and distribution information.
5 */
6
7#include "scheduler.hpp"
8
Alexander Afanasyev18bbf812014-01-29 01:40:23 -08009namespace nfd {
Alexander Afanasyev920af2f2014-01-25 22:56:11 -080010
11struct EventIdImpl
12{
13 EventIdImpl(const Scheduler::EventQueue::iterator& event)
14 : m_event(event)
15 , m_isValid(true)
16 {
17 }
18
19 void
20 invalidate()
21 {
22 m_isValid = false;
23 }
24
25 bool
26 isValid() const
27 {
28 return m_isValid;
29 }
30
31 operator const Scheduler::EventQueue::iterator&() const
32 {
33 return m_event;
34 }
35
36 void
37 reset(const Scheduler::EventQueue::iterator& newIterator)
38 {
39 m_event = newIterator;
40 m_isValid = true;
41 }
42
43private:
44 Scheduler::EventQueue::iterator m_event;
45 bool m_isValid;
46};
47
48Scheduler::EventInfo::EventInfo(const time::Duration& after,
49 const time::Duration& period,
50 const Event& event)
51 : m_scheduledTime(time::now() + after)
52 , m_period(period)
53 , m_event(event)
54{
55}
56
57Scheduler::EventInfo::EventInfo(const time::Point& when, const EventInfo& previousEvent)
58 : m_scheduledTime(when)
59 , m_period(previousEvent.m_period)
60 , m_event(previousEvent.m_event)
61 , m_eventId(previousEvent.m_eventId)
62{
63}
64
65time::Duration
66Scheduler::EventInfo::expiresFromNow() const
67{
68 time::Point now = time::now();
69 if (now > m_scheduledTime)
70 return time::seconds(0); // event should be scheduled ASAP
71 else
72 return m_scheduledTime - now;
73}
74
75
76Scheduler::Scheduler(boost::asio::io_service& ioService)
77 : m_ioService(ioService)
78 , m_scheduledEvent(m_events.end())
79 , m_deadlineTimer(ioService)
Alexander Afanasyev94ceb122014-02-03 14:47:57 -080080 , m_isEventExecuting(false)
Alexander Afanasyev920af2f2014-01-25 22:56:11 -080081{
82}
83
84EventId
85Scheduler::scheduleEvent(const time::Duration& after,
86 const Event& event)
87{
88 return schedulePeriodicEvent(after, time::nanoseconds(-1), event);
89}
90
91EventId
92Scheduler::schedulePeriodicEvent(const time::Duration& after,
93 const time::Duration& period,
94 const Event& event)
95{
96 EventQueue::iterator i = m_events.insert(EventInfo(after, period, event));
97 i->m_eventId = make_shared<EventIdImpl>(boost::cref(i));
Alexander Afanasyev94ceb122014-02-03 14:47:57 -080098
99 if (!m_isEventExecuting)
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800100 {
Alexander Afanasyev94ceb122014-02-03 14:47:57 -0800101 if (m_scheduledEvent == m_events.end() ||
102 *i < *m_scheduledEvent)
103 {
104 m_deadlineTimer.expires_from_now(after);
105 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
106 m_scheduledEvent = i;
107 }
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800108 }
109
110 return i->m_eventId;
111}
112
113void
114Scheduler::cancelEvent(const EventId& eventId)
115{
Junxiao Shi234a5322014-01-30 22:40:48 -0700116 if (!static_cast<bool>(eventId) || !eventId->isValid())
117 return; // event empty, already fired, or cancelled
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800118
119 if (static_cast<EventQueue::iterator>(*eventId) != m_scheduledEvent) {
120 m_events.erase(*eventId);
121 eventId->invalidate();
122 return;
123 }
124
125 m_deadlineTimer.cancel();
126 m_events.erase(static_cast<EventQueue::iterator>(*eventId));
127 eventId->invalidate();
128
Alexander Afanasyev94ceb122014-02-03 14:47:57 -0800129 if (!m_isEventExecuting)
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800130 {
Alexander Afanasyev94ceb122014-02-03 14:47:57 -0800131 if (!m_events.empty())
132 {
133 m_deadlineTimer.expires_from_now(m_events.begin()->expiresFromNow());
134 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
135 m_scheduledEvent = m_events.begin();
136 }
137 else
138 {
139 m_deadlineTimer.cancel();
140 m_scheduledEvent = m_events.end();
141 }
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800142 }
143}
144
145void
146Scheduler::onEvent(const boost::system::error_code& error)
147{
148 if (error) // e.g., cancelled
149 {
150 return;
151 }
Alexander Afanasyev94ceb122014-02-03 14:47:57 -0800152
153 m_isEventExecuting = true;
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800154
155 // process all expired events
156 time::Point now = time::now();
157 while(!m_events.empty() && m_events.begin()->m_scheduledTime <= now)
158 {
159 EventQueue::iterator head = m_events.begin();
160
Alexander Afanasyev94ceb122014-02-03 14:47:57 -0800161 Event event = head->m_event;
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800162 if (head->m_period < 0)
163 {
164 head->m_eventId->invalidate();
165 m_events.erase(head);
166 }
167 else
168 {
169 // "reschedule" and update EventId data of the event
170 EventInfo event(now + head->m_period, *head);
171 EventQueue::iterator i = m_events.insert(event);
172 i->m_eventId->reset(i);
173 m_events.erase(head);
174 }
Alexander Afanasyev94ceb122014-02-03 14:47:57 -0800175
176 try
177 {
178 event();
179 }
180 catch(...)
181 {
182 m_isEventExecuting = false;
183 for (EventQueue::iterator i = m_events.begin();
184 i != m_events.end();
185 ++i)
186 {
187 i->m_eventId->invalidate();
188 }
189 m_events.clear();
190
191 throw;
192 }
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800193 }
194
195 if (!m_events.empty())
196 {
197 m_deadlineTimer.expires_from_now(m_events.begin()->m_scheduledTime - now);
198 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
Alexander Afanasyev94ceb122014-02-03 14:47:57 -0800199 m_scheduledEvent = m_events.begin();
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800200 }
Alexander Afanasyev94ceb122014-02-03 14:47:57 -0800201 else
202 {
203 m_scheduledEvent = m_events.end();
204 }
205
206 m_isEventExecuting = false;
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800207}
208
209
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800210} // namespace nfd