Swoole源碼學習記錄(十五)——Timer模塊分析

swoole版本:1.7.7-stable Github地址:點此查看


1.Timer

1.1.swTimer_interval_node

聲明:

// swoole.h 1045-1050h
typedef struct _swTimer_interval_node
{
    struct _swTimerList_node *next, *prev;
    struct timeval lasttime;
    uint32_t interval;
} swTimer_interval_node;
成員 說明
next,prev 鏈表的後繼、前驅指針
struct timeval lasttime 持續時間
uint32_t interval 間隔時間

說明:
swTimer_interval_node結構體是一個鏈表節點,存放一個固定間隔的定時器,其中lasttime爲當前定時器從上一次執行到現在經過的時間,interval存放了定時器間隔。
該結構體用於swoole原本的timer相關操作。

1.2.swTimer_node

聲明:

// swoole.h 1052-1058h
typedef struct _swTimer_node
{
    struct _swTimer_node *next, *prev;
    void *data;
    uint32_t exec_msec;
    uint32_t interval;
} swTimer_node;
成員 說明
next,prev 鏈表的後繼、前驅指針
void *data 數據域,存放額外的變量
uint32_t exec_msec 定時器應當執行的時間
uint32_t interval 間隔時間(無用,應廢棄)

說明:
swTimer_node結構體是一個鏈表節點,存放一個需要在指定時間執行的定時器,其中exec_msec爲當前定時器需要執行的指定時間,interval存放了定時器間隔。
該結構體用於swoole的after函數操作。

1.3.swTimer

聲明:

// swoole.h 1060-1081h
typedef struct _swTimer
{
    swTimer_node *root;
    /*--------------timerfd & signal timer--------------*/
    swHashMap *list;
    int num;
    int interval;
    int use_pipe;
    int lasttime;
    int fd;
    swPipe pipe;
    /*-----------------for EventTimer-------------------*/
    struct timeval basetime;
    /*--------------------------------------------------*/
    int (*add)(struct _swTimer *timer, int _msec, int _interval, void *data);
    int (*del)(struct _swTimer *timer, int _interval_ms);
    int (*select)(struct _swTimer *timer);
    void (*free)(struct _swTimer *timer);
    /*-----------------event callback-------------------*/
    void (*onTimer)(struct _swTimer *timer, int interval_msec);
    void (*onTimeout)(struct _swTimer *timer, void *data);
} swTimer;
成員 說明
swTimer_node *root after的鏈表根節點
swHashMap *list timer的鏈表根節點
int num 當前定時器的數量
int interval 定時器的基礎響應間隔
int use_pipe 是否使用管道通信
int lasttime 持續時間(已廢棄)
int fd 管道的寫fd
swPipe pipe 管道
struct timeval basetime EventTimer的基礎時間

說明:
swTimer結構體定時器的實體對象,用於存儲、管理和執行衆多定時任務,包括timer和after兩種不同類型的定時任務。

1.4.swTimer公共操作函數

1.4.1.swTimer_init

聲明:

// swoole.h 1083
int swTimer_init(int interval_ms, int no_pipe);

功能:初始化一個swTimer對象
核心源碼:

    // timer.c 38-94h
    swTimer *timer = &SwooleG.timer;
    timer->interval = interval;
    timer->lasttime = interval;

#ifndef HAVE_TIMERFD
    SwooleG.use_timerfd = 0;
