1  /*
2  * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  *   list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  *   this list of conditions and the following disclaimer in the documentation
12  *   and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26 
27 #include <ngx_config.h>
28 #include <ngx_core.h>
29 #include <ngx_event.h>
30 #include <ngx_channel.h>
31 #include <ngx_cycle.h>
32 
33 #include <pthread.h>
34 
35 #if (NGX_HAVE_FSTACK)
36 static void * ngx_ff_host_event_create_conf(ngx_cycle_t *cycle);
37 static char * ngx_ff_host_event_init_conf(ngx_cycle_t *cycle,
38     void *conf);
39 static ngx_int_t ngx_ff_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer);
40 static ngx_int_t ngx_ff_epoll_add_event(ngx_event_t *ev,
41     ngx_int_t event, ngx_uint_t flags);
42 static ngx_int_t ngx_ff_epoll_del_event(ngx_event_t *ev,
43     ngx_int_t event, ngx_uint_t flags);
44 static ngx_int_t ngx_ff_epoll_process_events(ngx_cycle_t *cycle,
45     ngx_msec_t timer, ngx_uint_t flags);
46 
47 static int ep = -1;
48 static struct epoll_event *event_list;
49 static ngx_uint_t nevents;
50 
51 typedef struct {
52     ngx_uint_t  events;
53 } ngx_ff_host_event_conf_t;
54 
55 
56 static ngx_command_t  ngx_ff_host_event_commands[] = {
57     ngx_null_command
58 };
59 
60 ngx_core_module_t  ngx_ff_host_event_module_ctx = {
61     ngx_string("ff_host_event"),
62     ngx_ff_host_event_create_conf,          /* create configuration */
63     ngx_ff_host_event_init_conf,            /* init configuration */
64 };
65 
66 ngx_module_t  ngx_ff_host_event_module = {
67     NGX_MODULE_V1,
68     &ngx_ff_host_event_module_ctx,          /* module context */
69     ngx_ff_host_event_commands,             /* module directives */
70     NGX_CORE_MODULE,                     /* module type */
71     NULL,                                /* init master */
72     NULL,                                /* init module */
73     NULL,                                /* init process */
74     NULL,                                /* init thread */
75     NULL,                                /* exit thread */
76     NULL,                                /* exit process */
77     NULL,                                /* exit master */
78     NGX_MODULE_V1_PADDING
79 };
80 
81 static void *
ngx_ff_host_event_create_conf(ngx_cycle_t * cycle)82 ngx_ff_host_event_create_conf(ngx_cycle_t *cycle)
83 {
84     ngx_ff_host_event_conf_t  *cf;
85     cf = ngx_palloc(cycle->pool, sizeof(ngx_ff_host_event_conf_t));
86     if (cf == NULL) {
87         return NULL;
88     }
89     cf->events = NGX_CONF_UNSET;
90     return cf;
91 }
92 
93 static char *
ngx_ff_host_event_init_conf(ngx_cycle_t * cycle,void * conf)94 ngx_ff_host_event_init_conf(ngx_cycle_t *cycle, void *conf)
95 {
96     ngx_ff_host_event_conf_t *cf = conf;
97     cf->events = 8;
98     return NGX_CONF_OK;
99 }
100 
101 
102 static ngx_int_t
ngx_ff_epoll_init(ngx_cycle_t * cycle,ngx_msec_t timer)103 ngx_ff_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
104 {
105     if (ep == -1) {
106         /* The size is just a hint */
107         ep = epoll_create(100);
108 
109         if (ep == -1) {
110             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
111                           "epoll_create() failed");
112             return NGX_ERROR;
113         }
114     }
115 
116     if (event_list) {
117         ngx_free(event_list);
118     }
119 
120     nevents = 64;
121 
122     event_list = ngx_alloc(sizeof(struct epoll_event) * nevents, cycle->log);
123     if (event_list == NULL) {
124         return NGX_ERROR;
125     }
126 
127     return NGX_OK;
128 }
129 
130 static void
ngx_ff_epoll_done(ngx_cycle_t * cycle)131 ngx_ff_epoll_done(ngx_cycle_t *cycle)
132 {
133     if (close(ep) == -1) {
134         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
135                       "epoll close() failed");
136     }
137 
138     ep = -1;
139 
140     ngx_free(event_list);
141 
142     event_list = NULL;
143     nevents = 0;
144 }
145 
146 static ngx_int_t
ngx_ff_epoll_add_event(ngx_event_t * ev,ngx_int_t event,ngx_uint_t flags)147 ngx_ff_epoll_add_event(ngx_event_t *ev, ngx_int_t event,
148     ngx_uint_t flags)
149 {
150     int                  op;
151     uint32_t             events, prev;
152     ngx_event_t         *e;
153     ngx_connection_t    *c;
154     struct epoll_event   ee;
155 
156     c = ev->data;
157 
158     events = (uint32_t) event;
159 
160     if (event == NGX_READ_EVENT) {
161         e = c->write;
162         prev = EPOLLOUT;
163 #if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)
164         events = EPOLLIN|EPOLLRDHUP;
165 #endif
166 
167     } else {
168         e = c->read;
169         prev = EPOLLIN|EPOLLRDHUP;
170 #if (NGX_WRITE_EVENT != EPOLLOUT)
171         events = EPOLLOUT;
172 #endif
173     }
174 
175     if (e->active) {
176         op = EPOLL_CTL_MOD;
177         events |= prev;
178 
179     } else {
180         op = EPOLL_CTL_ADD;
181     }
182 
183     ee.events = events | (uint32_t) flags;
184     ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
185 
186     ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
187                    "epoll add event: fd:%d op:%d ev:%08XD",
188                    c->fd, op, ee.events);
189 
190     if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
191         ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
192                       "epoll_ctl(%d, %d) failed", op, c->fd);
193         return NGX_ERROR;
194     }
195 
196     ev->active = 1;
197 #if 0
198     ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;
199 #endif
200 
201     return NGX_OK;
202 }
203 
204 static ngx_int_t
ngx_ff_epoll_del_event(ngx_event_t * ev,ngx_int_t event,ngx_uint_t flags)205 ngx_ff_epoll_del_event(ngx_event_t *ev, ngx_int_t event,
206     ngx_uint_t flags)
207 {
208     int                  op;
209     uint32_t             prev;
210     ngx_event_t         *e;
211     ngx_connection_t    *c;
212     struct epoll_event   ee;
213 
214     /*
215      * when the file descriptor is closed, the epoll automatically deletes
216      * it from its queue, so we do not need to delete explicitly the event
217      * before the closing the file descriptor
218      */
219 
220     if (flags & NGX_CLOSE_EVENT) {
221         ev->active = 0;
222         return NGX_OK;
223     }
224 
225     c = ev->data;
226 
227     if (event == NGX_READ_EVENT) {
228         e = c->write;
229         prev = EPOLLOUT;
230 
231     } else {
232         e = c->read;
233         prev = EPOLLIN|EPOLLRDHUP;
234     }
235 
236     if (e->active) {
237         op = EPOLL_CTL_MOD;
238         ee.events = prev | (uint32_t) flags;
239         ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
240 
241     } else {
242         op = EPOLL_CTL_DEL;
243         ee.events = 0;
244         ee.data.ptr = NULL;
245     }
246 
247     ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
248                    "epoll del event: fd:%d op:%d ev:%08XD",
249                    c->fd, op, ee.events);
250 
251     if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
252         ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
253                       "epoll_ctl(%d, %d) failed", op, c->fd);
254         return NGX_ERROR;
255     }
256 
257     ev->active = 0;
258 
259     return NGX_OK;
260 }
261 
262 static ngx_int_t
ngx_ff_epoll_add_connection(ngx_connection_t * c)263 ngx_ff_epoll_add_connection(ngx_connection_t *c)
264 {
265     struct epoll_event  ee;
266 
267     ee.events = EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP;
268     ee.data.ptr = (void *) ((uintptr_t) c | c->read->instance);
269 
270     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
271                    "epoll add connection: fd:%d ev:%08XD", c->fd, ee.events);
272 
273     if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) == -1) {
274         ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
275                       "epoll_ctl(EPOLL_CTL_ADD, %d) failed", c->fd);
276         return NGX_ERROR;
277     }
278 
279     c->read->active = 1;
280     c->write->active = 1;
281 
282     return NGX_OK;
283 }
284 
285 
286 static ngx_int_t
ngx_ff_epoll_del_connection(ngx_connection_t * c,ngx_uint_t flags)287 ngx_ff_epoll_del_connection(ngx_connection_t *c, ngx_uint_t flags)
288 {
289     int                 op;
290     struct epoll_event  ee;
291 
292     /*
293      * when the file descriptor is closed the epoll automatically deletes
294      * it from its queue so we do not need to delete explicitly the event
295      * before the closing the file descriptor
296      */
297 
298     if (flags & NGX_CLOSE_EVENT) {
299         c->read->active = 0;
300         c->write->active = 0;
301         return NGX_OK;
302     }
303 
304     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
305                    "epoll del connection: fd:%d", c->fd);
306 
307     op = EPOLL_CTL_DEL;
308     ee.events = 0;
309     ee.data.ptr = NULL;
310 
311     if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
312         ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
313                       "epoll_ctl(%d, %d) failed", op, c->fd);
314         return NGX_ERROR;
315     }
316 
317     c->read->active = 0;
318     c->write->active = 0;
319 
320     return NGX_OK;
321 }
322 
323 static ngx_int_t
ngx_ff_epoll_process_events(ngx_cycle_t * cycle,ngx_msec_t timer,ngx_uint_t flags)324 ngx_ff_epoll_process_events(ngx_cycle_t *cycle,
325     ngx_msec_t timer, ngx_uint_t flags)
326 {
327     int                events;
328     uint32_t           revents;
329     ngx_int_t          instance, i;
330     ngx_uint_t         level;
331     ngx_err_t          err;
332     ngx_event_t       *rev, *wev;
333     ngx_connection_t  *c;
334 
335     /* NGX_TIMER_INFINITE == INFTIM */
336 /*
337     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
338                    "epoll timer: %M", timer);
339 */
340     events = epoll_wait(ep, event_list, (int) nevents, timer);
341 
342     err = (events == -1) ? ngx_errno : 0;
343 
344     if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
345         ngx_time_update();
346     }
347 
348     if (err) {
349         if (err == NGX_EINTR) {
350             level = NGX_LOG_INFO;
351         } else {
352             level = NGX_LOG_ALERT;
353         }
354 
355         ngx_log_error(level, cycle->log, err, "epoll_wait() failed");
356         return NGX_ERROR;
357     }
358 
359     if (events == 0) {
360         if (timer != NGX_TIMER_INFINITE) {
361             return NGX_OK;
362         }
363 
364         ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
365                       "epoll_wait() returned no events without timeout");
366         return NGX_ERROR;
367     }
368 
369     for (i = 0; i < events; i++) {
370         c = event_list[i].data.ptr;
371 
372         instance = (uintptr_t) c & 1;
373         c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);
374 
375         rev = c->read;
376 
377         if (c->fd == -1 || rev->instance != instance) {
378 
379             /*
380              * the stale event from a file descriptor
381              * that was just closed in this iteration
382              */
383 
384             ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
385                            "epoll: stale event %p", c);
386             continue;
387         }
388 
389         revents = event_list[i].events;
390 
391         ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
392                        "epoll: fd:%d ev:%04XD d:%p",
393                        c->fd, revents, event_list[i].data.ptr);
394 
395         if (revents & (EPOLLERR|EPOLLHUP)) {
396             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
397                            "epoll_wait() error on fd:%d ev:%04XD",
398                            c->fd, revents);
399 
400             /*
401              * if the error events were returned, add EPOLLIN and EPOLLOUT
402              * to handle the events at least in one active handler
403              */
404 
405             revents |= EPOLLIN|EPOLLOUT;
406         }
407 
408         if ((revents & EPOLLIN) && rev->active) {
409             rev->ready = 1;
410             rev->available = 1;
411             rev->handler(rev);
412         }
413 
414         wev = c->write;
415 
416         if ((revents & EPOLLOUT) && wev->active) {
417 
418             if (c->fd == -1 || wev->instance != instance) {
419 
420                 /*
421                  * the stale event from a file descriptor
422                  * that was just closed in this iteration
423                  */
424 
425                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
426                                "epoll: stale event %p", c);
427                 continue;
428             }
429 
430             wev->ready = 1;
431 #if (NGX_THREADS)
432             wev->complete = 1;
433 #endif
434             wev->handler(wev);
435         }
436     }
437 
438     return NGX_OK;
439 }
440 
441 ngx_event_actions_t   ngx_ff_host_event_actions = {
442     ngx_ff_epoll_add_event,             /* add an event */
443     ngx_ff_epoll_del_event,             /* delete an event */
444     ngx_ff_epoll_add_event,             /* enable an event */
445     ngx_ff_epoll_add_event,             /* disable an event */
446     ngx_ff_epoll_add_connection,        /* add an connection */
447     ngx_ff_epoll_del_connection,        /* delete an connection */
448     NULL,                               /* trigger a notify */
449     ngx_ff_epoll_process_events,        /* process the events */
450     ngx_ff_epoll_init,                  /* init the events */
451     ngx_ff_epoll_done,                  /* done the events */
452 };
453 
454 #endif
455