|  |  | 
|  | /* | 
|  | * Copyright (C) Igor Sysoev | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include <ngx_config.h> | 
|  | #include <ngx_core.h> | 
|  | #include <ngx_event.h> | 
|  |  | 
|  |  | 
|  | #if (NGX_TEST_BUILD_EPOLL) | 
|  |  | 
|  | /* epoll declarations */ | 
|  |  | 
|  | #define EPOLLIN        0x001 | 
|  | #define EPOLLPRI       0x002 | 
|  | #define EPOLLOUT       0x004 | 
|  | #define EPOLLRDNORM    0x040 | 
|  | #define EPOLLRDBAND    0x080 | 
|  | #define EPOLLWRNORM    0x100 | 
|  | #define EPOLLWRBAND    0x200 | 
|  | #define EPOLLMSG       0x400 | 
|  | #define EPOLLERR       0x008 | 
|  | #define EPOLLHUP       0x010 | 
|  |  | 
|  | #define EPOLLET        0x80000000 | 
|  | #define EPOLLONESHOT   0x40000000 | 
|  |  | 
|  | #define EPOLL_CTL_ADD  1 | 
|  | #define EPOLL_CTL_DEL  2 | 
|  | #define EPOLL_CTL_MOD  3 | 
|  |  | 
|  | typedef union epoll_data { | 
|  | void         *ptr; | 
|  | int           fd; | 
|  | uint32_t      u32; | 
|  | uint64_t      u64; | 
|  | } epoll_data_t; | 
|  |  | 
|  | struct epoll_event { | 
|  | uint32_t      events; | 
|  | epoll_data_t  data; | 
|  | }; | 
|  |  | 
|  | int epoll_create(int size); | 
|  | int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); | 
|  | int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout); | 
|  |  | 
|  | int epoll_create(int size) | 
|  | { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) | 
|  | { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout) | 
|  | { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  |  | 
|  | typedef struct { | 
|  | ngx_uint_t  events; | 
|  | } ngx_epoll_conf_t; | 
|  |  | 
|  |  | 
|  | static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer); | 
|  | static void ngx_epoll_done(ngx_cycle_t *cycle); | 
|  | static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, | 
|  | ngx_uint_t flags); | 
|  | static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, | 
|  | ngx_uint_t flags); | 
|  | static ngx_int_t ngx_epoll_add_connection(ngx_connection_t *c); | 
|  | static ngx_int_t ngx_epoll_del_connection(ngx_connection_t *c, | 
|  | ngx_uint_t flags); | 
|  | static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, | 
|  | ngx_uint_t flags); | 
|  |  | 
|  | static void *ngx_epoll_create_conf(ngx_cycle_t *cycle); | 
|  | static char *ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf); | 
|  |  | 
|  | static int                  ep = -1; | 
|  | static struct epoll_event  *event_list; | 
|  | static ngx_uint_t           nevents; | 
|  |  | 
|  |  | 
|  | static ngx_str_t      epoll_name = ngx_string("epoll"); | 
|  |  | 
|  | static ngx_command_t  ngx_epoll_commands[] = { | 
|  |  | 
|  | { ngx_string("epoll_events"), | 
|  | NGX_EVENT_CONF|NGX_CONF_TAKE1, | 
|  | ngx_conf_set_num_slot, | 
|  | 0, | 
|  | offsetof(ngx_epoll_conf_t, events), | 
|  | NULL }, | 
|  |  | 
|  | ngx_null_command | 
|  | }; | 
|  |  | 
|  |  | 
|  | ngx_event_module_t  ngx_epoll_module_ctx = { | 
|  | &epoll_name, | 
|  | ngx_epoll_create_conf,               /* create configuration */ | 
|  | ngx_epoll_init_conf,                 /* init configuration */ | 
|  |  | 
|  | { | 
|  | ngx_epoll_add_event,             /* add an event */ | 
|  | ngx_epoll_del_event,             /* delete an event */ | 
|  | ngx_epoll_add_event,             /* enable an event */ | 
|  | ngx_epoll_del_event,             /* disable an event */ | 
|  | ngx_epoll_add_connection,        /* add an connection */ | 
|  | ngx_epoll_del_connection,        /* delete an connection */ | 
|  | NULL,                            /* process the changes */ | 
|  | ngx_epoll_process_events,        /* process the events */ | 
|  | ngx_epoll_init,                  /* init the events */ | 
|  | ngx_epoll_done,                  /* done the events */ | 
|  | } | 
|  | }; | 
|  |  | 
|  | ngx_module_t  ngx_epoll_module = { | 
|  | NGX_MODULE_V1, | 
|  | &ngx_epoll_module_ctx,               /* module context */ | 
|  | ngx_epoll_commands,                  /* module directives */ | 
|  | NGX_EVENT_MODULE,                    /* module type */ | 
|  | NULL,                                /* init master */ | 
|  | NULL,                                /* init module */ | 
|  | NULL,                                /* init process */ | 
|  | NULL,                                /* init thread */ | 
|  | NULL,                                /* exit thread */ | 
|  | NULL,                                /* exit process */ | 
|  | NULL,                                /* exit master */ | 
|  | NGX_MODULE_V1_PADDING | 
|  | }; | 
|  |  | 
|  |  | 
|  | static ngx_int_t | 
|  | ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) | 
|  | { | 
|  | ngx_epoll_conf_t  *epcf; | 
|  |  | 
|  | epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module); | 
|  |  | 
|  | if (ep == -1) { | 
|  | ep = epoll_create(cycle->connection_n / 2); | 
|  |  | 
|  | if (ep == -1) { | 
|  | ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, | 
|  | "epoll_create() failed"); | 
|  | return NGX_ERROR; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (nevents < epcf->events) { | 
|  | if (event_list) { | 
|  | ngx_free(event_list); | 
|  | } | 
|  |  | 
|  | event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events, | 
|  | cycle->log); | 
|  | if (event_list == NULL) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  | } | 
|  |  | 
|  | nevents = epcf->events; | 
|  |  | 
|  | ngx_io = ngx_os_io; | 
|  |  | 
|  | ngx_event_actions = ngx_epoll_module_ctx.actions; | 
|  |  | 
|  | #if (NGX_HAVE_CLEAR_EVENT) | 
|  | ngx_event_flags = NGX_USE_CLEAR_EVENT | 
|  | #else | 
|  | ngx_event_flags = NGX_USE_LEVEL_EVENT | 
|  | #endif | 
|  | |NGX_USE_GREEDY_EVENT | 
|  | |NGX_USE_EPOLL_EVENT; | 
|  |  | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | ngx_epoll_done(ngx_cycle_t *cycle) | 
|  | { | 
|  | if (close(ep) == -1) { | 
|  | ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, | 
|  | "epoll close() failed"); | 
|  | } | 
|  |  | 
|  | ep = -1; | 
|  |  | 
|  | ngx_free(event_list); | 
|  |  | 
|  | event_list = NULL; | 
|  | nevents = 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static ngx_int_t | 
|  | ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) | 
|  | { | 
|  | int                  op; | 
|  | uint32_t             events, prev; | 
|  | ngx_event_t         *e; | 
|  | ngx_connection_t    *c; | 
|  | struct epoll_event   ee; | 
|  |  | 
|  | c = ev->data; | 
|  |  | 
|  | events = (uint32_t) event; | 
|  |  | 
|  | if (event == NGX_READ_EVENT) { | 
|  | e = c->write; | 
|  | prev = EPOLLOUT; | 
|  | #if (NGX_READ_EVENT != EPOLLIN) | 
|  | events = EPOLLIN; | 
|  | #endif | 
|  |  | 
|  | } else { | 
|  | e = c->read; | 
|  | prev = EPOLLIN; | 
|  | #if (NGX_WRITE_EVENT != EPOLLOUT) | 
|  | events = EPOLLOUT; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | if (e->active) { | 
|  | op = EPOLL_CTL_MOD; | 
|  | events |= prev; | 
|  |  | 
|  | } else { | 
|  | op = EPOLL_CTL_ADD; | 
|  | } | 
|  |  | 
|  | ee.events = events | (uint32_t) flags; | 
|  | ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); | 
|  |  | 
|  | ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, | 
|  | "epoll add event: fd:%d op:%d ev:%08XD", | 
|  | c->fd, op, ee.events); | 
|  |  | 
|  | if (epoll_ctl(ep, op, c->fd, &ee) == -1) { | 
|  | ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, | 
|  | "epoll_ctl(%d, %d) failed", op, c->fd); | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | ev->active = 1; | 
|  | #if 0 | 
|  | ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0; | 
|  | #endif | 
|  |  | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  |  | 
|  | static ngx_int_t | 
|  | ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) | 
|  | { | 
|  | int                  op; | 
|  | uint32_t             prev; | 
|  | ngx_event_t         *e; | 
|  | ngx_connection_t    *c; | 
|  | struct epoll_event   ee; | 
|  |  | 
|  | /* | 
|  | * when the file descriptor is closed, the epoll automatically deletes | 
|  | * it from its queue, so we do not need to delete explicity the event | 
|  | * before the closing the file descriptor | 
|  | */ | 
|  |  | 
|  | if (flags & NGX_CLOSE_EVENT) { | 
|  | ev->active = 0; | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  | c = ev->data; | 
|  |  | 
|  | if (event == NGX_READ_EVENT) { | 
|  | e = c->write; | 
|  | prev = EPOLLOUT; | 
|  |  | 
|  | } else { | 
|  | e = c->read; | 
|  | prev = EPOLLIN; | 
|  | } | 
|  |  | 
|  | if (e->active) { | 
|  | op = EPOLL_CTL_MOD; | 
|  | ee.events = prev | (uint32_t) flags; | 
|  | ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); | 
|  |  | 
|  | } else { | 
|  | op = EPOLL_CTL_DEL; | 
|  | ee.events = 0; | 
|  | ee.data.ptr = NULL; | 
|  | } | 
|  |  | 
|  | ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, | 
|  | "epoll del event: fd:%d op:%d ev:%08XD", | 
|  | c->fd, op, ee.events); | 
|  |  | 
|  | if (epoll_ctl(ep, op, c->fd, &ee) == -1) { | 
|  | ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, | 
|  | "epoll_ctl(%d, %d) failed", op, c->fd); | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | ev->active = 0; | 
|  |  | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  |  | 
|  | static ngx_int_t | 
|  | ngx_epoll_add_connection(ngx_connection_t *c) | 
|  | { | 
|  | struct epoll_event  ee; | 
|  |  | 
|  | ee.events = EPOLLIN|EPOLLOUT|EPOLLET; | 
|  | ee.data.ptr = (void *) ((uintptr_t) c | c->read->instance); | 
|  |  | 
|  | ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | 
|  | "epoll add connection: fd:%d ev:%08XD", c->fd, ee.events); | 
|  |  | 
|  | if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) == -1) { | 
|  | ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, | 
|  | "epoll_ctl(EPOLL_CTL_ADD, %d) failed", c->fd); | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | c->read->active = 1; | 
|  | c->write->active = 1; | 
|  |  | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  |  | 
|  | static ngx_int_t | 
|  | ngx_epoll_del_connection(ngx_connection_t *c, ngx_uint_t flags) | 
|  | { | 
|  | int                 op; | 
|  | struct epoll_event  ee; | 
|  |  | 
|  | /* | 
|  | * when the file descriptor is closed the epoll automatically deletes | 
|  | * it from its queue so we do not need to delete explicity the event | 
|  | * before the closing the file descriptor | 
|  | */ | 
|  |  | 
|  | if (flags & NGX_CLOSE_EVENT) { | 
|  | c->read->active = 0; | 
|  | c->write->active = 0; | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  | ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, | 
|  | "epoll del connection: fd:%d", c->fd); | 
|  |  | 
|  | op = EPOLL_CTL_DEL; | 
|  | ee.events = 0; | 
|  | ee.data.ptr = NULL; | 
|  |  | 
|  | if (epoll_ctl(ep, op, c->fd, &ee) == -1) { | 
|  | ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, | 
|  | "epoll_ctl(%d, %d) failed", op, c->fd); | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | c->read->active = 0; | 
|  | c->write->active = 0; | 
|  |  | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  |  | 
|  | static ngx_int_t | 
|  | ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) | 
|  | { | 
|  | int                events; | 
|  | uint32_t           revents; | 
|  | ngx_int_t          instance, i; | 
|  | ngx_uint_t         level; | 
|  | ngx_err_t          err; | 
|  | ngx_log_t         *log; | 
|  | ngx_event_t       *rev, *wev, **queue; | 
|  | ngx_connection_t  *c; | 
|  |  | 
|  | /* NGX_TIMER_INFINITE == INFTIM */ | 
|  |  | 
|  | ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, | 
|  | "epoll timer: %M", timer); | 
|  |  | 
|  | events = epoll_wait(ep, event_list, (int) nevents, timer); | 
|  |  | 
|  | if (events == -1) { | 
|  | err = ngx_errno; | 
|  | } else { | 
|  | err = 0; | 
|  | } | 
|  |  | 
|  | if (flags & NGX_UPDATE_TIME) { | 
|  | ngx_time_update(0, 0); | 
|  | } | 
|  |  | 
|  | if (err) { | 
|  | if (err == NGX_EINTR) { | 
|  |  | 
|  | if (ngx_event_timer_alarm) { | 
|  | ngx_event_timer_alarm = 0; | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  | level = NGX_LOG_INFO; | 
|  |  | 
|  | } else { | 
|  | level = NGX_LOG_ALERT; | 
|  | } | 
|  |  | 
|  | ngx_log_error(level, cycle->log, err, "epoll_wait() failed"); | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | if (events == 0) { | 
|  | if (timer != NGX_TIMER_INFINITE) { | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, | 
|  | "epoll_wait() returned no events without timeout"); | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | ngx_mutex_lock(ngx_posted_events_mutex); | 
|  |  | 
|  | log = cycle->log; | 
|  |  | 
|  | for (i = 0; i < events; i++) { | 
|  | c = event_list[i].data.ptr; | 
|  |  | 
|  | instance = (uintptr_t) c & 1; | 
|  | c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1); | 
|  |  | 
|  | rev = c->read; | 
|  |  | 
|  | if (c->fd == -1 || rev->instance != instance) { | 
|  |  | 
|  | /* | 
|  | * the stale event from a file descriptor | 
|  | * that was just closed in this iteration | 
|  | */ | 
|  |  | 
|  | ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, | 
|  | "epoll: stale event %p", c); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | #if (NGX_DEBUG0) | 
|  | log = c->log ? c->log : cycle->log; | 
|  | #endif | 
|  |  | 
|  | revents = event_list[i].events; | 
|  |  | 
|  | ngx_log_debug3(NGX_LOG_DEBUG_EVENT, log, 0, | 
|  | "epoll: fd:%d ev:%04XD d:%p", | 
|  | c->fd, revents, event_list[i].data.ptr); | 
|  |  | 
|  | if (revents & (EPOLLERR|EPOLLHUP)) { | 
|  | ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, | 
|  | "epoll_wait() error on fd:%d ev:%04XD", | 
|  | c->fd, revents); | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) { | 
|  | ngx_log_error(NGX_LOG_ALERT, log, 0, | 
|  | "strange epoll_wait() events fd:%d ev:%04XD", | 
|  | c->fd, revents); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if ((revents & (EPOLLERR|EPOLLHUP)) | 
|  | && (revents & (EPOLLIN|EPOLLOUT)) == 0) | 
|  | { | 
|  | /* | 
|  | * if the error events were returned without EPOLLIN or EPOLLOUT, | 
|  | * then add these flags to handle the events at least in one | 
|  | * active handler | 
|  | */ | 
|  |  | 
|  | revents |= EPOLLIN|EPOLLOUT; | 
|  | } | 
|  |  | 
|  | if ((revents & EPOLLIN) && rev->active) { | 
|  |  | 
|  | if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) { | 
|  | rev->posted_ready = 1; | 
|  |  | 
|  | } else { | 
|  | rev->ready = 1; | 
|  | } | 
|  |  | 
|  | if (flags & NGX_POST_EVENTS) { | 
|  | queue = (ngx_event_t **) (rev->accept ? | 
|  | &ngx_posted_accept_events : &ngx_posted_events); | 
|  |  | 
|  | ngx_locked_post_event(rev, queue); | 
|  |  | 
|  | } else { | 
|  | rev->handler(rev); | 
|  | } | 
|  | } | 
|  |  | 
|  | wev = c->write; | 
|  |  | 
|  | if ((revents & EPOLLOUT) && wev->active) { | 
|  |  | 
|  | if (flags & NGX_POST_THREAD_EVENTS) { | 
|  | wev->posted_ready = 1; | 
|  |  | 
|  | } else { | 
|  | wev->ready = 1; | 
|  | } | 
|  |  | 
|  | if (flags & NGX_POST_EVENTS) { | 
|  | ngx_locked_post_event(wev, &ngx_posted_events); | 
|  |  | 
|  | } else { | 
|  | wev->handler(wev); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | ngx_mutex_unlock(ngx_posted_events_mutex); | 
|  |  | 
|  | return NGX_OK; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void * | 
|  | ngx_epoll_create_conf(ngx_cycle_t *cycle) | 
|  | { | 
|  | ngx_epoll_conf_t  *epcf; | 
|  |  | 
|  | epcf = ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t)); | 
|  | if (epcf == NULL) { | 
|  | return NGX_CONF_ERROR; | 
|  | } | 
|  |  | 
|  | epcf->events = NGX_CONF_UNSET; | 
|  |  | 
|  | return epcf; | 
|  | } | 
|  |  | 
|  |  | 
|  | static char * | 
|  | ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf) | 
|  | { | 
|  | ngx_epoll_conf_t *epcf = conf; | 
|  |  | 
|  | ngx_conf_init_uint_value(epcf->events, 512); | 
|  |  | 
|  | return NGX_CONF_OK; | 
|  | } |