#endif

    timer->list = swHashMap_new(SW_HASHMAP_INIT_BUCKET_N, free);
    if (!timer->list)
    {
        return SW_ERR;
    }

    if (SwooleG.use_timerfd)
    {
        if (swTimer_timerfd_set(timer, interval) < 0)
        {
            return SW_ERR;
        }
        timer->use_pipe = 0;
    }
    else
    {
        if (use_pipe)
        {
            if (swPipeNotify_auto(&timer->pipe, 0, 0) < 0)
            {
                return SW_ERR;
            }
            timer->fd = timer->pipe.getFd(&timer->pipe, 0);
            timer->use_pipe = 1;
        }
        else
        {
            timer->fd = 1;
            timer->use_pipe = 0;
        }

        if (swTimer_signal_set(timer, interval) < 0)
        {
            return SW_ERR;
        }
        swSignal_add(SIGALRM, swTimer_signal_handler);
    }

    if (timer->fd > 1)
    {
        SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_TIMER, swTimer_event_handler);
        SwooleG.main_reactor->add(SwooleG.main_reactor, SwooleG.timer.fd, SW_FD_TIMER);
    }

    timer->add = swTimer_add;
    timer->del = swTimer_del;
    timer->select = swTimer_select;
    timer->free = swTimer_free;

源碼解釋:
獲取SwooleG中的timer對象,設置timer響應間隔和lasttime參數。如果沒有定義HAVE_TIMERFD,則設置不使用timerfd。隨後,使用HashMap初始化timer鏈表list。
如果使用了timerfd,調用swTimer_timerfd_set函數設置timer的基礎響應間隔,並設置不使用管道。
如果不使用timerfd而使用signalfd,則先判定是否需要管道,如果需要,則創建管道並獲取管道的寫fd。隨後,調用swTimer_signal_set函數設置Linux系統提供的精確定時器,並通過swSignal_add添加對SIGALRM信號的處理回調函數swTimer_signal_handler
接着,將管道寫fd加入main_reactor的監聽中,並設置回調函數swTimer_event_handler
最後設置swTimer的四個回調操作函數。

1.4.2.swTimer_signal_handler

聲明:

// swoole.h 1085
void swTimer_signal_handler(int sig);

功能:SIGALRM信號的回調處理函數
核心源碼:

    // timer.c 338-344h
    SwooleG.signal_alarm = 1;
    uint64_t flag = 1;

    if (SwooleG.timer.use_pipe)
    {
        SwooleG.timer.pipe.write(&SwooleG.timer.pipe, &flag, sizeof(flag));
    }

源碼解釋:
設置SwooleG的signal_alarm標記爲true,如果設定使用了管道,則通過管道發送一個flag通知Timer。

1.4.3.swTimer_event_handler

聲明:

// swoole.h 1086
int swTimer_event_handler(swReactor *reactor, swEvent *event);

功能:timer的事件處理回調函數
核心源碼:

    // timer.c 323-334h
    uint64_t exp;
    swTimer *timer = &SwooleG.timer;

    if (read(timer->fd, &exp, sizeof(uint64_t)) < 0)
    {
        return SW_ERR;
    }
    SwooleG.signal_alarm = 0;
    return swTimer_select(timer);

源碼解釋:
嘗試從管道中讀取數據,如果讀取成功,則重置SwooleG的signal_alarm標記,並調用swTimer_select來處理定時任務;

1.4.4.其他函數

swTimer_node_insertswTimer_node_printswTimer_node_deleteswTimer_node_destory四個函數是鏈表操作函數,不再詳細分析。

1.5.Timer私有操作函數

1.5.1.swTimer_signal_set

聲明:

// timer.c 24h
static int swTimer_signal_set(swTimer *timer, int interval);

功能:調用系統settimer函數啓動定時器
核心源碼:

    struct itimerval timer_set;
    int sec = interval / 1000;
    int msec = (((float) interval / 1000) - sec) * 1000;

    struct timeval now;
    if (gettimeofday(&now, NULL) < 0)
    {
        swWarn("gettimeofday() failed. Error: %s[%d]", strerror(errno), errno);
        return SW_ERR;
    }

    memset(&timer_set, 0, sizeof(timer_set));
    timer_set.it_interval.tv_sec = sec;
    timer_set.it_interval.tv_usec = msec * 1000;

    timer_set.it_value.tv_sec = sec;
    timer_set.it_value.tv_usec = timer_set.it_interval.tv_usec;

    if (timer_set.it_value.tv_usec > 1e6)
    {
        timer_set.it_value.tv_usec = timer_set.it_value.tv_usec - 1e6;
        timer_set.it_value.tv_sec += 1;
    }

    if (setitimer(ITIMER_REAL, &timer_set, NULL) < 0)
    {
        swWarn("setitimer() failed. Error: %s[%d]", strerror(errno), errno);
        return SW_ERR;
    }

