mirror of
https://github.com/nginx/nginx.git
synced 2025-01-10 03:58:28 +08:00
85b44b46fb
There should be at least one worker connection for each listening socket, plus an additional connection for channel between worker and master, or starting worker processes will fail.
1331 lines
32 KiB
C
1331 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_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;
|
|
|
|
|
|
#if (NGX_STAT_STUB)
|
|
|
|
static ngx_atomic_t ngx_stat_accepted0;
|
|
ngx_atomic_t *ngx_stat_accepted = &ngx_stat_accepted0;
|
|
static ngx_atomic_t ngx_stat_handled0;
|
|
ngx_atomic_t *ngx_stat_handled = &ngx_stat_handled0;
|
|
static ngx_atomic_t ngx_stat_requests0;
|
|
ngx_atomic_t *ngx_stat_requests = &ngx_stat_requests0;
|
|
static ngx_atomic_t ngx_stat_active0;
|
|
ngx_atomic_t *ngx_stat_active = &ngx_stat_active0;
|
|
static ngx_atomic_t ngx_stat_reading0;
|
|
ngx_atomic_t *ngx_stat_reading = &ngx_stat_reading0;
|
|
static ngx_atomic_t ngx_stat_writing0;
|
|
ngx_atomic_t *ngx_stat_writing = &ngx_stat_writing0;
|
|
static 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("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
|
|
};
|
|
|
|
|
|
static 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_WIN32)
|
|
|
|
/* handle signals from master in case of network inactivity */
|
|
|
|
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);
|
|
|
|
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_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;
|
|
}
|
|
}
|
|
|
|
/* iocp */
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* iocp */
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static char *
|
|
ngx_event_init_conf(ngx_cycle_t *cycle, void *conf)
|
|
{
|
|
#if (NGX_HAVE_REUSEPORT)
|
|
ngx_uint_t i;
|
|
ngx_listening_t *ls;
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
if (cycle->connection_n < cycle->listening.nelts + 1) {
|
|
|
|
/*
|
|
* there should be at least one connection for each listening
|
|
* socket, plus an additional connection for channel
|
|
*/
|
|
|
|
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
|
|
"%ui worker_connections are not enough "
|
|
"for %ui listening sockets",
|
|
cycle->connection_n, cycle->listening.nelts);
|
|
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
#if (NGX_HAVE_REUSEPORT)
|
|
|
|
ls = cycle->listening.elts;
|
|
for (i = 0; i < cycle->listening.nelts; i++) {
|
|
|
|
if (!ls[i].reuseport || ls[i].worker != 0) {
|
|
continue;
|
|
}
|
|
|
|
if (ngx_clone_listening(cycle, &ls[i]) != NGX_OK) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
/* cloning may change cycle->listening.elts */
|
|
|
|
ls = cycle->listening.elts;
|
|
}
|
|
|
|
#endif
|
|
|
|
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;
|
|
ngx_str_set(&shm.name, "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, %uA",
|
|
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_WIN32)
|
|
|
|
/*
|
|
* disable accept mutex on win32 as it may cause deadlock if
|
|
* grabbed by a process which can't accept connections
|
|
*/
|
|
|
|
ngx_use_accept_mutex = 0;
|
|
|
|
#endif
|
|
|
|
ngx_queue_init(&ngx_posted_accept_events);
|
|
ngx_queue_init(&ngx_posted_events);
|
|
|
|
if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
for (m = 0; cycle->modules[m]; m++) {
|
|
if (cycle->modules[m]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
if (cycle->modules[m]->ctx_index != ecf->use) {
|
|
continue;
|
|
}
|
|
|
|
module = cycle->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;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
|
|
ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
|
|
"the \"timer_resolution\" directive is not supported "
|
|
"with the configured event method, ignored");
|
|
ngx_timer_resolution = 0;
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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];
|
|
} 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++) {
|
|
|
|
#if (NGX_HAVE_REUSEPORT)
|
|
if (ls[i].reuseport && ls[i].worker != ngx_worker) {
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
c = ngx_get_connection(ls[i].fd, cycle->log);
|
|
|
|
if (c == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
c->type = ls[i].type;
|
|
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 = (c->type == SOCK_STREAM) ? ngx_event_accept
|
|
: ngx_event_recvmsg;
|
|
|
|
#if (NGX_HAVE_REUSEPORT)
|
|
|
|
if (ls[i].reuseport) {
|
|
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
#endif
|
|
|
|
if (ngx_use_accept_mutex) {
|
|
continue;
|
|
}
|
|
|
|
#if (NGX_HAVE_EPOLLEXCLUSIVE)
|
|
|
|
if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)
|
|
&& ccf->worker_processes > 1)
|
|
{
|
|
if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)
|
|
== NGX_ERROR)
|
|
{
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
#endif
|
|
|
|
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 = ngx_count_modules(cf->cycle, NGX_EVENT_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; cf->cycle->modules[i]; i++) {
|
|
if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
m = cf->cycle->modules[i]->ctx;
|
|
|
|
if (m->create_conf) {
|
|
(*ctx)[cf->cycle->modules[i]->ctx_index] =
|
|
m->create_conf(cf->cycle);
|
|
if ((*ctx)[cf->cycle->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; cf->cycle->modules[i]; i++) {
|
|
if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
m = cf->cycle->modules[i]->ctx;
|
|
|
|
if (m->init_conf) {
|
|
rv = m->init_conf(cf->cycle,
|
|
(*ctx)[cf->cycle->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";
|
|
}
|
|
|
|
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; cf->cycle->modules[m]; m++) {
|
|
if (cf->cycle->modules[m]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
module = cf->cycle->modules[m]->ctx;
|
|
if (module->name->len == value[1].len) {
|
|
if (ngx_strcmp(module->name->data, value[1].data) == 0) {
|
|
ecf->use = cf->cycle->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
|
|
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_DEVPOLL) && !(NGX_TEST_BUILD_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; cycle->modules[i]; i++) {
|
|
|
|
if (cycle->modules[i]->type != NGX_EVENT_MODULE) {
|
|
continue;
|
|
}
|
|
|
|
event_module = cycle->modules[i]->ctx;
|
|
|
|
if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
module = cycle->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, 0);
|
|
ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);
|
|
|
|
return NGX_CONF_OK;
|
|
}
|