blob: 37205337f88593cc119e06f3d15871718f32cead [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
41}
42
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080043// IntervalGeneratorPtr
44// IntervalGenerator:: Null;
45
46void errorCallback(int err)
47{
Alexander Afanasyevfc720362013-01-24 21:49:48 -080048 _LOG_ERROR ("Fatal error: " << err);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080049}
50
51Scheduler::Scheduler()
52 : m_running(false)
53{
54 event_set_fatal_callback(errorCallback);
55 evthread_use_pthreads();
56 m_base = event_base_new();
Zhenkai Zhub16be8f2013-01-25 16:12:30 -080057
58 // This is a hack to prevent event_base_loop from exiting;
59 // the flag EVLOOP_NO_EXIT_ON_EMPTY is somehow ignored, at least on Mac OS X
60 // it's going to be scheduled to 10 years later
61 timeval tv;
62 tv.tv_sec = 365 * 24 * 3600;
63 tv.tv_usec = 0;
64 m_ev = evtimer_new(m_base, dummyCallback, 0);
65 int res = evtimer_add(m_ev, &tv);
66 if (res < 0)
67 {
68 _LOG_ERROR("heck");
69 }
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080070}
71
72Scheduler::~Scheduler()
73{
Alexander Afanasyevfc720362013-01-24 21:49:48 -080074 shutdown ();
Zhenkai Zhub16be8f2013-01-25 16:12:30 -080075 evtimer_del(m_ev);
76 event_free(m_ev);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080077 event_base_free(m_base);
78}
79
80void
81Scheduler::eventLoop()
82{
83 while(true)
84 {
85 if (event_base_loop(m_base, EVLOOP_NO_EXIT_ON_EMPTY) < 0)
86 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -080087 _LOG_DEBUG ("scheduler loop break error");
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080088 }
Zhenkai Zhub16be8f2013-01-25 16:12:30 -080089 else
90 {
91 _LOG_DEBUG ("scheduler loop break normal");
92 }
Alexander Afanasyevfc720362013-01-24 21:49:48 -080093
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -080094 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -080095 ScopedLock lock(m_mutex);
Alexander Afanasyev34edd4d2013-01-17 17:55:45 -080096 if (!m_running)
97 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -080098 _LOG_DEBUG ("scheduler loop break normal");
Alexander Afanasyev34edd4d2013-01-17 17:55:45 -080099 break;
100 }
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800101 }
102 }
103}
104
105void
106Scheduler::start()
107{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800108 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800109 if (!m_running)
110 {
111 m_thread = boost::thread(&Scheduler::eventLoop, this);
112 m_running = true;
113 }
114}
115
116void
117Scheduler::shutdown()
118{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800119 bool breakAndWait = false;
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800120 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800121 ScopedLock lock (m_mutex);
122 if (m_running)
123 {
124 m_running = false;
125 breakAndWait = true;
126 }
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800127 }
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800128
129 if (breakAndWait)
130 {
131 event_base_loopbreak(m_base);
132 m_thread.join();
133 }
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800134}
135
Alexander Afanasyeve41e7d22013-01-19 15:13:47 -0800136TaskPtr
137Scheduler::scheduleOneTimeTask (SchedulerPtr scheduler, double delay,
138 const Task::Callback &callback, const Task::Tag &tag)
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800139{
Alexander Afanasyeve41e7d22013-01-19 15:13:47 -0800140 TaskPtr task = make_shared<OneTimeTask> (callback, tag, scheduler, delay);
141 if (scheduler->addTask (task))
142 return task;
143 else
144 return TaskPtr ();
145}
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800146
Alexander Afanasyeve41e7d22013-01-19 15:13:47 -0800147TaskPtr
148Scheduler::schedulePeriodicTask (SchedulerPtr scheduler, IntervalGeneratorPtr delayGenerator,
149 const Task::Callback &callback, const Task::Tag &tag)
150{
151 TaskPtr task = make_shared<PeriodicTask> (callback, tag, scheduler, delayGenerator);
152
153 if (scheduler->addTask (task))
154 return task;
155 else
156 return TaskPtr ();
157}
158
159bool
160Scheduler::addTask(TaskPtr newTask)
161{
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800162 if (addToMap(newTask))
163 {
164 newTask->reset();
165 int res = evtimer_add(newTask->ev(), newTask->tv());
166 if (res < 0)
167 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800168 _LOG_ERROR ("evtimer_add failed for " << newTask->tag());
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800169 }
170 return true;
171 }
172 else
173 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800174 _LOG_ERROR ("fail to add task: " << newTask->tag());
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800175 }
176
177 return false;
178}
179
Alexander Afanasyevfb4c43f2013-01-18 17:43:25 -0800180void
Alexander Afanasyev997ba632013-01-18 17:40:23 -0800181Scheduler::deleteTask(TaskPtr task)
182{
183 deleteTask (task->tag ());
184}
185
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800186void
Zhenkai Zhu66ddb232013-01-18 17:53:52 -0800187Scheduler::rescheduleTask(const TaskPtr &task)
188{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800189 ScopedLock lock(m_mutex);
Zhenkai Zhud2ca3922013-01-18 17:58:48 -0800190 TaskMapIt it = m_taskMap.find(task->tag());
Zhenkai Zhu66ddb232013-01-18 17:53:52 -0800191 if (it != m_taskMap.end())
192 {
193 TaskPtr task = it->second;
194 task->reset();
195 int res = evtimer_add(task->ev(), task->tv());
196 if (res < 0)
197 {
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800198 _LOG_ERROR ("evtimer_add failed for " << task->tag());
Zhenkai Zhu66ddb232013-01-18 17:53:52 -0800199 }
200 }
201 else
202 {
203 addTask(task);
204 }
205}
206
207void
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800208Scheduler::rescheduleTask(const Task::Tag &tag)
209{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800210 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800211 TaskMapIt it = m_taskMap.find(tag);
212 if (it != m_taskMap.end())
213 {
214 TaskPtr task = it->second;
215 task->reset();
216 int res = evtimer_add(task->ev(), task->tv());
217 if (res < 0)
218 {
219 cout << "evtimer_add failed for " << task->tag() << endl;
220 }
221 }
222}
223
224bool
225Scheduler::addToMap(const TaskPtr &task)
226{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800227 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800228 if (m_taskMap.find(task->tag()) == m_taskMap.end())
229 {
230 m_taskMap.insert(make_pair(task->tag(), task));
231 return true;
232 }
233 return false;
234}
235
236void
237Scheduler::deleteTask(const Task::Tag &tag)
238{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800239 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800240 TaskMapIt it = m_taskMap.find(tag);
241 if (it != m_taskMap.end())
242 {
243 TaskPtr task = it->second;
244 evtimer_del(task->ev());
245 m_taskMap.erase(it);
246 }
247}
248
249void
250Scheduler::deleteTask(const Task::TaskMatcher &matcher)
251{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800252 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800253 TaskMapIt it = m_taskMap.begin();
254 while(it != m_taskMap.end())
255 {
256 TaskPtr task = it->second;
257 if (matcher(task))
258 {
259 evtimer_del(task->ev());
260 // Use post increment; map.erase invalidate the iterator that is beening erased,
261 // but does not invalidate other iterators. This seems to be the convention to
262 // erase something from C++ STL map while traversing.
263 m_taskMap.erase(it++);
264 }
265 else
266 {
267 ++it;
268 }
269 }
270}
271
272int
273Scheduler::size()
274{
Alexander Afanasyevfc720362013-01-24 21:49:48 -0800275 ScopedLock lock(m_mutex);
Alexander Afanasyev1b0e0082013-01-17 16:48:26 -0800276 return m_taskMap.size();
277}