Published on

skynet定时器

Authors
  • avatar
    Name
    Ushen
    Twitter

skynet的定时器,timer线程会不断更新当前所有的定时器,时间到了将消息推送到队列

static void *
thread_timer(void *p) {
    struct monitor * m = p;
    skynet_initthread(THREAD_TIMER);
    for (;;) {
    skynet_updatetime();
    skynet_socket_updatetime();
    CHECK_ABORT
    wakeup(m,m->count-1);
    usleep(2500);
    if (SIG) {
    signal_hup();
    SIG = 0;
            }
        }
    // wakeup socket thread
    skynet_socket_exit();
    // wakeup all worker thread
    pthread_mutex_lock(&m->mutex);
    m->quit = 1;
    pthread_cond_broadcast(&m->cond);
    pthread_mutex_unlock(&m->mutex);
    return NULL;
}

先看timer的结构体和定义,这里的starttime和current是用clock_gettime获取的

函数"clock_gettime"是基于Linux C语言的时间函数,他可以用于计算精度和纳秒

extern int clock_gettime (clockid_t __clock_id, structtimespec *__tp) __THROW;

struct timespec
{
 __time_t tv_sec;            /* Seconds.  */
 __syscall_s long_t tv_nsec;  /* Nanoseconds.  */
};

__clock_id : 时钟类型,posix标准定义了下面的四种基本类型,Linux系统有其他的扩充
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变。
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间。需要注意是不是进程开始到当前代码的时间。
CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间。需要注意是不是线程开始到当前代码的时间。

这里用的参数是CLOCK_REALTIME,得到秒和纳秒

starttime是秒,current会被除10000000,所以是1/100秒

current_point是用参数CLOCK_MONOTONIC获取,这里的单位也是1/100秒

两个链表near 和 t

near是放时间较近的一些定时器。

t则是放其他的一些定时器,这里的一维长度4,代表着等级,根据时间长短分为四个等级

#define TIME_NEAR_SHIFT 8
#define TIME_NEAR (1 << TIME_NEAR_SHIFT)
#define TIME_LEVEL_SHIFT 6
#define TIME_LEVEL (1 << TIME_LEVEL_SHIFT)
#define TIME_NEAR_MASK (TIME_NEAR-1)
#define TIME_LEVEL_MASK (TIME_LEVEL-1)

struct timer_event {
 uint32_t handle;
 int session;
};

struct timer_node {
 struct timer_node *next;
 uint32_t expire;
};

struct link_list {
 struct timer_node head;
 struct timer_node *tail;
};

struct timer {
 struct link_list near[TIME_NEAR];
 struct link_list t[4][TIME_LEVEL];
 struct spinlock lock;
 uint32_t time;
 uint32_t starttime;
 uint64_t current;
 uint64_t current_point;
};

static struct timer * TI = NULL;

timer线程每次首先会调用skynet_updatetime这个方法,通过gettime获取系统开启到当前的时间cp

然后通过current_ponint算出这次刷新到上次刷新的一个插值diff。

然后执行diff次timer_update方法

static uint64_t
gettime() {
    uint64_t t;
    struct timespec ti;
    clock_gettime(CLOCK_MONOTONIC, &ti);
    t = (uint64_t)ti.tv_sec * 100;
    t += ti.tv_nsec / 10000000;
    return t;
}

void
skynet_updatetime(void) {
    uint64_t cp = gettime();
    if(cp < TI->current_point) {
        skynet_error(NULL, "time diff error: change from %lld to %lld", cp, TI->current_point);
        TI->current_point = cp;
    } else if (cp != TI->current_point) {
        uint32_t diff = (uint32_t)(cp - TI->current_point);
        TI->current_point = cp;
        TI->current += diff;
        int i;
        for (i=0;i<diff;i++) {
            timer_update(TI);
        }
    }
}

先调用timer_execute看看有没有可以处理的定时器消息,然后用timer_shift偏移,然后再次查看又没有可以处理的消息。

再timer_shift中,每次会++T->time,代表shift的次数。

static void 
timer_update(struct timer *T) {
 SPIN_LOCK(T);

 // try to dispatch timeout 0 (rare condition)
 timer_execute(T);

 // shift time first, and then dispatch timer message
 timer_shift(T);

 timer_execute(T);

 SPIN_UNLOCK(T);
}

static inline void
timer_execute(struct timer *T) {
    int idx = T->time & TIME_NEAR_MASK;
    
    while (T->near[idx].head.next) {
        struct timer_node *current = link_clear(&T->near[idx]);
        SPIN_UNLOCK(T);
        // dispatch_list don't need lock T
        dispatch_list(current);
        SPIN_LOCK(T);
    }
}

static void
timer_shift(struct timer *T) {
    int mask = TIME_NEAR;
    uint32_t ct = ++T->time;
    if (ct == 0) {
        move_list(T, 3, 0);
    } else {
        uint32_t time = ct >> TIME_NEAR_SHIFT;
        int i=0;

        while ((ct & (mask-1))==0) {
            int idx=time & TIME_LEVEL_MASK;
            if (idx!=0) {
                move_list(T, i, idx);
                break;              
            }
            mask <<= TIME_LEVEL_SHIFT;
            time >>= TIME_LEVEL_SHIFT;
            ++i;
        }
    }
}