1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_event.h>
11
12
13static ngx_int_t ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer);
14static void ngx_poll_done(ngx_cycle_t *cycle);
15static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event,
16    ngx_uint_t flags);
17static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event,
18    ngx_uint_t flags);
19static ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
20    ngx_uint_t flags);
21static char *ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf);
22
23
24static struct pollfd  *event_list;
25static ngx_uint_t      nevents;
26
27
28static ngx_str_t           poll_name = ngx_string("poll");
29
30static ngx_event_module_t  ngx_poll_module_ctx = {
31    &poll_name,
32    NULL,                                  /* create configuration */
33    ngx_poll_init_conf,                    /* init configuration */
34
35    {
36        ngx_poll_add_event,                /* add an event */
37        ngx_poll_del_event,                /* delete an event */
38        ngx_poll_add_event,                /* enable an event */
39        ngx_poll_del_event,                /* disable an event */
40        NULL,                              /* add an connection */
41        NULL,                              /* delete an connection */
42        NULL,                              /* trigger a notify */
43        ngx_poll_process_events,           /* process the events */
44        ngx_poll_init,                     /* init the events */
45        ngx_poll_done                      /* done the events */
46    }
47
48};
49
50ngx_module_t  ngx_poll_module = {
51    NGX_MODULE_V1,
52    &ngx_poll_module_ctx,                  /* module context */
53    NULL,                                  /* module directives */
54    NGX_EVENT_MODULE,                      /* module type */
55    NULL,                                  /* init master */
56    NULL,                                  /* init module */
57    NULL,                                  /* init process */
58    NULL,                                  /* init thread */
59    NULL,                                  /* exit thread */
60    NULL,                                  /* exit process */
61    NULL,                                  /* exit master */
62    NGX_MODULE_V1_PADDING
63};
64
65
66
67static ngx_int_t
68ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
69{
70    struct pollfd   *list;
71
72    if (event_list == NULL) {
73        nevents = 0;
74    }
75
76    if (ngx_process >= NGX_PROCESS_WORKER
77        || cycle->old_cycle == NULL
78        || cycle->old_cycle->connection_n < cycle->connection_n)
79    {
80        list = ngx_alloc(sizeof(struct pollfd) * cycle->connection_n,
81                         cycle->log);
82        if (list == NULL) {
83            return NGX_ERROR;
84        }
85
86        if (event_list) {
87            ngx_memcpy(list, event_list, sizeof(ngx_event_t *) * nevents);
88            ngx_free(event_list);
89        }
90
91        event_list = list;
92    }
93
94    ngx_io = ngx_os_io;
95
96    ngx_event_actions = ngx_poll_module_ctx.actions;
97
98    ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_FD_EVENT;
99
100    return NGX_OK;
101}
102
103
104static void
105ngx_poll_done(ngx_cycle_t *cycle)
106{
107    ngx_free(event_list);
108
109    event_list = NULL;
110}
111
112
113static ngx_int_t
114ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
115{
116    ngx_event_t       *e;
117    ngx_connection_t  *c;
118
119    c = ev->data;
120
121    ev->active = 1;
122
123    if (ev->index != NGX_INVALID_INDEX) {
124        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
125                      "poll event fd:%d ev:%i is already set", c->fd, event);
126        return NGX_OK;
127    }
128
129    if (event == NGX_READ_EVENT) {
130        e = c->write;
131#if (NGX_READ_EVENT != POLLIN)
132        event = POLLIN;
133#endif
134
135    } else {
136        e = c->read;
137#if (NGX_WRITE_EVENT != POLLOUT)
138        event = POLLOUT;
139#endif
140    }
141
142    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
143                   "poll add event: fd:%d ev:%i", c->fd, event);
144
145    if (e == NULL || e->index == NGX_INVALID_INDEX) {
146        event_list[nevents].fd = c->fd;
147        event_list[nevents].events = (short) event;
148        event_list[nevents].revents = 0;
149
150        ev->index = nevents;
151        nevents++;
152
153    } else {
154        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
155                       "poll add index: %i", e->index);
156
157        event_list[e->index].events |= (short) event;
158        ev->index = e->index;
159    }
160
161    return NGX_OK;
162}
163
164
165static ngx_int_t
166ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
167{
168    ngx_event_t       *e;
169    ngx_connection_t  *c;
170
171    c = ev->data;
172
173    ev->active = 0;
174
175    if (ev->index == NGX_INVALID_INDEX) {
176        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
177                      "poll event fd:%d ev:%i is already deleted",
178                      c->fd, event);
179        return NGX_OK;
180    }
181
182    if (event == NGX_READ_EVENT) {
183        e = c->write;
184#if (NGX_READ_EVENT != POLLIN)
185        event = POLLIN;
186#endif
187
188    } else {
189        e = c->read;
190#if (NGX_WRITE_EVENT != POLLOUT)
191        event = POLLOUT;
192#endif
193    }
194
195    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
196                   "poll del event: fd:%d ev:%i", c->fd, event);
197
198    if (e == NULL || e->index == NGX_INVALID_INDEX) {
199        nevents--;
200
201        if (ev->index < nevents) {
202
203            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
204                           "index: copy event %ui to %i", nevents, ev->index);
205
206            event_list[ev->index] = event_list[nevents];
207
208            c = ngx_cycle->files[event_list[nevents].fd];
209
210            if (c->fd == -1) {
211                ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
212                              "unexpected last event");
213
214            } else {
215                if (c->read->index == nevents) {
216                    c->read->index = ev->index;
217                }
218
219                if (c->write->index == nevents) {
220                    c->write->index = ev->index;
221                }
222            }
223        }
224
225    } else {
226        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
227                       "poll del index: %i", e->index);
228
229        event_list[e->index].events &= (short) ~event;
230    }
231
232    ev->index = NGX_INVALID_INDEX;
233
234    return NGX_OK;
235}
236
237
238static ngx_int_t
239ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
240{
241    int                 ready, revents;
242    ngx_err_t           err;
243    ngx_uint_t          i, found, level;
244    ngx_event_t        *ev;
245    ngx_queue_t        *queue;
246    ngx_connection_t   *c;
247
248    /* NGX_TIMER_INFINITE == INFTIM */
249
250#if (NGX_DEBUG0)
251    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
252        for (i = 0; i < nevents; i++) {
253            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
254                           "poll: %ui: fd:%d ev:%04Xd",
255                           i, event_list[i].fd, event_list[i].events);
256        }
257    }
258#endif
259
260    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll timer: %M", timer);
261
262    ready = poll(event_list, (u_int) nevents, (int) timer);
263
264    err = (ready == -1) ? ngx_errno : 0;
265
266    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
267        ngx_time_update();
268    }
269
270    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
271                   "poll ready %d of %ui", ready, nevents);
272
273    if (err) {
274        if (err == NGX_EINTR) {
275
276            if (ngx_event_timer_alarm) {
277                ngx_event_timer_alarm = 0;
278                return NGX_OK;
279            }
280
281            level = NGX_LOG_INFO;
282
283        } else {
284            level = NGX_LOG_ALERT;
285        }
286
287        ngx_log_error(level, cycle->log, err, "poll() failed");
288        return NGX_ERROR;
289    }
290
291    if (ready == 0) {
292        if (timer != NGX_TIMER_INFINITE) {
293            return NGX_OK;
294        }
295
296        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
297                      "poll() returned no events without timeout");
298        return NGX_ERROR;
299    }
300
301    for (i = 0; i < nevents && ready; i++) {
302
303        revents = event_list[i].revents;
304
305#if 1
306        ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
307                       "poll: %ui: fd:%d ev:%04Xd rev:%04Xd",
308                       i, event_list[i].fd, event_list[i].events, revents);
309#else
310        if (revents) {
311            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
312                           "poll: %ui: fd:%d ev:%04Xd rev:%04Xd",
313                           i, event_list[i].fd, event_list[i].events, revents);
314        }
315#endif
316
317        if (revents & POLLNVAL) {
318            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
319                          "poll() error fd:%d ev:%04Xd rev:%04Xd",
320                          event_list[i].fd, event_list[i].events, revents);
321        }
322
323        if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
324            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
325                          "strange poll() events fd:%d ev:%04Xd rev:%04Xd",
326                          event_list[i].fd, event_list[i].events, revents);
327        }
328
329        if (event_list[i].fd == -1) {
330            /*
331             * the disabled event, a workaround for our possible bug,
332             * see the comment below
333             */
334            continue;
335        }
336
337        c = ngx_cycle->files[event_list[i].fd];
338
339        if (c->fd == -1) {
340            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected event");
341
342            /*
343             * it is certainly our fault and it should be investigated,
344             * in the meantime we disable this event to avoid a CPU spinning
345             */
346
347            if (i == nevents - 1) {
348                nevents--;
349            } else {
350                event_list[i].fd = -1;
351            }
352
353            continue;
354        }
355
356        if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
357
358            /*
359             * if the error events were returned, add POLLIN and POLLOUT
360             * to handle the events at least in one active handler
361             */
362
363            revents |= POLLIN|POLLOUT;
364        }
365
366        found = 0;
367
368        if ((revents & POLLIN) && c->read->active) {
369            found = 1;
370
371            ev = c->read;
372            ev->ready = 1;
373
374            queue = ev->accept ? &ngx_posted_accept_events
375                               : &ngx_posted_events;
376
377            ngx_post_event(ev, queue);
378        }
379
380        if ((revents & POLLOUT) && c->write->active) {
381            found = 1;
382
383            ev = c->write;
384            ev->ready = 1;
385
386            ngx_post_event(ev, &ngx_posted_events);
387        }
388
389        if (found) {
390            ready--;
391            continue;
392        }
393    }
394
395    if (ready != 0) {
396        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "poll ready != events");
397    }
398
399    return NGX_OK;
400}
401
402
403static char *
404ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf)
405{
406    ngx_event_conf_t  *ecf;
407
408    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
409
410    if (ecf->use != ngx_poll_module.ctx_index) {
411        return NGX_CONF_OK;
412    }
413
414    return NGX_CONF_OK;
415}
416