Fastdfs Source Analysis ---- timer design

fastdfs timer, design methods are: time round. Is a group of similar timeout timer, be mounted on top of a linked list. Multiple sets of chain mapping in the hash table top

According to our tradition, directly on the source code yourself down to read. (Already after detailed notes)

#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;
}



Here to explain the tradition: too many to write blog recently, I not want to go with drawing software easel composition. Also unfinished architecture diagram, or to return to the source code to understand.
So Recently code, mainly detailed notes. Did not put the design model diagram, communication between programmers, in fact, the code is enough to convey the information. Behind time, slowly add about architecture diagram, different levels of programmers the ability to accept different.

Published 77 original articles · won praise 71 · views 20000 +

Guess you like

Origin blog.csdn.net/jacky128256/article/details/104495054