源碼解釋:
首先將interval拆分成秒和毫秒,並將兩個值添加進timer_set,隨後調用setitimer函數設置系統定時器。

1.5.2.swTimer_timerfd_set

聲明:

// timer.c 25h
static int swTimer_timerfd_set(swTimer *timer, int interval);

功能:調用timerfd相關函數啓動timerfd定時器
核心源碼:

    // timer.c 100h
    if (timer->fd == 0)
    {
        timer->fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
        if (timer->fd < 0)
        {
            swWarn("timerfd_create() failed. Error: %s[%d]", strerror(errno), errno);
            return SW_ERR;
        }
    }

    timer_set.it_interval.tv_sec = sec;
    timer_set.it_interval.tv_nsec = msec * 1000 * 1000;

    timer_set.it_value.tv_sec = now.tv_sec + sec;
    timer_set.it_value.tv_nsec = (now.tv_usec * 1000) + timer_set.it_interval.tv_nsec;

    if (timer_set.it_value.tv_nsec > 1e9)
    {
        timer_set.it_value.tv_nsec = timer_set.it_value.tv_nsec - 1e9;
        timer_set.it_value.tv_sec += 1;
    }

    if (timerfd_settime(timer->fd, TFD_TIMER_ABSTIME, &timer_set, NULL) == -1)
    {
        swWarn("timerfd_settime() failed. Error: %s[%d]", strerror(errno), errno);
        return SW_ERR;
    }

源碼解釋:
調用timerfd_create函數創建一個timerfd,並將返回的fd賦值給timer.fd;隨後設置timer_set的值,並調用timerfd_settime函數設置定時器相關屬性。

1.5.3.swTimer_del

聲明:

// timer.c 26h
static int swTimer_del(swTimer *timer, int ms);

功能:從timer的列表中移除一個指定定時器
核心源碼:

    swHashMap_del_int(timer->list, ms);
    return SW_OK;

源碼解釋:
從timer的list中移除ms對應的定時器

1.5.4.swTimer_free

聲明:

// timer.c 27h
static void swTimer_free(swTimer *timer);

功能:釋放timer的內存
核心源碼:

    swHashMap_free(timer->list);
    if (timer->use_pipe)
    {
        timer->pipe.close(&timer->pipe);
    }
    else if (close(timer->fd) < 0)
    {
        swSysError("close(%d) failed.", timer->fd);
    }
    if (timer->root)
    {
        swTimer_node_destory(&timer->root);
    }

源碼解釋:
釋放list,關閉管道,釋放root指向的鏈表

1.5.5.swTimer_add

聲明:

// timer.c 28h
static int swTimer_add(swTimer *timer, int msec, int interval, void *data);

功能:向timer中添加一個定時器
核心源碼:

    if (interval == 0)
    {
        return swTimer_addtimeout(timer, msec, data);
    }
    swTimer_interval_node *node = sw_malloc(sizeof(swTimer_interval_node));
    if (node == NULL)
    {
        swWarn("malloc failed.");
        return SW_ERR;
    }

    bzero(node, sizeof(swTimer_interval_node));
    node->interval = msec;
    if (gettimeofday(&node->lasttime, NULL) < 0)
    {
        swSysError("gettimeofday() failed.");
        return SW_ERR;
    }

    if (msec < timer->interval)
    {
        int new_interval = swoole_common_divisor(msec, timer->interval);
        timer->interval = new_interval;
        swTimer_set(timer, new_interval);
    }
    swHashMap_add_int(timer->list, msec, node, NULL);
    timer->num++;

源碼解釋:
如果interval爲0,說明這個定時器是個timeout類型定時器,調用swTimer_addtimeout函數。
否則,創建一個swTimer_interval_node結構體,設置其相關屬性,並根據interval計算timer的基礎響應間隔,並調用swTimer_set函數設置新的定時間隔。
最後,將新的定時任務節點添加進timer的list,並將定時器數量增加1。

