Mercurial
comparison third_party/libuv/src/unix/kqueue.c @ 160:948de3f54cea
[ThirdParty] Added libuv
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 14 Jan 2026 19:39:52 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 159:05cf9467a1c3 | 160:948de3f54cea |
|---|---|
| 1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. | |
| 2 * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 3 * of this software and associated documentation files (the "Software"), to | |
| 4 * deal in the Software without restriction, including without limitation the | |
| 5 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
| 6 * sell copies of the Software, and to permit persons to whom the Software is | |
| 7 * furnished to do so, subject to the following conditions: | |
| 8 * | |
| 9 * The above copyright notice and this permission notice shall be included in | |
| 10 * all copies or substantial portions of the Software. | |
| 11 * | |
| 12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 13 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 14 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 15 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 16 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 17 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
| 18 * IN THE SOFTWARE. | |
| 19 */ | |
| 20 | |
| 21 #include "uv.h" | |
| 22 #include "internal.h" | |
| 23 | |
| 24 #include <assert.h> | |
| 25 #include <stdlib.h> | |
| 26 #include <string.h> | |
| 27 #include <errno.h> | |
| 28 | |
| 29 #include <sys/sysctl.h> | |
| 30 #include <sys/types.h> | |
| 31 #include <sys/event.h> | |
| 32 #include <sys/time.h> | |
| 33 #if defined(__FreeBSD__) | |
| 34 #include <sys/user.h> | |
| 35 #endif | |
| 36 #include <unistd.h> | |
| 37 #include <fcntl.h> | |
| 38 #include <time.h> | |
| 39 | |
| 40 /* | |
| 41 * Required on | |
| 42 * - Until at least FreeBSD 11.0 | |
| 43 * - Older versions of Mac OS X | |
| 44 * | |
| 45 * http://www.boost.org/doc/libs/1_61_0/boost/asio/detail/kqueue_reactor.hpp | |
| 46 */ | |
| 47 #ifndef EV_OOBAND | |
| 48 #define EV_OOBAND EV_FLAG1 | |
| 49 #endif | |
| 50 | |
| 51 static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags); | |
| 52 | |
| 53 | |
| 54 int uv__kqueue_init(uv_loop_t* loop) { | |
| 55 loop->backend_fd = kqueue(); | |
| 56 if (loop->backend_fd == -1) | |
| 57 return UV__ERR(errno); | |
| 58 | |
| 59 uv__cloexec(loop->backend_fd, 1); | |
| 60 | |
| 61 return 0; | |
| 62 } | |
| 63 | |
| 64 | |
| 65 #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 | |
| 66 static _Atomic int uv__has_forked_with_cfrunloop; | |
| 67 #endif | |
| 68 | |
| 69 int uv__io_fork(uv_loop_t* loop) { | |
| 70 int err; | |
| 71 loop->backend_fd = -1; | |
| 72 err = uv__kqueue_init(loop); | |
| 73 if (err) | |
| 74 return err; | |
| 75 | |
| 76 #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 | |
| 77 if (loop->cf_state != NULL) { | |
| 78 /* We cannot start another CFRunloop and/or thread in the child | |
| 79 process; CF aborts if you try or if you try to touch the thread | |
| 80 at all to kill it. So the best we can do is ignore it from now | |
| 81 on. This means we can't watch directories in the same way | |
| 82 anymore (like other BSDs). It also means we cannot properly | |
| 83 clean up the allocated resources; calling | |
| 84 uv__fsevents_loop_delete from uv_loop_close will crash the | |
| 85 process. So we sidestep the issue by pretending like we never | |
| 86 started it in the first place. | |
| 87 */ | |
| 88 atomic_store_explicit(&uv__has_forked_with_cfrunloop, | |
| 89 1, | |
| 90 memory_order_relaxed); | |
| 91 uv__free(loop->cf_state); | |
| 92 loop->cf_state = NULL; | |
| 93 } | |
| 94 #endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ | |
| 95 return err; | |
| 96 } | |
| 97 | |
| 98 | |
| 99 int uv__io_check_fd(uv_loop_t* loop, int fd) { | |
| 100 struct kevent ev[2]; | |
| 101 struct stat sb; | |
| 102 #ifdef __APPLE__ | |
| 103 char path[MAXPATHLEN]; | |
| 104 #endif | |
| 105 | |
| 106 if (uv__fstat(fd, &sb)) | |
| 107 return UV__ERR(errno); | |
| 108 | |
| 109 /* On FreeBSD, kqueue only supports EVFILT_READ notification for regular files | |
| 110 * and always reports ready events for writing, resulting in busy-looping. | |
| 111 * | |
| 112 * On Darwin, DragonFlyBSD, NetBSD and OpenBSD, kqueue reports ready events for | |
| 113 * regular files as readable and writable only once, acting like an EV_ONESHOT. | |
| 114 * | |
| 115 * Neither of the above cases should be added to the kqueue. | |
| 116 */ | |
| 117 if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) | |
| 118 return UV_EINVAL; | |
| 119 | |
| 120 #ifdef __APPLE__ | |
| 121 /* On Darwin (both macOS and iOS), in addition to regular files, FIFOs also don't | |
| 122 * work properly with kqueue: the disconnection from the last writer won't trigger | |
| 123 * an event for kqueue in spite of what the man pages say. Thus, we also disallow | |
| 124 * the case of S_IFIFO. */ | |
| 125 if (S_ISFIFO(sb.st_mode)) { | |
| 126 /* File descriptors of FIFO, pipe and kqueue share the same type of file, | |
| 127 * therefore there is no way to tell them apart via stat.st_mode&S_IFMT. | |
| 128 * Fortunately, FIFO is the only one that has a persisted file on filesystem, | |
| 129 * from which we're able to make the distinction for it. */ | |
| 130 if (!fcntl(fd, F_GETPATH, path)) | |
| 131 return UV_EINVAL; | |
| 132 } | |
| 133 #endif | |
| 134 | |
| 135 EV_SET(ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0); | |
| 136 EV_SET(ev + 1, fd, EVFILT_READ, EV_DELETE, 0, 0, 0); | |
| 137 if (kevent(loop->backend_fd, ev, 2, NULL, 0, NULL)) | |
| 138 return UV__ERR(errno); | |
| 139 | |
| 140 return 0; | |
| 141 } | |
| 142 | |
| 143 | |
| 144 static void uv__kqueue_delete(int kqfd, const struct kevent *ev) { | |
| 145 struct kevent change; | |
| 146 | |
| 147 EV_SET(&change, ev->ident, ev->filter, EV_DELETE, 0, 0, 0); | |
| 148 | |
| 149 if (0 == kevent(kqfd, &change, 1, NULL, 0, NULL)) | |
| 150 return; | |
| 151 | |
| 152 if (errno == EBADF || errno == ENOENT) | |
| 153 return; | |
| 154 | |
| 155 abort(); | |
| 156 } | |
| 157 | |
| 158 | |
| 159 void uv__io_poll(uv_loop_t* loop, int timeout) { | |
| 160 uv__loop_internal_fields_t* lfields; | |
| 161 struct kevent events[1024]; | |
| 162 struct kevent* ev; | |
| 163 struct timespec spec; | |
| 164 unsigned int nevents; | |
| 165 unsigned int revents; | |
| 166 struct uv__queue* q; | |
| 167 uv__io_t* w; | |
| 168 uv_process_t* process; | |
| 169 sigset_t* pset; | |
| 170 sigset_t set; | |
| 171 uint64_t base; | |
| 172 uint64_t diff; | |
| 173 int have_signals; | |
| 174 int filter; | |
| 175 int fflags; | |
| 176 int count; | |
| 177 int nfds; | |
| 178 int fd; | |
| 179 int op; | |
| 180 int i; | |
| 181 int user_timeout; | |
| 182 int reset_timeout; | |
| 183 | |
| 184 if (loop->nfds == 0) { | |
| 185 assert(uv__queue_empty(&loop->watcher_queue)); | |
| 186 return; | |
| 187 } | |
| 188 | |
| 189 lfields = uv__get_internal_fields(loop); | |
| 190 nevents = 0; | |
| 191 | |
| 192 while (!uv__queue_empty(&loop->watcher_queue)) { | |
| 193 q = uv__queue_head(&loop->watcher_queue); | |
| 194 uv__queue_remove(q); | |
| 195 uv__queue_init(q); | |
| 196 | |
| 197 w = uv__queue_data(q, uv__io_t, watcher_queue); | |
| 198 assert(w->pevents != 0); | |
| 199 assert(w->fd >= 0); | |
| 200 assert(w->fd < (int) loop->nwatchers); | |
| 201 | |
| 202 if ((w->events & POLLIN) == 0 && (w->pevents & POLLIN) != 0) { | |
| 203 filter = EVFILT_READ; | |
| 204 fflags = 0; | |
| 205 op = EV_ADD; | |
| 206 | |
| 207 if (w->cb == uv__fs_event) { | |
| 208 filter = EVFILT_VNODE; | |
| 209 fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME | |
| 210 | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE; | |
| 211 op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */ | |
| 212 } | |
| 213 | |
| 214 EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0); | |
| 215 | |
| 216 if (++nevents == ARRAY_SIZE(events)) { | |
| 217 if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) | |
| 218 abort(); | |
| 219 nevents = 0; | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 if ((w->events & POLLOUT) == 0 && (w->pevents & POLLOUT) != 0) { | |
| 224 EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0); | |
| 225 | |
| 226 if (++nevents == ARRAY_SIZE(events)) { | |
| 227 if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) | |
| 228 abort(); | |
| 229 nevents = 0; | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 if ((w->events & UV__POLLPRI) == 0 && (w->pevents & UV__POLLPRI) != 0) { | |
| 234 EV_SET(events + nevents, w->fd, EV_OOBAND, EV_ADD, 0, 0, 0); | |
| 235 | |
| 236 if (++nevents == ARRAY_SIZE(events)) { | |
| 237 if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) | |
| 238 abort(); | |
| 239 nevents = 0; | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 w->events = w->pevents; | |
| 244 } | |
| 245 | |
| 246 pset = NULL; | |
| 247 if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { | |
| 248 pset = &set; | |
| 249 sigemptyset(pset); | |
| 250 sigaddset(pset, SIGPROF); | |
| 251 } | |
| 252 | |
| 253 assert(timeout >= -1); | |
| 254 base = loop->time; | |
| 255 count = 48; /* Benchmarks suggest this gives the best throughput. */ | |
| 256 | |
| 257 if (lfields->flags & UV_METRICS_IDLE_TIME) { | |
| 258 reset_timeout = 1; | |
| 259 user_timeout = timeout; | |
| 260 timeout = 0; | |
| 261 } else { | |
| 262 reset_timeout = 0; | |
| 263 } | |
| 264 | |
| 265 for (;; nevents = 0) { | |
| 266 /* Only need to set the provider_entry_time if timeout != 0. The function | |
| 267 * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. | |
| 268 */ | |
| 269 if (timeout != 0) | |
| 270 uv__metrics_set_provider_entry_time(loop); | |
| 271 | |
| 272 if (timeout != -1) { | |
| 273 spec.tv_sec = timeout / 1000; | |
| 274 spec.tv_nsec = (timeout % 1000) * 1000000; | |
| 275 } | |
| 276 | |
| 277 if (pset != NULL) | |
| 278 pthread_sigmask(SIG_BLOCK, pset, NULL); | |
| 279 | |
| 280 /* Store the current timeout in a location that's globally accessible so | |
| 281 * other locations like uv__work_done() can determine whether the queue | |
| 282 * of events in the callback were waiting when poll was called. | |
| 283 */ | |
| 284 lfields->current_timeout = timeout; | |
| 285 | |
| 286 nfds = kevent(loop->backend_fd, | |
| 287 events, | |
| 288 nevents, | |
| 289 events, | |
| 290 ARRAY_SIZE(events), | |
| 291 timeout == -1 ? NULL : &spec); | |
| 292 | |
| 293 if (nfds == -1) | |
| 294 assert(errno == EINTR); | |
| 295 else if (nfds == 0) | |
| 296 /* Unlimited timeout should only return with events or signal. */ | |
| 297 assert(timeout != -1); | |
| 298 | |
| 299 if (pset != NULL) | |
| 300 pthread_sigmask(SIG_UNBLOCK, pset, NULL); | |
| 301 | |
| 302 /* Update loop->time unconditionally. It's tempting to skip the update when | |
| 303 * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the | |
| 304 * operating system didn't reschedule our process while in the syscall. | |
| 305 */ | |
| 306 uv__update_time(loop); | |
| 307 | |
| 308 if (nfds == 0 || nfds == -1) { | |
| 309 /* If kqueue is empty or interrupted, we might still have children ready | |
| 310 * to reap immediately. */ | |
| 311 if (loop->flags & UV_LOOP_REAP_CHILDREN) { | |
| 312 loop->flags &= ~UV_LOOP_REAP_CHILDREN; | |
| 313 uv__wait_children(loop); | |
| 314 assert((reset_timeout == 0 ? timeout : user_timeout) == 0); | |
| 315 return; /* Equivalent to fall-through behavior. */ | |
| 316 } | |
| 317 | |
| 318 if (reset_timeout != 0) { | |
| 319 timeout = user_timeout; | |
| 320 reset_timeout = 0; | |
| 321 } else if (nfds == 0) { | |
| 322 return; | |
| 323 } | |
| 324 | |
| 325 /* Interrupted by a signal. Update timeout and poll again. */ | |
| 326 goto update_timeout; | |
| 327 } | |
| 328 | |
| 329 have_signals = 0; | |
| 330 nevents = 0; | |
| 331 | |
| 332 assert(loop->watchers != NULL); | |
| 333 loop->watchers[loop->nwatchers] = (void*) events; | |
| 334 loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; | |
| 335 for (i = 0; i < nfds; i++) { | |
| 336 ev = events + i; | |
| 337 fd = ev->ident; | |
| 338 | |
| 339 /* Handle kevent NOTE_EXIT results */ | |
| 340 if (ev->filter == EVFILT_PROC) { | |
| 341 uv__queue_foreach(q, &loop->process_handles) { | |
| 342 process = uv__queue_data(q, uv_process_t, queue); | |
| 343 if (process->pid == fd) { | |
| 344 process->flags |= UV_HANDLE_REAP; | |
| 345 loop->flags |= UV_LOOP_REAP_CHILDREN; | |
| 346 break; | |
| 347 } | |
| 348 } | |
| 349 nevents++; | |
| 350 continue; | |
| 351 } | |
| 352 | |
| 353 /* Skip invalidated events, see uv__platform_invalidate_fd */ | |
| 354 if (fd == -1) | |
| 355 continue; | |
| 356 w = loop->watchers[fd]; | |
| 357 | |
| 358 if (w == NULL) { | |
| 359 /* File descriptor that we've stopped watching, disarm it. */ | |
| 360 uv__kqueue_delete(loop->backend_fd, ev); | |
| 361 continue; | |
| 362 } | |
| 363 | |
| 364 #if UV__KQUEUE_EVFILT_USER | |
| 365 if (ev->filter == EVFILT_USER) { | |
| 366 w = &loop->async_io_watcher; | |
| 367 assert(fd == w->fd); | |
| 368 uv__metrics_update_idle_time(loop); | |
| 369 w->cb(loop, w, w->events); | |
| 370 nevents++; | |
| 371 continue; | |
| 372 } | |
| 373 #endif | |
| 374 | |
| 375 if (ev->filter == EVFILT_VNODE) { | |
| 376 assert(w->events == POLLIN); | |
| 377 assert(w->pevents == POLLIN); | |
| 378 uv__metrics_update_idle_time(loop); | |
| 379 w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */ | |
| 380 nevents++; | |
| 381 continue; | |
| 382 } | |
| 383 | |
| 384 revents = 0; | |
| 385 | |
| 386 if (ev->filter == EVFILT_READ) { | |
| 387 if (w->pevents & POLLIN) | |
| 388 revents |= POLLIN; | |
| 389 else | |
| 390 uv__kqueue_delete(loop->backend_fd, ev); | |
| 391 | |
| 392 if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP)) | |
| 393 revents |= UV__POLLRDHUP; | |
| 394 } | |
| 395 | |
| 396 if (ev->filter == EV_OOBAND) { | |
| 397 if (w->pevents & UV__POLLPRI) | |
| 398 revents |= UV__POLLPRI; | |
| 399 else | |
| 400 uv__kqueue_delete(loop->backend_fd, ev); | |
| 401 } | |
| 402 | |
| 403 if (ev->filter == EVFILT_WRITE) { | |
| 404 if (w->pevents & POLLOUT) | |
| 405 revents |= POLLOUT; | |
| 406 else | |
| 407 uv__kqueue_delete(loop->backend_fd, ev); | |
| 408 } | |
| 409 | |
| 410 if (ev->flags & EV_ERROR) | |
| 411 revents |= POLLERR; | |
| 412 | |
| 413 if (revents == 0) | |
| 414 continue; | |
| 415 | |
| 416 /* Run signal watchers last. This also affects child process watchers | |
| 417 * because those are implemented in terms of signal watchers. | |
| 418 */ | |
| 419 if (w == &loop->signal_io_watcher) { | |
| 420 have_signals = 1; | |
| 421 } else { | |
| 422 uv__metrics_update_idle_time(loop); | |
| 423 w->cb(loop, w, revents); | |
| 424 } | |
| 425 | |
| 426 nevents++; | |
| 427 } | |
| 428 | |
| 429 if (loop->flags & UV_LOOP_REAP_CHILDREN) { | |
| 430 loop->flags &= ~UV_LOOP_REAP_CHILDREN; | |
| 431 uv__wait_children(loop); | |
| 432 } | |
| 433 | |
| 434 uv__metrics_inc_events(loop, nevents); | |
| 435 if (reset_timeout != 0) { | |
| 436 timeout = user_timeout; | |
| 437 reset_timeout = 0; | |
| 438 uv__metrics_inc_events_waiting(loop, nevents); | |
| 439 } | |
| 440 | |
| 441 if (have_signals != 0) { | |
| 442 uv__metrics_update_idle_time(loop); | |
| 443 loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); | |
| 444 } | |
| 445 | |
| 446 loop->watchers[loop->nwatchers] = NULL; | |
| 447 loop->watchers[loop->nwatchers + 1] = NULL; | |
| 448 | |
| 449 if (have_signals != 0) | |
| 450 return; /* Event loop should cycle now so don't poll again. */ | |
| 451 | |
| 452 if (nevents != 0) { | |
| 453 if (nfds == ARRAY_SIZE(events) && --count != 0) { | |
| 454 /* Poll for more events but don't block this time. */ | |
| 455 timeout = 0; | |
| 456 continue; | |
| 457 } | |
| 458 return; | |
| 459 } | |
| 460 | |
| 461 update_timeout: | |
| 462 if (timeout == 0) | |
| 463 return; | |
| 464 | |
| 465 if (timeout == -1) | |
| 466 continue; | |
| 467 | |
| 468 assert(timeout > 0); | |
| 469 | |
| 470 diff = loop->time - base; | |
| 471 if (diff >= (uint64_t) timeout) | |
| 472 return; | |
| 473 | |
| 474 timeout -= diff; | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 | |
| 479 void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { | |
| 480 struct kevent* events; | |
| 481 uintptr_t i; | |
| 482 uintptr_t nfds; | |
| 483 | |
| 484 assert(loop->watchers != NULL); | |
| 485 assert(fd >= 0); | |
| 486 | |
| 487 events = (struct kevent*) loop->watchers[loop->nwatchers]; | |
| 488 nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; | |
| 489 if (events == NULL) | |
| 490 return; | |
| 491 | |
| 492 /* Invalidate events with same file descriptor */ | |
| 493 for (i = 0; i < nfds; i++) | |
| 494 if ((int) events[i].ident == fd && events[i].filter != EVFILT_PROC) | |
| 495 events[i].ident = -1; | |
| 496 } | |
| 497 | |
| 498 | |
| 499 static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) { | |
| 500 uv_fs_event_t* handle; | |
| 501 struct kevent ev; | |
| 502 int events; | |
| 503 const char* path; | |
| 504 #if defined(F_GETPATH) | |
| 505 /* MAXPATHLEN == PATH_MAX but the former is what XNU calls it internally. */ | |
| 506 char pathbuf[MAXPATHLEN]; | |
| 507 #endif | |
| 508 | |
| 509 handle = container_of(w, uv_fs_event_t, event_watcher); | |
| 510 | |
| 511 if (fflags & (NOTE_ATTRIB | NOTE_EXTEND)) | |
| 512 events = UV_CHANGE; | |
| 513 else | |
| 514 events = UV_RENAME; | |
| 515 | |
| 516 path = NULL; | |
| 517 #if defined(F_GETPATH) | |
| 518 /* Also works when the file has been unlinked from the file system. Passing | |
| 519 * in the path when the file has been deleted is arguably a little strange | |
| 520 * but it's consistent with what the inotify backend does. | |
| 521 */ | |
| 522 if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0) | |
| 523 path = uv__basename_r(pathbuf); | |
| 524 #elif defined(F_KINFO) | |
| 525 /* We try to get the file info reference from the file descriptor. | |
| 526 * the struct's kf_structsize must be initialised beforehand | |
| 527 * whether with the KINFO_FILE_SIZE constant or this way. | |
| 528 */ | |
| 529 struct stat statbuf; | |
| 530 struct kinfo_file kf; | |
| 531 | |
| 532 if (handle->event_watcher.fd != -1 && | |
| 533 (!uv__fstat(handle->event_watcher.fd, &statbuf) && !(statbuf.st_mode & S_IFDIR))) { | |
| 534 /* we are purposely not using KINFO_FILE_SIZE here | |
| 535 * as it is not available on non intl archs | |
| 536 * and here it gives 1392 too on intel. | |
| 537 * anyway, the man page also mentions we can proceed | |
| 538 * this way. | |
| 539 */ | |
| 540 kf.kf_structsize = sizeof(kf); | |
| 541 if (fcntl(handle->event_watcher.fd, F_KINFO, &kf) == 0) | |
| 542 path = uv__basename_r(kf.kf_path); | |
| 543 } | |
| 544 #endif | |
| 545 handle->cb(handle, path, events, 0); | |
| 546 | |
| 547 if (handle->event_watcher.fd == -1) | |
| 548 return; | |
| 549 | |
| 550 /* Watcher operates in one-shot mode, re-arm it. */ | |
| 551 fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME | |
| 552 | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE; | |
| 553 | |
| 554 EV_SET(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0); | |
| 555 | |
| 556 if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL)) | |
| 557 abort(); | |
| 558 } | |
| 559 | |
| 560 | |
| 561 int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { | |
| 562 uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); | |
| 563 return 0; | |
| 564 } | |
| 565 | |
| 566 | |
| 567 int uv_fs_event_start(uv_fs_event_t* handle, | |
| 568 uv_fs_event_cb cb, | |
| 569 const char* path, | |
| 570 unsigned int flags) { | |
| 571 int fd; | |
| 572 int r; | |
| 573 #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 | |
| 574 struct stat statbuf; | |
| 575 #endif | |
| 576 | |
| 577 if (uv__is_active(handle)) | |
| 578 return UV_EINVAL; | |
| 579 | |
| 580 handle->cb = cb; | |
| 581 handle->path = uv__strdup(path); | |
| 582 if (handle->path == NULL) | |
| 583 return UV_ENOMEM; | |
| 584 | |
| 585 /* TODO open asynchronously - but how do we report back errors? */ | |
| 586 fd = open(handle->path, O_RDONLY); | |
| 587 if (fd == -1) { | |
| 588 uv__free(handle->path); | |
| 589 handle->path = NULL; | |
| 590 return UV__ERR(errno); | |
| 591 } | |
| 592 | |
| 593 #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 | |
| 594 /* Nullify field to perform checks later */ | |
| 595 handle->cf_cb = NULL; | |
| 596 handle->realpath = NULL; | |
| 597 handle->realpath_len = 0; | |
| 598 handle->cf_flags = flags; | |
| 599 | |
| 600 if (uv__fstat(fd, &statbuf)) | |
| 601 goto fallback; | |
| 602 /* FSEvents works only with directories */ | |
| 603 if (!(statbuf.st_mode & S_IFDIR)) | |
| 604 goto fallback; | |
| 605 | |
| 606 if (0 == atomic_load_explicit(&uv__has_forked_with_cfrunloop, | |
| 607 memory_order_relaxed)) { | |
| 608 /* The fallback fd is no longer needed */ | |
| 609 uv__close_nocheckstdio(fd); | |
| 610 handle->event_watcher.fd = -1; | |
| 611 r = uv__fsevents_init(handle); | |
| 612 if (r == 0) { | |
| 613 uv__handle_start(handle); | |
| 614 } else { | |
| 615 uv__free(handle->path); | |
| 616 handle->path = NULL; | |
| 617 } | |
| 618 return r; | |
| 619 } | |
| 620 fallback: | |
| 621 #endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ | |
| 622 | |
| 623 r = uv__io_init_start(handle->loop, | |
| 624 &handle->event_watcher, | |
| 625 uv__fs_event, | |
| 626 fd, | |
| 627 POLLIN); | |
| 628 | |
| 629 if (!r) | |
| 630 uv__handle_start(handle); | |
| 631 | |
| 632 return r; | |
| 633 } | |
| 634 | |
| 635 | |
| 636 int uv_fs_event_stop(uv_fs_event_t* handle) { | |
| 637 int r; | |
| 638 r = 0; | |
| 639 | |
| 640 if (!uv__is_active(handle)) | |
| 641 return 0; | |
| 642 | |
| 643 uv__handle_stop(handle); | |
| 644 | |
| 645 #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 | |
| 646 if (0 == atomic_load_explicit(&uv__has_forked_with_cfrunloop, | |
| 647 memory_order_relaxed)) | |
| 648 if (handle->cf_cb != NULL) | |
| 649 r = uv__fsevents_close(handle); | |
| 650 #endif | |
| 651 | |
| 652 if (handle->event_watcher.fd != -1) { | |
| 653 uv__io_close(handle->loop, &handle->event_watcher); | |
| 654 uv__close(handle->event_watcher.fd); | |
| 655 handle->event_watcher.fd = -1; | |
| 656 } | |
| 657 | |
| 658 uv__free(handle->path); | |
| 659 handle->path = NULL; | |
| 660 | |
| 661 return r; | |
| 662 } | |
| 663 | |
| 664 | |
| 665 void uv__fs_event_close(uv_fs_event_t* handle) { | |
| 666 uv_fs_event_stop(handle); | |
| 667 } |