blob: 227eeadda50839c99b5db27546ab51a6f44ca1d0 [file] [log] [blame]
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -08001/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2013 University of California, Los Angeles
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
19 * Alexander Afanasyev <alexander.afanasyev@ucla.edu>
20 */
21
22#include "scheduler.h"
Alexander Afanasyeve41e7d22013-01-19 15:13:47 -080023#include "one-time-task.h"
24#include "periodic-task.h"
Alexander Afanasyevfc720362013-01-24 21:49:48 -080025#include "logging.h"
Alexander Afanasyeve41e7d22013-01-19 15:13:47 -080026
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080027#include <utility>
Alexander Afanasyeve41e7d22013-01-19 15:13:47 -080028#include <boost/make_shared.hpp>
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080029
Alexander Afanasyevfc720362013-01-24 21:49:48 -080030INIT_LOGGER ("Scheduler");
31
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080032using namespace std;
Alexander Afanasyeve41e7d22013-01-19 15:13:47 -080033using namespace boost;
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080034
35#define EVLOOP_NO_EXIT_ON_EMPTY 0x04
36
Zhenkai Zhub16be8f2013-01-25 16:12:30 -080037static void
38dummyCallback(evutil_socket_t fd, short what, void *arg)
39{
40 // 1 year later, that was a long run for the app
Zhenkai Zhu3fe11722013-01-25 16:22:46 -080041 // let's wait for another year
42 timeval tv;
43 tv.tv_sec = 365 * 24 * 3600;
44 tv.tv_usec = 0;
45 event *ev = *(event **)arg;
46 int res = evtimer_add(ev, &tv);
Zhenkai Zhub16be8f2013-01-25 16:12:30 -080047}
48
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080049// IntervalGeneratorPtr
50// IntervalGenerator:: Null;
51
52void errorCallback(int err)
53{
Alexander Afanasyevfc720362013-01-24 21:49:48 -080054 _LOG_ERROR ("Fatal error: " << err);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080055}
56
57Scheduler::Scheduler()
58 : m_running(false)
59{
60 event_set_fatal_callback(errorCallback);
61 evthread_use_pthreads();
62 m_base = event_base_new();
Zhenkai Zhub16be8f2013-01-25 16:12:30 -080063
64 // This is a hack to prevent event_base_loop from exiting;
65 // the flag EVLOOP_NO_EXIT_ON_EMPTY is somehow ignored, at least on Mac OS X
66 // it's going to be scheduled to 10 years later
67 timeval tv;
68 tv.tv_sec = 365 * 24 * 3600;
69 tv.tv_usec = 0;
Zhenkai Zhu3fe11722013-01-25 16:22:46 -080070 m_ev = evtimer_new(m_base, dummyCallback, &m_ev);
Zhenkai Zhub16be8f2013-01-25 16:12:30 -080071 int res = evtimer_add(m_ev, &tv);
72 if (res < 0)
73 {
74 _LOG_ERROR("heck");
75 }
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080076}
77
78Scheduler::~Scheduler()
79{
Alexander Afanasyevfc720362013-01-24 21:49:48 -080080 shutdown ();
Zhenkai Zhub16be8f2013-01-25 16:12:30 -080081 evtimer_del(m_ev);
82 event_free(m_ev);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080083 event_base_free(m_base);
84}
85
86void
87Scheduler::eventLoop()
88{
89 while(true)
90 {
91 if (event_base_loop(m_base, EVLOOP_NO_EXIT_ON_EMPTY) < 0)
92 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -080093 _LOG_DEBUG ("scheduler loop break error");
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080094 }
Zhenkai Zhub16be8f2013-01-25 16:12:30 -080095 else
96 {
97 _LOG_DEBUG ("scheduler loop break normal");
98 }
Alexander Afanasyevfc720362013-01-24 21:49:48 -080099
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800100 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800101 ScopedLock lock(m_mutex);
Alexander Afanasyev34edd4d2013-01-17 17:55:45 -0800102 if (!m_running)
103 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800104 _LOG_DEBUG ("scheduler loop break normal");
Alexander Afanasyev34edd4d2013-01-17 17:55:45 -0800105 break;
106 }
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800107 }
Zhenkai Zhu238db852013-01-25 16:27:28 -0800108
109 // just to prevent craziness in CPU usage which supposedly should not happen
110 // after adding the dummy event
111 usleep(1000);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800112 }
113}
114
115void
116Scheduler::start()
117{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800118 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800119 if (!m_running)
120 {
121 m_thread = boost::thread(&Scheduler::eventLoop, this);
122 m_running = true;
123 }
124}
125
126void
127Scheduler::shutdown()
128{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800129 bool breakAndWait = false;
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800130 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800131 ScopedLock lock (m_mutex);
132 if (m_running)
133 {
134 m_running = false;
135 breakAndWait = true;
136 }
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800137 }
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800138
139 if (breakAndWait)
140 {
141 event_base_loopbreak(m_base);
142 m_thread.join();
143 }
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800144}
145
Alexander Afanasyeve41e7d22013-01-19 15:13:47 -0800146TaskPtr
147Scheduler::scheduleOneTimeTask (SchedulerPtr scheduler, double delay,
148 const Task::Callback &callback, const Task::Tag &tag)
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800149{
Alexander Afanasyeve41e7d22013-01-19 15:13:47 -0800150 TaskPtr task = make_shared<OneTimeTask> (callback, tag, scheduler, delay);
151 if (scheduler->addTask (task))
152 return task;
153 else
154 return TaskPtr ();
155}
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800156
Alexander Afanasyeve41e7d22013-01-19 15:13:47 -0800157TaskPtr
158Scheduler::schedulePeriodicTask (SchedulerPtr scheduler, IntervalGeneratorPtr delayGenerator,
159 const Task::Callback &callback, const Task::Tag &tag)
160{
161 TaskPtr task = make_shared<PeriodicTask> (callback, tag, scheduler, delayGenerator);
162
163 if (scheduler->addTask (task))
164 return task;
165 else
166 return TaskPtr ();
167}
168
169bool
170Scheduler::addTask(TaskPtr newTask)
171{
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800172 if (addToMap(newTask))
173 {
174 newTask->reset();
175 int res = evtimer_add(newTask->ev(), newTask->tv());
176 if (res < 0)
177 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800178 _LOG_ERROR ("evtimer_add failed for " << newTask->tag());
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800179 }
180 return true;
181 }
182 else
183 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800184 _LOG_ERROR ("fail to add task: " << newTask->tag());
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800185 }
186
187 return false;
188}
189
Alexander Afanasyevfb4c43f2013-01-18 17:43:25 -0800190void
Alexander Afanasyev997ba632013-01-18 17:40:23 -0800191Scheduler::deleteTask(TaskPtr task)
192{
193 deleteTask (task->tag ());
194}
195
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800196void
Zhenkai Zhu66ddb232013-01-18 17:53:52 -0800197Scheduler::rescheduleTask(const TaskPtr &task)
198{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800199 ScopedLock lock(m_mutex);
Zhenkai Zhud2ca3922013-01-18 17:58:48 -0800200 TaskMapIt it = m_taskMap.find(task->tag());
Zhenkai Zhu66ddb232013-01-18 17:53:52 -0800201 if (it != m_taskMap.end())
202 {
203 TaskPtr task = it->second;
204 task->reset();
205 int res = evtimer_add(task->ev(), task->tv());
206 if (res < 0)
207 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800208 _LOG_ERROR ("evtimer_add failed for " << task->tag());
Zhenkai Zhu66ddb232013-01-18 17:53:52 -0800209 }
210 }
211 else
212 {
213 addTask(task);
214 }
215}
216
217void
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800218Scheduler::rescheduleTask(const Task::Tag &tag)
219{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800220 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800221 TaskMapIt it = m_taskMap.find(tag);
222 if (it != m_taskMap.end())
223 {
224 TaskPtr task = it->second;
225 task->reset();
226 int res = evtimer_add(task->ev(), task->tv());
227 if (res < 0)
228 {
229 cout << "evtimer_add failed for " << task->tag() << endl;
230 }
231 }
232}
233
234bool
235Scheduler::addToMap(const TaskPtr &task)
236{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800237 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800238 if (m_taskMap.find(task->tag()) == m_taskMap.end())
239 {
240 m_taskMap.insert(make_pair(task->tag(), task));
241 return true;
242 }
243 return false;
244}
245
246void
247Scheduler::deleteTask(const Task::Tag &tag)
248{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800249 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800250 TaskMapIt it = m_taskMap.find(tag);
251 if (it != m_taskMap.end())
252 {
253 TaskPtr task = it->second;
254 evtimer_del(task->ev());
255 m_taskMap.erase(it);
256 }
257}
258
259void
260Scheduler::deleteTask(const Task::TaskMatcher &matcher)
261{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800262 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800263 TaskMapIt it = m_taskMap.begin();
264 while(it != m_taskMap.end())
265 {
266 TaskPtr task = it->second;
267 if (matcher(task))
268 {
269 evtimer_del(task->ev());
270 // Use post increment; map.erase invalidate the iterator that is beening erased,
271 // but does not invalidate other iterators. This seems to be the convention to
272 // erase something from C++ STL map while traversing.
273 m_taskMap.erase(it++);
274 }
275 else
276 {
277 ++it;
278 }
279 }
280}
281
282int
283Scheduler::size()
284{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800285 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800286 return m_taskMap.size();
287}