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 static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);
22 static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);
23 
24 
25 static fd_set         master_read_fd_set;
26 static fd_set         master_write_fd_set;
27 static fd_set         work_read_fd_set;
28 static fd_set         work_write_fd_set;
29 static fd_set         work_except_fd_set;
30 
31 static ngx_uint_t     max_read;
32 static ngx_uint_t     max_write;
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_read = 0;
112     max_write = 0;
113 
114     return NGX_OK;
115 }
116 
117 
118 static void
ngx_select_done(ngx_cycle_t * cycle)119 ngx_select_done(ngx_cycle_t *cycle)
120 {
121     ngx_free(event_index);
122 
123     event_index = NULL;
124 }
125 
126 
127 static ngx_int_t
ngx_select_add_event(ngx_event_t * ev,ngx_int_t event,ngx_uint_t flags)128 ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
129 {
130     ngx_connection_t  *c;
131 
132     c = ev->data;
133 
134     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
135                    "select add event fd:%d ev:%i", c->fd, event);
136 
137     if (ev->index != NGX_INVALID_INDEX) {
138         ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
139                       "select event fd:%d ev:%i is already set", c->fd, event);
140         return NGX_OK;
141     }
142 
143     if ((event == NGX_READ_EVENT && ev->write)
144         || (event == NGX_WRITE_EVENT && !ev->write))
145     {
146         ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
147                       "invalid select %s event fd:%d ev:%i",
148                       ev->write ? "write" : "read", c->fd, event);
149         return NGX_ERROR;
150     }
151 
152     if ((event == NGX_READ_EVENT && max_read >= FD_SETSIZE)
153         || (event == NGX_WRITE_EVENT && max_write >= FD_SETSIZE))
154     {
155         ngx_log_error(NGX_LOG_ERR, ev->log, 0,
156                       "maximum number of descriptors "
157                       "supported by select() is %d", FD_SETSIZE);
158         return NGX_ERROR;
159     }
160 
161     if (event == NGX_READ_EVENT) {
162         FD_SET(c->fd, &master_read_fd_set);
163         max_read++;
164 
165     } else if (event == NGX_WRITE_EVENT) {
166         FD_SET(c->fd, &master_write_fd_set);
167         max_write++;
168     }
169 
170     ev->active = 1;
171 
172     event_index[nevents] = ev;
173     ev->index = nevents;
174     nevents++;
175 
176     return NGX_OK;
177 }
178 
179 
180 static ngx_int_t
ngx_select_del_event(ngx_event_t * ev,ngx_int_t event,ngx_uint_t flags)181 ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
182 {
183     ngx_event_t       *e;
184     ngx_connection_t  *c;
185 
186     c = ev->data;
187 
188     ev->active = 0;
189 
190     if (ev->index == NGX_INVALID_INDEX) {
191         return NGX_OK;
192     }
193 
194     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
195                    "select del event fd:%d ev:%i", c->fd, event);
196 
197     if (event == NGX_READ_EVENT) {
198         FD_CLR(c->fd, &master_read_fd_set);
199         max_read--;
200 
201     } else if (event == NGX_WRITE_EVENT) {
202         FD_CLR(c->fd, &master_write_fd_set);
203         max_write--;
204     }
205 
206     if (ev->index < --nevents) {
207         e = event_index[nevents];
208         event_index[ev->index] = e;
209         e->index = ev->index;
210     }
211 
212     ev->index = NGX_INVALID_INDEX;
213 
214     return NGX_OK;
215 }
216 
217 
218 static ngx_int_t
ngx_select_process_events(ngx_cycle_t * cycle,ngx_msec_t timer,ngx_uint_t flags)219 ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
220     ngx_uint_t flags)
221 {
222     int                ready, nready;
223     ngx_err_t          err;
224     ngx_uint_t         i, found;
225     ngx_event_t       *ev;
226     ngx_queue_t       *queue;
227     struct timeval     tv, *tp;
228     ngx_connection_t  *c;
229 
230 #if (NGX_DEBUG)
231     if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
232         for (i = 0; i < nevents; i++) {
233             ev = event_index[i];
234             c = ev->data;
235             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
236                            "select event: fd:%d wr:%d", c->fd, ev->write);
237         }
238     }
239 #endif
240 
241     if (timer == NGX_TIMER_INFINITE) {
242         tp = NULL;
243 
244     } else {
245         tv.tv_sec = (long) (timer / 1000);
246         tv.tv_usec = (long) ((timer % 1000) * 1000);
247         tp = &tv;
248     }
249 
250     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
251                    "select timer: %M", timer);
252 
253     work_read_fd_set = master_read_fd_set;
254     work_write_fd_set = master_write_fd_set;
255     work_except_fd_set = master_write_fd_set;
256 
257     if (max_read || max_write) {
258         ready = select(0, &work_read_fd_set, &work_write_fd_set,
259                        &work_except_fd_set, tp);
260 
261     } else {
262 
263         /*
264          * Winsock select() requires that at least one descriptor set must be
265          * be non-null, and any non-null descriptor set must contain at least
266          * one handle to a socket.  Otherwise select() returns WSAEINVAL.
267          */
268 
269         ngx_msleep(timer);
270 
271         ready = 0;
272     }
273 
274     err = (ready == -1) ? ngx_socket_errno : 0;
275 
276     if (flags & NGX_UPDATE_TIME) {
277         ngx_time_update();
278     }
279 
280     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
281                    "select ready %d", ready);
282 
283     if (err) {
284         ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed");
285 
286         if (err == WSAENOTSOCK) {
287             ngx_select_repair_fd_sets(cycle);
288         }
289 
290         return NGX_ERROR;
291     }
292 
293     if (ready == 0) {
294         if (timer != NGX_TIMER_INFINITE) {
295             return NGX_OK;
296         }
297 
298         ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
299                       "select() returned no events without timeout");
300         return NGX_ERROR;
301     }
302 
303     nready = 0;
304 
305     for (i = 0; i < nevents; i++) {
306         ev = event_index[i];
307         c = ev->data;
308         found = 0;
309 
310         if (ev->write) {
311             if (FD_ISSET(c->fd, &work_write_fd_set)) {
312                 found++;
313                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
314                                "select write %d", c->fd);
315             }
316 
317             if (FD_ISSET(c->fd, &work_except_fd_set)) {
318                 found++;
319                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
320                                "select except %d", c->fd);
321             }
322 
323         } else {
324             if (FD_ISSET(c->fd, &work_read_fd_set)) {
325                 found++;
326                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
327                                "select read %d", c->fd);
328             }
329         }
330 
331         if (found) {
332             ev->ready = 1;
333             ev->available = -1;
334 
335             queue = ev->accept ? &ngx_posted_accept_events
336                                : &ngx_posted_events;
337 
338             ngx_post_event(ev, queue);
339 
340             nready += found;
341         }
342     }
343 
344     if (ready != nready) {
345         ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
346                       "select ready != events: %d:%d", ready, nready);
347 
348         ngx_select_repair_fd_sets(cycle);
349     }
350 
351     return NGX_OK;
352 }
353 
354 
355 static void
ngx_select_repair_fd_sets(ngx_cycle_t * cycle)356 ngx_select_repair_fd_sets(ngx_cycle_t *cycle)
357 {
358     int           n;
359     u_int         i;
360     socklen_t     len;
361     ngx_err_t     err;
362     ngx_socket_t  s;
363 
364     for (i = 0; i < master_read_fd_set.fd_count; i++) {
365 
366         s = master_read_fd_set.fd_array[i];
367         len = sizeof(int);
368 
369         if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {
370             err = ngx_socket_errno;
371 
372             ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
373                           "invalid descriptor #%d in read fd_set", s);
374 
375             FD_CLR(s, &master_read_fd_set);
376         }
377     }
378 
379     for (i = 0; i < master_write_fd_set.fd_count; i++) {
380 
381         s = master_write_fd_set.fd_array[i];
382         len = sizeof(int);
383 
384         if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {
385             err = ngx_socket_errno;
386 
387             ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
388                           "invalid descriptor #%d in write fd_set", s);
389 
390             FD_CLR(s, &master_write_fd_set);
391         }
392     }
393 }
394 
395 
396 static char *
ngx_select_init_conf(ngx_cycle_t * cycle,void * conf)397 ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)
398 {
399     ngx_event_conf_t  *ecf;
400 
401     ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
402 
403     if (ecf->use != ngx_select_module.ctx_index) {
404         return NGX_CONF_OK;
405     }
406 
407     return NGX_CONF_OK;
408 }
409