/* * Copyright (C) Igor Sysoev */ #include #include #include static ngx_int_t ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_poll_done(ngx_cycle_t *cycle); static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); static ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); static char *ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf); static struct pollfd *event_list; static ngx_int_t nevents; static ngx_str_t poll_name = ngx_string("poll"); ngx_event_module_t ngx_poll_module_ctx = { &poll_name, NULL, /* create configuration */ ngx_poll_init_conf, /* init configuration */ { ngx_poll_add_event, /* add an event */ ngx_poll_del_event, /* delete an event */ ngx_poll_add_event, /* enable an event */ ngx_poll_del_event, /* disable an event */ NULL, /* add an connection */ NULL, /* delete an connection */ NULL, /* process the changes */ ngx_poll_process_events, /* process the events */ ngx_poll_init, /* init the events */ ngx_poll_done /* done the events */ } }; ngx_module_t ngx_poll_module = { NGX_MODULE_V1, &ngx_poll_module_ctx, /* module context */ NULL, /* 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_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer) { struct pollfd *list; if (event_list == NULL) { nevents = 0; } if (ngx_process == NGX_PROCESS_WORKER || cycle->old_cycle == NULL || cycle->old_cycle->connection_n < cycle->connection_n) { list = ngx_alloc(sizeof(struct pollfd) * cycle->connection_n, cycle->log); if (list == NULL) { return NGX_ERROR; } if (event_list) { ngx_memcpy(list, event_list, sizeof(ngx_event_t *) * nevents); ngx_free(event_list); } event_list = list; } ngx_io = ngx_os_io; ngx_event_actions = ngx_poll_module_ctx.actions; ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_FD_EVENT; return NGX_OK; } static void ngx_poll_done(ngx_cycle_t *cycle) { ngx_free(event_list); event_list = NULL; } static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_event_t *e; ngx_connection_t *c; c = ev->data; ev->active = 1; if (ev->index != NGX_INVALID_INDEX) { ngx_log_error(NGX_LOG_ALERT, ev->log, 0, "poll event fd:%d ev:%i is already set", c->fd, event); return NGX_OK; } if (event == NGX_READ_EVENT) { e = c->write; #if (NGX_READ_EVENT != POLLIN) event = POLLIN; #endif } else { e = c->read; #if (NGX_WRITE_EVENT != POLLOUT) event = POLLOUT; #endif } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, "poll add event: fd:%d ev:%i", c->fd, event); if (e == NULL || e->index == NGX_INVALID_INDEX) { event_list[nevents].fd = c->fd; event_list[nevents].events = (short) event; event_list[nevents].revents = 0; ev->index = nevents; nevents++; } else { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, "poll add index: %i", e->index); event_list[e->index].events |= (short) event; ev->index = e->index; } return NGX_OK; } static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_event_t *e; ngx_connection_t *c; c = ev->data; ev->active = 0; if (ev->index == NGX_INVALID_INDEX) { ngx_log_error(NGX_LOG_ALERT, ev->log, 0, "poll event fd:%d ev:%i is already deleted", c->fd, event); return NGX_OK; } if (event == NGX_READ_EVENT) { e = c->write; #if (NGX_READ_EVENT != POLLIN) event = POLLIN; #endif } else { e = c->read; #if (NGX_WRITE_EVENT != POLLOUT) event = POLLOUT; #endif } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, "poll del event: fd:%d ev:%i", c->fd, event); if (e == NULL || e->index == NGX_INVALID_INDEX) { nevents--; if (ev->index < (ngx_uint_t) nevents) { ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, "index: copy event %ui to %i", nevents, ev->index); event_list[ev->index] = event_list[nevents]; c = ngx_cycle->files[event_list[nevents].fd]; if (c->fd == -1) { ngx_log_error(NGX_LOG_ALERT, ev->log, 0, "unexpected last event"); } else { if (c->read->index == (ngx_uint_t) nevents) { c->read->index = ev->index; } if (c->write->index == (ngx_uint_t) nevents) { c->write->index = ev->index; } } } } else { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, "poll del index: %i", e->index); event_list[e->index].events &= (short) ~event; } ev->index = NGX_INVALID_INDEX; return NGX_OK; } static ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) { int ready, revents; ngx_err_t err; ngx_int_t i, nready; ngx_uint_t found, level; ngx_event_t *ev, **queue; ngx_connection_t *c; /* NGX_TIMER_INFINITE == INFTIM */ #if (NGX_DEBUG0) if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) { for (i = 0; i < nevents; i++) { ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll: %d: fd:%d ev:%04Xd", i, event_list[i].fd, event_list[i].events); } } #endif ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll timer: %M", timer); ready = poll(event_list, (u_int) nevents, (int) timer); if (ready == -1) { err = ngx_errno; } else { err = 0; } if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { ngx_time_update(); } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll ready %d of %d", ready, nevents); 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, "poll() failed"); return NGX_ERROR; } if (ready == 0) { if (timer != NGX_TIMER_INFINITE) { return NGX_OK; } ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "poll() returned no events without timeout"); return NGX_ERROR; } ngx_mutex_lock(ngx_posted_events_mutex); nready = 0; for (i = 0; i < nevents && ready; i++) { revents = event_list[i].revents; #if 1 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll: %d: fd:%d ev:%04Xd rev:%04Xd", i, event_list[i].fd, event_list[i].events, revents); #else if (revents) { ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll: %d: fd:%d ev:%04Xd rev:%04Xd", i, event_list[i].fd, event_list[i].events, revents); } #endif if (revents & POLLNVAL) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "poll() error fd:%d ev:%04Xd rev:%04Xd", event_list[i].fd, event_list[i].events, revents); } if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "strange poll() events fd:%d ev:%04Xd rev:%04Xd", event_list[i].fd, event_list[i].events, revents); } if (event_list[i].fd == -1) { /* * the disabled event, a workaround for our possible bug, * see the comment below */ continue; } c = ngx_cycle->files[event_list[i].fd]; if (c->fd == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected event"); /* * it is certainly our fault and it should be investigated, * in the meantime we disable this event to avoid a CPU spinning */ if (i == nevents - 1) { nevents--; } else { event_list[i].fd = -1; } continue; } 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; } found = 0; if (revents & POLLIN) { found = 1; ev = c->read; if ((flags & NGX_POST_THREAD_EVENTS) && !ev->accept) { ev->posted_ready = 1; } else { ev->ready = 1; } queue = (ngx_event_t **) (ev->accept ? &ngx_posted_accept_events: &ngx_posted_events); ngx_locked_post_event(ev, queue); } if (revents & POLLOUT) { found = 1; ev = c->write; if (flags & NGX_POST_THREAD_EVENTS) { ev->posted_ready = 1; } else { ev->ready = 1; } ngx_locked_post_event(ev, &ngx_posted_events); } if (found) { ready--; continue; } } ngx_mutex_unlock(ngx_posted_events_mutex); if (ready != 0) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "poll ready != events"); } return nready; } static char * ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf) { ngx_event_conf_t *ecf; ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module); if (ecf->use != ngx_poll_module.ctx_index) { return NGX_CONF_OK; } #if (NGX_THREADS) ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "poll() is not supported in the threaded mode"); return NGX_CONF_ERROR; #else return NGX_CONF_OK; #endif }