1.5.6.swTimer_set

聲明:

// timer.c 29h
static int swTimer_set(swTimer *timer, int new_interval);

功能:設置timer的定時器響應間隔
核心源碼:

    if (SwooleG.use_timerfd)
    {
        return swTimer_timerfd_set(timer, new_interval);
    }
    else
    {
        return swTimer_signal_set(timer, new_interval);
    }

源碼解釋:
如果使用了timerfd,調用swTimer_timerfd_set;否則,調用swTimer_signal_set;

1.5.7.swTimer_addtimeout

聲明:

// timer.c 30h
int swTimer_addtimeout(swTimer *timer, int timeout_ms, void *data);

功能:從timer的列表中移除一個指定定時器
核心源碼:

    int new_interval = swoole_common_divisor(timeout_ms, timer->interval);
    if (new_interval < timer->interval)
    {
        swTimer_set(timer, new_interval);
        timer->interval = new_interval;
    }

    struct timeval now;
    if (gettimeofday(&now, NULL) < 0)
    {
        swWarn("gettimeofday() failed. Error: %s[%d]", strerror(errno), errno);
        return SW_ERR;
    }

    uint32_t now_ms = now.tv_sec * 1000 + now.tv_usec / 1000;
    swTimer_node *node = sw_malloc(sizeof(swTimer_node));
    if (node == NULL)
    {
        swWarn("malloc(%d) failed. Error: %s[%d]", (int ) sizeof(swTimer_node), strerror(errno), errno);
        return SW_ERR;
    }

    bzero(node, sizeof(swTimer_node));
    node->data = data;
    node->exec_msec = now_ms + timeout_ms;
    swTimer_node_insert(&timer->root, node);

源碼解釋:
首先計算timer定時器最小時間間隔,並設置新的定時器基礎響應間隔。隨後創建新的swTimer_node節點,並設置其屬性值,然後調用swTimer_node_insert函數在timer的root鏈表中添加新節點。(需要注意的是,因爲這個定時器是一次性的,因此並不會改變timer->num的值)

1.5.8.swTimer_select

聲明:

// timer.c 31h
int swTimer_select(swTimer *timer);

功能:遍歷timer列表找到需要響應的定時器
核心源碼:

    uint64_t key;
    swTimer_interval_node *timer_node;
    struct timeval now;

    if (gettimeofday(&now, NULL) < 0)
    {
        swSysError("gettimeofday() failed.");
        return SW_ERR;
    }
    //swWarn("%d.%d", now.tv_sec, now.tv_usec);

    if (timer->onTimeout == NULL)
    {
        swWarn("timer->onTimeout is NULL");
        return SW_ERR;
    }
    /**
     * timeout task list
     */
    uint32_t now_ms = now.tv_sec * 1000 + now.tv_usec / 1000;
    swTimer_node *tmp = timer->root;
    while (tmp)
    {
        if (tmp->exec_msec > now_ms)
        {
            break;
        }
        else
        {
            timer->onTimeout(timer, tmp->data);
            timer->root = tmp->next;
            sw_free(tmp);
            tmp = timer->root;
        }
    }

    if (timer->onTimer == NULL)
    {
        swWarn("timer->onTimer is NULL");
        return SW_ERR;
    }
    uint32_t interval = 0;
    do
    {
        //swWarn("timer foreach start\n----------------------------------------------");
        timer_node = swHashMap_each_int(timer->list, &key);

        //hashmap empty
        if (timer_node == NULL)
        {
            break;
        }
        //the interval time(ms)
        interval = (now.tv_sec - timer_node->lasttime.tv_sec) * 1000 + (now.tv_usec - timer_node->lasttime.tv_usec) / 1000;

        /**
         * deviation 1ms
         */
        if (interval >= timer_node->interval - 1)
        {
            memcpy(&timer_node->lasttime, &now, sizeof(now));
            timer->onTimer(timer, timer_node->interval);
        }
    } while (timer_node);

