blob: c72e2796f4e828fb4d81c1d6fd5e9d81bc72c20f [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)
80{
81}
82
83EventId
84Scheduler::scheduleEvent(const time::Duration& after,
85 const Event& event)
86{
87 return schedulePeriodicEvent(after, time::nanoseconds(-1), event);
88}
89
90EventId
91Scheduler::schedulePeriodicEvent(const time::Duration& after,
92 const time::Duration& period,
93 const Event& event)
94{
95 EventQueue::iterator i = m_events.insert(EventInfo(after, period, event));
96 i->m_eventId = make_shared<EventIdImpl>(boost::cref(i));
97
98 if (m_scheduledEvent == m_events.end() ||
99 *i < *m_scheduledEvent)
100 {
101 m_deadlineTimer.expires_from_now(after);
102 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
103 m_scheduledEvent = i;
104 }
105
106 return i->m_eventId;
107}
108
109void
110Scheduler::cancelEvent(const EventId& eventId)
111{
Junxiao Shi234a5322014-01-30 22:40:48 -0700112 if (!static_cast<bool>(eventId) || !eventId->isValid())
113 return; // event empty, already fired, or cancelled
Alexander Afanasyev920af2f2014-01-25 22:56:11 -0800114
115 if (static_cast<EventQueue::iterator>(*eventId) != m_scheduledEvent) {
116 m_events.erase(*eventId);
117 eventId->invalidate();
118 return;
119 }
120
121 m_deadlineTimer.cancel();
122 m_events.erase(static_cast<EventQueue::iterator>(*eventId));
123 eventId->invalidate();
124
125 if (!m_events.empty())
126 {
127 m_deadlineTimer.expires_from_now(m_events.begin()->expiresFromNow());
128 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
129 m_scheduledEvent = m_events.begin();
130 }
131 else
132 {
133 m_deadlineTimer.cancel();
134 m_scheduledEvent = m_events.end();
135 }
136}
137
138void
139Scheduler::onEvent(const boost::system::error_code& error)
140{
141 if (error) // e.g., cancelled
142 {
143 return;
144 }
145
146 // process all expired events
147 time::Point now = time::now();
148 while(!m_events.empty() && m_events.begin()->m_scheduledTime <= now)
149 {
150 EventQueue::iterator head = m_events.begin();
151
152 head->m_event();
153 if (head->m_period < 0)
154 {
155 head->m_eventId->invalidate();
156 m_events.erase(head);
157 }
158 else
159 {
160 // "reschedule" and update EventId data of the event
161 EventInfo event(now + head->m_period, *head);
162 EventQueue::iterator i = m_events.insert(event);
163 i->m_eventId->reset(i);
164 m_events.erase(head);
165 }
166 }
167
168 if (!m_events.empty())
169 {
170 m_deadlineTimer.expires_from_now(m_events.begin()->m_scheduledTime - now);
171 m_deadlineTimer.async_wait(bind(&Scheduler::onEvent, this, _1));
172 }
173}
174
175
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800176} // namespace nfd