|  | 
 | /* | 
 |  * Copyright (C) Igor Sysoev | 
 |  */ | 
 |  | 
 |  | 
 | #include <ngx_config.h> | 
 | #include <ngx_core.h> | 
 | #include <ngx_event.h> | 
 |  | 
 |  | 
 | #if (NGX_TEST_BUILD_EVENTPORT) | 
 |  | 
 | #define ushort_t  u_short | 
 | #define uint_t    u_int | 
 |  | 
 | /* Solaris declarations */ | 
 |  | 
 | #define PORT_SOURCE_AIO         1 | 
 | #define PORT_SOURCE_TIMER       2 | 
 | #define PORT_SOURCE_USER        3 | 
 | #define PORT_SOURCE_FD          4 | 
 | #define PORT_SOURCE_ALERT       5 | 
 | #define PORT_SOURCE_MQ          6 | 
 |  | 
 | #define ETIME                   64 | 
 |  | 
 | #define SIGEV_PORT              4 | 
 |  | 
 | typedef struct { | 
 |     int         portev_events;  /* event data is source specific */ | 
 |     ushort_t    portev_source;  /* event source */ | 
 |     ushort_t    portev_pad;     /* port internal use */ | 
 |     uintptr_t   portev_object;  /* source specific object */ | 
 |     void       *portev_user;    /* user cookie */ | 
 | } port_event_t; | 
 |  | 
 | typedef struct  port_notify { | 
 |     int         portnfy_port;   /* bind request(s) to port */ | 
 |     void       *portnfy_user;   /* user defined */ | 
 | } port_notify_t; | 
 |  | 
 | typedef struct itimerspec {         /* definition per POSIX.4 */ | 
 |     struct timespec it_interval;    /* timer period */ | 
 |     struct timespec it_value;       /* timer expiration */ | 
 | } itimerspec_t; | 
 |  | 
 | int port_create(void) | 
 | { | 
 |     return -1; | 
 | } | 
 |  | 
 | int port_associate(int port, int source, uintptr_t object, int events, | 
 |     void *user) | 
 | { | 
 |     return -1; | 
 | } | 
 |  | 
 | int port_dissociate(int port, int source, uintptr_t object) | 
 | { | 
 |     return -1; | 
 | } | 
 |  | 
 | int port_getn(int port, port_event_t list[], uint_t max, uint_t *nget, | 
 |     struct timespec *timeout) | 
 | { | 
 |     return -1; | 
 | } | 
 |  | 
 | int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid) | 
 | { | 
 |     return -1; | 
 | } | 
 |  | 
 | int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, | 
 |     struct itimerspec *ovalue) | 
 | { | 
 |     return -1; | 
 | } | 
 |  | 
 | int timer_delete(timer_t timerid) | 
 | { | 
 |     return -1; | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 |  | 
 | typedef struct { | 
 |     u_int  events; | 
 | } ngx_eventport_conf_t; | 
 |  | 
 |  | 
 | static ngx_int_t ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer); | 
 | static void ngx_eventport_done(ngx_cycle_t *cycle); | 
 | static ngx_int_t ngx_eventport_add_event(ngx_event_t *ev, int event, | 
 |     u_int flags); | 
 | static ngx_int_t ngx_eventport_del_event(ngx_event_t *ev, int event, | 
 |     u_int flags); | 
 | static ngx_int_t ngx_eventport_process_events(ngx_cycle_t *cycle, | 
 |     ngx_msec_t timer, ngx_uint_t flags); | 
 |  | 
 | static void *ngx_eventport_create_conf(ngx_cycle_t *cycle); | 
 | static char *ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf); | 
 |  | 
 | static int            ep = -1; | 
 | static port_event_t  *event_list; | 
 | static u_int          nevents; | 
 | static timer_t        event_timer = -1; | 
 |  | 
 | static ngx_str_t      eventport_name = ngx_string("eventport"); | 
 |  | 
 |  | 
 | static ngx_command_t  ngx_eventport_commands[] = { | 
 |  | 
 |     { ngx_string("eventport_events"), | 
 |       NGX_EVENT_CONF|NGX_CONF_TAKE1, | 
 |       ngx_conf_set_num_slot, | 
 |       0, | 
 |       offsetof(ngx_eventport_conf_t, events), | 
 |       NULL }, | 
 |  | 
 |       ngx_null_command | 
 | }; | 
 |  | 
 |  | 
 | ngx_event_module_t  ngx_eventport_module_ctx = { | 
 |     &eventport_name, | 
 |     ngx_eventport_create_conf,             /* create configuration */ | 
 |     ngx_eventport_init_conf,               /* init configuration */ | 
 |  | 
 |     { | 
 |         ngx_eventport_add_event,           /* add an event */ | 
 |         ngx_eventport_del_event,           /* delete an event */ | 
 |         ngx_eventport_add_event,           /* enable an event */ | 
 |         ngx_eventport_del_event,           /* disable an event */ | 
 |         NULL,                              /* add an connection */ | 
 |         NULL,                              /* delete an connection */ | 
 |         NULL,                              /* process the changes */ | 
 |         ngx_eventport_process_events,      /* process the events */ | 
 |         ngx_eventport_init,                /* init the events */ | 
 |         ngx_eventport_done,                /* done the events */ | 
 |     } | 
 |  | 
 | }; | 
 |  | 
 | ngx_module_t  ngx_eventport_module = { | 
 |     NGX_MODULE_V1, | 
 |     &ngx_eventport_module_ctx,             /* module context */ | 
 |     ngx_eventport_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_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer) | 
 | { | 
 |     port_notify_t          pn; | 
 |     struct itimerspec      its; | 
 |     struct sigevent        sev; | 
 |     ngx_eventport_conf_t  *epcf; | 
 |  | 
 |     epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_eventport_module); | 
 |  | 
 |     if (ep == -1) { | 
 |         ep = port_create(); | 
 |  | 
 |         if (ep == -1) { | 
 |             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, | 
 |                           "port_create() failed"); | 
 |             return NGX_ERROR; | 
 |         } | 
 |     } | 
 |  | 
 |     if (nevents < epcf->events) { | 
 |         if (event_list) { | 
 |             ngx_free(event_list); | 
 |         } | 
 |  | 
 |         event_list = ngx_alloc(sizeof(port_event_t) * epcf->events, | 
 |                                cycle->log); | 
 |         if (event_list == NULL) { | 
 |             return NGX_ERROR; | 
 |         } | 
 |     } | 
 |  | 
 |     ngx_event_flags = NGX_USE_EVENTPORT_EVENT; | 
 |  | 
 |     if (timer) { | 
 |         ngx_memzero(&pn, sizeof(port_notify_t)); | 
 |         pn.portnfy_port = ep; | 
 |  | 
 |         ngx_memzero(&sev, sizeof(struct sigevent)); | 
 |         sev.sigev_notify = SIGEV_PORT; | 
 | #if !(NGX_TEST_BUILD_EVENTPORT) | 
 |         sev.sigev_value.sival_ptr = &pn; | 
 | #endif | 
 |  | 
 |         if (timer_create(CLOCK_REALTIME, &sev, &event_timer) == -1) { | 
 |             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, | 
 |                           "timer_create() failed"); | 
 |             return NGX_ERROR; | 
 |         } | 
 |  | 
 |         its.it_interval.tv_sec = timer / 1000; | 
 |         its.it_interval.tv_nsec = (timer % 1000) * 1000000; | 
 |         its.it_value.tv_sec = timer / 1000; | 
 |         its.it_value.tv_nsec = (timer % 1000) * 1000000; | 
 |  | 
 |         if (timer_settime(event_timer, 0, &its, NULL) == -1) { | 
 |             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, | 
 |                           "timer_settime() failed"); | 
 |             return NGX_ERROR; | 
 |         } | 
 |  | 
 |         ngx_event_flags |= NGX_USE_TIMER_EVENT; | 
 |     } | 
 |  | 
 |     nevents = epcf->events; | 
 |  | 
 |     ngx_io = ngx_os_io; | 
 |  | 
 |     ngx_event_actions = ngx_eventport_module_ctx.actions; | 
 |  | 
 |     return NGX_OK; | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | ngx_eventport_done(ngx_cycle_t *cycle) | 
 | { | 
 |     if (event_timer != -1) { | 
 |         if (timer_delete(event_timer) == -1) { | 
 |             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, | 
 |                           "timer_delete() failed"); | 
 |         } | 
 |  | 
 |         event_timer = -1; | 
 |     } | 
 |  | 
 |     if (close(ep) == -1) { | 
 |         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, | 
 |                       "close() event port failed"); | 
 |     } | 
 |  | 
 |     ep = -1; | 
 |  | 
 |     ngx_free(event_list); | 
 |  | 
 |     event_list = NULL; | 
 |     nevents = 0; | 
 | } | 
 |  | 
 |  | 
 | static ngx_int_t | 
 | ngx_eventport_add_event(ngx_event_t *ev, int event, u_int flags) | 
 | { | 
 |     int                events, prev; | 
 |     ngx_event_t       *e; | 
 |     ngx_connection_t  *c; | 
 |  | 
 |     c = ev->data; | 
 |  | 
 |     events = event; | 
 |  | 
 |     if (event == NGX_READ_EVENT) { | 
 |         e = c->write; | 
 |         prev = POLLOUT; | 
 | #if (NGX_READ_EVENT != POLLIN) | 
 |         events = POLLIN; | 
 | #endif | 
 |  | 
 |     } else { | 
 |         e = c->read; | 
 |         prev = POLLIN; | 
 | #if (NGX_WRITE_EVENT != POLLOUT) | 
 |         events = POLLOUT; | 
 | #endif | 
 |     } | 
 |  | 
 |     if (e->oneshot) { | 
 |         events |= prev; | 
 |     } | 
 |  | 
 |     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, | 
 |                    "eventport add event: fd:%d ev:%04Xd", c->fd, events); | 
 |  | 
 |     if (port_associate(ep, PORT_SOURCE_FD, c->fd, events, | 
 |                        (void *) ((uintptr_t) ev | ev->instance)) | 
 |         == -1) | 
 |     { | 
 |         ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, | 
 |                       "port_associate() failed"); | 
 |         return NGX_ERROR; | 
 |     } | 
 |  | 
 |     ev->active = 1; | 
 |     ev->oneshot = 1; | 
 |  | 
 |     return NGX_OK; | 
 | } | 
 |  | 
 |  | 
 | static ngx_int_t | 
 | ngx_eventport_del_event(ngx_event_t *ev, int event, u_int flags) | 
 | { | 
 |     ngx_event_t       *e; | 
 |     ngx_connection_t  *c; | 
 |  | 
 |     /* | 
 |      * when the file descriptor is closed, the event port automatically | 
 |      * dissociates it from the port, so we do not need to dissociate explicity | 
 |      * the event before the closing the file descriptor | 
 |      */ | 
 |  | 
 |     if (flags & NGX_CLOSE_EVENT) { | 
 |         ev->active = 0; | 
 |         ev->oneshot = 0; | 
 |         return NGX_OK; | 
 |     } | 
 |  | 
 |     c = ev->data; | 
 |  | 
 |     if (event == NGX_READ_EVENT) { | 
 |         e = c->write; | 
 |         event = POLLOUT; | 
 |  | 
 |     } else { | 
 |         e = c->read; | 
 |         event = POLLIN; | 
 |     } | 
 |  | 
 |     if (e->oneshot) { | 
 |         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, | 
 |                        "eventport change event: fd:%d ev:%04Xd", c->fd, event); | 
 |  | 
 |         if (port_associate(ep, PORT_SOURCE_FD, c->fd, event, | 
 |                            (void *) ((uintptr_t) ev | ev->instance)) | 
 |             == -1) | 
 |         { | 
 |             ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, | 
 |                           "port_associate() failed"); | 
 |             return NGX_ERROR; | 
 |         } | 
 |  | 
 |     } else { | 
 |         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, | 
 |                        "eventport del event: fd:%d", c->fd); | 
 |  | 
 |         if (port_dissociate(ep, PORT_SOURCE_FD, c->fd) == -1) { | 
 |             ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, | 
 |                           "port_dissociate() failed"); | 
 |             return NGX_ERROR; | 
 |         } | 
 |     } | 
 |  | 
 |     ev->active = 0; | 
 |     ev->oneshot = 0; | 
 |  | 
 |     return NGX_OK; | 
 | } | 
 |  | 
 |  | 
 | ngx_int_t | 
 | ngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, | 
 |     ngx_uint_t flags) | 
 | { | 
 |     int                 n, revents; | 
 |     u_int               events; | 
 |     ngx_err_t           err; | 
 |     ngx_int_t           instance; | 
 |     ngx_uint_t          i, level; | 
 |     ngx_event_t        *ev, *rev, *wev, **queue; | 
 |     ngx_connection_t   *c; | 
 |     struct timespec     ts, *tp; | 
 |  | 
 |     if (timer == NGX_TIMER_INFINITE) { | 
 |         tp = NULL; | 
 |  | 
 |     } else { | 
 |         ts.tv_sec = timer / 1000; | 
 |         ts.tv_nsec = (timer % 1000) * 1000000; | 
 |         tp = &ts; | 
 |     } | 
 |  | 
 |     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, | 
 |                    "eventport timer: %M", timer); | 
 |  | 
 |     events = 1; | 
 |  | 
 |     n = port_getn(ep, event_list, nevents, &events, tp); | 
 |  | 
 |     err = ngx_errno; | 
 |  | 
 |     if (flags & NGX_UPDATE_TIME) { | 
 |         ngx_time_update(0, 0); | 
 |     } | 
 |  | 
 |     if (n == -1) { | 
 |         if (err == ETIME) { | 
 |             if (timer != NGX_TIMER_INFINITE) { | 
 |                 return NGX_OK; | 
 |             } | 
 |  | 
 |             ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, | 
 |                           "port_getn() returned no events without timeout"); | 
 |             return NGX_ERROR; | 
 |         } | 
 |  | 
 |         level = (err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT; | 
 |         ngx_log_error(level, cycle->log, err, "port_getn() failed"); | 
 |         return NGX_ERROR; | 
 |     } | 
 |  | 
 |     if (events == 0) { | 
 |         if (timer != NGX_TIMER_INFINITE) { | 
 |             return NGX_OK; | 
 |         } | 
 |  | 
 |         ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, | 
 |                       "port_getn() returned no events without timeout"); | 
 |         return NGX_ERROR; | 
 |     } | 
 |  | 
 |     ngx_mutex_lock(ngx_posted_events_mutex); | 
 |  | 
 |     for (i = 0; i < events; i++) { | 
 |  | 
 |         if (event_list[i].portev_source == PORT_SOURCE_TIMER) { | 
 |             ngx_time_update(0, 0); | 
 |             continue; | 
 |         } | 
 |  | 
 |         ev = event_list[i].portev_user; | 
 |  | 
 |         switch (event_list[i].portev_source) { | 
 |  | 
 |         case PORT_SOURCE_FD: | 
 |  | 
 |             instance = (uintptr_t) ev & 1; | 
 |             ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1); | 
 |  | 
 |             if (ev->closed || ev->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, | 
 |                                "eventport: stale event %p", ev); | 
 |                 continue; | 
 |             } | 
 |  | 
 |             revents = event_list[i].portev_events; | 
 |  | 
 |             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, | 
 |                            "eventport: fd:%d, ev:%04Xd", | 
 |                            event_list[i].portev_object, revents); | 
 |  | 
 |             if (revents & (POLLERR|POLLHUP|POLLNVAL)) { | 
 |                 ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, | 
 |                               "prot_getn() error fd:%d ev:%04Xd", | 
 |                               event_list[i].portev_object, revents); | 
 |             } | 
 |  | 
 |             if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) { | 
 |                 ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, | 
 |                               "strange port_getn() events fd:%d ev:%04Xd", | 
 |                               event_list[i].portev_object, revents); | 
 |             } | 
 |  | 
 |             if ((revents & (POLLERR|POLLHUP|POLLNVAL)) | 
 |                  && (revents & (POLLIN|POLLOUT)) == 0) | 
 |             { | 
 |                 /* | 
 |                  * if the error events were returned without POLLIN or POLLOUT, | 
 |                  * then add these flags to handle the events at least in one | 
 |                  * active handler | 
 |                  */ | 
 |  | 
 |                 revents |= POLLIN|POLLOUT; | 
 |             } | 
 |  | 
 |             c = ev->data; | 
 |             rev = c->read; | 
 |             wev = c->write; | 
 |  | 
 |             rev->active = 0; | 
 |             wev->active = 0; | 
 |  | 
 |             if (revents & POLLIN) { | 
 |  | 
 |                 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); | 
 |                 } | 
 |  | 
 |                 if (rev->accept) { | 
 |                     if (ngx_use_accept_mutex) { | 
 |                         ngx_accept_events = 1; | 
 |                         continue; | 
 |                     } | 
 |  | 
 |                     if (port_associate(ep, PORT_SOURCE_FD, c->fd, POLLIN, | 
 |                                        (void *) ((uintptr_t) ev | ev->instance)) | 
 |                         == -1) | 
 |                     { | 
 |                         ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, | 
 |                                       "port_associate() failed"); | 
 |                         return NGX_ERROR; | 
 |                     } | 
 |                 } | 
 |             } | 
 |  | 
 |             if (revents & POLLOUT) { | 
 |  | 
 |                 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); | 
 |                 } | 
 |             } | 
 |  | 
 |             continue; | 
 |  | 
 |         default: | 
 |             ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, | 
 |                           "unexpected even_port object %d", | 
 |                           event_list[i].portev_object); | 
 |             continue; | 
 |         } | 
 |     } | 
 |  | 
 |     ngx_mutex_unlock(ngx_posted_events_mutex); | 
 |  | 
 |     return NGX_OK; | 
 | } | 
 |  | 
 |  | 
 | static void * | 
 | ngx_eventport_create_conf(ngx_cycle_t *cycle) | 
 | { | 
 |     ngx_eventport_conf_t  *epcf; | 
 |  | 
 |     epcf = ngx_palloc(cycle->pool, sizeof(ngx_eventport_conf_t)); | 
 |     if (epcf == NULL) { | 
 |         return NGX_CONF_ERROR; | 
 |     } | 
 |  | 
 |     epcf->events = NGX_CONF_UNSET; | 
 |  | 
 |     return epcf; | 
 | } | 
 |  | 
 |  | 
 | static char * | 
 | ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf) | 
 | { | 
 |     ngx_eventport_conf_t *epcf = conf; | 
 |  | 
 |     ngx_conf_init_uint_value(epcf->events, 32); | 
 |  | 
 |     return NGX_CONF_OK; | 
 | } |