源碼解釋:
首先獲取當前系統時間。
判定onTimeout回調是否被設置,如果未設置則返回錯誤。隨後,遍歷timeout定時任務列表,找到exec_msec時間小於等於當前時間的任務,調用onTimeout響應這些回調,並移除該任務。
判定onTimer回調是否被設置,如果未設置則返回錯誤。隨後,遍歷定時任務列表,判定當前節點是否需要響應(當前時間 - lasttime >= interval - 1ms),如果需要響應則設置新的lasttime並調用onTimer回調。

2.EventTimer

2.1.EventTimer原理

EventTimer的實現原理是利用了epoll的timeout超時設置。通過設置epoll的timeout,就能在timeout時間後捕獲一個事件,在捕獲該事件後,通過遍歷對應的事件列表即可得知哪些事件需要處理。

2.2.EventTimer私有操作函數

2.2.1.swEventTimer_add

聲明:

// EventTimer.c 19h
static int swEventTimer_add(swTimer *timer, int _msec, int interval, void *data);

功能:向timer中添加一個定時器
核心源碼:

    swTimer_node *node = sw_malloc(sizeof(swTimer_node));
    if (!node)
    {
        swSysError("malloc(%d) failed.", (int )sizeof(swTimer_node));
        return SW_ERR;
    }

    int now_msec = swEventTimer_get_relative_msec();
    if (now_msec < 0)
    {
        return SW_ERR;
    }
    node->data = data;
    node->exec_msec = now_msec + _msec;
    node->interval = interval ? _msec : 0;
    swTimer_node_insert(&timer->root, node);

源碼解釋:
初始化並向Timer的root中添加一個節點。

2.2.2.swEventTimer_del

聲明:

// timer.c 20h
static int swEventTimer_del(swTimer *timer, int _msec);

功能:從timer的列表中移除一個指定定時器
核心源碼:

    if (timer->root)
    {
        swTimer_node_destory(&timer->root);
    }

源碼解釋:
從timer的root中移除ms對應的定時器

2.2.3.swEventTimer_select

聲明:

// timer.c 21h
static int swEventTimer_select(swTimer *timer);

功能:從timer中選出需要響應的定時器
核心源碼:

    uint32_t now_msec = swEventTimer_get_relative_msec();
    if (now_msec < 0)
    {
        return SW_ERR;
    }

    swTimer_node *tmp = timer->root;
    while (tmp)
    {
        if (tmp->exec_msec > now_msec)
        {
            break;
        }
        else
        {
            if (tmp->interval > 0)
            {
                timer->onTimer(timer, tmp->interval);
            }
            else
            {
                timer->onTimeout(timer, tmp->data);
            }

            timer->root = tmp->next;
            if (timer->root)
            {
                timer->root->prev = NULL;
            }
            if (tmp->interval > 0)
            {
                tmp->exec_msec += tmp->interval;
                swTimer_node_insert(&SwooleG.timer.root, tmp);
            }
            else
            {
                sw_free(tmp);
            }
            tmp = timer->root;
        }
    }
    if (timer->root == NULL)
    {
        SwooleG.main_reactor->timeout_msec = -1;
    }
    else
    {
        SwooleG.main_reactor->timeout_msec = timer->root->exec_msec - now_msec;
    }

源碼解釋:
遍歷root鏈表,如果節點已經需要響應(exec_msec大於當前時間),則根據interval是否爲0來執行各種不同的回調函數,並且如果interval爲0,還需要移除當前節點。
最後,重新設置SwooleG.main_reactor的timeout時間。如果timer中沒有定時任務,則設定爲無超時。

2.2.4.swEventTimer_free

聲明:

// timer.c 22h
static void swEventTimer_free(swTimer *timer);

功能:釋放timer
核心源碼:

    if (timer->root)
    {
        swTimer_node_destory(&timer->root);
    }

源碼解釋:
釋放timer的root鏈表

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章