fastdfs的定时器,设计方法为:时间轮。就是一组超时时间相似的定时器,挂载一条链表上面进行。多组链表映射在哈希表上面

按照我们的传统,直接上源码,自己拉下去读。(已经经过详细的注释)

#ifndef __FAST_TIMER_H__
#define __FAST_TIMER_H__
#include <stdint.h>
#include "common_define.h"

/* fastdfs的定时器设计模型为:

FastTimer->slots[slot_count]
槽 [0] [1] [2] [3] [slot_count]

定时器 FastTimerEntry0 FastTimerEntry2 FastTimerEntry1 FastTimerEntry6
FastTimerEntry3 FastTimerEntry4
FastTimerEntry5
*/

// 定时器节点
typedef struct fast_timer_entry {
int64_t expires; // 定时器到期时间
void * data; // 存储定时超时后,回调函数的参数
struct fast_timer_entry * prev; // 链表环
struct fast_timer_entry * next;
bool rehash; //定时器定时时间发生变化后,定时器节点需要重新寻找槽位(换槽)进行挂载
} FastTimerEntry;


typedef struct fast_timer_slot {
struct fast_timer_entry head;
} FastTimerSlot;


// 定时器管理器
typedef struct fast_timer {
int slot_count; // time wheel slot count
int64_t base_time; // base time for slot 0
int64_t current_time; /* 创建(或更新)定时器管理器时的时间戳。
当前时间 < current_time值,则没有任何节点超时 */
FastTimerSlot * slots; /* 定时器数槽位,每个槽下面挂一条定时器链表((expires-base_time)%slot_count相等的
定时器),链表每个节点是一个定时器*/
} FastTimer;


#ifdef __cplusplus
extern "C"
{
#endif

int fast_timer_init(FastTimer * timer, const int slot_count,
const int64_t current_time);
void fast_timer_destroy(FastTimer * timer);

int fast_timer_add(FastTimer * timer, FastTimerEntry * entry);
int fast_timer_remove(FastTimer * timer, FastTimerEntry * entry);
int fast_timer_modify(FastTimer * timer, FastTimerEntry * entry,
const int64_t new_expires);

FastTimerSlot * fast_timer_slot_get(FastTimer * timer, const int64_t current_time);
int fast_timer_timeouts_get(FastTimer * timer, const int64_t current_time,
FastTimerEntry * head);

#ifdef __cplusplus
}


#endif

#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "logger.h"
#include "fast_timer.h"


/* 创建slot_count个定时器,挂在timer下面进行管理 */
int fast_timer_init(FastTimer * timer, const int slot_count,
const int64_t current_time)
{
int bytes;

if (slot_count <= 0 || current_time <= 0) {
return EINVAL;
}

timer->slot_count = slot_count;
timer->base_time = current_time; //base time for slot 0
timer->current_time = current_time;
bytes = sizeof(FastTimerSlot) *slot_count;
timer->slots = (FastTimerSlot *)malloc(bytes);

if (timer->slots == NULL) {
return errno != 0 ? errno: ENOMEM;
}

memset(timer->slots, 0, bytes);
return 0;
}


/* 销毁定时器 */
void fast_timer_destroy(FastTimer * timer)
{
if (timer->slots != NULL) {
free(timer->slots);
timer->slots = NULL;
}
}


// timer是expires定时器管理器。获取定时器管理器中,超时时间为expires的定时器所挂载的槽位索引
#define TIMER_GET_SLOT_INDEX(timer, expires) \
(((expires) - timer->base_time) % timer->slot_count)

// 获取定时器链表首地址
#define TIMER_GET_SLOT_POINTER(timer, expires) \
(timer->slots + TIMER_GET_SLOT_INDEX(timer, expires))


