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