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