Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2002 Igor Sysoev, http://sysoev.ru |
| 3 | */ |
| 4 | |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 5 | |
| 6 | #include <ngx_config.h> |
Igor Sysoev | 016b852 | 2002-08-29 16:59:54 +0000 | [diff] [blame] | 7 | #include <ngx_core.h> |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 8 | #include <ngx_types.h> |
| 9 | #include <ngx_log.h> |
| 10 | #include <ngx_connection.h> |
| 11 | #include <ngx_event.h> |
| 12 | #include <ngx_kqueue_module.h> |
| 13 | |
| 14 | #if (USE_KQUEUE) && !(HAVE_KQUEUE) |
| 15 | #error "kqueue is not supported on this platform" |
| 16 | #endif |
| 17 | |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 18 | |
| 19 | |
| 20 | static int kq; |
| 21 | static struct kevent *change_list, *event_list; |
| 22 | static int nchanges, nevents; |
| 23 | |
| 24 | static ngx_event_t timer_queue; |
| 25 | |
Igor Sysoev | 31f8818 | 2002-09-27 15:05:29 +0000 | [diff] [blame] | 26 | int ngx_kqueue_init(int max_connections, ngx_log_t *log) |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 27 | { |
| 28 | int size = sizeof(struct kevent) * 512; |
| 29 | |
| 30 | nchanges = 0; |
| 31 | nevents = 512; |
| 32 | |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 33 | kq = kqueue(); |
| 34 | |
| 35 | if (kq == -1) { |
| 36 | ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "kqueue() failed"); |
Igor Sysoev | 31f8818 | 2002-09-27 15:05:29 +0000 | [diff] [blame] | 37 | return NGX_ERROR; |
Igor Sysoev | 0ad17c0 | 2002-08-26 15:18:19 +0000 | [diff] [blame] | 38 | } |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 39 | |
Igor Sysoev | 31f8818 | 2002-09-27 15:05:29 +0000 | [diff] [blame] | 40 | ngx_test_null(change_list, ngx_alloc(size, log), NGX_ERROR); |
| 41 | ngx_test_null(event_list, ngx_alloc(size, log), NGX_ERROR); |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 42 | |
| 43 | timer_queue.timer_prev = &timer_queue; |
| 44 | timer_queue.timer_next = &timer_queue; |
| 45 | |
| 46 | #if !(USE_KQUEUE) |
| 47 | ngx_event_actions.add = ngx_kqueue_add_event; |
| 48 | ngx_event_actions.del = ngx_kqueue_del_event; |
Igor Sysoev | a58e3ca | 2002-09-02 14:48:24 +0000 | [diff] [blame] | 49 | ngx_event_actions.timer = ngx_kqueue_add_timer; |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 50 | ngx_event_actions.process = ngx_kqueue_process_events; |
| 51 | #endif |
Igor Sysoev | 31f8818 | 2002-09-27 15:05:29 +0000 | [diff] [blame] | 52 | |
| 53 | return NGX_OK; |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 54 | } |
| 55 | |
| 56 | int ngx_kqueue_add_event(ngx_event_t *ev, int event, u_int flags) |
| 57 | { |
Igor Sysoev | 3a40d48 | 2002-09-12 14:42:29 +0000 | [diff] [blame] | 58 | ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1: 0; |
| 59 | |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 60 | return ngx_kqueue_set_event(ev, event, EV_ADD | flags); |
| 61 | } |
| 62 | |
| 63 | int ngx_kqueue_del_event(ngx_event_t *ev, int event) |
| 64 | { |
Igor Sysoev | 2ba1ee0 | 2002-10-04 17:58:04 +0000 | [diff] [blame] | 65 | ngx_event_t *e; |
| 66 | |
| 67 | if (ev->index <= nchanges && change_list[ev->index].udata == ev) { |
| 68 | change_list[ev->index] = change_list[nchanges]; |
| 69 | e = (ngx_event_t *) change_list[ev->index].udata; |
| 70 | e->index = ev->index; |
| 71 | nchanges--; |
| 72 | |
| 73 | return NGX_OK; |
| 74 | } |
| 75 | |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 76 | return ngx_kqueue_set_event(ev, event, EV_DELETE); |
| 77 | } |
| 78 | |
| 79 | int ngx_kqueue_set_event(ngx_event_t *ev, int filter, u_int flags) |
| 80 | { |
| 81 | struct timespec ts = { 0, 0 }; |
| 82 | ngx_connection_t *cn = (ngx_connection_t *) ev->data; |
| 83 | |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 84 | ngx_log_debug(ev->log, "kqueue set event: %d: ft:%d f:%08x" _ |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 85 | cn->fd _ filter _ flags); |
| 86 | |
| 87 | if (nchanges >= nevents) { |
| 88 | ngx_log_error(NGX_LOG_WARN, ev->log, 0, |
Igor Sysoev | 31f8818 | 2002-09-27 15:05:29 +0000 | [diff] [blame] | 89 | "kqueue change list is filled up"); |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 90 | |
| 91 | if (kevent(kq, change_list, nchanges, NULL, 0, &ts) == -1) { |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 92 | ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent failed"); |
| 93 | return NGX_ERROR; |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 94 | } |
| 95 | nchanges = 0; |
| 96 | } |
| 97 | |
| 98 | change_list[nchanges].ident = cn->fd; |
| 99 | change_list[nchanges].filter = filter; |
| 100 | change_list[nchanges].flags = flags; |
| 101 | change_list[nchanges].fflags = 0; |
| 102 | change_list[nchanges].data = 0; |
| 103 | change_list[nchanges].udata = ev; |
Igor Sysoev | 2ba1ee0 | 2002-10-04 17:58:04 +0000 | [diff] [blame] | 104 | |
| 105 | if (flags == EV_ADD) |
| 106 | ev->index = nchanges; |
| 107 | |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 108 | nchanges++; |
| 109 | |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 110 | return NGX_OK; |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | int ngx_kqueue_process_events(ngx_log_t *log) |
| 114 | { |
| 115 | int events, i; |
| 116 | u_int timer = 0, delta = 0; |
Igor Sysoev | d4324e6 | 2002-09-17 17:49:32 +0000 | [diff] [blame] | 117 | ngx_event_t *ev; |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 118 | struct timeval tv; |
| 119 | struct timespec ts, *tp = NULL; |
| 120 | |
| 121 | if (timer_queue.timer_next != &timer_queue) { |
| 122 | timer = timer_queue.timer_next->timer_delta; |
| 123 | ts.tv_sec = timer / 1000; |
| 124 | ts.tv_nsec = (timer % 1000) * 1000000; |
| 125 | tp = &ts; |
| 126 | gettimeofday(&tv, NULL); |
| 127 | delta = tv.tv_sec * 1000 + tv.tv_usec / 1000; |
| 128 | } |
| 129 | |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 130 | ngx_log_debug(log, "kevent timer: %d" _ timer); |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 131 | |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 132 | events = kevent(kq, change_list, nchanges, event_list, nevents, tp); |
| 133 | if (events == -1) { |
| 134 | ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "kevent failed"); |
| 135 | return NGX_ERROR; |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | nchanges = 0; |
| 139 | |
| 140 | if (timer) { |
| 141 | gettimeofday(&tv, NULL); |
| 142 | delta = tv.tv_sec * 1000 + tv.tv_usec / 1000 - delta; |
| 143 | |
| 144 | } else { |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 145 | ngx_assert((events != 0), return NGX_ERROR, log, |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 146 | "kevent returns no events without timeout"); |
| 147 | } |
| 148 | |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 149 | ngx_log_debug(log, "kevent timer: %d, delta: %d" _ timer _ delta); |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 150 | |
| 151 | if (timer) { |
| 152 | if (delta >= timer) { |
Igor Sysoev | d4324e6 | 2002-09-17 17:49:32 +0000 | [diff] [blame] | 153 | for ( ;; ) { |
| 154 | ev = timer_queue.timer_next; |
| 155 | |
| 156 | if (ev == &timer_queue || delta < ev->timer_delta) |
| 157 | break; |
| 158 | |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 159 | delta -= ev->timer_delta; |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 160 | ngx_del_timer(ev); |
Igor Sysoev | 0ad17c0 | 2002-08-26 15:18:19 +0000 | [diff] [blame] | 161 | ev->timedout = 1; |
Igor Sysoev | 016b852 | 2002-08-29 16:59:54 +0000 | [diff] [blame] | 162 | if (ev->event_handler(ev) == NGX_ERROR) |
Igor Sysoev | 0ad17c0 | 2002-08-26 15:18:19 +0000 | [diff] [blame] | 163 | ev->close_handler(ev); |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 164 | } |
| 165 | |
| 166 | } else { |
| 167 | timer_queue.timer_next->timer_delta -= delta; |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | for (i = 0; i < events; i++) { |
| 172 | |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 173 | ngx_log_debug(log, "kevent: %d: ft:%d f:%08x ff:%08x d:%d ud:%08x" _ |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 174 | event_list[i].ident _ event_list[i].filter _ |
| 175 | event_list[i].flags _ event_list[i].fflags _ |
| 176 | event_list[i].data _ event_list[i].udata); |
| 177 | |
| 178 | if (event_list[i].flags & EV_ERROR) { |
| 179 | ngx_log_error(NGX_LOG_ALERT, log, event_list[i].data, |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 180 | "kevent error on %d", event_list[i].ident); |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 181 | continue; |
| 182 | } |
| 183 | |
| 184 | ev = (ngx_event_t *) event_list[i].udata; |
| 185 | |
| 186 | switch (event_list[i].filter) { |
| 187 | |
| 188 | case EVFILT_READ: |
| 189 | case EVFILT_WRITE: |
| 190 | ev->ready = 1; |
| 191 | ev->available = event_list[i].data; |
| 192 | |
| 193 | if (event_list[i].flags & EV_EOF) { |
| 194 | ev->eof = 1; |
| 195 | ev->error = event_list[i].fflags; |
| 196 | } |
| 197 | |
Igor Sysoev | 3a40d48 | 2002-09-12 14:42:29 +0000 | [diff] [blame] | 198 | if (ev->oneshot) |
| 199 | ngx_del_timer(ev); |
| 200 | |
Igor Sysoev | 016b852 | 2002-08-29 16:59:54 +0000 | [diff] [blame] | 201 | if (ev->event_handler(ev) == NGX_ERROR) |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 202 | ev->close_handler(ev); |
| 203 | |
| 204 | break; |
| 205 | |
| 206 | default: |
| 207 | ngx_assert(0, /* void */, log, |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 208 | "unknown kevent filter %d" _ event_list[i].filter); |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 209 | } |
| 210 | } |
| 211 | |
Igor Sysoev | 1af7c82 | 2002-09-13 14:47:42 +0000 | [diff] [blame] | 212 | return NGX_OK; |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 213 | } |
| 214 | |
Igor Sysoev | a58e3ca | 2002-09-02 14:48:24 +0000 | [diff] [blame] | 215 | void ngx_kqueue_add_timer(ngx_event_t *ev, ngx_msec_t timer) |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 216 | { |
| 217 | ngx_event_t *e; |
| 218 | |
Igor Sysoev | a58e3ca | 2002-09-02 14:48:24 +0000 | [diff] [blame] | 219 | ngx_log_debug(ev->log, "set timer: %d" _ timer); |
| 220 | |
Igor Sysoev | 3a40d48 | 2002-09-12 14:42:29 +0000 | [diff] [blame] | 221 | ngx_assert((!ev->timer_next && !ev->timer_prev), return, ev->log, |
| 222 | "timer already set"); |
| 223 | |
Igor Sysoev | 6de5c2c | 2002-08-06 16:39:45 +0000 | [diff] [blame] | 224 | for (e = timer_queue.timer_next; |
| 225 | e != &timer_queue && timer > e->timer_delta; |
| 226 | e = e->timer_next) |
| 227 | timer -= e->timer_delta; |
| 228 | |
| 229 | ev->timer_delta = timer; |
| 230 | |
| 231 | ev->timer_next = e; |
| 232 | ev->timer_prev = e->timer_prev; |
| 233 | |
| 234 | e->timer_prev->timer_next = ev; |
| 235 | e->timer_prev = ev; |
| 236 | } |