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
13#if (NGX_TEST_BUILD_EVENTPORT)
14
15#define ushort_t  u_short
16#define uint_t    u_int
17
18#ifndef CLOCK_REALTIME
19#define CLOCK_REALTIME          0
20typedef int     clockid_t;
21typedef void *  timer_t;
22#endif
23
24/* Solaris declarations */
25
26#define PORT_SOURCE_AIO         1
27#define PORT_SOURCE_TIMER       2
28#define PORT_SOURCE_USER        3
29#define PORT_SOURCE_FD          4
30#define PORT_SOURCE_ALERT       5
31#define PORT_SOURCE_MQ          6
32
33#ifndef ETIME
34#define ETIME                   64
35#endif
36
37#define SIGEV_PORT              4
38
39typedef struct {
40    int         portev_events;  /* event data is source specific */
41    ushort_t    portev_source;  /* event source */
42    ushort_t    portev_pad;     /* port internal use */
43    uintptr_t   portev_object;  /* source specific object */
44    void       *portev_user;    /* user cookie */
45} port_event_t;
46
47typedef struct  port_notify {
48    int         portnfy_port;   /* bind request(s) to port */
49    void       *portnfy_user;   /* user defined */
50} port_notify_t;
51
52#if (__FreeBSD__ && __FreeBSD_version < 700005) || (NGX_DARWIN)
53
54typedef struct itimerspec {     /* definition per POSIX.4 */
55    struct timespec it_interval;/* timer period */
56    struct timespec it_value;   /* timer expiration */
57} itimerspec_t;
58
59#endif
60
61int port_create(void);
62
63int port_create(void)
64{
65    return -1;
66}
67
68
69int port_associate(int port, int source, uintptr_t object, int events,
70    void *user);
71
72int port_associate(int port, int source, uintptr_t object, int events,
73    void *user)
74{
75    return -1;
76}
77
78
79int port_dissociate(int port, int source, uintptr_t object);
80
81int port_dissociate(int port, int source, uintptr_t object)
82{
83    return -1;
84}
85
86
87int port_getn(int port, port_event_t list[], uint_t max, uint_t *nget,
88    struct timespec *timeout);
89
90int port_getn(int port, port_event_t list[], uint_t max, uint_t *nget,
91    struct timespec *timeout)
92{
93    return -1;
94}
95
96int port_send(int port, int events, void *user);
97
98int port_send(int port, int events, void *user)
99{
100    return -1;
101}
102
103
104int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid);
105
106int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
107{
108    return -1;
109}
110
111
112int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
113    struct itimerspec *ovalue);
114
115int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
116    struct itimerspec *ovalue)
117{
118    return -1;
119}
120
121
122int timer_delete(timer_t timerid);
123
124int timer_delete(timer_t timerid)
125{
126    return -1;
127}
128
129#endif
130
131
132typedef struct {
133    ngx_uint_t  events;
134} ngx_eventport_conf_t;
135
136
137static ngx_int_t ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer);
138static void ngx_eventport_done(ngx_cycle_t *cycle);
139static ngx_int_t ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event,
140    ngx_uint_t flags);
141static ngx_int_t ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event,
142    ngx_uint_t flags);
143static ngx_int_t ngx_eventport_notify(ngx_event_handler_pt handler);
144static ngx_int_t ngx_eventport_process_events(ngx_cycle_t *cycle,
145    ngx_msec_t timer, ngx_uint_t flags);
146
147static void *ngx_eventport_create_conf(ngx_cycle_t *cycle);
148static char *ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf);
149
150static int            ep = -1;
151static port_event_t  *event_list;
152static ngx_uint_t     nevents;
153static timer_t        event_timer = (timer_t) -1;
154static ngx_event_t    notify_event;
155
156static ngx_str_t      eventport_name = ngx_string("eventport");
157
158
159static ngx_command_t  ngx_eventport_commands[] = {
160
161    { ngx_string("eventport_events"),
162      NGX_EVENT_CONF|NGX_CONF_TAKE1,
163      ngx_conf_set_num_slot,
164      0,
165      offsetof(ngx_eventport_conf_t, events),
166      NULL },
167
168      ngx_null_command
169};
170
171
172static ngx_event_module_t  ngx_eventport_module_ctx = {
173    &eventport_name,
174    ngx_eventport_create_conf,             /* create configuration */
175    ngx_eventport_init_conf,               /* init configuration */
176
177    {
178        ngx_eventport_add_event,           /* add an event */
179        ngx_eventport_del_event,           /* delete an event */
180        ngx_eventport_add_event,           /* enable an event */
181        ngx_eventport_del_event,           /* disable an event */
182        NULL,                              /* add an connection */
183        NULL,                              /* delete an connection */
184        ngx_eventport_notify,              /* trigger a notify */
185        ngx_eventport_process_events,      /* process the events */
186        ngx_eventport_init,                /* init the events */
187        ngx_eventport_done,                /* done the events */
188    }
189
190};
191
192ngx_module_t  ngx_eventport_module = {
193    NGX_MODULE_V1,
194    &ngx_eventport_module_ctx,             /* module context */
195    ngx_eventport_commands,                /* module directives */
196    NGX_EVENT_MODULE,                      /* module type */
197    NULL,                                  /* init master */
198    NULL,                                  /* init module */
199    NULL,                                  /* init process */
200    NULL,                                  /* init thread */
201    NULL,                                  /* exit thread */
202    NULL,                                  /* exit process */
203    NULL,                                  /* exit master */
204    NGX_MODULE_V1_PADDING
205};
206
207
208static ngx_int_t
209ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer)
210{
211    port_notify_t          pn;
212    struct itimerspec      its;
213    struct sigevent        sev;
214    ngx_eventport_conf_t  *epcf;
215
216    epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_eventport_module);
217
218    if (ep == -1) {
219        ep = port_create();
220
221        if (ep == -1) {
222            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
223                          "port_create() failed");
224            return NGX_ERROR;
225        }
226
227        notify_event.active = 1;
228        notify_event.log = cycle->log;
229    }
230
231    if (nevents < epcf->events) {
232        if (event_list) {
233            ngx_free(event_list);
234        }
235
236        event_list = ngx_alloc(sizeof(port_event_t) * epcf->events,
237                               cycle->log);
238        if (event_list == NULL) {
239            return NGX_ERROR;
240        }
241    }
242
243    ngx_event_flags = NGX_USE_EVENTPORT_EVENT;
244
245    if (timer) {
246        ngx_memzero(&pn, sizeof(port_notify_t));
247        pn.portnfy_port = ep;
248
249        ngx_memzero(&sev, sizeof(struct sigevent));
250        sev.sigev_notify = SIGEV_PORT;
251#if !(NGX_TEST_BUILD_EVENTPORT)
252        sev.sigev_value.sival_ptr = &pn;
253#endif
254
255        if (timer_create(CLOCK_REALTIME, &sev, &event_timer) == -1) {
256            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
257                          "timer_create() failed");
258            return NGX_ERROR;
259        }
260
261        its.it_interval.tv_sec = timer / 1000;
262        its.it_interval.tv_nsec = (timer % 1000) * 1000000;
263        its.it_value.tv_sec = timer / 1000;
264        its.it_value.tv_nsec = (timer % 1000) * 1000000;
265
266        if (timer_settime(event_timer, 0, &its, NULL) == -1) {
267            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
268                          "timer_settime() failed");
269            return NGX_ERROR;
270        }
271
272        ngx_event_flags |= NGX_USE_TIMER_EVENT;
273    }
274
275    nevents = epcf->events;
276
277    ngx_io = ngx_os_io;
278
279    ngx_event_actions = ngx_eventport_module_ctx.actions;
280
281    return NGX_OK;
282}
283
284
285static void
286ngx_eventport_done(ngx_cycle_t *cycle)
287{
288    if (event_timer != (timer_t) -1) {
289        if (timer_delete(event_timer) == -1) {
290            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
291                          "timer_delete() failed");
292        }
293
294        event_timer = (timer_t) -1;
295    }
296
297    if (close(ep) == -1) {
298        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
299                      "close() event port failed");
300    }
301
302    ep = -1;
303
304    ngx_free(event_list);
305
306    event_list = NULL;
307    nevents = 0;
308}
309
310
311static ngx_int_t
312ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
313{
314    ngx_int_t          events, prev;
315    ngx_event_t       *e;
316    ngx_connection_t  *c;
317
318    c = ev->data;
319
320    events = event;
321
322    if (event == NGX_READ_EVENT) {
323        e = c->write;
324        prev = POLLOUT;
325#if (NGX_READ_EVENT != POLLIN)
326        events = POLLIN;
327#endif
328
329    } else {
330        e = c->read;
331        prev = POLLIN;
332#if (NGX_WRITE_EVENT != POLLOUT)
333        events = POLLOUT;
334#endif
335    }
336
337    if (e->oneshot) {
338        events |= prev;
339    }
340
341    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
342                   "eventport add event: fd:%d ev:%04Xi", c->fd, events);
343
344    if (port_associate(ep, PORT_SOURCE_FD, c->fd, events,
345                       (void *) ((uintptr_t) ev | ev->instance))
346        == -1)
347    {
348        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
349                      "port_associate() failed");
350        return NGX_ERROR;
351    }
352
353    ev->active = 1;
354    ev->oneshot = 1;
355
356    return NGX_OK;
357}
358
359
360static ngx_int_t
361ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
362{
363    ngx_event_t       *e;
364    ngx_connection_t  *c;
365
366    /*
367     * when the file descriptor is closed, the event port automatically
368     * dissociates it from the port, so we do not need to dissociate explicitly
369     * the event before the closing the file descriptor
370     */
371
372    if (flags & NGX_CLOSE_EVENT) {
373        ev->active = 0;
374        ev->oneshot = 0;
375        return NGX_OK;
376    }
377
378    c = ev->data;
379
380    if (event == NGX_READ_EVENT) {
381        e = c->write;
382        event = POLLOUT;
383
384    } else {
385        e = c->read;
386        event = POLLIN;
387    }
388
389    if (e->oneshot) {
390        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
391                       "eventport change event: fd:%d ev:%04Xi", c->fd, event);
392
393        if (port_associate(ep, PORT_SOURCE_FD, c->fd, event,
394                           (void *) ((uintptr_t) ev | ev->instance))
395            == -1)
396        {
397            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
398                          "port_associate() failed");
399            return NGX_ERROR;
400        }
401
402    } else {
403        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
404                       "eventport del event: fd:%d", c->fd);
405
406        if (port_dissociate(ep, PORT_SOURCE_FD, c->fd) == -1) {
407            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
408                          "port_dissociate() failed");
409            return NGX_ERROR;
410        }
411    }
412
413    ev->active = 0;
414    ev->oneshot = 0;
415
416    return NGX_OK;
417}
418
419
420static ngx_int_t
421ngx_eventport_notify(ngx_event_handler_pt handler)
422{
423    notify_event.handler = handler;
424
425    if (port_send(ep, 0, &notify_event) != 0) {
426        ngx_log_error(NGX_LOG_ALERT, notify_event.log, ngx_errno,
427                      "port_send() failed");
428        return NGX_ERROR;
429    }
430
431    return NGX_OK;
432}
433
434
435static ngx_int_t
436ngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
437    ngx_uint_t flags)
438{
439    int                 n, revents;
440    u_int               events;
441    ngx_err_t           err;
442    ngx_int_t           instance;
443    ngx_uint_t          i, level;
444    ngx_event_t        *ev, *rev, *wev;
445    ngx_queue_t        *queue;
446    ngx_connection_t   *c;
447    struct timespec     ts, *tp;
448
449    if (timer == NGX_TIMER_INFINITE) {
450        tp = NULL;
451
452    } else {
453        ts.tv_sec = timer / 1000;
454        ts.tv_nsec = (timer % 1000) * 1000000;
455        tp = &ts;
456    }
457
458    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
459                   "eventport timer: %M", timer);
460
461    events = 1;
462
463    n = port_getn(ep, event_list, (u_int) nevents, &events, tp);
464
465    err = ngx_errno;
466
467    if (flags & NGX_UPDATE_TIME) {
468        ngx_time_update();
469    }
470
471    if (n == -1) {
472        if (err == ETIME) {
473            if (timer != NGX_TIMER_INFINITE) {
474                return NGX_OK;
475            }
476
477            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
478                          "port_getn() returned no events without timeout");
479            return NGX_ERROR;
480        }
481
482        level = (err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT;
483        ngx_log_error(level, cycle->log, err, "port_getn() failed");
484        return NGX_ERROR;
485    }
486
487    if (events == 0) {
488        if (timer != NGX_TIMER_INFINITE) {
489            return NGX_OK;
490        }
491
492        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
493                      "port_getn() returned no events without timeout");
494        return NGX_ERROR;
495    }
496
497    for (i = 0; i < events; i++) {
498
499        if (event_list[i].portev_source == PORT_SOURCE_TIMER) {
500            ngx_time_update();
501            continue;
502        }
503
504        ev = event_list[i].portev_user;
505
506        switch (event_list[i].portev_source) {
507
508        case PORT_SOURCE_FD:
509
510            instance = (uintptr_t) ev & 1;
511            ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1);
512
513            if (ev->closed || ev->instance != instance) {
514
515                /*
516                 * the stale event from a file descriptor
517                 * that was just closed in this iteration
518                 */
519
520                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
521                               "eventport: stale event %p", ev);
522                continue;
523            }
524
525            revents = event_list[i].portev_events;
526
527            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
528                           "eventport: fd:%d, ev:%04Xd",
529                           (int) event_list[i].portev_object, revents);
530
531            if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
532                ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
533                               "port_getn() error fd:%d ev:%04Xd",
534                               (int) event_list[i].portev_object, revents);
535            }
536
537            if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
538                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
539                              "strange port_getn() events fd:%d ev:%04Xd",
540                              (int) event_list[i].portev_object, revents);
541            }
542
543            if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
544
545                /*
546                 * if the error events were returned, add POLLIN and POLLOUT
547                 * to handle the events at least in one active handler
548                 */
549
550                revents |= POLLIN|POLLOUT;
551            }
552
553            c = ev->data;
554            rev = c->read;
555            wev = c->write;
556
557            rev->active = 0;
558            wev->active = 0;
559
560            if (revents & POLLIN) {
561                rev->ready = 1;
562
563                if (flags & NGX_POST_EVENTS) {
564                    queue = rev->accept ? &ngx_posted_accept_events
565                                        : &ngx_posted_events;
566
567                    ngx_post_event(rev, queue);
568
569                } else {
570                    rev->handler(rev);
571
572                    if (ev->closed || ev->instance != instance) {
573                        continue;
574                    }
575                }
576
577                if (rev->accept) {
578                    if (ngx_use_accept_mutex) {
579                        ngx_accept_events = 1;
580                        continue;
581                    }
582
583                    if (port_associate(ep, PORT_SOURCE_FD, c->fd, POLLIN,
584                                       (void *) ((uintptr_t) ev | ev->instance))
585                        == -1)
586                    {
587                        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
588                                      "port_associate() failed");
589                        return NGX_ERROR;
590                    }
591                }
592            }
593
594            if (revents & POLLOUT) {
595                wev->ready = 1;
596
597                if (flags & NGX_POST_EVENTS) {
598                    ngx_post_event(wev, &ngx_posted_events);
599
600                } else {
601                    wev->handler(wev);
602                }
603            }
604
605            continue;
606
607        case PORT_SOURCE_USER:
608
609            ev->handler(ev);
610
611            continue;
612
613        default:
614            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
615                          "unexpected eventport object %d",
616                          (int) event_list[i].portev_object);
617            continue;
618        }
619    }
620
621    return NGX_OK;
622}
623
624
625static void *
626ngx_eventport_create_conf(ngx_cycle_t *cycle)
627{
628    ngx_eventport_conf_t  *epcf;
629
630    epcf = ngx_palloc(cycle->pool, sizeof(ngx_eventport_conf_t));
631    if (epcf == NULL) {
632        return NULL;
633    }
634
635    epcf->events = NGX_CONF_UNSET;
636
637    return epcf;
638}
639
640
641static char *
642ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf)
643{
644    ngx_eventport_conf_t *epcf = conf;
645
646    ngx_conf_init_uint_value(epcf->events, 32);
647
648    return NGX_CONF_OK;
649}
650