void closeFd(int fd, aeEventLoop* el) { aeDeleteTimeEvent(el, time_event_id); if(client_fd != -1){ aeDeleteFileEvent(el, client_fd, AE_READABLE); close(client_fd); client_fd = -1; } if(read_fd != -1){ aeDeleteFileEvent(el, read_fd, AE_READABLE); close(read_fd); read_fd = -1; } if(master_fd != -1){ aeDeleteFileEvent(el, master_fd, AE_READABLE); close(master_fd); master_fd = -1; } if (listen_fd != -1) { aeDeleteFileEvent(el, listen_fd, AE_READABLE); close(listen_fd); listen_fd = -1; } state = START_STATE; buf_master_cmd.len = 0; buf_slave_answer.len = 0; clear_queue(&q); if((time_event_id = aeCreateTimeEvent(el, 1,timeEvent , 0, 0)) == AE_ERR) { log4c_category_log(mycat, LOG4C_PRIORITY_ERROR, "Unrecoverable error creating time event errno = %d at line %d in file %s",errno, __LINE__, __FILE__); } }
//定时器的入口,输出一句话 int PrintTimer(struct aeEventLoop *eventLoop, long long id, void *clientData){ /*static int i = 0; //printf("Test Output: %d\n", i++); //10秒后再次执行该函数 return 10000;*/ printf("event test.....\n"); aeDeleteTimeEvent(eventLoop, id); //return 5000; }
/* Process time events */ static int processTimeEvents(aeEventLoop *eventLoop) { int processed = 0; aeTimeEvent *te; long long maxId; te = eventLoop->timeEventHead; maxId = eventLoop->timeEventNextId - 1; while (te) { long now_sec, now_ms; long long id; if (te->id > maxId) { te = te->next; continue; } aeGetTime(&now_sec, &now_ms); if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { int retval; id = te->id; retval = te->timeProc(eventLoop, id, te->clientData); processed++; /* After an event is processed our time event list may * no longer be the same, so we restart from head. * Still we make sure to don't process events registered * by event handlers itself in order to don't loop forever. * To do so we saved the max ID we want to handle. * * FUTURE OPTIMIZATIONS: * Note that this is NOT great algorithmically. Redis uses * a single time event so it's not a problem but the right * way to do this is to add the new elements on head, and * to flag deleted elements in a special way for later * deletion (putting references to the nodes to delete into * another linked list). */ if (retval != AE_NOMORE) { aeAddMillisecondsToNow(retval, &te->when_sec, &te->when_ms); } else { aeDeleteTimeEvent(eventLoop, id); } te = eventLoop->timeEventHead; } else { te = te->next; } } return processed; }
int aeTimeCallback3(struct aeEventLoop *eventLoop, aeTimeEvent *te, void *clientData) { static int count = 0; count++; if(count > 10) aeDeleteTimeEvent(eventLoop, te); printf("aeTimeCallback3, time = %d\n", time(NULL)); return 3000; }
void cmd_mgr_destroy (command_manager * mgr) { aeDeleteTimeEvent (mgr->el, mgr->cron_teid); dictRelease (mgr->commands); index_helper_destroy (mgr->idx_helper); mempool_destroy (mgr->mp_cmdctx); zfree (mgr); }
static void * worker_thread (void *arg) { workerArg *wa = (workerArg *) arg; int ret; int flags; workerState ws; // set read flag to be nonblock flags = fcntl (wa->rfd, F_GETFL, 0); assert (flags != -1); ret = fcntl (wa->rfd, F_SETFL, flags | O_NONBLOCK); assert (ret != -1); init_worker_state (&ws); ws.arg = wa; ws.size = wa->size; ws.tps = wa->tps; ws.el = aeCreateEventLoop (2048); assert (ws.el != NULL); ret = aeCreateFileEvent (ws.el, ws.arg->rfd, AE_READABLE, command_handler, &ws); assert (ret != AE_ERR); ws.teid = aeCreateTimeEvent (ws.el, 1, worker_cron, &ws, NULL); assert (ws.teid != -1LL); aeMain (ws.el); sb_clear (&ws.cin); sb_clear (&ws.in); sb_clear (&ws.out); if (ws.teid != -1LL) { aeDeleteTimeEvent (ws.el, ws.teid); } if (ws.el != NULL) { aeDeleteEventLoop (ws.el); } return NULL; }
static int processTimeEvents(aeEventLoop *eventLoop) { int processed = 0; aeTimeEvent *te; long long maxId; te = eventLoop->timeEventHead; maxId = eventLoop->timeEventNextId-1; while(te) { long now_sec, now_ms; long long id; if (te->id > maxId) { te = te->next; continue; } aeGetTime(&now_sec, &now_ms); if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { int retval; id = te->id; retval = te->timeProc(eventLoop, id, te->clientData); processed++; if (retval != AE_NOMORE) { aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); } else { aeDeleteTimeEvent(eventLoop, id); } te = eventLoop->timeEventHead; } else { te = te->next; } } return processed; }
/* Process time events */ static int processTimeEvents(aeEventLoop *eventLoop) { int processed = 0; aeTimeEvent *te; long long maxId; time_t now = time(NULL); /* If the system clock is moved to the future, and then set back to the * right value, time events may be delayed in a random way. Often this * means that scheduled operations will not be performed soon enough. * * Here we try to detect system clock skews, and force all the time * events to be processed ASAP when this happens: the idea is that * processing events earlier is less dangerous than delaying them * indefinitely, and practice suggests it is. */ if (now < eventLoop->lastTime) { te = eventLoop->timeEventHead; while(te) { te->when_sec = 0; te = te->next; } } eventLoop->lastTime = now; te = eventLoop->timeEventHead; maxId = eventLoop->timeEventNextId-1; while(te) { long now_sec, now_ms; long long id; if (te->id > maxId) { te = te->next; continue; } aeGetTime(&now_sec, &now_ms); if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { int retval; id = te->id; retval = te->timeProc(eventLoop, id, te->clientData); processed++; /* After an event is processed our time event list may * no longer be the same, so we restart from head. * Still we make sure to don't process events registered * by event handlers itself in order to don't loop forever. * To do so we saved the max ID we want to handle. * * FUTURE OPTIMIZATIONS: * Note that this is NOT great algorithmically. Redis uses * a single time event so it's not a problem but the right * way to do this is to add the new elements on head, and * to flag deleted elements in a special way for later * deletion (putting references to the nodes to delete into * another linked list). */ if (retval != AE_NOMORE) { aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); } else { aeDeleteTimeEvent(eventLoop, id); } te = eventLoop->timeEventHead; } else { te = te->next; } } return processed; }
int easysocketbenchmark::handletimmer(aeEventLoop *el, long long id, void *clientData){ easysocketbenchmark *esb = (easysocketbenchmark *)clientData; aeDeleteTimeEvent(el, id); esb->stopbenchmark(); }
/* Process every pending time event, then every pending file event * (that may be registered by time event callbacks just processed). * Without special flags the function sleeps until some file event * fires, or when the next time event occurrs (if any). * * If flags is 0, the function does nothing and returns. * if flags has AE_ALL_EVENTS set, all the kind of events are processed. * if flags has AE_FILE_EVENTS set, file events are processed. * if flags has AE_TIME_EVENTS set, time events are processed. * if flags has AE_DONT_WAIT set the function returns ASAP until all * the events that's possible to process without to wait are processed. * * The function returns the number of events processed. */ int aeProcessEvents(aeEventLoop *eventLoop, int flags) { int maxfd = 0, numfd = 0, processed = 0; fd_set rfds, wfds, efds; aeFileEvent *fe = eventLoop->fileEventHead; aeTimeEvent *te; long long maxId; AE_NOTUSED(flags); /* Nothing to do? return ASAP */ if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); /* Check file events */ if (flags & AE_FILE_EVENTS) { while (fe != NULL) { if (fe->mask & AE_READABLE) FD_SET(fe->fd, &rfds); if (fe->mask & AE_WRITABLE) FD_SET(fe->fd, &wfds); if (fe->mask & AE_EXCEPTION) FD_SET(fe->fd, &efds); if (maxfd < fe->fd) maxfd = fe->fd; numfd++; fe = fe->next; } } /* Note that we want call select() even if there are no * file events to process as long as we want to process time * events, in order to sleep until the next time event is ready * to fire. */ if (numfd || ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) { int retval; aeTimeEvent *shortest = NULL; struct timeval tv, *tvp; if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT)) shortest = aeSearchNearestTimer(eventLoop); if (shortest) { long now_sec, now_ms; /* Calculate the time missing for the nearest * timer to fire. */ aeGetTime(&now_sec, &now_ms); tvp = &tv; tvp->tv_sec = shortest->when_sec - now_sec; if (shortest->when_ms < now_ms) { tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000; tvp->tv_sec --; } else { tvp->tv_usec = (shortest->when_ms - now_ms)*1000; } } else { /* If we have to check for events but need to return * ASAP because of AE_DONT_WAIT we need to se the timeout * to zero */ if (flags & AE_DONT_WAIT) { tv.tv_sec = tv.tv_usec = 0; tvp = &tv; } else { /* Otherwise we can block */ tvp = NULL; /* wait forever */ } } retval = select(maxfd+1, &rfds, &wfds, &efds, tvp); if (retval > 0) { fe = eventLoop->fileEventHead; while(fe != NULL) { int fd = (int) fe->fd; if ((fe->mask & AE_READABLE && FD_ISSET(fd, &rfds)) || (fe->mask & AE_WRITABLE && FD_ISSET(fd, &wfds)) || (fe->mask & AE_EXCEPTION && FD_ISSET(fd, &efds))) { int mask = 0; if (fe->mask & AE_READABLE && FD_ISSET(fd, &rfds)) mask |= AE_READABLE; if (fe->mask & AE_WRITABLE && FD_ISSET(fd, &wfds)) mask |= AE_WRITABLE; if (fe->mask & AE_EXCEPTION && FD_ISSET(fd, &efds)) mask |= AE_EXCEPTION; fe->fileProc(eventLoop, fe->fd, fe->clientData, mask); processed++; /* After an event is processed our file event list * may no longer be the same, so what we do * is to clear the bit for this file descriptor and * restart again from the head. */ fe = eventLoop->fileEventHead; FD_CLR(fd, &rfds); FD_CLR(fd, &wfds); FD_CLR(fd, &efds); } else { fe = fe->next; } } } } /* Check time events */ if (flags & AE_TIME_EVENTS) { te = eventLoop->timeEventHead; maxId = eventLoop->timeEventNextId-1; while(te) { long now_sec, now_ms; long long id; if (te->id > maxId) { te = te->next; continue; } aeGetTime(&now_sec, &now_ms); if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { int retval; id = te->id; retval = te->timeProc(eventLoop, id, te->clientData); /* After an event is processed our time event list may * no longer be the same, so we restart from head. * Still we make sure to don't process events registered * by event handlers itself in order to don't loop forever. * To do so we saved the max ID we want to handle. */ if (retval != AE_NOMORE) { aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); } else { aeDeleteTimeEvent(eventLoop, id); } te = eventLoop->timeEventHead; } else { te = te->next; } } } return processed; /* return the number of processed file/time events */ }
// 处理时间事件 // 所有满足条件的时间事件都会被执行 static int processTimeEvents(aeEventLoop *eventLoop) { int processed = 0; aeTimeEvent *te; long long maxId; // 遍历所有时间事件 te = eventLoop->timeEventHead; maxId = eventLoop->timeEventNextId-1; while(te) { long now_sec, now_ms; long long id; // 略过无效事件 if (te->id > maxId) { te = te->next; continue; } // 获取当前时间 aeGetTime(&now_sec, &now_ms); // 如果当前时间等于或大于事件的执行时间 // 那么执行这个事件 if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { // 记录返回值 int retval; // 执行事件 id = te->id; retval = te->timeProc(eventLoop, id, te->clientData); // 更新执行计数器 processed++; /* After an event is processed our time event list may * no longer be the same, so we restart from head. * Still we make sure to don't process events registered * by event handlers itself in order to don't loop forever. * To do so we saved the max ID we want to handle. * * FUTURE OPTIMIZATIONS: * Note that this is NOT great algorithmically. Redis uses * a single time event so it's not a problem but the right * way to do this is to add the new elements on head, and * to flag deleted elements in a special way for later * deletion (putting references to the nodes to delete into * another linked list). */ if (retval != AE_NOMORE) { // 如果 retval 不等于 AE_NOMORE // 那么修改这个事件的执行时间 // 让事件在稍候再次执行 aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); } else { // 如果执行成功, 那么删除这个事件 aeDeleteTimeEvent(eventLoop, id); } // 因为执行事件之后,事件列表可能已经被改变了 // 因此需要返回到表头,继续开始执行事件 te = eventLoop->timeEventHead; } else { // 如果事件的执行时间未到,那么继续检查链表中的下个事件 te = te->next; } } // 返回处理事件的数量 return processed; }
/* * 处理所有已到达的时间事件 * * eventLoop 事件处理器指针 * */ static int processTimeEvents(aeEventLoop *eventLoop) { // 初始化已处理的事件计数器 int processed = 0; // 定义时间事件 aeTimeEvent *te; // 定义最大的时间事件ID long long maxId; // 获取当前时间 time_t now = time(NULL); /* If the system clock is moved to the future, and then set back to the * right value, time events may be delayed in a random way. Often this * means that scheduled operations will not be performed soon enough. * * Here we try to detect system clock skews, and force all the time * events to be processed ASAP when this happens: the idea is that * processing events earlier is less dangerous than delaying them * indefinitely, and practice suggests it is. */ // 当前时间小于最后一次执行事件的时间,表示系统时间移动到了未来时间后又回调到了正确的时间,这里的处理是为了防止这种情况的出现 if (now < eventLoop->lastTime) { // 获取事件处理器事件头事件 te = eventLoop->timeEventHead; // 遍历事件处理器 while(te) { // 将事件到达时间设为0 te->when_sec = 0; // 继续处理下个事件 te = te->next; } } // 将最后一次执行事件的时间设为当前时间 eventLoop->lastTime = now; // 重新获取事件处理器事件头事件 te = eventLoop->timeEventHead; // 将最大事件ID设为下个事件ID减1 maxId = eventLoop->timeEventNextId-1; // 遍历事件处理器 while(te) { // 时间事件到达的秒与微秒 long now_sec, now_ms; // 时间事件ID long long id; // 当前时间事件的ID大于最大事件ID if (te->id > maxId) { // 处理下一个时间事件 te = te->next; // 跳到下次循环 continue; } // 调用对应复用库中的aeGetTime方法 aeGetTime(&now_sec, &now_ms); // 当前时间大于时间事件到达时间,表示时间事件需要执行了 if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { // 定义retval变量 int retval; // 获取当前处理的时间事件ID id = te->id; // 调用时间事件处理方法,返回-1或一个整数,大于-1时表示这个事件需要循环处理 retval = te->timeProc(eventLoop, id, te->clientData); // 已处理的事件计数器加1 processed++; /* After an event is processed our time event list may * no longer be the same, so we restart from head. * Still we make sure to don't process events registered * by event handlers itself in order to don't loop forever. * To do so we saved the max ID we want to handle. * * FUTURE OPTIMIZATIONS: * Note that this is NOT great algorithmically. Redis uses * a single time event so it's not a problem but the right * way to do this is to add the new elements on head, and * to flag deleted elements in a special way for later * deletion (putting references to the nodes to delete into * another linked list). */ // 是否有需要循环执行这个事件时间 if (retval != AE_NOMORE) { // retval毫秒之后继续执行这个时间事件 aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); } else { // 删除时间事件 aeDeleteTimeEvent(eventLoop, id); } // 前当前处理事件设为时间处理器的头事件,继续循环 te = eventLoop->timeEventHead; } else { // 将当前处理事件设为下一个时间事件,继续循环 te = te->next; } } // 返回已处理的时间事件数量 return processed; }
/* Main */ int clusterMain (int argc, char **argv) { long long teid; redisLog (REDIS_WARNING, "Server started, Redis version " REDIS_VERSION); #ifdef __linux__ linuxOvercommitMemoryWarning (); #endif loadDataFromDisk (); server.last_bgsave_seqnum = server.smr_seqnum; /* Warning the user about suspicious maxmemory setting. */ if (server.maxmemory > 0 && server.maxmemory < 1024 * 1024) { redisLog (REDIS_WARNING, "WARNING: You specified a maxmemory value that is less than 1MB (current value is %llu bytes). Are you sure this is what you really want?", server.maxmemory); } smrConnect (); /* initializeCron for handling sigterm */ teid = aeCreateTimeEvent (server.el, 1, initializeCron, NULL, NULL); aeMain (server.el); aeDeleteTimeEvent (server.el, teid); if (server.need_rckpt) { redisLog (REDIS_NOTICE, "Need more checkpoint from %s:%d", server.ckpt_host, server.ckpt_port); smrDisconnect (); server.smr_init_flags = SMR_INIT_RCKPT; if (getdump (server.ckpt_host, server.ckpt_port, server.rdb_filename, "0-8191", REDIS_GETDUMP_DEFAULT_NET_LIMIT_MB) != REDIS_OK) { exit (1); } emptyDb (NULL); loadDataFromDisk (); server.last_bgsave_seqnum = server.smr_seqnum; smrConnect (); aeMain (server.el); } if (!server.is_ready) { redisLog (REDIS_WARNING, "Invalid initialization state"); exit (1); } if (server.last_bgsave_seqnum) { if (smr_seq_ckpted (server.smr_conn, server.last_bgsave_seqnum) != 0) { redisLog (REDIS_WARNING, "Failed to notify checkpointed sequence to smr"); exit (1); } else { redisLog (REDIS_NOTICE, "Checkpointed sequence is sent to SMR, seqnum:%lld", server.last_bgsave_seqnum); } } server.smr_seqnum_reset = 0; server.last_catchup_check_mstime = mstime (); server.smr_init_flags = SMR_INIT_CATCHUP_PHASE1; if (smr_catchup (server.smr_conn) == -1) { redisLog (REDIS_WARNING, "Failed to catchup errno(%d)", errno); exit (1); } server.smr_init_flags = SMR_INIT_CATCHUP_PHASE2; checkSmrCatchup (); aeSetBeforeSleepProc (server.el, beforeSleep); aeMain (server.el); aeDeleteEventLoop (server.el); exit (0); }
/*** * 进行一次定时事件处理 * @eventLoop[IN]: 事件循环处理器(Reactor组件) * @return: 本次得到处理的事件数量 */ static int processTimeEvents(aeEventLoop *eventLoop) { /* 记录本次得到处理的定时事件数 */ int processed = 0; aeTimeEvent *te; long long maxId; /* 系统当前绝对时间 */ time_t now = time(NULL); /* If the system clock is moved to the future, and then set back to the * right value, time events may be delayed in a random way. Often this * means that scheduled operations will not be performed soon enough. * * Here we try to detect system clock skews, and force all the time * events to be processed ASAP when this happens: the idea is that * processing events earlier is less dangerous than delaying them * indefinitely, and practice suggests it is. */ /*** * 如果发现系统时间, 则将时间值纠正. * 这里我们尝试检测系统时间是否不准确, 如果是, 则尽可能快的处理所有定时事件, * 毕竟提前处理定时事件总比无限期的延迟定时时间更安全, 实践证明这种思想是正确的 */ /* 正常情况下, 当前系统时间值必定是大于eventLoop中记录的上次操作的时间值, 如果不是, * 则说明系统事件不准确, 此时将所有定时事件的超时时间的秒值设为0(尽可能快递处理所有定时事件), * 同时eventLoop的上次操作事件更新为当前系统时间 */ if (now < eventLoop->lastTime) { te = eventLoop->timeEventHead; while(te) { te->when_sec = 0; te = te->next; } } eventLoop->lastTime = now; te = eventLoop->timeEventHead; /* 记录当前定时事件链表中最大的定时器ID */ maxId = eventLoop->timeEventNextId-1; while(te) { long now_sec, now_ms; long long id; /* 为什么会出现某个定时事件的id会大于maxId呢? 可能是因为ae中的所有操作都是非线程 * 安全的, 比如在maxId获取的同时执行了aeCreateTimeEvent操作, 此时我们忽略新增的事件 * 等到下一轮while检测处理(以避免可能出现的无限循环) */ if (te->id > maxId) { te = te->next; continue; } /* 获取系统当前时间的秒和毫秒部分 */ aeGetTime(&now_sec, &now_ms); /* 如果当前时间的秒值已经大于定时事件的超时值的秒部分; 或者秒部分等于当前系统时间的秒部分‘ * 但系统的毫秒部分大于事件的毫秒部分; 则说明该事件已经超时, 可以处理该定时事件了 */ if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { int retval; id = te->id; /* 调用定时事件处理函数 */ retval = te->timeProc(eventLoop, id, te->clientData); /* 本次已处理定时事件数增1 */ processed++; /* After an event is processed our time event list may * no longer be the same, so we restart from head. * Still we make sure to don't process events registered * by event handlers itself in order to don't loop forever. * To do so we saved the max ID we want to handle. * * FUTURE OPTIMIZATIONS: * Note that this is NOT great algorithmically. Redis uses * a single time event so it's not a problem but the right * way to do this is to add the new elements on head, and * to flag deleted elements in a special way for later * deletion (putting references to the nodes to delete into * another linked list). */ /* 定时事件处理函数的返回值用于指示该定时事件是循环定时还是单次定时事件 * 如果返回AE_NOMORE, 说明是单次定时, 则此时可以删除该定时事件; * 如果不是返回AE_NOMORE(此时返回的值代表下次超时的相对值), 说明是循环定时事件, 则修改该事件的超时时间值 */ if (retval != AE_NOMORE) { /* 将retval值(相对时间值)加到当前系统绝对事件, 并转化为秒值和毫秒值 */ aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); } else { aeDeleteTimeEvent(eventLoop, id); } /* 处理后链表可能发生了变化, 所以我们再次从链表头开始处理 */ te = eventLoop->timeEventHead; } else { te = te->next; } } return processed; }
static int processTimeEvents(aeEventLoop *eventLoop) { int processed = 0; aeTimeEvent *te; long long maxId; time_t now = time(NULL); /* If the system clock is moved to the future, and then set back to the * right value, time events may be delayed in a random way. Often this * means that scheduled operations will not be performed soon enough. * * Here we try to detect system clock skews, and force all the time * events to be processed ASAP when this happens: the idea is that * processing events earlier is less dangerous than delaying them * indefinitely, and practice suggests it is. */ // 通过重置事件的运行时间, // 防止因时间穿插(skew)而造成的事件处理混乱 if (now < eventLoop->lastTime) { te = eventLoop->timeEventHead; while(te) { te->when_sec = 0; te = te->next; } } // 更新最后一次处理时间事件的时间 eventLoop->lastTime = now; // 遍历链表 // 执行那些已经到达的事件 te = eventLoop->timeEventHead; maxId = eventLoop->timeEventNextId-1; while(te) { long now_sec, now_ms; long long id; // 跳过无效事件 if (te->id > maxId) { te = te->next; continue; } // 获取当前时间 aeGetTime(&now_sec, &now_ms); // 如果当前时间等于或等于事件的执行时间,那么说明事件已到达,执行这个事件 if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { int retval; id = te->id; // 执行事件处理器,并获取返回值 retval = te->timeProc(eventLoop, id, te->clientData); processed++; /* After an event is processed our time event list may * no longer be the same, so we restart from head. * Still we make sure to don't process events registered * by event handlers itself in order to don't loop forever. * To do so we saved the max ID we want to handle. * * FUTURE OPTIMIZATIONS: * Note that this is NOT great algorithmically. Redis uses * a single time event so it's not a problem but the right * way to do this is to add the new elements on head, and * to flag deleted elements in a special way for later * deletion (putting references to the nodes to delete into * another linked list). */ /* 如果事件处理器返回AE_NOMORE,那么这个事件为定时事件:该事件在达到一次之后就会被删除,之后不再到达。 如果事件处理器返回一个非AE NOMORE的整数值,那么这个事件为周期性时间:当一个时间事件到达之后,服务器会根据事件处理器返回的值, 对时间事件的when属性进行更新,让这个事件在一段时间之后再次到达,并以这种方式一直更新并运行下去。 比如说,如果一个时间事件的赴理器返回整数值30,那么服务器应该对这个时间事件进行更新,让这个事件在30毫秒之后再次到达。 * 决定时间事件是否要持续执行的 flag */ // 记录是否有需要循环执行这个事件时间 if (retval != AE_NOMORE) { // 是的, retval 毫秒之后继续执行这个时间事件 aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); } else { // 不,将这个事件删除 aeDeleteTimeEvent(eventLoop, id); } // 因为执行事件之后,事件列表可能已经被改变了 // 因此需要将 te 放回表头,继续开始执行事件 te = eventLoop->timeEventHead; } else { te = te->next; } } return processed; }