/* Process time events. */ static int process_time_events(ae_event_loop *el) { int processed = 0; ae_time_event *te; long long maxid; te = el->time_event_head; maxid = el->time_event_next_id - 1; while (te) { long now_sec, now_ms; long long id; /* Don't process the time event registered during this process. */ if (te->id > maxid) { te = te->next; continue; } ae_get_time(&now_sec, &now_ms); /* timeout */ if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { int ret; id = te->id; ret = te->time_proc(el, id, te->client_data); 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 (ret > 0) { ae_add_milliseconds_to_now(ret, &te->when_sec, &te->when_ms); } else { ae_delete_time_event(el, id); } te = el->time_event_head; } else { te = te->next; } } return processed; }
static void ae_add_milliseconds_to_now(long long milliseconds, long *sec, long *ms) { long cur_sec, cur_ms, when_sec, when_ms; ae_get_time(&cur_sec, &cur_ms); when_sec = cur_sec + milliseconds / 1000; when_ms = cur_ms + milliseconds % 1000; /* cur_ms < 1000, when_ms < 2000, so just one time is enough. */ if (when_ms >= 1000) { ++when_sec; when_ms -= 1000; } *sec = when_sec; *ms = when_ms; }
static int process_time_event(ae_event_loop * ev_loop) { int processed = 0; ae_time_event * te; long long maxid; time_t now = time(NULL); if (now < ev_loop->last_time) { te = ev_loop->time_event_head; while (te) { te->when_sec = 0; te = te->next; } } ev_loop->last_time = now; te = ev_loop->time_event_head; maxid = ev_loop->time_event_next_id - 1; while (te) { long now_sec, now_ms; long long id; if (te->id > maxid) { te = te->next; continue; } ae_get_time(&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->time_func(ev_loop, id, te->client_data); processed++; if (retval != AE_NOMORE) ae_add_milliseconds_to_now(retval, &te->when_sec, &te->when_ms); else ae_delete_time_event(ev_loop, id); te = ev_loop->time_event_head; } else { te = te->next; } } return processed; }
static void ae_add_milliseconds_to_now(long long milliseconds, long * sec, long * ms) { long cur_sec, cur_ms, when_sec, when_ms; ae_get_time(&cur_sec, &cur_ms); when_sec = cur_sec + milliseconds / 1000; when_ms = cur_ms + milliseconds % 1000; if (when_ms >= 1000) { when_sec++; when_ms -= 1000; } *sec = when_sec; *ms = when_ms; }
/* 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 occurs (if any). If flag is 0, the function does nothing and returns. if flag has AE_ALL_EVENTS set, all the kind of events are processed. if flag has AE_FILE_EVENTS set, file events are processed. if flag has AE_TIME_EVENTS set, time events are processed. if flag has AE_DONT_WAIT set, the function returns ASAP (As soon as possible) until all the events that's possible to process without to wait are processed. The function returns the number of events processed. */ int ae_process_events(ae_event_loop *el, int flags) { int processed = 0, numevents; /* Nothing to do ? return ASAP */ if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) { return 0; } /* 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 (el->maxfd != -1 || ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) { int j; ae_time_event *shortest = NULL; struct timeval tv, *tvp; if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT)) { shortest = ae_search_nearest_timer(el); } if (shortest) { long now_sec, now_ms; /* Calculate the time missing for the nearest timer to fire. */ ae_get_time(&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; } if (tvp->tv_sec < 0) { tvp->tv_sec = 0; } if (tvp->tv_usec < 0) { tvp->tv_usec = 0; } } else { /* If we have to check for events but need to return ASAP because of AE_DONT_WAIT we need to set 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 */ } } numevents = ae_api_poll(el, tvp); for (j = 0; j < numevents; ++j) { ae_file_event *fe = &el->events[el->fired[j].fd]; int mask = el->fired[j].mask; int fd = el->fired[j].fd; int rfired = 0; /* Note the fe->mask & mask & ... code: maybe an already processed event removed an element that fired and we still didn't processed, so we check if the events is still valid. */ if (fe->mask & mask & AE_READABLE) { rfired = 1; fe->r_file_proc(el, fd, fe->client_data, mask); } if (fe->mask & mask & AE_WRITABLE) { if (!rfired || fe->w_file_proc != fe->r_file_proc) { fe->w_file_proc(el, fd, fe->client_data, mask); } } ++processed; } } /* Check time events */ if (flags & AE_TIME_EVENTS) { processed += process_time_events(el); } /* Return the number of processed file/time events */ return processed; }
int ae_process_events(ae_event_loop * ev_loop, int flags) { int processed = 0, numevents = 0; struct timeval * tvp = NULL; if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0; // There is some fd registered if (ev_loop->maxfd != -1 || ((flags & AE_TIME_EVENTS) && (flags & AE_DONT_WAIT))) { int j; ae_time_event * shortest = NULL; struct timeval tv, *tvp = NULL; if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT)) shortest = ae_search_nearest_timer(ev_loop); if (shortest) { long now_sec, now_ms; ae_get_time(&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; } if (tvp->tv_sec < 0) tvp->tv_sec = 0; if (tvp->tv_usec < 0) tvp->tv_usec = 0; } else { if (flags & AE_DONT_WAIT) { tv.tv_sec = tv.tv_usec = 0; tvp = &tv; } else { tvp = NULL; } } numevents = ae_api_poll(ev_loop, tvp); for (j = 0; j < numevents; j++) { // Registered event ae_file_event * fe = &ev_loop->events[ev_loop->fired[j].fd]; // Fired event int mask = ev_loop->fired[j].mask; int fd = ev_loop->fired[j].fd; int rfired = 0; if (fe != NULL && (fe->mask & mask & AE_READABLE)) { rfired = 1; fe->r_file_func(ev_loop, fd, fe->client_data, mask); } if (fe != NULL && (fe->mask & mask & AE_WRITABLE)) { if (!rfired || fe->w_file_func != fe->r_file_func) fe->w_file_func(ev_loop, fd, fe->client_data, mask); } processed++; } } if (flags & AE_TIME_EVENTS) processed += process_time_event(ev_loop); return processed; }