blob: 4eb6d6ca4dcd7a93b87826debed524ff944d1fe8 [file] [log] [blame]
Zhenkai Zhu97019eb2013-01-08 00:21:43 -08001#ifndef EVENT_SCHEDULER_H
2#define EVENT_SCHEDULER_H
Zhenkai Zhubc2f6282013-01-08 16:40:58 -08003
4// use pthread
5
Zhenkai Zhu97019eb2013-01-08 00:21:43 -08006#include <event2/event.h>
Zhenkai Zhu97019eb2013-01-08 00:21:43 -08007#include <event2/thread.h>
Zhenkai Zhuf8e81e02013-01-15 16:02:47 -08008#include <event2/event-config.h>
9#include <event2/util.h>
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080010
11#include <boost/function.hpp>
12#include <boost/shared_ptr.hpp>
13#include <boost/random/mersenne_twister.hpp>
14#include <boost/random/uniform_real.hpp>
15#include <boost/random/variate_generator.hpp>
16#include <boost/exception/all.hpp>
Zhenkai Zhubc2f6282013-01-08 16:40:58 -080017#include <boost/thread/shared_mutex.hpp>
18#include <boost/thread/thread.hpp>
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080019#include <math.h>
Zhenkai Zhubc2f6282013-01-08 16:40:58 -080020#include <map>
21#include <sys/time.h>
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080022
23#define _OVERRIDE
24#ifdef __GNUC__
25#if __GNUC_MAJOR >= 4 && __GNUC_MINOR__ >= 7
26 #undef _OVERRIDE
27 #define _OVERRIDE override
28#endif // __GNUC__ version
29#endif // __GNUC__
30
31using namespace std;
32
Zhenkai Zhu4eabef12013-01-08 20:29:52 -080033// callback used by libevent
Zhenkai Zhubc2f6282013-01-08 16:40:58 -080034static void
35eventCallback(evutil_socket_t fd, short what, void *arg);
36
37class Scheduler;
38typedef boost::shared_ptr<Scheduler> SchedulerPtr;
39class IntervalGenerator;
40typedef boost::shared_ptr<IntervalGenerator> IntervalGeneratorPtr;
41class Task;
42typedef boost::shared_ptr<Task> TaskPtr;
43
44class IntervalGenerator
45{
46public:
47 virtual double
48 nextInterval() = 0;
49 static IntervalGeneratorPtr Null;
50};
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080051
Zhenkai Zhu4eabef12013-01-08 20:29:52 -080052
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080053class Task
54{
55public:
Zhenkai Zhu4eabef12013-01-08 20:29:52 -080056 // callback of this task
Zhenkai Zhubc2f6282013-01-08 16:40:58 -080057 typedef boost::function<void ()> Callback;
Zhenkai Zhu4eabef12013-01-08 20:29:52 -080058 // tag identifies this task, should be unique
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080059 typedef string Tag;
Zhenkai Zhu4eabef12013-01-08 20:29:52 -080060 // used to match tasks
Zhenkai Zhubc2f6282013-01-08 16:40:58 -080061 typedef boost::function<bool (const TaskPtr &task)> TaskMatcher;
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080062
Zhenkai Zhu4eabef12013-01-08 20:29:52 -080063
64 // Task is associated with Schedulers due to the requirement that libevent event is associated with an libevent event_base
Zhenkai Zhu66dc5a92013-01-08 21:41:15 -080065 Task(const Callback &callback, const Tag &tag, const SchedulerPtr &scheduler);
66 virtual ~Task();
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080067
68 virtual void
Zhenkai Zhu66dc5a92013-01-08 21:41:15 -080069 run() = 0;
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080070
71 Tag
72 tag() { return m_tag; }
73
Zhenkai Zhubc2f6282013-01-08 16:40:58 -080074 event *
75 ev() { return m_event; }
76
77 timeval *
78 tv() { return m_tv; }
79
Zhenkai Zhu66dc5a92013-01-08 21:41:15 -080080 // Task needs to be resetted after the callback is invoked if it is to be schedule again; just for safety
81 // it's called by scheduler automatically when addTask or rescheduleTask is called;
82 // Tasks should do preparation work here (e.g. set up new delay, etc. )
83 virtual void
84 reset() = 0;
85
86 // set delay
87 // This overrides whatever delay kept in m_tv
Zhenkai Zhubc2f6282013-01-08 16:40:58 -080088 void
89 setTv(double delay);
90
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080091protected:
92 Callback m_callback;
93 Tag m_tag;
Zhenkai Zhubc2f6282013-01-08 16:40:58 -080094 SchedulerPtr m_scheduler;
95 bool m_invoked;
96 event *m_event;
97 timeval *m_tv;
Zhenkai Zhu97019eb2013-01-08 00:21:43 -080098};
99
Zhenkai Zhu66dc5a92013-01-08 21:41:15 -0800100class OneTimeTask : public Task
101{
102public:
103 OneTimeTask(const Callback &callback, const Tag &tag, const SchedulerPtr &scheduler, double delay);
104 virtual ~OneTimeTask(){}
105
106 // invoke callback and mark self as invoked and deregister self from scheduler
107 virtual void
108 run() _OVERRIDE;
109
110 // after reset, the task is marked as un-invoked and can be add to scheduler again, with same delay
111 // if not invoked yet, no effect
112 virtual void
113 reset() _OVERRIDE;
114
115private:
116 // this is to deregister itself from scheduler automatically after invoke
117 void
118 deregisterSelf();
119};
120
121class PeriodicTask : public Task
122{
123public:
124 // generator is needed only when this is a periodic task
125 // two simple generators implementation (SimpleIntervalGenerator and RandomIntervalGenerator) are provided;
126 // if user needs more complex pattern in the intervals between calls, extend class IntervalGenerator
Zhenkai Zhu06b8e952013-01-15 12:21:15 -0800127 PeriodicTask(const Callback &callback, const Tag &tag, const SchedulerPtr &scheduler, const IntervalGeneratorPtr &generator);
Zhenkai Zhu66dc5a92013-01-08 21:41:15 -0800128 virtual ~PeriodicTask(){}
129
130 // invoke callback, reset self and ask scheduler to schedule self with the next delay interval
131 virtual void
132 run() _OVERRIDE;
133
134 // set the next delay and mark as un-invoke
135 virtual void
136 reset() _OVERRIDE;
137
138private:
139 IntervalGeneratorPtr m_generator;
140};
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800141
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800142struct SchedulerException : virtual boost::exception, virtual exception { };
143
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800144class Scheduler
145{
146public:
147 Scheduler();
148 virtual ~Scheduler();
149
Zhenkai Zhu4eabef12013-01-08 20:29:52 -0800150 // start event scheduling
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800151 virtual void
152 start();
153
Zhenkai Zhu4eabef12013-01-08 20:29:52 -0800154 // stop event scheduling
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800155 virtual void
156 shutdown();
157
Zhenkai Zhu66dc5a92013-01-08 21:41:15 -0800158 // if task with the same tag exists, the task is not added and return false
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800159 virtual bool
160 addTask(const TaskPtr &task);
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800161
Zhenkai Zhu4eabef12013-01-08 20:29:52 -0800162 // delete task by tag, regardless of whether it's invoked or not
163 // if no task is found, no effect
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800164 virtual void
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800165 deleteTask(const Task::Tag &tag);
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800166
Zhenkai Zhu4eabef12013-01-08 20:29:52 -0800167 // delete tasks by matcher, regardless of whether it's invoked or not
168 // this is flexiable in that you can use any form of criteria in finding tasks to delete
169 // but keep in mind this is a linear scan
170
171 // if no task is found, no effect
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800172 virtual void
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800173 deleteTask(const Task::TaskMatcher &matcher);
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800174
Zhenkai Zhu4eabef12013-01-08 20:29:52 -0800175 // task must already have been added to the scheduler, otherwise this is no effect
Zhenkai Zhu66dc5a92013-01-08 21:41:15 -0800176 // this is usually used by PeriodicTask
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800177 virtual void
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800178 rescheduleTask(const Task::Tag &tag);
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800179
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800180 void
181 eventLoop();
182
183 event_base *
184 base() { return m_base; }
185
186 // used in test
187 int
188 size();
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800189
190protected:
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800191 bool
192 addToMap(const TaskPtr &task);
193
194protected:
195 typedef map<Task::Tag, TaskPtr> TaskMap;
196 typedef map<Task::Tag, TaskPtr>::iterator TaskMapIt;
197 typedef boost::shared_mutex Mutex;
198 typedef boost::unique_lock<Mutex> WriteLock;
199 typedef boost::shared_lock<Mutex> ReadLock;
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800200 TaskMap m_taskMap;
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800201 Mutex m_mutex;
Zhenkai Zhuf8e81e02013-01-15 16:02:47 -0800202 bool m_running;
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800203 event_base *m_base;
204 boost::thread m_thread;
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800205};
206
Zhenkai Zhu4eabef12013-01-08 20:29:52 -0800207
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800208class SimpleIntervalGenerator : public IntervalGenerator
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800209{
210public:
211 SimpleIntervalGenerator(double interval) : m_interval(interval) {}
212 ~SimpleIntervalGenerator(){}
213 virtual double
214 nextInterval() _OVERRIDE { return m_interval; }
215private:
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800216 double m_interval;
217};
218
Zhenkai Zhu4eabef12013-01-08 20:29:52 -0800219// generates intervals with uniform distribution
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800220class RandomIntervalGenerator : public IntervalGenerator
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800221{
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800222public:
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800223 typedef enum
224 {
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800225 UP = 1,
226 DOWN = 2,
227 EVEN = 3
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800228 } Direction;
229
230public:
Zhenkai Zhu4eabef12013-01-08 20:29:52 -0800231 // percent is random-range/interval; e.g. if interval is 10 and you wish the random-range to be 2
232 // e.g. 9 ~ 11, percent = 0.2
233 // direction shifts the random range; e.g. in the above example, UP would produce a range of
234 // 10 ~ 12, DOWN of 8 ~ 10, and EVEN of 9 ~ 11
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800235 RandomIntervalGenerator(double interval, double percent, Direction direction = EVEN);
236 ~RandomIntervalGenerator(){}
237 virtual double
238 nextInterval() _OVERRIDE;
239
240private:
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800241 inline double fractional(double x) { double dummy; return abs(modf(x, &dummy)); }
242
243private:
244 typedef boost::mt19937 RNG_TYPE;
245 RNG_TYPE m_rng;
246 boost::uniform_real<> m_dist;
Zhenkai Zhubc2f6282013-01-08 16:40:58 -0800247 boost::variate_generator<RNG_TYPE &, boost::uniform_real<> > m_random;
Zhenkai Zhu97019eb2013-01-08 00:21:43 -0800248 Direction m_direction;
249 double m_interval;
250 double m_percent;
251
252};
253#endif // EVENT_SCHEDULER_H