// 定时器节点,映射到定时器管理某个槽位上面,头插到该槽位下面悬挂的链表。新加入的定时器节点是准确映射到槽位的
int fast_timer_add(FastTimer * timer, FastTimerEntry * entry)
{
FastTimerSlot * slot;

slot = TIMER_GET_SLOT_POINTER(timer, entry->expires >
timer->current_time ? entry->expires: timer->current_time);
entry->next = slot->head.next;

if (slot->head.next != NULL) {
slot->head.next->prev = entry;
}

entry->prev = &slot->head;
slot->head.next = entry;
entry->rehash = false;
return 0;
}


/*
修改定时器节点的定时时间。如果新设定的超时时间与旧的超时时间相对,则不做任何处理。
如果是减小定时器节点时间,则从槽位移除目标的定时器,修改超时时间后重新添加到定时器管理器
如果是增加定时器节点时间,则只修改定时器节点的超时值,并标识是否需要重新映射到槽位(由其它函数在合适的时间对整个管理器下面节点统一重映射)
*/
int fast_timer_modify(FastTimer * timer, FastTimerEntry * entry,
const int64_t new_expires)
{
if (new_expires == entry->expires) {
return 0;
}

if (new_expires < entry->expires) {
fast_timer_remove(timer, entry);
entry->expires = new_expires;
return fast_timer_add(timer, entry);
}

entry->rehash = TIMER_GET_SLOT_INDEX(timer, new_expires) != TIMER_GET_SLOT_INDEX(timer, entry->expires);
entry->expires = new_expires; //lazy move
return 0;
}


// 将定时器从所在链表取下
int fast_timer_remove(FastTimer * timer, FastTimerEntry * entry)
{
if (entry->prev == NULL) {
return ENOENT; //already removed
}

if (entry->next != NULL) {
entry->next->prev = entry->prev;
entry->prev->next = entry->next;
entry->next = NULL;
}
else {
entry->prev->next = NULL;
}

entry->prev = NULL;
return 0;
}


//
FastTimerSlot * fast_timer_slot_get(FastTimer * timer, const int64_t current_time)
{
if (timer->current_time >= current_time) {
return NULL;
}

return TIMER_GET_SLOT_POINTER(timer, timer->current_time++);
}


/*
获取超时的(超时时间 < 参数指定的当前实时时间)定时器节点,从原槽位链表取下,插入head开头的链表中(有可能是多条槽位节点全部超时,取决于
current_time的值)。
并更新定时器管理节点的参照值timer->current_time
*/
int fast_timer_timeouts_get(FastTimer * timer, const int64_t current_time,
FastTimerEntry * head)
{
FastTimerSlot * slot;
FastTimerEntry * entry;
FastTimerEntry * first;
FastTimerEntry * last;
FastTimerEntry * tail;
int count;

head->prev = NULL;
head->next = NULL;

// 定时器管理器尚未启动,返回
if (timer->current_time >= current_time) {
return 0;
}

first = NULL;
last = NULL;
tail = head;
count = 0;

while (timer->current_time < current_time) {
slot = TIMER_GET_SLOT_POINTER(timer, timer->current_time++);
entry = slot->head.next;

while (entry != NULL) {
if (entry->expires >= current_time) { //not expired

if (first != NULL) {
first->prev->next = entry;
entry->prev = first->prev;

tail->next = first;
first->prev = tail;
tail = last;
first = NULL;
}

if (entry->rehash) {
last = entry;
entry = entry->next;

last->rehash = false;
fast_timer_remove(timer, last);
fast_timer_add(timer, last);
continue;
}
}
else { //expired
count++;

if (first == NULL) {
first = entry;
}
}

last = entry;
entry = entry->next;
}

if (first != NULL) {
first->prev->next = NULL;

tail->next = first;
first->prev = tail;
tail = last;
first = NULL;
}
}

if (count > 0) {
tail->next = NULL;
}

return count;
}

这里解释一下传统:最近要写的博客太多,不太想去用画图软件画架构图。 而且画完架构图,还是要回归源码才能看懂。
所以近段时间的代码,主要是注释详细。没有放设计模型图,程序员之间交流,代码其实是足够表达信息的。 后面有时间了,慢慢加一下架构图,不同层级的程序员接受能力不一样。