|  | 
 | /* | 
 |  * 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; | 
 | } |