blob: 388d5e721bdeca7e8e0d84384b72c29c42f03d91 [file] [log] [blame]
Alexander Afanasyevf6468892014-01-29 01:04:14 -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
Alexander Afanasyeve2dcdfd2014-02-07 15:53:28 -08007#include "common.hpp"
8
Alexander Afanasyevf6468892014-01-29 01:04:14 -08009#include "scheduler.hpp"
10
11namespace ndn {
12
13struct EventIdImpl
14{
15 EventIdImpl(const Scheduler::EventQueue::iterator& event)
16 : m_event(event)
17 , m_isValid(true)
18 {
19 }
20
21 void
22 invalidate()
23 {
24 m_isValid = false;
25 }
26
27 bool
28 isValid() const
29 {
30 return m_isValid;
31 }
32
33 operator const Scheduler::EventQueue::iterator&() const
34 {
35 return m_event;
36 }
37
38 void
39 reset(const Scheduler::EventQueue::iterator& newIterator)
40 {
41 m_event = newIterator;
42 m_isValid = true;
43 }
44
45private:
46 Scheduler::EventQueue::iterator m_event;
47 bool m_isValid;
48};
49
50Scheduler::EventInfo::EventInfo(const time::Duration& after,
51 const time::Duration& period,
52 const Event& event)
53 : m_scheduledTime(time::now() + after)
54 , m_period(period)
55 , m_event(event)
56{
57}
58
59Scheduler::EventInfo::EventInfo(const time::Point& when, const EventInfo& previousEvent)
60 : m_scheduledTime(when)
61 , m_period(previousEvent.m_period)
62 , m_event(previousEvent.m_event)
63 , m_eventId(previousEvent.m_eventId)
64{
65}
66
67time::Duration
68Scheduler::EventInfo::expiresFromNow() const
69{
70 time::Point now = time::now();
71 if (now > m_scheduledTime)
72 return time::seconds(0); // event should be scheduled ASAP
73 else
74 return m_scheduledTime - now;
75}
76
77
78Scheduler::Scheduler(boost::asio::io_service& ioService)
79 : m_ioService(ioService)
80 , m_scheduledEvent(m_events.end())
81 , m_deadlineTimer(ioService)
Yingdi Yuf2a82092014-02-03 16:49:15 -080082 , m_isEventExecuting(false)
Alexander Afanasyevf6468892014-01-29 01:04:14 -080083{
84}
85
86EventId
87Scheduler::scheduleEvent(const time::Duration& after,
88 const Event& event)
89{
90 return schedulePeriodicEvent(after, time::nanoseconds(-1), event);
91}
92
93EventId
94Scheduler::schedulePeriodicEvent(const time::Duration& after,
95 const time::Duration& period,
96 const Event& event)
97{
98 EventQueue::iterator i = m_events.insert(EventInfo(after, period, event));
99 i->m_eventId = make_shared<EventIdImpl>(boost::cref(i));
Yingdi Yuf2a82092014-02-03 16:49:15 -0800100
101 if (!m_isEventExecuting)
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800102 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800103 if (m_scheduledEvent == m_events.end() ||
104 *i < *m_scheduledEvent)
105 {
106 m_deadlineTimer.expires_from_now(after);
107 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
108 m_scheduledEvent = i;
109 }
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800110 }
111
112 return i->m_eventId;
113}
114
115void
116Scheduler::cancelEvent(const EventId& eventId)
117{
Yingdi Yuab136552014-02-03 14:31:02 -0800118 if (!static_cast<bool>(eventId) || !eventId->isValid())
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800119 return; // event already fired or cancelled
120
121 if (static_cast<EventQueue::iterator>(*eventId) != m_scheduledEvent) {
122 m_events.erase(*eventId);
123 eventId->invalidate();
124 return;
125 }
126
127 m_deadlineTimer.cancel();
128 m_events.erase(static_cast<EventQueue::iterator>(*eventId));
129 eventId->invalidate();
130
Yingdi Yuf2a82092014-02-03 16:49:15 -0800131 if (!m_isEventExecuting)
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800132 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800133 if (!m_events.empty())
134 {
135 m_deadlineTimer.expires_from_now(m_events.begin()->expiresFromNow());
136 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
137 m_scheduledEvent = m_events.begin();
138 }
139 else
140 {
141 m_scheduledEvent = m_events.end();
142 }
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800143 }
144}
145
146void
147Scheduler::onEvent(const boost::system::error_code& error)
148{
149 if (error) // e.g., cancelled
150 {
151 return;
152 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800153
154 m_isEventExecuting = true;
155
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800156 // process all expired events
157 time::Point now = time::now();
158 while(!m_events.empty() && m_events.begin()->m_scheduledTime <= now)
159 {
160 EventQueue::iterator head = m_events.begin();
161
Yingdi Yuf2a82092014-02-03 16:49:15 -0800162 Event event = head->m_event;
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800163 if (head->m_period < 0)
164 {
Yingdi Yuf2a82092014-02-03 16:49:15 -0800165 head->m_eventId->invalidate();
166 m_events.erase(head);
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800167 }
168 else
169 {
170 // "reschedule" and update EventId data of the event
171 EventInfo event(now + head->m_period, *head);
172 EventQueue::iterator i = m_events.insert(event);
173 i->m_eventId->reset(i);
Yingdi Yuf2a82092014-02-03 16:49:15 -0800174 m_events.erase(head);
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800175 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800176
177 event();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800178 }
179
180 if (!m_events.empty())
181 {
182 m_deadlineTimer.expires_from_now(m_events.begin()->m_scheduledTime - now);
183 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
Yingdi Yuf2a82092014-02-03 16:49:15 -0800184 m_scheduledEvent = m_events.begin();
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800185 }
Yingdi Yuf2a82092014-02-03 16:49:15 -0800186 else
187 {
188 m_scheduledEvent = m_events.end();
189 }
190
191 m_isEventExecuting = false;
Alexander Afanasyevf6468892014-01-29 01:04:14 -0800192}
193
194
195} // namespace ndn