mirror of
https://github.com/nginx/nginx.git
synced 2024-12-12 02:09:04 +08:00
bac0cb3bbd
And corresponding variable $connections_waiting was added. Previously, waiting connections were counted as the difference between active connections and the sum of reading and writing connections. That made it impossible to count more than one request in one connection as reading or writing (as is the case for SPDY). Also, we no longer count connections in handshake state as waiting.
1330 lines
32 KiB
C
1330 lines
32 KiB
C
|
|
/*
|
|
* Copyright (C) Igor Sysoev
|
|
* Copyright (C) Nginx, Inc.
|
|
*/
|
|
|
|
|
|
#include <ngx_config.h>
|
|
#include <ngx_core.h>
|
|
#include <ngx_event.h>
|
|
|
|
|
|
#define DEFAULT_CONNECTIONS 512
|
|
|
|
|
|
extern ngx_module_t ngx_kqueue_module;
|
|
extern ngx_module_t ngx_eventport_module;
|
|
extern ngx_module_t ngx_devpoll_module;
|
|
extern ngx_module_t ngx_epoll_module;
|
|
extern ngx_module_t ngx_rtsig_module;
|
|
extern ngx_module_t ngx_select_module;
|
|
|
|
|
|
static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf);
|
|
static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle);
|
|
static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle);
|
|
static char *ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
|
|
|
static char *ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd,
|
|
void *conf);
|
|
static char *ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
|
static char *ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd,
|
|
void *conf);
|
|
|
|
static void *ngx_event_core_create_conf(ngx_cycle_t *cycle);
|
|
static char *ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf);
|
|
|
|
|
|
static ngx_uint_t ngx_timer_resolution;
|
|
sig_atomic_t ngx_event_timer_alarm;
|
|
|
|
static ngx_uint_t ngx_event_max_module;
|
|
|
|
ngx_uint_t ngx_event_flags;
|
|
ngx_event_actions_t ngx_event_actions;
|
|
|
|
|
|
static ngx_atomic_t connection_counter = 1;
|
|
ngx_atomic_t *ngx_connection_counter = &connection_counter;
|
|
|
|
|
|
ngx_atomic_t *ngx_accept_mutex_ptr;
|
|
ngx_shmtx_t ngx_accept_mutex;
|
|
ngx_uint_t ngx_use_accept_mutex;
|
|
ngx_uint_t ngx_accept_events;
|
|
ngx_uint_t ngx_accept_mutex_held;
|
|
ngx_msec_t ngx_accept_mutex_delay;
|
|
ngx_int_t ngx_accept_disabled;
|
|
ngx_file_t ngx_accept_mutex_lock_file;
|
|
|
|
|
|
#if (NGX_STAT_STUB)
|
|
|
|
ngx_atomic_t ngx_stat_accepted0;
|
|
ngx_atomic_t *ngx_stat_accepted = &ngx_stat_accepted0;
|
|
ngx_atomic_t ngx_stat_handled0;
|
|
ngx_atomic_t *ngx_stat_handled = &ngx_stat_handled0;
|
|
ngx_atomic_t ngx_stat_requests0;
|
|
ngx_atomic_t *ngx_stat_requests = &ngx_stat_requests0;
|
|
ngx_atomic_t ngx_stat_active0;
|
|
ngx_atomic_t *ngx_stat_active = &ngx_stat_active0;
|
|
ngx_atomic_t ngx_stat_reading0;
|
|
ngx_atomic_t *ngx_stat_reading = &ngx_stat_reading0;
|
|
ngx_atomic_t ngx_stat_writing0;
|
|
ngx_atomic_t *ngx_stat_writing = &ngx_stat_writing0;
|
|
ngx_atomic_t ngx_stat_waiting0;
|
|
ngx_atomic_t *ngx_stat_waiting = &ngx_stat_waiting0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static ngx_command_t ngx_events_commands[] = {
|
|
|
|
{ ngx_string("events"),
|
|
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
|
|
ngx_events_block,
|
|
0,
|
|
0,
|
|
NULL },
|
|
|
|
ngx_null_command
|
|
};
|
|
|
|
|
|
static ngx_core_module_t ngx_events_module_ctx = {
|
|
ngx_string("events"),
|
|
NULL,
|
|
ngx_event_init_conf
|
|
};
|
|
|
|
|
|
ngx_module_t ngx_events_module = {
|
|
NGX_MODULE_V1,
|
|
&ngx_events_module_ctx, /* module context */
|
|
ngx_events_commands, /* module directives */
|
|
NGX_CORE_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_str_t event_core_name = ngx_string("event_core");
|
|
|
|
|
|
static ngx_command_t ngx_event_core_commands[] = {
|
|
|
|
{ ngx_string("worker_connections"),
|
|
NGX_EVENT_CONF|NGX_CONF_TAKE1,
|
|
ngx_event_connections,
|
|
0,
|
|
0,
|
|
NULL },
|
|
|
|
{ ngx_string("connections"),
|
|
NGX_EVENT_CONF|NGX_CONF_TAKE1,
|
|
ngx_event_connections,
|
|
0,
|
|
0,
|
|
NULL },
|
|
|
|
{ ngx_string("use"),
|
|
NGX_EVENT_CONF|NGX_CONF_TAKE1,
|
|
ngx_event_use,
|
|
0,
|
|
0,
|
|
NULL },
|
|
|
|
{ ngx_string("multi_accept"),
|
|
NGX_EVENT_CONF|NGX_CONF_FLAG,
|
|
ngx_conf_set_flag_slot,
|
|
0,
|
|
offsetof(ngx_event_conf_t, multi_accept),
|
|
NULL },
|
|
|
|
{ ngx_string("accept_mutex"),
|
|
NGX_EVENT_CONF|NGX_CONF_FLAG,
|
|
ngx_conf_set_flag_slot,
|
|
0,
|
|
offsetof(ngx_event_conf_t, accept_mutex),
|
|
NULL },
|
|
|
|
{ ngx_string("accept_mutex_delay"),
|
|
NGX_EVENT_CONF|NGX_CONF_TAKE1,
|
|
ngx_conf_set_msec_slot,
|
|
0,
|
|
offsetof(ngx_event_conf_t, accept_mutex_delay),
|
|
NULL },
|
|
|
|
{ ngx_string("debug_connection"),
|
|
NGX_EVENT_CONF|NGX_CONF_TAKE1,
|
|
ngx_event_debug_connection,
|
|
0,
|
|
0,
|
|
NULL },
|
|
|
|
ngx_null_command
|
|
};
|
|
|
|
|
|
ngx_event_module_t ngx_event_core_module_ctx = {
|
|
&event_core_name,
|
|
ngx_event_core_create_conf, /* create configuration */
|
|
ngx_event_core_init_conf, /* init configuration */
|
|
|
|
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
|
};
|
|
|
|
|
|
ngx_module_t ngx_event_core_module = {
|
|
NGX_MODULE_V1,
|
|
&ngx_event_core_module_ctx, /* module context */
|
|
ngx_event_core_commands, /* module directives */
|
|
NGX_EVENT_MODULE, /* module type */
|
|
NULL, /* init master */
|
|
ngx_event_module_init, /* init module */
|
|
ngx_event_process_init, /* init process */
|
|
NULL, /* init thread */
|
|
NULL, /* exit thread */
|
|
NULL, /* exit process */
|
|
NULL, /* exit master */
|
|
NGX_MODULE_V1_PADDING
|
|
};
|
|
|
|
|
|
void
|
|
ngx_process_events_and_timers(ngx_cycle_t *cycle)
|
|
{
|
|
ngx_uint_t flags;
|
|
ngx_msec_t timer, delta;
|
|
|
|
if (ngx_timer_resolution) {
|
|
timer = NGX_TIMER_INFINITE;
|
|
flags = 0;
|
|
|
|
} else {
|
|
timer = ngx_event_find_timer();
|
|
flags = NGX_UPDATE_TIME;
|
|
|
|
#if (NGX_THREADS)
|
|
|
|
if (timer == NGX_TIMER_INFINITE || timer > 500) {
|
|
timer = 500;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
if (ngx_use_accept_mutex) {
|
|
if (ngx_accept_disabled > 0) {
|
|
ngx_accept_disabled--;
|
|
|
|
} else {
|
|
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
|
|
return;
|
|
}
|
|
|
|
if (ngx_accept_mutex_held) {
|
|
flags |= NGX_POST_EVENTS;
|
|
|
|
} else {
|
|
if (timer == NGX_TIMER_INFINITE
|
|
|| timer > ngx_accept_mutex_delay)
|
|
{
|
|
timer = ngx_accept_mutex_delay;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delta = ngx_current_msec;
|
|
|
|
(void) ngx_process_events(cycle, timer, flags);
|
|
|
|
delta = ngx_current_msec - delta;
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
|
|
"timer delta: %M", delta);
|
|
|
|
if (ngx_posted_accept_events) {
|
|
ngx_event_process_posted(cycle, &ngx_posted_accept_events);
|
|
}
|
|
|
|
if (ngx_accept_mutex_held) {
|
|
ngx_shmtx_unlock(&ngx_accept_mutex);
|
|
}
|
|
|
|
if (delta) {
|
|
ngx_event_expire_timers();
|
|
}
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
|
|
"posted events %p", ngx_posted_events);
|
|
|
|
if (ngx_posted_events) {
|
|
if (ngx_threaded) {
|
|
ngx_wakeup_worker_thread(cycle);
|
|
|
|
} else {
|
|
ngx_event_process_posted(cycle, &ngx_posted_events);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ngx_int_t
|
|
ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)
|
|
{
|
|
if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
|
|
|
|
/* kqueue, epoll */
|
|
|
|
if (!rev->active && !rev->ready) {
|
|
if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT)
|
|
== NGX_ERROR)
|
|
{
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
return NGX_OK;
|
|
|
|
} else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {
|
|
|
|
/* select, poll, /dev/poll */
|
|
|
|
if (!rev->active && !rev->ready) {
|
|
if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT)
|
|
== NGX_ERROR)
|
|
{
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (rev->active && (rev->ready || (flags & NGX_CLOSE_EVENT))) {
|
|
if (ngx_del_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT | flags)
|
|
== NGX_ERROR)
|
|
{
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
} else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
|
|
|
|
/* event ports */
|
|
|
|
if (!rev->active && !rev->ready) {
|
|
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (rev->oneshot && !rev->ready) {
|
|
if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
}
|
|
|
|
/* aio, iocp, rtsig */
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
ngx_int_t
|
|
ngx_handle_write_event(ngx_event_t *wev, size_t lowat)
|
|
{
|
|
ngx_connection_t *c;
|
|
|
|
if (lowat) {
|
|
c = wev->data;
|
|
|
|
if (ngx_send_lowat(c, lowat) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
|
|
|
|
/* kqueue, epoll */
|
|
|
|
if (!wev->active && !wev->ready) {
|
|
if (ngx_add_event(wev, NGX_WRITE_EVENT,
|
|
NGX_CLEAR_EVENT | (lowat ? NGX_LOWAT_EVENT : 0))
|
|
== NGX_ERROR)
|
|
{
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
return NGX_OK;
|
|
|
|
} else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {
|
|
|
|
/* select, poll, /dev/poll */
|
|
|
|
if (!wev->active && !wev->ready) {
|
|
if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)
|
|
== NGX_ERROR)
|
|
{
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (wev->active && wev->ready) {
|
|
if (ngx_del_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)
|
|
== NGX_ERROR)
|
|
{
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
} else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
|
|
|
|
/* event ports */
|
|
|
|
if (!wev->active && !wev->ready) {
|
|
if (ngx_add_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (wev->oneshot && wev->ready) {
|
|
if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
}
|
|
|
|
/* aio, iocp, rtsig */
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static char *
|
|
ngx_event_init_conf(ngx_cycle_t *cycle, void *conf)
|
|
{
|
|
if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) {
|
|
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
|
|
"no \"events\" section in configuration");
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_event_module_init(ngx_cycle_t *cycle)
|
|
{
|
|
void ***cf;
|
|
u_char *shared;
|
|
size_t size, cl;
|
|
ngx_shm_t shm;
|
|
ngx_time_t *tp;
|
|
ngx_core_conf_t *ccf;
|
|
ngx_event_conf_t *ecf;
|
|
|
|
cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
|
|
ecf = (*cf)[ngx_event_core_module.ctx_index];
|
|
|
|
if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
|
|
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
|
|
"using the \"%s\" event method", ecf->name);
|
|
}
|
|
|
|
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
|
|
|
|
ngx_timer_resolution = ccf->timer_resolution;
|
|
|
|
#if !(NGX_WIN32)
|
|
{
|
|
ngx_int_t limit;
|
|
struct rlimit rlmt;
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
"getrlimit(RLIMIT_NOFILE) failed, ignored");
|
|
|
|
} else {
|
|
if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur
|
|
&& (ccf->rlimit_nofile == NGX_CONF_UNSET
|
|
|| ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))
|
|
{
|
|
limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?
|
|
(ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;
|
|
|
|
ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
|
|
"%ui worker_connections exceed "
|
|
"open file resource limit: %i",
|
|
ecf->connections, limit);
|
|
}
|
|
}
|
|
}
|
|
#endif /* !(NGX_WIN32) */
|
|
|
|
|
|
if (ccf->master == 0) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
if (ngx_accept_mutex_ptr) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
/* cl should be equal to or greater than cache line size */
|
|
|
|
cl = 128;
|
|
|
|
size = cl /* ngx_accept_mutex */
|
|
+ cl /* ngx_connection_counter */
|
|
+ cl; /* ngx_temp_number */
|
|
|
|
#if (NGX_STAT_STUB)
|
|
|
|
size += cl /* ngx_stat_accepted */
|
|
+ cl /* ngx_stat_handled */
|
|
+ cl /* ngx_stat_requests */
|
|
+ cl /* ngx_stat_active */
|
|
+ cl /* ngx_stat_reading */
|
|
+ cl /* ngx_stat_writing */
|
|
+ cl; /* ngx_stat_waiting */
|
|
|
|
#endif
|
|
|
|
shm.size = size;
|
|
shm.name.len = sizeof("nginx_shared_zone");
|
|
shm.name.data = (u_char *) "nginx_shared_zone";
|
|
shm.log = cycle->log;
|
|
|
|
if (ngx_shm_alloc(&shm) != NGX_OK) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
shared = shm.addr;
|
|
|
|
ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
|
|
ngx_accept_mutex.spin = (ngx_uint_t) -1;
|
|
|
|
if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared,
|
|
cycle->lock_file.data)
|
|
!= NGX_OK)
|
|
{
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);
|
|
|
|
(void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);
|
|
|
|
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
|
|
"counter: %p, %d",
|
|
ngx_connection_counter, *ngx_connection_counter);
|
|
|
|
ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);
|
|
|
|
tp = ngx_timeofday();
|
|
|
|
ngx_random_number = (tp->msec << 16) + ngx_pid;
|
|
|
|
#if (NGX_STAT_STUB)
|
|
|
|
ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);
|
|
ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);
|
|
ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);
|
|
ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
|
|
ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
|
|
ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
|
|
ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);
|
|
|
|
#endif
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
#if !(NGX_WIN32)
|
|
|
|
static void
|
|
ngx_timer_signal_handler(int signo)
|
|
{
|
|
ngx_event_timer_alarm = 1;
|
|
|
|
#if 1
|
|
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, "timer signal");
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_event_process_init(ngx_cycle_t *cycle)
|
|
{
|
|
ngx_uint_t m, i;
|
|
ngx_event_t *rev, *wev;
|
|
ngx_listening_t *ls;
|
|
ngx_connection_t *c, *next, *old;
|
|
ngx_core_conf_t *ccf;
|
|
ngx_event_conf_t *ecf;
|
|
ngx_event_module_t *module;
|
|
|
|
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
|
|
ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
|
|
|
|
if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
|
|
ngx_use_accept_mutex = 1;
|
|
ngx_accept_mutex_held = 0;
|
|
ngx_accept_mutex_delay = ecf->accept_mutex_delay;
|
|
|
|
} else {
|
|
ngx_use_accept_mutex = 0;
|
|
}
|
|
|
|
#if (NGX_THREADS)
|
|
ngx_posted_events_mutex = ngx_mutex_init(cycle->log, 0);
|
|
if (ngx_posted_events_mutex == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
#endif
|
|
|
|
if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
for (m = 0; ngx_modules[m]; m++) {
|
|
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
if (ngx_modules[m]->ctx_index != ecf->use) {
|
|
continue;
|
|
}
|
|
|
|
module = ngx_modules[m]->ctx;
|
|
|
|
if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
|
|
/* fatal */
|
|
exit(2);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
#if !(NGX_WIN32)
|
|
|
|
if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
|
|
struct sigaction sa;
|
|
struct itimerval itv;
|
|
|
|
ngx_memzero(&sa, sizeof(struct sigaction));
|
|
sa.sa_handler = ngx_timer_signal_handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
if (sigaction(SIGALRM, &sa, NULL) == -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
"sigaction(SIGALRM) failed");
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
itv.it_interval.tv_sec = ngx_timer_resolution / 1000;
|
|
itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;
|
|
itv.it_value.tv_sec = ngx_timer_resolution / 1000;
|
|
itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000;
|
|
|
|
if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
"setitimer() failed");
|
|
}
|
|
}
|
|
|
|
if (ngx_event_flags & NGX_USE_FD_EVENT) {
|
|
struct rlimit rlmt;
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
|
|
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
"getrlimit(RLIMIT_NOFILE) failed");
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
cycle->files_n = (ngx_uint_t) rlmt.rlim_cur;
|
|
|
|
cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n,
|
|
cycle->log);
|
|
if (cycle->files == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
cycle->connections =
|
|
ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
|
|
if (cycle->connections == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
c = cycle->connections;
|
|
|
|
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
|
|
cycle->log);
|
|
if (cycle->read_events == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
rev = cycle->read_events;
|
|
for (i = 0; i < cycle->connection_n; i++) {
|
|
rev[i].closed = 1;
|
|
rev[i].instance = 1;
|
|
#if (NGX_THREADS)
|
|
rev[i].lock = &c[i].lock;
|
|
rev[i].own_lock = &c[i].lock;
|
|
#endif
|
|
}
|
|
|
|
cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
|
|
cycle->log);
|
|
if (cycle->write_events == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
wev = cycle->write_events;
|
|
for (i = 0; i < cycle->connection_n; i++) {
|
|
wev[i].closed = 1;
|
|
#if (NGX_THREADS)
|
|
wev[i].lock = &c[i].lock;
|
|
wev[i].own_lock = &c[i].lock;
|
|
#endif
|
|
}
|
|
|
|
i = cycle->connection_n;
|
|
next = NULL;
|
|
|
|
do {
|
|
i--;
|
|
|
|
c[i].data = next;
|
|
c[i].read = &cycle->read_events[i];
|
|
c[i].write = &cycle->write_events[i];
|
|
c[i].fd = (ngx_socket_t) -1;
|
|
|
|
next = &c[i];
|
|
|
|
#if (NGX_THREADS)
|
|
c[i].lock = 0;
|
|
#endif
|
|
} while (i);
|
|
|
|
cycle->free_connections = next;
|
|
cycle->free_connection_n = cycle->connection_n;
|
|
|
|
/* for each listening socket */
|
|
|
|
ls = cycle->listening.elts;
|
|
for (i = 0; i < cycle->listening.nelts; i++) {
|
|
|
|
c = ngx_get_connection(ls[i].fd, cycle->log);
|
|
|
|
if (c == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
c->log = &ls[i].log;
|
|
|
|
c->listening = &ls[i];
|
|
ls[i].connection = c;
|
|
|
|
rev = c->read;
|
|
|
|
rev->log = c->log;
|
|
rev->accept = 1;
|
|
|
|
#if (NGX_HAVE_DEFERRED_ACCEPT)
|
|
rev->deferred_accept = ls[i].deferred_accept;
|
|
#endif
|
|
|
|
if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
|
|
if (ls[i].previous) {
|
|
|
|
/*
|
|
* delete the old accept events that were bound to
|
|
* the old cycle read events array
|
|
*/
|
|
|
|
old = ls[i].previous->connection;
|
|
|
|
if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)
|
|
== NGX_ERROR)
|
|
{
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
old->fd = (ngx_socket_t) -1;
|
|
}
|
|
}
|
|
|
|
#if (NGX_WIN32)
|
|
|
|
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
|
|
ngx_iocp_conf_t *iocpcf;
|
|
|
|
rev->handler = ngx_event_acceptex;
|
|
|
|
if (ngx_use_accept_mutex) {
|
|
continue;
|
|
}
|
|
|
|
if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
ls[i].log.handler = ngx_acceptex_log_error;
|
|
|
|
iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);
|
|
if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex)
|
|
== NGX_ERROR)
|
|
{
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
} else {
|
|
rev->handler = ngx_event_accept;
|
|
|
|
if (ngx_use_accept_mutex) {
|
|
continue;
|
|
}
|
|
|
|
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
rev->handler = ngx_event_accept;
|
|
|
|
if (ngx_use_accept_mutex) {
|
|
continue;
|
|
}
|
|
|
|
if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
|
|
if (ngx_add_conn(c) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
} else {
|
|
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
ngx_int_t
|
|
ngx_send_lowat(ngx_connection_t *c, size_t lowat)
|
|
{
|
|
int sndlowat;
|
|
|
|
#if (NGX_HAVE_LOWAT_EVENT)
|
|
|
|
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
|
|
c->write->available = lowat;
|
|
return NGX_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
if (lowat == 0 || c->sndlowat) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
sndlowat = (int) lowat;
|
|
|
|
if (setsockopt(c->fd, SOL_SOCKET, SO_SNDLOWAT,
|
|
(const void *) &sndlowat, sizeof(int))
|
|
== -1)
|
|
{
|
|
ngx_connection_error(c, ngx_socket_errno,
|
|
"setsockopt(SO_SNDLOWAT) failed");
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
c->sndlowat = 1;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static char *
|
|
ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
|
{
|
|
char *rv;
|
|
void ***ctx;
|
|
ngx_uint_t i;
|
|
ngx_conf_t pcf;
|
|
ngx_event_module_t *m;
|
|
|
|
if (*(void **) conf) {
|
|
return "is duplicate";
|
|
}
|
|
|
|
/* count the number of the event modules and set up their indices */
|
|
|
|
ngx_event_max_module = 0;
|
|
for (i = 0; ngx_modules[i]; i++) {
|
|
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
ngx_modules[i]->ctx_index = ngx_event_max_module++;
|
|
}
|
|
|
|
ctx = ngx_pcalloc(cf->pool, sizeof(void *));
|
|
if (ctx == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
*ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
|
|
if (*ctx == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
*(void **) conf = ctx;
|
|
|
|
for (i = 0; ngx_modules[i]; i++) {
|
|
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
m = ngx_modules[i]->ctx;
|
|
|
|
if (m->create_conf) {
|
|
(*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);
|
|
if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
pcf = *cf;
|
|
cf->ctx = ctx;
|
|
cf->module_type = NGX_EVENT_MODULE;
|
|
cf->cmd_type = NGX_EVENT_CONF;
|
|
|
|
rv = ngx_conf_parse(cf, NULL);
|
|
|
|
*cf = pcf;
|
|
|
|
if (rv != NGX_CONF_OK)
|
|
return rv;
|
|
|
|
for (i = 0; ngx_modules[i]; i++) {
|
|
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
m = ngx_modules[i]->ctx;
|
|
|
|
if (m->init_conf) {
|
|
rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);
|
|
if (rv != NGX_CONF_OK) {
|
|
return rv;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
|
|
static char *
|
|
ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
|
{
|
|
ngx_event_conf_t *ecf = conf;
|
|
|
|
ngx_str_t *value;
|
|
|
|
if (ecf->connections != NGX_CONF_UNSET_UINT) {
|
|
return "is duplicate";
|
|
}
|
|
|
|
if (ngx_strcmp(cmd->name.data, "connections") == 0) {
|
|
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
|
"the \"connections\" directive is deprecated, "
|
|
"use the \"worker_connections\" directive instead");
|
|
}
|
|
|
|
value = cf->args->elts;
|
|
ecf->connections = ngx_atoi(value[1].data, value[1].len);
|
|
if (ecf->connections == (ngx_uint_t) NGX_ERROR) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"invalid number \"%V\"", &value[1]);
|
|
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
cf->cycle->connection_n = ecf->connections;
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
|
|
static char *
|
|
ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
|
{
|
|
ngx_event_conf_t *ecf = conf;
|
|
|
|
ngx_int_t m;
|
|
ngx_str_t *value;
|
|
ngx_event_conf_t *old_ecf;
|
|
ngx_event_module_t *module;
|
|
|
|
if (ecf->use != NGX_CONF_UNSET_UINT) {
|
|
return "is duplicate";
|
|
}
|
|
|
|
value = cf->args->elts;
|
|
|
|
if (cf->cycle->old_cycle->conf_ctx) {
|
|
old_ecf = ngx_event_get_conf(cf->cycle->old_cycle->conf_ctx,
|
|
ngx_event_core_module);
|
|
} else {
|
|
old_ecf = NULL;
|
|
}
|
|
|
|
|
|
for (m = 0; ngx_modules[m]; m++) {
|
|
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
module = ngx_modules[m]->ctx;
|
|
if (module->name->len == value[1].len) {
|
|
if (ngx_strcmp(module->name->data, value[1].data) == 0) {
|
|
ecf->use = ngx_modules[m]->ctx_index;
|
|
ecf->name = module->name->data;
|
|
|
|
if (ngx_process == NGX_PROCESS_SINGLE
|
|
&& old_ecf
|
|
&& old_ecf->use != ecf->use)
|
|
{
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"when the server runs without a master process "
|
|
"the \"%V\" event type must be the same as "
|
|
"in previous configuration - \"%s\" "
|
|
"and it cannot be changed on the fly, "
|
|
"to change it you need to stop server "
|
|
"and start it again",
|
|
&value[1], old_ecf->name);
|
|
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"invalid event type \"%V\"", &value[1]);
|
|
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
|
|
static char *
|
|
ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
|
{
|
|
#if (NGX_DEBUG)
|
|
ngx_event_conf_t *ecf = conf;
|
|
|
|
ngx_int_t rc;
|
|
ngx_str_t *value;
|
|
ngx_url_t u;
|
|
ngx_cidr_t c, *cidr;
|
|
ngx_uint_t i;
|
|
struct sockaddr_in *sin;
|
|
#if (NGX_HAVE_INET6)
|
|
struct sockaddr_in6 *sin6;
|
|
#endif
|
|
|
|
value = cf->args->elts;
|
|
|
|
#if (NGX_HAVE_UNIX_DOMAIN)
|
|
|
|
if (ngx_strcmp(value[1].data, "unix:") == 0) {
|
|
cidr = ngx_array_push(&ecf->debug_connection);
|
|
if (cidr == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
cidr->family = AF_UNIX;
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
rc = ngx_ptocidr(&value[1], &c);
|
|
|
|
if (rc != NGX_ERROR) {
|
|
if (rc == NGX_DONE) {
|
|
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
|
"low address bits of %V are meaningless",
|
|
&value[1]);
|
|
}
|
|
|
|
cidr = ngx_array_push(&ecf->debug_connection);
|
|
if (cidr == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
*cidr = c;
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
ngx_memzero(&u, sizeof(ngx_url_t));
|
|
u.host = value[1];
|
|
|
|
if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
|
|
if (u.err) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"%s in debug_connection \"%V\"",
|
|
u.err, &u.host);
|
|
}
|
|
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
cidr = ngx_array_push_n(&ecf->debug_connection, u.naddrs);
|
|
if (cidr == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));
|
|
|
|
for (i = 0; i < u.naddrs; i++) {
|
|
cidr[i].family = u.addrs[i].sockaddr->sa_family;
|
|
|
|
switch (cidr[i].family) {
|
|
|
|
#if (NGX_HAVE_INET6)
|
|
case AF_INET6:
|
|
sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;
|
|
cidr[i].u.in6.addr = sin6->sin6_addr;
|
|
ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);
|
|
break;
|
|
#endif
|
|
|
|
default: /* AF_INET */
|
|
sin = (struct sockaddr_in *) u.addrs[i].sockaddr;
|
|
cidr[i].u.in.addr = sin->sin_addr.s_addr;
|
|
cidr[i].u.in.mask = 0xffffffff;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
|
"\"debug_connection\" is ignored, you need to rebuild "
|
|
"nginx using --with-debug option to enable it");
|
|
|
|
#endif
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
|
|
static void *
|
|
ngx_event_core_create_conf(ngx_cycle_t *cycle)
|
|
{
|
|
ngx_event_conf_t *ecf;
|
|
|
|
ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
|
|
if (ecf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ecf->connections = NGX_CONF_UNSET_UINT;
|
|
ecf->use = NGX_CONF_UNSET_UINT;
|
|
ecf->multi_accept = NGX_CONF_UNSET;
|
|
ecf->accept_mutex = NGX_CONF_UNSET;
|
|
ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
|
|
ecf->name = (void *) NGX_CONF_UNSET;
|
|
|
|
#if (NGX_DEBUG)
|
|
|
|
if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,
|
|
sizeof(ngx_cidr_t)) == NGX_ERROR)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
return ecf;
|
|
}
|
|
|
|
|
|
static char *
|
|
ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
|
|
{
|
|
ngx_event_conf_t *ecf = conf;
|
|
|
|
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
|
|
int fd;
|
|
#endif
|
|
#if (NGX_HAVE_RTSIG)
|
|
ngx_uint_t rtsig;
|
|
ngx_core_conf_t *ccf;
|
|
#endif
|
|
ngx_int_t i;
|
|
ngx_module_t *module;
|
|
ngx_event_module_t *event_module;
|
|
|
|
module = NULL;
|
|
|
|
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
|
|
|
|
fd = epoll_create(100);
|
|
|
|
if (fd != -1) {
|
|
(void) close(fd);
|
|
module = &ngx_epoll_module;
|
|
|
|
} else if (ngx_errno != NGX_ENOSYS) {
|
|
module = &ngx_epoll_module;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if (NGX_HAVE_RTSIG)
|
|
|
|
if (module == NULL) {
|
|
module = &ngx_rtsig_module;
|
|
rtsig = 1;
|
|
|
|
} else {
|
|
rtsig = 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if (NGX_HAVE_DEVPOLL)
|
|
|
|
module = &ngx_devpoll_module;
|
|
|
|
#endif
|
|
|
|
#if (NGX_HAVE_KQUEUE)
|
|
|
|
module = &ngx_kqueue_module;
|
|
|
|
#endif
|
|
|
|
#if (NGX_HAVE_SELECT)
|
|
|
|
if (module == NULL) {
|
|
module = &ngx_select_module;
|
|
}
|
|
|
|
#endif
|
|
|
|
if (module == NULL) {
|
|
for (i = 0; ngx_modules[i]; i++) {
|
|
|
|
if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
event_module = ngx_modules[i]->ctx;
|
|
|
|
if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
module = ngx_modules[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (module == NULL) {
|
|
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
|
|
cycle->connection_n = ecf->connections;
|
|
|
|
ngx_conf_init_uint_value(ecf->use, module->ctx_index);
|
|
|
|
event_module = module->ctx;
|
|
ngx_conf_init_ptr_value(ecf->name, event_module->name->data);
|
|
|
|
ngx_conf_init_value(ecf->multi_accept, 0);
|
|
ngx_conf_init_value(ecf->accept_mutex, 1);
|
|
ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);
|
|
|
|
|
|
#if (NGX_HAVE_RTSIG)
|
|
|
|
if (!rtsig) {
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
if (ecf->accept_mutex) {
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
|
|
|
|
if (ccf->worker_processes == 0) {
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
|
|
"the \"rtsig\" method requires \"accept_mutex\" to be on");
|
|
|
|
return NGX_CONF_ERROR;
|
|
|
|
#else
|
|
|
|
return NGX_CONF_OK;
|
|
|
|
#endif
|
|
}
|