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 static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);
14 static void ngx_select_done(ngx_cycle_t *cycle);
15 static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,
16     ngx_uint_t flags);
17 static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event,
18     ngx_uint_t flags);
19 static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
20     ngx_uint_t flags);
21 #if !(NGX_HAVE_FSTACK)
22 static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);
23 #endif
24 static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);
25 
26 
27 static fd_set         master_read_fd_set;
28 static fd_set         master_write_fd_set;
29 static fd_set         work_read_fd_set;
30 static fd_set         work_write_fd_set;
31 
32 static ngx_int_t      max_fd;
33 static ngx_uint_t     nevents;
34 
35 static ngx_event_t  **event_index;
36 
37 
38 static ngx_str_t           select_name = ngx_string("select");
39 
40 static ngx_event_module_t  ngx_select_module_ctx = {
41     &select_name,
42     NULL,                                  /* create configuration */
43     ngx_select_init_conf,                  /* init configuration */
44 
45     {
46         ngx_select_add_event,              /* add an event */
47         ngx_select_del_event,              /* delete an event */
48         ngx_select_add_event,              /* enable an event */
49         ngx_select_del_event,              /* disable an event */
50         NULL,                              /* add an connection */
51         NULL,                              /* delete an connection */
52         NULL,                              /* trigger a notify */
53         ngx_select_process_events,         /* process the events */
54         ngx_select_init,                   /* init the events */
55         ngx_select_done                    /* done the events */
56     }
57 
58 };
59 
60 ngx_module_t  ngx_select_module = {
61     NGX_MODULE_V1,
62     &ngx_select_module_ctx,                /* module context */
63     NULL,                                  /* module directives */
64     NGX_EVENT_MODULE,                      /* module type */
65     NULL,                                  /* init master */
66     NULL,                                  /* init module */
67     NULL,                                  /* init process */
68     NULL,                                  /* init thread */
69     NULL,                                  /* exit thread */
70     NULL,                                  /* exit process */
71     NULL,                                  /* exit master */
72     NGX_MODULE_V1_PADDING
73 };
74 
75 
76 static ngx_int_t
ngx_select_init(ngx_cycle_t * cycle,ngx_msec_t timer)77 ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer)
78 {
79     ngx_event_t  **index;
80 
81     if (event_index == NULL) {
82         FD_ZERO(&master_read_fd_set);
83         FD_ZERO(&master_write_fd_set);
84         nevents = 0;
85     }
86 
87     if (ngx_process >= NGX_PROCESS_WORKER
88         || cycle->old_cycle == NULL
89         || cycle->old_cycle->connection_n < cycle->connection_n)
90     {
91         index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n,
92                           cycle->log);
93         if (index == NULL) {
94             return NGX_ERROR;
95         }
96 
97         if (event_index) {
98             ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents);
99             ngx_free(event_index);
100         }
101 
102         event_index = index;
103     }
104 
105     ngx_io = ngx_os_io;
106 
107     ngx_event_actions = ngx_select_module_ctx.actions;
108 
109     ngx_event_flags = NGX_USE_LEVEL_EVENT;
110 
111     max_fd = -1;
112 
113     return NGX_OK;
114 }
115 
116 
117 static void
ngx_select_done(ngx_cycle_t * cycle)118 ngx_select_done(ngx_cycle_t *cycle)
119 {
120     ngx_free(event_index);
121 
122     event_index = NULL;
123 }
124 
125 
126 static ngx_int_t
ngx_select_add_event(ngx_event_t * ev,ngx_int_t event,ngx_uint_t flags)127 ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
128 {
129     ngx_connection_t  *c;
130 
131     c = ev->data;
132 
133     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
134                    "select add event fd:%d ev:%i", c->fd, event);
135 
136     if (ev->index != NGX_INVALID_INDEX) {
137         ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
138                       "select event fd:%d ev:%i is already set", c->fd, event);
139         return NGX_OK;
140     }
141 
142     if ((event == NGX_READ_EVENT && ev->write)
143         || (event == NGX_WRITE_EVENT && !ev->write))
144     {
145         ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
146                       "invalid select %s event fd:%d ev:%i",
147                       ev->write ? "write" : "read", c->fd, event);
148         return NGX_ERROR;
149     }
150 
151     if (event == NGX_READ_EVENT) {
152         FD_SET(c->fd, &master_read_fd_set);
153 
154     } else if (event == NGX_WRITE_EVENT) {
155         FD_SET(c->fd, &master_write_fd_set);
156     }
157 
158     if (max_fd != -1 && max_fd < c->fd) {
159         max_fd = c->fd;
160     }
161 
162     ev->active = 1;
163 
164     event_index[nevents] = ev;
165     ev->index = nevents;
166     nevents++;
167 
168     return NGX_OK;
169 }
170 
171 
172 static ngx_int_t
ngx_select_del_event(ngx_event_t * ev,ngx_int_t event,ngx_uint_t flags)173 ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
174 {
175     ngx_event_t       *e;
176     ngx_connection_t  *c;
177 
178     c = ev->data;
179 
180     ev->active = 0;
181 
182     if (ev->index == NGX_INVALID_INDEX) {
183         return NGX_OK;
184     }
185 
186     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
187                    "select del event fd:%d ev:%i", c->fd, event);
188 
189     if (event == NGX_READ_EVENT) {
190         FD_CLR(c->fd, &master_read_fd_set);
191 
192     } else if (event == NGX_WRITE_EVENT) {
193         FD_CLR(c->fd, &master_write_fd_set);
194     }
195 
196     if (max_fd == c->fd) {
197         max_fd = -1;
198     }
199 
200     if (ev->index < --nevents) {
201         e = event_index[nevents];
202         event_index[ev->index] = e;
203         e->index = ev->index;
204     }
205 
206     ev->index = NGX_INVALID_INDEX;
207 
208     return NGX_OK;
209 }
210 
211 
212 static ngx_int_t
ngx_select_process_events(ngx_cycle_t * cycle,ngx_msec_t timer,ngx_uint_t flags)213 ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
214     ngx_uint_t flags)
215 {
216     int                ready, nready;
217     ngx_err_t          err;
218     ngx_uint_t         i, found;
219     ngx_event_t       *ev;
220     ngx_queue_t       *queue;
221     struct timeval     tv, *tp;
222     ngx_connection_t  *c;
223 
224     if (max_fd == -1) {
225         for (i = 0; i < nevents; i++) {
226             c = event_index[i]->data;
227             if (max_fd < c->fd) {
228                 max_fd = c->fd;
229             }
230         }
231 
232         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
233                        "change max_fd: %i", max_fd);
234     }
235 
236 #if (NGX_DEBUG)
237     if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
238         for (i = 0; i < nevents; i++) {
239             ev = event_index[i];
240             c = ev->data;
241             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
242                            "select event: fd:%d wr:%d", c->fd, ev->write);
243         }
244 
245         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
246                        "max_fd: %i", max_fd);
247     }
248 #endif
249 
250     if (timer == NGX_TIMER_INFINITE) {
251         tp = NULL;
252 
253     } else {
254         tv.tv_sec = (long) (timer / 1000);
255         tv.tv_usec = (long) ((timer % 1000) * 1000);
256         tp = &tv;
257     }
258 
259     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
260                    "select timer: %M", timer);
261 
262     work_read_fd_set = master_read_fd_set;
263     work_write_fd_set = master_write_fd_set;
264 
265     ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp);
266 
267     err = (ready == -1) ? ngx_errno : 0;
268 
269     if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
270         ngx_time_update();
271     }
272 
273     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
274                    "select ready %d", ready);
275 
276     if (err) {
277         ngx_uint_t  level;
278 
279         if (err == NGX_EINTR) {
280 
281             if (ngx_event_timer_alarm) {
282                 ngx_event_timer_alarm = 0;
283                 return NGX_OK;
284             }
285 
286             level = NGX_LOG_INFO;
287 
288         } else {
289             level = NGX_LOG_ALERT;
290         }
291 
292         ngx_log_error(level, cycle->log, err, "select() failed");
293 
294         if (err == NGX_EBADF) {
295 #if !(NGX_HAVE_FSTACK)
296             ngx_select_repair_fd_sets(cycle);
297 #endif
298         }
299 
300         return NGX_ERROR;
301     }
302 
303     if (ready == 0) {
304         if (timer != NGX_TIMER_INFINITE) {
305             return NGX_OK;
306         }
307 
308 #if !(NGX_HAVE_FSTACK)
309         ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
310                       "select() returned no events without timeout");
311 #endif
312         return NGX_ERROR;
313     }
314 
315     nready = 0;
316 
317     for (i = 0; i < nevents; i++) {
318         ev = event_index[i];
319         c = ev->data;
320         found = 0;
321 
322         if (ev->write) {
323             if (FD_ISSET(c->fd, &work_write_fd_set)) {
324                 found = 1;
325                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
326                                "select write %d", c->fd);
327             }
328 
329         } else {
330             if (FD_ISSET(c->fd, &work_read_fd_set)) {
331                 found = 1;
332                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
333                                "select read %d", c->fd);
334             }
335         }
336 
337         if (found) {
338             ev->ready = 1;
339 
340             queue = ev->accept ? &ngx_posted_accept_events
341                                : &ngx_posted_events;
342 
343             ngx_post_event(ev, queue);
344 
345             nready++;
346         }
347     }
348 
349     if (ready != nready) {
350         ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
351                       "select ready != events: %d:%d", ready, nready);
352 
353 #if !(NGX_HAVE_FSTACK)
354         ngx_select_repair_fd_sets(cycle);
355 #endif
356     }
357 
358     return NGX_OK;
359 }
360 
361 #if !(NGX_HAVE_FSTACK)
362 static void
ngx_select_repair_fd_sets(ngx_cycle_t * cycle)363 ngx_select_repair_fd_sets(ngx_cycle_t *cycle)
364 {
365     int           n;
366     socklen_t     len;
367     ngx_err_t     err;
368     ngx_socket_t  s;
369 
370     for (s = 0; s <= max_fd; s++) {
371 
372         if (FD_ISSET(s, &master_read_fd_set) == 0) {
373             continue;
374         }
375 
376         len = sizeof(int);
377 
378         if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {
379             err = ngx_socket_errno;
380 
381             ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
382                           "invalid descriptor #%d in read fd_set", s);
383 
384             FD_CLR(s, &master_read_fd_set);
385         }
386     }
387 
388     for (s = 0; s <= max_fd; s++) {
389 
390         if (FD_ISSET(s, &master_write_fd_set) == 0) {
391             continue;
392         }
393 
394         len = sizeof(int);
395 
396         if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {
397             err = ngx_socket_errno;
398 
399             ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
400                           "invalid descriptor #%d in write fd_set", s);
401 
402             FD_CLR(s, &master_write_fd_set);
403         }
404     }
405 
406     max_fd = -1;
407 }
408 #endif
409 
410 static char *
ngx_select_init_conf(ngx_cycle_t * cycle,void * conf)411 ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)
412 {
413     ngx_event_conf_t  *ecf;
414 
415     ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
416 
417     if (ecf->use != ngx_select_module.ctx_index) {
418         return NGX_CONF_OK;
419     }
420 
421     /* disable warning: the default FD_SETSIZE is 1024U in FreeBSD 5.x */
422 
423     if (cycle->connection_n > FD_SETSIZE) {
424         ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
425                       "the maximum number of files "
426                       "supported by select() is %ud", FD_SETSIZE);
427         return NGX_CONF_ERROR;
428     }
429 
430     return NGX_CONF_OK;
431 }
432