ngx_iocp_module.c revision e18a033b
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#include <ngx_iocp_module.h>
12
13
14static ngx_int_t ngx_iocp_init(ngx_cycle_t *cycle, ngx_msec_t timer);
15static ngx_thread_value_t __stdcall ngx_iocp_timer(void *data);
16static void ngx_iocp_done(ngx_cycle_t *cycle);
17static ngx_int_t ngx_iocp_add_event(ngx_event_t *ev, ngx_int_t event,
18    ngx_uint_t key);
19static ngx_int_t ngx_iocp_del_connection(ngx_connection_t *c, ngx_uint_t flags);
20static ngx_int_t ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
21    ngx_uint_t flags);
22static void *ngx_iocp_create_conf(ngx_cycle_t *cycle);
23static char *ngx_iocp_init_conf(ngx_cycle_t *cycle, void *conf);
24
25
26static ngx_str_t      iocp_name = ngx_string("iocp");
27
28static ngx_command_t  ngx_iocp_commands[] = {
29
30    { ngx_string("iocp_threads"),
31      NGX_EVENT_CONF|NGX_CONF_TAKE1,
32      ngx_conf_set_num_slot,
33      0,
34      offsetof(ngx_iocp_conf_t, threads),
35      NULL },
36
37    { ngx_string("post_acceptex"),
38      NGX_EVENT_CONF|NGX_CONF_TAKE1,
39      ngx_conf_set_num_slot,
40      0,
41      offsetof(ngx_iocp_conf_t, post_acceptex),
42      NULL },
43
44    { ngx_string("acceptex_read"),
45      NGX_EVENT_CONF|NGX_CONF_FLAG,
46      ngx_conf_set_flag_slot,
47      0,
48      offsetof(ngx_iocp_conf_t, acceptex_read),
49      NULL },
50
51      ngx_null_command
52};
53
54
55static ngx_event_module_t  ngx_iocp_module_ctx = {
56    &iocp_name,
57    ngx_iocp_create_conf,                  /* create configuration */
58    ngx_iocp_init_conf,                    /* init configuration */
59
60    {
61        ngx_iocp_add_event,                /* add an event */
62        NULL,                              /* delete an event */
63        NULL,                              /* enable an event */
64        NULL,                              /* disable an event */
65        NULL,                              /* add an connection */
66        ngx_iocp_del_connection,           /* delete an connection */
67        NULL,                              /* trigger a notify */
68        ngx_iocp_process_events,           /* process the events */
69        ngx_iocp_init,                     /* init the events */
70        ngx_iocp_done                      /* done the events */
71    }
72
73};
74
75ngx_module_t  ngx_iocp_module = {
76    NGX_MODULE_V1,
77    &ngx_iocp_module_ctx,                  /* module context */
78    ngx_iocp_commands,                     /* module directives */
79    NGX_EVENT_MODULE,                      /* module type */
80    NULL,                                  /* init master */
81    NULL,                                  /* init module */
82    NULL,                                  /* init process */
83    NULL,                                  /* init thread */
84    NULL,                                  /* exit thread */
85    NULL,                                  /* exit process */
86    NULL,                                  /* exit master */
87    NGX_MODULE_V1_PADDING
88};
89
90
91ngx_os_io_t ngx_iocp_io = {
92    ngx_overlapped_wsarecv,
93    NULL,
94    ngx_udp_overlapped_wsarecv,
95    NULL,
96    NULL,
97    NULL,
98    ngx_overlapped_wsasend_chain,
99    0
100};
101
102
103static HANDLE      iocp;
104static ngx_tid_t   timer_thread;
105static ngx_msec_t  msec;
106
107
108static ngx_int_t
109ngx_iocp_init(ngx_cycle_t *cycle, ngx_msec_t timer)
110{
111    ngx_iocp_conf_t  *cf;
112
113    cf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);
114
115    if (iocp == NULL) {
116        iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,
117                                      cf->threads);
118    }
119
120    if (iocp == NULL) {
121        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
122                      "CreateIoCompletionPort() failed");
123        return NGX_ERROR;
124    }
125
126    ngx_io = ngx_iocp_io;
127
128    ngx_event_actions = ngx_iocp_module_ctx.actions;
129
130    ngx_event_flags = NGX_USE_IOCP_EVENT;
131
132    if (timer == 0) {
133        return NGX_OK;
134    }
135
136    /*
137     * The waitable timer could not be used, because
138     * GetQueuedCompletionStatus() does not set a thread to alertable state
139     */
140
141    if (timer_thread == NULL) {
142
143        msec = timer;
144
145        if (ngx_create_thread(&timer_thread, ngx_iocp_timer, &msec, cycle->log)
146            != 0)
147        {
148            return NGX_ERROR;
149        }
150    }
151
152    ngx_event_flags |= NGX_USE_TIMER_EVENT;
153
154    return NGX_OK;
155}
156
157
158static ngx_thread_value_t __stdcall
159ngx_iocp_timer(void *data)
160{
161    ngx_msec_t  timer = *(ngx_msec_t *) data;
162
163    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
164                   "THREAD %p %p", &msec, data);
165
166    for ( ;; ) {
167        Sleep(timer);
168
169        ngx_time_update();
170#if 1
171        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, "timer");
172#endif
173    }
174
175#if defined(__WATCOMC__) || defined(__GNUC__)
176    return 0;
177#endif
178}
179
180
181static void
182ngx_iocp_done(ngx_cycle_t *cycle)
183{
184    if (CloseHandle(iocp) == -1) {
185        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
186                      "iocp CloseHandle() failed");
187    }
188
189    iocp = NULL;
190}
191
192
193static ngx_int_t
194ngx_iocp_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t key)
195{
196    ngx_connection_t  *c;
197
198    c = (ngx_connection_t *) ev->data;
199
200    c->read->active = 1;
201    c->write->active = 1;
202
203    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
204                   "iocp add: fd:%d k:%ui ov:%p", c->fd, key, &ev->ovlp);
205
206    if (CreateIoCompletionPort((HANDLE) c->fd, iocp, key, 0) == NULL) {
207        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
208                      "CreateIoCompletionPort() failed");
209        return NGX_ERROR;
210    }
211
212    return NGX_OK;
213}
214
215
216static ngx_int_t
217ngx_iocp_del_connection(ngx_connection_t *c, ngx_uint_t flags)
218{
219#if 0
220    if (flags & NGX_CLOSE_EVENT) {
221        return NGX_OK;
222    }
223
224    if (CancelIo((HANDLE) c->fd) == 0) {
225        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, "CancelIo() failed");
226        return NGX_ERROR;
227    }
228#endif
229
230    return NGX_OK;
231}
232
233
234static
235ngx_int_t ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
236    ngx_uint_t flags)
237{
238    int                rc;
239    u_int              key;
240    u_long             bytes;
241    ngx_err_t          err;
242    ngx_msec_t         delta;
243    ngx_event_t       *ev;
244    ngx_event_ovlp_t  *ovlp;
245
246    if (timer == NGX_TIMER_INFINITE) {
247        timer = INFINITE;
248    }
249
250    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "iocp timer: %M", timer);
251
252    rc = GetQueuedCompletionStatus(iocp, &bytes, (PULONG_PTR) &key,
253                                   (LPOVERLAPPED *) &ovlp, (u_long) timer);
254
255    if (rc == 0) {
256        err = ngx_errno;
257    } else {
258        err = 0;
259    }
260
261    delta = ngx_current_msec;
262
263    if (flags & NGX_UPDATE_TIME) {
264        ngx_time_update();
265    }
266
267    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
268                   "iocp: %d b:%d k:%d ov:%p", rc, bytes, key, ovlp);
269
270    if (timer != INFINITE) {
271        delta = ngx_current_msec - delta;
272
273        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
274                       "iocp timer: %M, delta: %M", timer, delta);
275    }
276
277    if (err) {
278        if (ovlp == NULL) {
279            if (err != WAIT_TIMEOUT) {
280                ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
281                              "GetQueuedCompletionStatus() failed");
282
283                return NGX_ERROR;
284            }
285
286            return NGX_OK;
287        }
288
289        ovlp->error = err;
290    }
291
292    if (ovlp == NULL) {
293        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
294                      "GetQueuedCompletionStatus() returned no operation");
295        return NGX_ERROR;
296    }
297
298
299    ev = ovlp->event;
300
301    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err, "iocp event:%p", ev);
302
303
304    if (err == ERROR_NETNAME_DELETED /* the socket was closed */
305        || err == ERROR_OPERATION_ABORTED /* the operation was canceled */)
306    {
307
308        /*
309         * the WSA_OPERATION_ABORTED completion notification
310         * for a file descriptor that was closed
311         */
312
313        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err,
314                       "iocp: aborted event %p", ev);
315
316        return NGX_OK;
317    }
318
319    if (err) {
320        ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
321                      "GetQueuedCompletionStatus() returned operation error");
322    }
323
324    switch (key) {
325
326    case NGX_IOCP_ACCEPT:
327        if (bytes) {
328            ev->ready = 1;
329        }
330        break;
331
332    case NGX_IOCP_IO:
333        ev->complete = 1;
334        ev->ready = 1;
335        break;
336
337    case NGX_IOCP_CONNECT:
338        ev->ready = 1;
339    }
340
341    ev->available = bytes;
342
343    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
344                   "iocp event handler: %p", ev->handler);
345
346    ev->handler(ev);
347
348    return NGX_OK;
349}
350
351
352static void *
353ngx_iocp_create_conf(ngx_cycle_t *cycle)
354{
355    ngx_iocp_conf_t  *cf;
356
357    cf = ngx_palloc(cycle->pool, sizeof(ngx_iocp_conf_t));
358    if (cf == NULL) {
359        return NGX_CONF_ERROR;
360    }
361
362    cf->threads = NGX_CONF_UNSET;
363    cf->post_acceptex = NGX_CONF_UNSET;
364    cf->acceptex_read = NGX_CONF_UNSET;
365
366    return cf;
367}
368
369
370static char *
371ngx_iocp_init_conf(ngx_cycle_t *cycle, void *conf)
372{
373    ngx_iocp_conf_t *cf = conf;
374
375    ngx_conf_init_value(cf->threads, 0);
376    ngx_conf_init_value(cf->post_acceptex, 10);
377    ngx_conf_init_value(cf->acceptex_read, 1);
378
379    return NGX_CONF_OK;
380}
381