3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
10 #include <ngx_event.h>
19 static ngx_int_t
ngx_kqueue_init(ngx_cycle_t
*cycle
, ngx_msec_t timer
);
20 static void ngx_kqueue_done(ngx_cycle_t
*cycle
);
21 static ngx_int_t
ngx_kqueue_add_event(ngx_event_t
*ev
, ngx_int_t event
,
23 static ngx_int_t
ngx_kqueue_del_event(ngx_event_t
*ev
, ngx_int_t event
,
25 static ngx_int_t
ngx_kqueue_set_event(ngx_event_t
*ev
, ngx_int_t filter
,
27 static ngx_int_t
ngx_kqueue_process_changes(ngx_cycle_t
*cycle
, ngx_uint_t
try);
28 static ngx_int_t
ngx_kqueue_process_events(ngx_cycle_t
*cycle
, ngx_msec_t timer
,
30 static ngx_inline
void ngx_kqueue_dump_event(ngx_log_t
*log
,
33 static void *ngx_kqueue_create_conf(ngx_cycle_t
*cycle
);
34 static char *ngx_kqueue_init_conf(ngx_cycle_t
*cycle
, void *conf
);
40 * The "change_list" should be declared as ngx_thread_volatile.
41 * However, the use of the change_list is localized in kqueue functions and
42 * is protected by the mutex so even the "icc -ipo" should not build the code
43 * with the race condition. Thus we avoid the declaration to make a more
47 static struct kevent
*change_list
, *change_list0
, *change_list1
;
48 static struct kevent
*event_list
;
49 static ngx_uint_t max_changes
, nchanges
, nevents
;
52 static ngx_mutex_t
*list_mutex
;
53 static ngx_mutex_t
*kevent_mutex
;
58 static ngx_str_t kqueue_name
= ngx_string("kqueue");
60 static ngx_command_t ngx_kqueue_commands
[] = {
62 { ngx_string("kqueue_changes"),
63 NGX_EVENT_CONF
|NGX_CONF_TAKE1
,
64 ngx_conf_set_num_slot
,
66 offsetof(ngx_kqueue_conf_t
, changes
),
69 { ngx_string("kqueue_events"),
70 NGX_EVENT_CONF
|NGX_CONF_TAKE1
,
71 ngx_conf_set_num_slot
,
73 offsetof(ngx_kqueue_conf_t
, events
),
80 ngx_event_module_t ngx_kqueue_module_ctx
= {
82 ngx_kqueue_create_conf
, /* create configuration */
83 ngx_kqueue_init_conf
, /* init configuration */
86 ngx_kqueue_add_event
, /* add an event */
87 ngx_kqueue_del_event
, /* delete an event */
88 ngx_kqueue_add_event
, /* enable an event */
89 ngx_kqueue_del_event
, /* disable an event */
90 NULL
, /* add an connection */
91 NULL
, /* delete an connection */
92 ngx_kqueue_process_changes
, /* process the changes */
93 ngx_kqueue_process_events
, /* process the events */
94 ngx_kqueue_init
, /* init the events */
95 ngx_kqueue_done
/* done the events */
100 ngx_module_t ngx_kqueue_module
= {
102 &ngx_kqueue_module_ctx
, /* module context */
103 ngx_kqueue_commands
, /* module directives */
104 NGX_EVENT_MODULE
, /* module type */
105 NULL
, /* init master */
106 NULL
, /* init module */
107 NULL
, /* init process */
108 NULL
, /* init thread */
109 NULL
, /* exit thread */
110 NULL
, /* exit process */
111 NULL
, /* exit master */
112 NGX_MODULE_V1_PADDING
117 ngx_kqueue_init(ngx_cycle_t
*cycle
, ngx_msec_t timer
)
119 ngx_kqueue_conf_t
*kcf
;
121 #if (NGX_HAVE_TIMER_EVENT)
125 kcf
= ngx_event_get_conf(cycle
->conf_ctx
, ngx_kqueue_module
);
127 if (ngx_kqueue
== -1) {
128 ngx_kqueue
= kqueue();
130 if (ngx_kqueue
== -1) {
131 ngx_log_error(NGX_LOG_EMERG
, cycle
->log
, ngx_errno
,
138 list_mutex
= ngx_mutex_init(cycle
->log
, 0);
139 if (list_mutex
== NULL
) {
143 kevent_mutex
= ngx_mutex_init(cycle
->log
, 0);
144 if (kevent_mutex
== NULL
) {
151 if (max_changes
< kcf
->changes
) {
156 if (kevent(ngx_kqueue
, change_list
, (int) nchanges
, NULL
, 0, &ts
)
159 ngx_log_error(NGX_LOG_ALERT
, cycle
->log
, ngx_errno
,
167 ngx_free(change_list0
);
170 change_list0
= ngx_alloc(kcf
->changes
* sizeof(struct kevent
),
172 if (change_list0
== NULL
) {
177 ngx_free(change_list1
);
180 change_list1
= ngx_alloc(kcf
->changes
* sizeof(struct kevent
),
182 if (change_list1
== NULL
) {
186 change_list
= change_list0
;
189 max_changes
= kcf
->changes
;
191 if (nevents
< kcf
->events
) {
193 ngx_free(event_list
);
196 event_list
= ngx_alloc(kcf
->events
* sizeof(struct kevent
), cycle
->log
);
197 if (event_list
== NULL
) {
202 ngx_event_flags
= NGX_USE_ONESHOT_EVENT
203 |NGX_USE_KQUEUE_EVENT
204 |NGX_USE_VNODE_EVENT
;
206 #if (NGX_HAVE_TIMER_EVENT)
210 kev
.filter
= EVFILT_TIMER
;
211 kev
.flags
= EV_ADD
|EV_ENABLE
;
219 if (kevent(ngx_kqueue
, &kev
, 1, NULL
, 0, &ts
) == -1) {
220 ngx_log_error(NGX_LOG_ALERT
, cycle
->log
, ngx_errno
,
221 "kevent(EVFILT_TIMER) failed");
225 ngx_event_flags
|= NGX_USE_TIMER_EVENT
;
230 #if (NGX_HAVE_CLEAR_EVENT)
231 ngx_event_flags
|= NGX_USE_CLEAR_EVENT
;
233 ngx_event_flags
|= NGX_USE_LEVEL_EVENT
;
236 #if (NGX_HAVE_LOWAT_EVENT)
237 ngx_event_flags
|= NGX_USE_LOWAT_EVENT
;
240 nevents
= kcf
->events
;
244 ngx_event_actions
= ngx_kqueue_module_ctx
.actions
;
251 ngx_kqueue_done(ngx_cycle_t
*cycle
)
253 if (close(ngx_kqueue
) == -1) {
254 ngx_log_error(NGX_LOG_ALERT
, cycle
->log
, ngx_errno
,
255 "kqueue close() failed");
261 ngx_mutex_destroy(kevent_mutex
);
262 ngx_mutex_destroy(list_mutex
);
265 ngx_free(change_list1
);
266 ngx_free(change_list0
);
267 ngx_free(event_list
);
280 ngx_kqueue_add_event(ngx_event_t
*ev
, ngx_int_t event
, ngx_uint_t flags
)
290 ev
->oneshot
= (flags
& NGX_ONESHOT_EVENT
) ? 1 : 0;
292 ngx_mutex_lock(list_mutex
);
296 if (ev
->index
< nchanges
297 && ((uintptr_t) change_list
[ev
->index
].udata
& (uintptr_t) ~1)
300 if (change_list
[ev
->index
].flags
== EV_DISABLE
) {
303 * if the EV_DISABLE is still not passed to a kernel
304 * we will not pass it
307 ngx_log_debug2(NGX_LOG_DEBUG_EVENT
, ev
->log
, 0,
308 "kevent activated: %d: ft:%i",
309 ngx_event_ident(ev
->data
), event
);
311 if (ev
->index
< --nchanges
) {
313 ((uintptr_t) change_list
[nchanges
].udata
& (uintptr_t) ~1);
314 change_list
[ev
->index
] = change_list
[nchanges
];
315 e
->index
= ev
->index
;
318 ngx_mutex_unlock(list_mutex
);
325 ngx_log_error(NGX_LOG_ALERT
, ev
->log
, 0,
326 "previous event on #%d were not passed in kernel", c
->fd
);
328 ngx_mutex_unlock(list_mutex
);
335 rc
= ngx_kqueue_set_event(ev
, event
, EV_ADD
|EV_ENABLE
|flags
);
337 ngx_mutex_unlock(list_mutex
);
344 ngx_kqueue_del_event(ngx_event_t
*ev
, ngx_int_t event
, ngx_uint_t flags
)
352 ngx_mutex_lock(list_mutex
);
354 if (ev
->index
< nchanges
355 && ((uintptr_t) change_list
[ev
->index
].udata
& (uintptr_t) ~1)
358 ngx_log_debug2(NGX_LOG_DEBUG_EVENT
, ev
->log
, 0,
359 "kevent deleted: %d: ft:%i",
360 ngx_event_ident(ev
->data
), event
);
362 /* if the event is still not passed to a kernel we will not pass it */
366 if (ev
->index
< nchanges
) {
368 ((uintptr_t) change_list
[nchanges
].udata
& (uintptr_t) ~1);
369 change_list
[ev
->index
] = change_list
[nchanges
];
370 e
->index
= ev
->index
;
373 ngx_mutex_unlock(list_mutex
);
379 * when the file descriptor is closed the kqueue automatically deletes
380 * its filters so we do not need to delete explicitly the event
381 * before the closing the file descriptor.
384 if (flags
& NGX_CLOSE_EVENT
) {
385 ngx_mutex_unlock(list_mutex
);
389 if (flags
& NGX_DISABLE_EVENT
) {
396 rc
= ngx_kqueue_set_event(ev
, event
, flags
);
398 ngx_mutex_unlock(list_mutex
);
405 ngx_kqueue_set_event(ngx_event_t
*ev
, ngx_int_t filter
, ngx_uint_t flags
)
413 ngx_log_debug3(NGX_LOG_DEBUG_EVENT
, ev
->log
, 0,
414 "kevent set event: %d: ft:%i fl:%04Xi",
415 c
->fd
, filter
, flags
);
417 if (nchanges
>= max_changes
) {
418 ngx_log_error(NGX_LOG_WARN
, ev
->log
, 0,
419 "kqueue change list is filled up");
424 if (kevent(ngx_kqueue
, change_list
, (int) nchanges
, NULL
, 0, &ts
)
427 ngx_log_error(NGX_LOG_ALERT
, ev
->log
, ngx_errno
, "kevent() failed");
434 kev
= &change_list
[nchanges
];
437 kev
->filter
= (short) filter
;
438 kev
->flags
= (u_short
) flags
;
439 kev
->udata
= NGX_KQUEUE_UDATA_T ((uintptr_t) ev
| ev
->instance
);
441 if (filter
== EVFILT_VNODE
) {
442 kev
->fflags
= NOTE_DELETE
|NOTE_WRITE
|NOTE_EXTEND
443 |NOTE_ATTRIB
|NOTE_RENAME
444 #if (__FreeBSD__ == 4 && __FreeBSD_version >= 430000) \
445 || __FreeBSD_version >= 500018
452 #if (NGX_HAVE_LOWAT_EVENT)
453 if (flags
& NGX_LOWAT_EVENT
) {
454 kev
->fflags
= NOTE_LOWAT
;
455 kev
->data
= ev
->available
;
467 ev
->index
= nchanges
;
470 if (flags
& NGX_FLUSH_EVENT
) {
474 ngx_log_debug0(NGX_LOG_DEBUG_EVENT
, ev
->log
, 0, "kevent flush");
476 if (kevent(ngx_kqueue
, change_list
, (int) nchanges
, NULL
, 0, &ts
)
479 ngx_log_error(NGX_LOG_ALERT
, ev
->log
, ngx_errno
, "kevent() failed");
491 ngx_kqueue_process_events(ngx_cycle_t
*cycle
, ngx_msec_t timer
,
495 ngx_int_t i
, instance
;
498 ngx_event_t
*ev
, **queue
;
499 struct timespec ts
, *tp
;
502 if (ngx_kqueue_process_changes(cycle
, 0) == NGX_ERROR
) {
513 if (timer
== NGX_TIMER_INFINITE
) {
518 ts
.tv_sec
= timer
/ 1000;
519 ts
.tv_nsec
= (timer
% 1000) * 1000000;
522 * 64-bit Darwin kernel has the bug: kernel level ts.tv_nsec is
523 * the int32_t while user level ts.tv_nsec is the long (64-bit),
524 * so on the big endian PowerPC all nanoseconds are lost.
527 #if (NGX_DARWIN_KEVENT_BUG)
534 ngx_log_debug2(NGX_LOG_DEBUG_EVENT
, cycle
->log
, 0,
535 "kevent timer: %M, changes: %d", timer
, n
);
537 events
= kevent(ngx_kqueue
, change_list
, n
, event_list
, (int) nevents
, tp
);
539 err
= (events
== -1) ? ngx_errno
: 0;
541 if (flags
& NGX_UPDATE_TIME
|| ngx_event_timer_alarm
) {
545 ngx_log_debug1(NGX_LOG_DEBUG_EVENT
, cycle
->log
, 0,
546 "kevent events: %d", events
);
549 if (err
== NGX_EINTR
) {
551 if (ngx_event_timer_alarm
) {
552 ngx_event_timer_alarm
= 0;
556 level
= NGX_LOG_INFO
;
559 level
= NGX_LOG_ALERT
;
562 ngx_log_error(level
, cycle
->log
, err
, "kevent() failed");
567 if (timer
!= NGX_TIMER_INFINITE
) {
571 ngx_log_error(NGX_LOG_ALERT
, cycle
->log
, 0,
572 "kevent() returned no events without timeout");
576 ngx_mutex_lock(ngx_posted_events_mutex
);
578 for (i
= 0; i
< events
; i
++) {
580 ngx_kqueue_dump_event(cycle
->log
, &event_list
[i
]);
582 if (event_list
[i
].flags
& EV_ERROR
) {
583 ngx_log_error(NGX_LOG_ALERT
, cycle
->log
, event_list
[i
].data
,
584 "kevent() error on %d filter:%d flags:%04Xd",
585 event_list
[i
].ident
, event_list
[i
].filter
,
586 event_list
[i
].flags
);
590 #if (NGX_HAVE_TIMER_EVENT)
592 if (event_list
[i
].filter
== EVFILT_TIMER
) {
599 ev
= (ngx_event_t
*) event_list
[i
].udata
;
601 switch (event_list
[i
].filter
) {
606 instance
= (uintptr_t) ev
& 1;
607 ev
= (ngx_event_t
*) ((uintptr_t) ev
& (uintptr_t) ~1);
609 if (ev
->closed
|| ev
->instance
!= instance
) {
612 * the stale event from a file descriptor
613 * that was just closed in this iteration
616 ngx_log_debug1(NGX_LOG_DEBUG_EVENT
, cycle
->log
, 0,
617 "kevent: stale event %p", ev
);
621 if (ev
->log
&& (ev
->log
->log_level
& NGX_LOG_DEBUG_CONNECTION
)) {
622 ngx_kqueue_dump_event(ev
->log
, &event_list
[i
]);
631 if ((flags
& NGX_POST_THREAD_EVENTS
) && !ev
->accept
) {
632 ev
->posted_ready
= 1;
633 ev
->posted_available
= event_list
[i
].data
;
635 if (event_list
[i
].flags
& EV_EOF
) {
637 ev
->posted_errno
= event_list
[i
].fflags
;
640 ngx_locked_post_event(ev
, &ngx_posted_events
);
647 ev
->available
= event_list
[i
].data
;
649 if (event_list
[i
].flags
& EV_EOF
) {
651 ev
->kq_errno
= event_list
[i
].fflags
;
670 ngx_log_error(NGX_LOG_ALERT
, cycle
->log
, 0,
671 "unexpected kevent() filter %d",
672 event_list
[i
].filter
);
676 if (flags
& NGX_POST_EVENTS
) {
677 queue
= (ngx_event_t
**) (ev
->accept
? &ngx_posted_accept_events
:
679 ngx_locked_post_event(ev
, queue
);
687 ngx_mutex_unlock(ngx_posted_events_mutex
);
694 ngx_kqueue_process_changes(ngx_cycle_t
*cycle
, ngx_uint_t
try)
700 struct kevent
*changes
;
702 ngx_mutex_lock(kevent_mutex
);
704 ngx_mutex_lock(list_mutex
);
707 ngx_mutex_unlock(list_mutex
);
708 ngx_mutex_unlock(kevent_mutex
);
712 changes
= change_list
;
713 if (change_list
== change_list0
) {
714 change_list
= change_list1
;
716 change_list
= change_list0
;
722 ngx_mutex_unlock(list_mutex
);
727 ngx_log_debug1(NGX_LOG_DEBUG_EVENT
, cycle
->log
, 0,
728 "kevent changes: %d", n
);
730 if (kevent(ngx_kqueue
, changes
, n
, NULL
, 0, &ts
) == -1) {
732 ngx_log_error((err
== NGX_EINTR
) ? NGX_LOG_INFO
: NGX_LOG_ALERT
,
733 cycle
->log
, err
, "kevent() failed");
740 ngx_mutex_unlock(kevent_mutex
);
746 static ngx_inline
void
747 ngx_kqueue_dump_event(ngx_log_t
*log
, struct kevent
*kev
)
749 ngx_log_debug6(NGX_LOG_DEBUG_EVENT
, log
, 0,
750 (kev
->ident
> 0x8000000 && kev
->ident
!= (unsigned) -1) ?
751 "kevent: %p: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p":
752 "kevent: %d: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p",
753 kev
->ident
, kev
->filter
,
754 kev
->flags
, kev
->fflags
,
755 kev
->data
, kev
->udata
);
760 ngx_kqueue_create_conf(ngx_cycle_t
*cycle
)
762 ngx_kqueue_conf_t
*kcf
;
764 kcf
= ngx_palloc(cycle
->pool
, sizeof(ngx_kqueue_conf_t
));
769 kcf
->changes
= NGX_CONF_UNSET
;
770 kcf
->events
= NGX_CONF_UNSET
;
777 ngx_kqueue_init_conf(ngx_cycle_t
*cycle
, void *conf
)
779 ngx_kqueue_conf_t
*kcf
= conf
;
781 ngx_conf_init_uint_value(kcf
->changes
, 512);
782 ngx_conf_init_uint_value(kcf
->events
, 512);