ceph Timer源码分析
ceph定时器主要用来实现某些定时任务,比如osd之间的心跳,monitor之间的心跳等.
源文件:
- src/common/timer.h
- src/common/timer.cc
- src/include/Context.h
定时器事件或者任务
ceph中的事件都继承自Context类,并且实现自己的事件处理方法finish().例如基于定时器的心跳机制就是在finish中每隔一段时间发送一个ping请求.
class Context {
Context(const Context& other);
const Context& operator=(const Context& other);
protected:
// finishe: 事件的处理方法
virtual void finish(int r) = 0;
public:
Context() {}
virtual ~Context() {} // we want a virtual destructor!!!
virtual void complete(int r) {
finish(r);
delete this;
}
};
定时器线程
定时器线程用于执行定时器任务,具体的任务处理方法由SafeTimer类的timer_thread()定义.
class SafeTimerThread : public Thread {
SafeTimer *parent;
public:
explicit SafeTimerThread(SafeTimer *s) : parent(s) {}
void *entry() {
parent->timer_thread();
return NULL;
}
};
定时器
定时器由定时器线程和定时器任务组成,由定时器线程周期的处理定时器任务.
class SafeTimer:
主要数据成员:
CephContext *cct
Mutex &lock
Cond cond
bool safe_callbacks
SafeTimerThread *thread 定时器线程
// schedule和events都表示定时器任务集,
// 前者采取时间到事件的映射方式,主要用于定时器线程按时间执行定时器任务.
// 后都采用事件到迭代器的映射方法,主要用于主线程按事件名取消事件.
multimap<utime_t, Context*> schedule
map<Context*, multimap<utime_t, Context *>::iterator> events
bool stopping 定时器线程的终止状态标志
主要成员函数:
init():
thread = new SafeTimerThread(this) 新建定时器线程
thread->create("safe_timer") 启动定时器线程
timer_thread(): 定时器线程的执行函数
确定定时器线程处于执行状态,即stopping为false.
当事件集schedule不为空时,循环检查事件是否期执行.
事件在schedule中是按照时间升序排列的.
如果第一个事件没有到时间,后面的事件就不用检查了,直接终止循环,然后等待.
如果第一个事件到定时时间,就调用它的finish()进行处理
处理完当前事件后,等待直到下一个事件的时间.
shutdown():
取消全部定时器事件
销毁定时器线程thread
add_event_after(double seconds, Context *callback):
基于相对时间when增加定时器事件
add_event_at(utime_t when, Context *callback):
基于绝对时间when增加定时器事件
cancel_event(Context *callback):
根据事件名取消定时器事件
cancel_all_event():
取消全部定时器事件
question:
定时器线程一个时刻只能处理一个事件,那如果有两个事件时间相同,怎么处理?
理论上一个线程不可能同时执行两个事件,问题的关键在于timer_thread()函数的在执行:定时器事件是按执行时间排序的,因此定时器线程执行事件的过程是,执行一个事件,等待一段时间,执行下一个事件…因此如果两个事件的执行时间相同,那么定时器线程不等待直接执行该事件,由于每个事件的执行时间很短,因此可以近似认为两个事件是同时执行的.这里其实用到了一个小技巧:pthread_cond_timedwait(cond, mutex, time),time是绝对时间,如果time值大于当前时间now,那么线程等待now-time,但如果time小于等于now,那么线程不等待直接执行.