Event API¶
NGINX's event-driven architecture is built on OS-level mechanisms (epoll, kqueue, select). Modules interact with the event loop through ngx_event_t and related functions.
Event Structure¶
struct ngx_event_t {
void *data; /* User data (usually ngx_connection_t *) */
ngx_event_handler_pt handler; /* Callback when event fires */
ngx_log_t *log; /* Log for this event */
unsigned ready:1; /* Event is ready */
unsigned active:1; /* Event is in the event queue */
unsigned timedout:1; /* Event timed out */
unsigned timer_set:1;/* Timer is set */
unsigned eof:1; /* EOF received */
unsigned error:1; /* Error occurred */
/* ... more flags ... */
};
typedef void (*ngx_event_handler_pt)(ngx_event_t *ev);
Timers¶
One-Shot Timer¶
static ngx_event_t timer_event;
static ngx_connection_t dumb_conn; /* Required: event needs a connection */
static void
my_timer_handler(ngx_event_t *ev)
{
ngx_log_error(NGX_LOG_INFO, ev->log, 0, "timer fired!");
/* Re-arm timer for periodic execution */
ngx_add_timer(ev, 5000);
}
static ngx_int_t
ngx_http_mymodule_init_process(ngx_cycle_t *cycle)
{
ngx_memzero(&timer_event, sizeof(ngx_event_t));
ngx_memzero(&dumb_conn, sizeof(ngx_connection_t));
dumb_conn.fd = -1; /* No real file descriptor */
timer_event.handler = my_timer_handler;
timer_event.log = cycle->log;
timer_event.data = &dumb_conn;
/* Fire after 5 seconds */
ngx_add_timer(&timer_event, 5000);
return NGX_OK;
}
Cancelling a Timer¶
static void
cancel_my_timer(void)
{
if (timer_event.timer_set) {
ngx_del_timer(&timer_event);
}
}
Request Timeout Example¶
static void
ngx_http_mymodule_timeout_handler(ngx_event_t *ev)
{
ngx_connection_t *c = ev->data;
ngx_http_request_t *r = c->data;
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"request timed out");
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
}
static ngx_int_t
ngx_http_mymodule_set_timeout(ngx_http_request_t *r, ngx_msec_t timeout)
{
ngx_event_t *wev = r->connection->write;
/* Set timeout handler */
wev->handler = ngx_http_mymodule_timeout_handler;
/* Add timer */
ngx_add_timer(wev, timeout);
return NGX_OK;
}
Connection Events¶
Read Event Handler¶
static void
ngx_http_mymodule_read_handler(ngx_event_t *rev)
{
ngx_connection_t *c = rev->data;
ngx_http_request_t *r = c->data;
ssize_t n;
u_char buf[4096];
if (rev->timedout) {
ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT,
"client timed out");
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
for ( ;; ) {
n = c->recv(c, buf, sizeof(buf));
if (n == NGX_AGAIN) {
/* No data available yet */
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
}
return;
}
if (n == 0) {
/* Connection closed */
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client closed connection");
ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
return;
}
if (n < 0) {
/* Error */
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return;
}
/* Process n bytes in buf */
ngx_log_error(NGX_LOG_DEBUG, c->log, 0,
"received %z bytes", n);
}
}
Write Event Handler¶
static void
ngx_http_mymodule_write_handler(ngx_event_t *wev)
{
ngx_connection_t *c = wev->data;
ngx_http_request_t *r = c->data;
ngx_chain_t *out;
ngx_int_t rc;
if (wev->timedout) {
ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, "write timed out");
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
/* Get pending output chain from module context */
ngx_http_mymodule_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_mymodule_module);
out = ctx->out;
if (out == NULL) {
/* Nothing to send */
if (wev->timer_set) {
ngx_del_timer(wev);
}
ngx_http_finalize_request(r, NGX_OK);
return;
}
/* Send data */
rc = ngx_http_output_filter(r, out);
if (rc == NGX_OK || rc == NGX_DONE) {
ctx->out = NULL;
ngx_http_finalize_request(r, rc);
return;
}
if (rc == NGX_AGAIN) {
/* Need to wait for socket to be writable */
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
}
return;
}
/* Error */
ngx_http_finalize_request(r, rc);
}
Event Loop Integration¶
Adding Events to the Loop¶
/* Add read event */
if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT) != NGX_OK) {
return NGX_ERROR;
}
/* Add write event */
if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT) != NGX_OK) {
return NGX_ERROR;
}
Handling Events After Add¶
/* After receiving data, re-arm read event */
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
return NGX_ERROR;
}
/* After sending data, re-arm write event if needed */
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
return NGX_ERROR;
}
Removing Events¶
/* Remove read event */
if (ngx_del_event(rev, NGX_READ_EVENT, NGX_CLOSE_EVENT) != NGX_OK) {
return NGX_ERROR;
}
Posted Events¶
Execute code later in the event loop iteration:
static void
ngx_http_mymodule_posted_handler(ngx_event_t *ev)
{
ngx_http_request_t *r = ev->data;
/* Continue request processing */
ngx_http_mymodule_continue(r);
}
static ngx_int_t
ngx_http_mymodule_defer_processing(ngx_http_request_t *r)
{
ngx_event_t *ev;
ev = ngx_pcalloc(r->pool, sizeof(ngx_event_t));
if (ev == NULL) {
return NGX_ERROR;
}
ev->handler = ngx_http_mymodule_posted_handler;
ev->data = r;
ev->log = r->connection->log;
/* Post to be executed in next loop iteration */
ngx_post_event(ev, &ngx_posted_events);
return NGX_DONE;
}
Complete Async Handler Example¶
typedef struct {
ngx_http_request_t *request;
ngx_event_t timer;
ngx_uint_t state;
u_char *response;
size_t response_len;
} ngx_http_mymodule_ctx_t;
#define STATE_WAITING 0
#define STATE_PROCESSING 1
#define STATE_DONE 2
static void
ngx_http_mymodule_timer_handler(ngx_event_t *ev)
{
ngx_http_mymodule_ctx_t *ctx = ev->data;
ngx_http_request_t *r = ctx->request;
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
"async operation complete");
ctx->state = STATE_DONE;
/* Resume request processing */
r->write_event_handler = ngx_http_core_run_phases;
ngx_http_core_run_phases(r);
}
static ngx_int_t
ngx_http_mymodule_handler(ngx_http_request_t *r)
{
ngx_http_mymodule_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_mymodule_module);
if (ctx == NULL) {
/* First call - start async operation */
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_mymodule_ctx_t));
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ctx->request = r;
ctx->state = STATE_WAITING;
ngx_http_set_ctx(r, ctx, ngx_http_mymodule_module);
/* Simulate async operation with timer */
ctx->timer.handler = ngx_http_mymodule_timer_handler;
ctx->timer.data = ctx;
ctx->timer.log = r->connection->log;
ngx_add_timer(&ctx->timer, 100); /* 100ms delay */
/* Tell NGINX we're not done yet */
r->main->count++;
return NGX_DONE;
}
if (ctx->state != STATE_DONE) {
/* Still waiting */
return NGX_DONE;
}
/* Async operation complete - send response */
return ngx_http_mymodule_send_response(r);
}
Best Practices¶
Event Guidelines
- Always check
timedout- Handle timeout in every event handler - Use
NGX_AGAIN- Return this when waiting for I/O - Re-arm events - Call
ngx_handle_read/write_eventafter processing - Clean up timers - Delete timers before finalizing requests
- Increment
r->main->count- When deferring request processing