Blame src/unix/kqueue.c

Packit Service 7c31a4
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Packit Service 7c31a4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
Packit Service 7c31a4
 * of this software and associated documentation files (the "Software"), to
Packit Service 7c31a4
 * deal in the Software without restriction, including without limitation the
Packit Service 7c31a4
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
Packit Service 7c31a4
 * sell copies of the Software, and to permit persons to whom the Software is
Packit Service 7c31a4
 * furnished to do so, subject to the following conditions:
Packit Service 7c31a4
 *
Packit Service 7c31a4
 * The above copyright notice and this permission notice shall be included in
Packit Service 7c31a4
 * all copies or substantial portions of the Software.
Packit Service 7c31a4
 *
Packit Service 7c31a4
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Packit Service 7c31a4
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Packit Service 7c31a4
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Packit Service 7c31a4
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Packit Service 7c31a4
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Packit Service 7c31a4
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
Packit Service 7c31a4
 * IN THE SOFTWARE.
Packit Service 7c31a4
 */
Packit Service 7c31a4
Packit Service 7c31a4
#include "uv.h"
Packit Service 7c31a4
#include "internal.h"
Packit Service 7c31a4
Packit Service 7c31a4
#include <assert.h>
Packit Service 7c31a4
#include <stdlib.h>
Packit Service 7c31a4
#include <string.h>
Packit Service 7c31a4
#include <errno.h>
Packit Service 7c31a4
Packit Service 7c31a4
#include <sys/sysctl.h>
Packit Service 7c31a4
#include <sys/types.h>
Packit Service 7c31a4
#include <sys/event.h>
Packit Service 7c31a4
#include <sys/time.h>
Packit Service 7c31a4
#include <unistd.h>
Packit Service 7c31a4
#include <fcntl.h>
Packit Service 7c31a4
#include <time.h>
Packit Service 7c31a4
Packit Service 7c31a4
/*
Packit Service 7c31a4
 * Required on
Packit Service 7c31a4
 * - Until at least FreeBSD 11.0
Packit Service 7c31a4
 * - Older versions of Mac OS X
Packit Service 7c31a4
 *
Packit Service 7c31a4
 * http://www.boost.org/doc/libs/1_61_0/boost/asio/detail/kqueue_reactor.hpp
Packit Service 7c31a4
 */
Packit Service 7c31a4
#ifndef EV_OOBAND
Packit Service 7c31a4
#define EV_OOBAND  EV_FLAG1
Packit Service 7c31a4
#endif
Packit Service 7c31a4
Packit Service 7c31a4
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags);
Packit Service 7c31a4
Packit Service 7c31a4
Packit Service 7c31a4
int uv__kqueue_init(uv_loop_t* loop) {
Packit Service 7c31a4
  loop->backend_fd = kqueue();
Packit Service 7c31a4
  if (loop->backend_fd == -1)
Packit Service 7c31a4
    return UV__ERR(errno);
Packit Service 7c31a4
Packit Service 7c31a4
  uv__cloexec(loop->backend_fd, 1);
Packit Service 7c31a4
Packit Service 7c31a4
  return 0;
Packit Service 7c31a4
}
Packit Service 7c31a4
Packit Service 7c31a4
Packit Service 7c31a4
#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
Packit Service 7c31a4
static int uv__has_forked_with_cfrunloop;
Packit Service 7c31a4
#endif
Packit Service 7c31a4
Packit Service 7c31a4
int uv__io_fork(uv_loop_t* loop) {
Packit Service 7c31a4
  int err;
Packit Service 7c31a4
  loop->backend_fd = -1;
Packit Service 7c31a4
  err = uv__kqueue_init(loop);
Packit Service 7c31a4
  if (err)
Packit Service 7c31a4
    return err;
Packit Service 7c31a4
Packit Service 7c31a4
#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
Packit Service 7c31a4
  if (loop->cf_state != NULL) {
Packit Service 7c31a4
    /* We cannot start another CFRunloop and/or thread in the child
Packit Service 7c31a4
       process; CF aborts if you try or if you try to touch the thread
Packit Service 7c31a4
       at all to kill it. So the best we can do is ignore it from now
Packit Service 7c31a4
       on. This means we can't watch directories in the same way
Packit Service 7c31a4
       anymore (like other BSDs). It also means we cannot properly
Packit Service 7c31a4
       clean up the allocated resources; calling
Packit Service 7c31a4
       uv__fsevents_loop_delete from uv_loop_close will crash the
Packit Service 7c31a4
       process. So we sidestep the issue by pretending like we never
Packit Service 7c31a4
       started it in the first place.
Packit Service 7c31a4
    */
Packit Service e2ebee
    uv__store_relaxed(&uv__has_forked_with_cfrunloop, 1);
Packit Service 7c31a4
    uv__free(loop->cf_state);
Packit Service 7c31a4
    loop->cf_state = NULL;
Packit Service 7c31a4
  }
Packit Service 7c31a4
#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
Packit Service 7c31a4
  return err;
Packit Service 7c31a4
}
Packit Service 7c31a4
Packit Service 7c31a4
Packit Service 7c31a4
int uv__io_check_fd(uv_loop_t* loop, int fd) {
Packit Service 7c31a4
  struct kevent ev;
Packit Service 7c31a4
  int rc;
Packit Service 7c31a4
Packit Service 7c31a4
  rc = 0;
Packit Service 7c31a4
  EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
Packit Service 7c31a4
  if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
Packit Service 7c31a4
    rc = UV__ERR(errno);
Packit Service 7c31a4
Packit Service 7c31a4
  EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
Packit Service 7c31a4
  if (rc == 0)
Packit Service 7c31a4
    if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
Packit Service 7c31a4
      abort();
Packit Service 7c31a4
Packit Service 7c31a4
  return rc;
Packit Service 7c31a4
}
Packit Service 7c31a4
Packit Service 7c31a4
Packit Service 7c31a4
void uv__io_poll(uv_loop_t* loop, int timeout) {
Packit Service 7c31a4
  struct kevent events[1024];
Packit Service 7c31a4
  struct kevent* ev;
Packit Service 7c31a4
  struct timespec spec;
Packit Service 7c31a4
  unsigned int nevents;
Packit Service 7c31a4
  unsigned int revents;
Packit Service 7c31a4
  QUEUE* q;
Packit Service 7c31a4
  uv__io_t* w;
Packit Service 7c31a4
  sigset_t* pset;
Packit Service 7c31a4
  sigset_t set;
Packit Service 7c31a4
  uint64_t base;
Packit Service 7c31a4
  uint64_t diff;
Packit Service 7c31a4
  int have_signals;
Packit Service 7c31a4
  int filter;
Packit Service 7c31a4
  int fflags;
Packit Service 7c31a4
  int count;
Packit Service 7c31a4
  int nfds;
Packit Service 7c31a4
  int fd;
Packit Service 7c31a4
  int op;
Packit Service 7c31a4
  int i;
Packit Service e2ebee
  int user_timeout;
Packit Service e2ebee
  int reset_timeout;
Packit Service 7c31a4
Packit Service 7c31a4
  if (loop->nfds == 0) {
Packit Service 7c31a4
    assert(QUEUE_EMPTY(&loop->watcher_queue));
Packit Service 7c31a4
    return;
Packit Service 7c31a4
  }
Packit Service 7c31a4
Packit Service 7c31a4
  nevents = 0;
Packit Service 7c31a4
Packit Service 7c31a4
  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
Packit Service 7c31a4
    q = QUEUE_HEAD(&loop->watcher_queue);
Packit Service 7c31a4
    QUEUE_REMOVE(q);
Packit Service 7c31a4
    QUEUE_INIT(q);
Packit Service 7c31a4
Packit Service 7c31a4
    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
Packit Service 7c31a4
    assert(w->pevents != 0);
Packit Service 7c31a4
    assert(w->fd >= 0);
Packit Service 7c31a4
    assert(w->fd < (int) loop->nwatchers);
Packit Service 7c31a4
Packit Service 7c31a4
    if ((w->events & POLLIN) == 0 && (w->pevents & POLLIN) != 0) {
Packit Service 7c31a4
      filter = EVFILT_READ;
Packit Service 7c31a4
      fflags = 0;
Packit Service 7c31a4
      op = EV_ADD;
Packit Service 7c31a4
Packit Service 7c31a4
      if (w->cb == uv__fs_event) {
Packit Service 7c31a4
        filter = EVFILT_VNODE;
Packit Service 7c31a4
        fflags = NOTE_ATTRIB | NOTE_WRITE  | NOTE_RENAME
Packit Service 7c31a4
               | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
Packit Service 7c31a4
        op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */
Packit Service 7c31a4
      }
Packit Service 7c31a4
Packit Service 7c31a4
      EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0);
Packit Service 7c31a4
Packit Service 7c31a4
      if (++nevents == ARRAY_SIZE(events)) {
Packit Service 7c31a4
        if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
Packit Service 7c31a4
          abort();
Packit Service 7c31a4
        nevents = 0;
Packit Service 7c31a4
      }
Packit Service 7c31a4
    }
Packit Service 7c31a4
Packit Service 7c31a4
    if ((w->events & POLLOUT) == 0 && (w->pevents & POLLOUT) != 0) {
Packit Service 7c31a4
      EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
Packit Service 7c31a4
Packit Service 7c31a4
      if (++nevents == ARRAY_SIZE(events)) {
Packit Service 7c31a4
        if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
Packit Service 7c31a4
          abort();
Packit Service 7c31a4
        nevents = 0;
Packit Service 7c31a4
      }
Packit Service 7c31a4
    }
Packit Service 7c31a4
Packit Service 7c31a4
   if ((w->events & UV__POLLPRI) == 0 && (w->pevents & UV__POLLPRI) != 0) {
Packit Service 7c31a4
      EV_SET(events + nevents, w->fd, EV_OOBAND, EV_ADD, 0, 0, 0);
Packit Service 7c31a4
Packit Service 7c31a4
      if (++nevents == ARRAY_SIZE(events)) {
Packit Service 7c31a4
        if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
Packit Service 7c31a4
          abort();
Packit Service 7c31a4
        nevents = 0;
Packit Service 7c31a4
      }
Packit Service 7c31a4
    }
Packit Service 7c31a4
Packit Service 7c31a4
    w->events = w->pevents;
Packit Service 7c31a4
  }
Packit Service 7c31a4
Packit Service 7c31a4
  pset = NULL;
Packit Service 7c31a4
  if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
Packit Service 7c31a4
    pset = &set;
Packit Service 7c31a4
    sigemptyset(pset);
Packit Service 7c31a4
    sigaddset(pset, SIGPROF);
Packit Service 7c31a4
  }
Packit Service 7c31a4
Packit Service 7c31a4
  assert(timeout >= -1);
Packit Service 7c31a4
  base = loop->time;
Packit Service 7c31a4
  count = 48; /* Benchmarks suggest this gives the best throughput. */
Packit Service 7c31a4
Packit Service e2ebee
  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
Packit Service e2ebee
    reset_timeout = 1;
Packit Service e2ebee
    user_timeout = timeout;
Packit Service e2ebee
    timeout = 0;
Packit Service e2ebee
  } else {
Packit Service e2ebee
    reset_timeout = 0;
Packit Service e2ebee
  }
Packit Service e2ebee
Packit Service 7c31a4
  for (;; nevents = 0) {
Packit Service e2ebee
    /* Only need to set the provider_entry_time if timeout != 0. The function
Packit Service e2ebee
     * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
Packit Service e2ebee
     */
Packit Service e2ebee
    if (timeout != 0)
Packit Service e2ebee
      uv__metrics_set_provider_entry_time(loop);
Packit Service e2ebee
Packit Service 7c31a4
    if (timeout != -1) {
Packit Service 7c31a4
      spec.tv_sec = timeout / 1000;
Packit Service 7c31a4
      spec.tv_nsec = (timeout % 1000) * 1000000;
Packit Service 7c31a4
    }
Packit Service 7c31a4
Packit Service 7c31a4
    if (pset != NULL)
Packit Service 7c31a4
      pthread_sigmask(SIG_BLOCK, pset, NULL);
Packit Service 7c31a4
Packit Service 7c31a4
    nfds = kevent(loop->backend_fd,
Packit Service 7c31a4
                  events,
Packit Service 7c31a4
                  nevents,
Packit Service 7c31a4
                  events,
Packit Service 7c31a4
                  ARRAY_SIZE(events),
Packit Service 7c31a4
                  timeout == -1 ? NULL : &spec);
Packit Service 7c31a4
Packit Service 7c31a4
    if (pset != NULL)
Packit Service 7c31a4
      pthread_sigmask(SIG_UNBLOCK, pset, NULL);
Packit Service 7c31a4
Packit Service 7c31a4
    /* Update loop->time unconditionally. It's tempting to skip the update when
Packit Service 7c31a4
     * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
Packit Service 7c31a4
     * operating system didn't reschedule our process while in the syscall.
Packit Service 7c31a4
     */
Packit Service 7c31a4
    SAVE_ERRNO(uv__update_time(loop));
Packit Service 7c31a4
Packit Service 7c31a4
    if (nfds == 0) {
Packit Service e2ebee
      if (reset_timeout != 0) {
Packit Service e2ebee
        timeout = user_timeout;
Packit Service e2ebee
        reset_timeout = 0;
Packit Service e2ebee
        if (timeout == -1)
Packit Service e2ebee
          continue;
Packit Service e2ebee
        if (timeout > 0)
Packit Service e2ebee
          goto update_timeout;
Packit Service e2ebee
      }
Packit Service e2ebee
Packit Service 7c31a4
      assert(timeout != -1);
Packit Service 7c31a4
      return;
Packit Service 7c31a4
    }
Packit Service 7c31a4
Packit Service 7c31a4
    if (nfds == -1) {
Packit Service 7c31a4
      if (errno != EINTR)
Packit Service 7c31a4
        abort();
Packit Service 7c31a4
Packit Service e2ebee
      if (reset_timeout != 0) {
Packit Service e2ebee
        timeout = user_timeout;
Packit Service e2ebee
        reset_timeout = 0;
Packit Service e2ebee
      }
Packit Service e2ebee
Packit Service 7c31a4
      if (timeout == 0)
Packit Service 7c31a4
        return;
Packit Service 7c31a4
Packit Service 7c31a4
      if (timeout == -1)
Packit Service 7c31a4
        continue;
Packit Service 7c31a4
Packit Service 7c31a4
      /* Interrupted by a signal. Update timeout and poll again. */
Packit Service 7c31a4
      goto update_timeout;
Packit Service 7c31a4
    }
Packit Service 7c31a4
Packit Service 7c31a4
    have_signals = 0;
Packit Service 7c31a4
    nevents = 0;
Packit Service 7c31a4
Packit Service 7c31a4
    assert(loop->watchers != NULL);
Packit Service 7c31a4
    loop->watchers[loop->nwatchers] = (void*) events;
Packit Service 7c31a4
    loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
Packit Service 7c31a4
    for (i = 0; i < nfds; i++) {
Packit Service 7c31a4
      ev = events + i;
Packit Service 7c31a4
      fd = ev->ident;
Packit Service 7c31a4
      /* Skip invalidated events, see uv__platform_invalidate_fd */
Packit Service 7c31a4
      if (fd == -1)
Packit Service 7c31a4
        continue;
Packit Service 7c31a4
      w = loop->watchers[fd];
Packit Service 7c31a4
Packit Service 7c31a4
      if (w == NULL) {
Packit Service 7c31a4
        /* File descriptor that we've stopped watching, disarm it.
Packit Service 7c31a4
         * TODO: batch up. */
Packit Service 7c31a4
        struct kevent events[1];
Packit Service 7c31a4
Packit Service 7c31a4
        EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
Packit Service 7c31a4
        if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
Packit Service 7c31a4
          if (errno != EBADF && errno != ENOENT)
Packit Service 7c31a4
            abort();
Packit Service 7c31a4
Packit Service 7c31a4
        continue;
Packit Service 7c31a4
      }
Packit Service 7c31a4
Packit Service 7c31a4
      if (ev->filter == EVFILT_VNODE) {
Packit Service 7c31a4
        assert(w->events == POLLIN);
Packit Service 7c31a4
        assert(w->pevents == POLLIN);
Packit Service e2ebee
        uv__metrics_update_idle_time(loop);
Packit Service 7c31a4
        w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */
Packit Service 7c31a4
        nevents++;
Packit Service 7c31a4
        continue;
Packit Service 7c31a4
      }
Packit Service 7c31a4
Packit Service 7c31a4
      revents = 0;
Packit Service 7c31a4
Packit Service 7c31a4
      if (ev->filter == EVFILT_READ) {
Packit Service 7c31a4
        if (w->pevents & POLLIN) {
Packit Service 7c31a4
          revents |= POLLIN;
Packit Service 7c31a4
          w->rcount = ev->data;
Packit Service 7c31a4
        } else {
Packit Service 7c31a4
          /* TODO batch up */
Packit Service 7c31a4
          struct kevent events[1];
Packit Service 7c31a4
          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
Packit Service 7c31a4
          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
Packit Service 7c31a4
            if (errno != ENOENT)
Packit Service 7c31a4
              abort();
Packit Service 7c31a4
        }
Packit Service 7c31a4
      }
Packit Service 7c31a4
Packit Service 7c31a4
      if (ev->filter == EV_OOBAND) {
Packit Service 7c31a4
        if (w->pevents & UV__POLLPRI) {
Packit Service 7c31a4
          revents |= UV__POLLPRI;
Packit Service 7c31a4
          w->rcount = ev->data;
Packit Service 7c31a4
        } else {
Packit Service 7c31a4
          /* TODO batch up */
Packit Service 7c31a4
          struct kevent events[1];
Packit Service 7c31a4
          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
Packit Service 7c31a4
          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
Packit Service 7c31a4
            if (errno != ENOENT)
Packit Service 7c31a4
              abort();
Packit Service 7c31a4
        }
Packit Service 7c31a4
      }
Packit Service 7c31a4
Packit Service 7c31a4
      if (ev->filter == EVFILT_WRITE) {
Packit Service 7c31a4
        if (w->pevents & POLLOUT) {
Packit Service 7c31a4
          revents |= POLLOUT;
Packit Service 7c31a4
          w->wcount = ev->data;
Packit Service 7c31a4
        } else {
Packit Service 7c31a4
          /* TODO batch up */
Packit Service 7c31a4
          struct kevent events[1];
Packit Service 7c31a4
          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
Packit Service 7c31a4
          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
Packit Service 7c31a4
            if (errno != ENOENT)
Packit Service 7c31a4
              abort();
Packit Service 7c31a4
        }
Packit Service 7c31a4
      }
Packit Service 7c31a4
Packit Service 7c31a4
      if (ev->flags & EV_ERROR)
Packit Service 7c31a4
        revents |= POLLERR;
Packit Service 7c31a4
Packit Service 7c31a4
      if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP))
Packit Service 7c31a4
        revents |= UV__POLLRDHUP;
Packit Service 7c31a4
Packit Service 7c31a4
      if (revents == 0)
Packit Service 7c31a4
        continue;
Packit Service 7c31a4
Packit Service 7c31a4
      /* Run signal watchers last.  This also affects child process watchers
Packit Service 7c31a4
       * because those are implemented in terms of signal watchers.
Packit Service 7c31a4
       */
Packit Service e2ebee
      if (w == &loop->signal_io_watcher) {
Packit Service 7c31a4
        have_signals = 1;
Packit Service e2ebee
      } else {
Packit Service e2ebee
        uv__metrics_update_idle_time(loop);
Packit Service 7c31a4
        w->cb(loop, w, revents);
Packit Service e2ebee
      }
Packit Service 7c31a4
Packit Service 7c31a4
      nevents++;
Packit Service 7c31a4
    }
Packit Service 7c31a4
Packit Service e2ebee
    if (reset_timeout != 0) {
Packit Service e2ebee
      timeout = user_timeout;
Packit Service e2ebee
      reset_timeout = 0;
Packit Service e2ebee
    }
Packit Service e2ebee
Packit Service e2ebee
    if (have_signals != 0) {
Packit Service e2ebee
      uv__metrics_update_idle_time(loop);
Packit Service 7c31a4
      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
Packit Service e2ebee
    }
Packit Service 7c31a4
Packit Service 7c31a4
    loop->watchers[loop->nwatchers] = NULL;
Packit Service 7c31a4
    loop->watchers[loop->nwatchers + 1] = NULL;
Packit Service 7c31a4
Packit Service 7c31a4
    if (have_signals != 0)
Packit Service 7c31a4
      return;  /* Event loop should cycle now so don't poll again. */
Packit Service 7c31a4
Packit Service 7c31a4
    if (nevents != 0) {
Packit Service 7c31a4
      if (nfds == ARRAY_SIZE(events) && --count != 0) {
Packit Service 7c31a4
        /* Poll for more events but don't block this time. */
Packit Service 7c31a4
        timeout = 0;
Packit Service 7c31a4
        continue;
Packit Service 7c31a4
      }
Packit Service 7c31a4
      return;
Packit Service 7c31a4
    }
Packit Service 7c31a4
Packit Service 7c31a4
    if (timeout == 0)
Packit Service 7c31a4
      return;
Packit Service 7c31a4
Packit Service 7c31a4
    if (timeout == -1)
Packit Service 7c31a4
      continue;
Packit Service 7c31a4
Packit Service 7c31a4
update_timeout:
Packit Service 7c31a4
    assert(timeout > 0);
Packit Service 7c31a4
Packit Service 7c31a4
    diff = loop->time - base;
Packit Service 7c31a4
    if (diff >= (uint64_t) timeout)
Packit Service 7c31a4
      return;
Packit Service 7c31a4
Packit Service 7c31a4
    timeout -= diff;
Packit Service 7c31a4
  }
Packit Service 7c31a4
}
Packit Service 7c31a4
Packit Service 7c31a4
Packit Service 7c31a4
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
Packit Service 7c31a4
  struct kevent* events;
Packit Service 7c31a4
  uintptr_t i;
Packit Service 7c31a4
  uintptr_t nfds;
Packit Service 7c31a4
Packit Service 7c31a4
  assert(loop->watchers != NULL);
Packit Service 7c31a4
  assert(fd >= 0);
Packit Service 7c31a4
Packit Service 7c31a4
  events = (struct kevent*) loop->watchers[loop->nwatchers];
Packit Service 7c31a4
  nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
Packit Service 7c31a4
  if (events == NULL)
Packit Service 7c31a4
    return;
Packit Service 7c31a4
Packit Service 7c31a4
  /* Invalidate events with same file descriptor */
Packit Service 7c31a4
  for (i = 0; i < nfds; i++)
Packit Service 7c31a4
    if ((int) events[i].ident == fd)
Packit Service 7c31a4
      events[i].ident = -1;
Packit Service 7c31a4
}
Packit Service 7c31a4
Packit Service 7c31a4
Packit Service 7c31a4
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
Packit Service 7c31a4
  uv_fs_event_t* handle;
Packit Service 7c31a4
  struct kevent ev;
Packit Service 7c31a4
  int events;
Packit Service 7c31a4
  const char* path;
Packit Service 7c31a4
#if defined(F_GETPATH)
Packit Service 7c31a4
  /* MAXPATHLEN == PATH_MAX but the former is what XNU calls it internally. */
Packit Service 7c31a4
  char pathbuf[MAXPATHLEN];
Packit Service 7c31a4
#endif
Packit Service 7c31a4
Packit Service 7c31a4
  handle = container_of(w, uv_fs_event_t, event_watcher);
Packit Service 7c31a4
Packit Service 7c31a4
  if (fflags & (NOTE_ATTRIB | NOTE_EXTEND))
Packit Service 7c31a4
    events = UV_CHANGE;
Packit Service 7c31a4
  else
Packit Service 7c31a4
    events = UV_RENAME;
Packit Service 7c31a4
Packit Service 7c31a4
  path = NULL;
Packit Service 7c31a4
#if defined(F_GETPATH)
Packit Service 7c31a4
  /* Also works when the file has been unlinked from the file system. Passing
Packit Service 7c31a4
   * in the path when the file has been deleted is arguably a little strange
Packit Service 7c31a4
   * but it's consistent with what the inotify backend does.
Packit Service 7c31a4
   */
Packit Service 7c31a4
  if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0)
Packit Service 7c31a4
    path = uv__basename_r(pathbuf);
Packit Service 7c31a4
#endif
Packit Service 7c31a4
  handle->cb(handle, path, events, 0);
Packit Service 7c31a4
Packit Service 7c31a4
  if (handle->event_watcher.fd == -1)
Packit Service 7c31a4
    return;
Packit Service 7c31a4
Packit Service 7c31a4
  /* Watcher operates in one-shot mode, re-arm it. */
Packit Service 7c31a4
  fflags = NOTE_ATTRIB | NOTE_WRITE  | NOTE_RENAME
Packit Service 7c31a4
         | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
Packit Service 7c31a4
Packit Service 7c31a4
  EV_SET(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0);
Packit Service 7c31a4
Packit Service 7c31a4
  if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
Packit Service 7c31a4
    abort();
Packit Service 7c31a4
}
Packit Service 7c31a4
Packit Service 7c31a4
Packit Service 7c31a4
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
Packit Service 7c31a4
  uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
Packit Service 7c31a4
  return 0;
Packit Service 7c31a4
}
Packit Service 7c31a4
Packit Service 7c31a4
Packit Service 7c31a4
int uv_fs_event_start(uv_fs_event_t* handle,
Packit Service 7c31a4
                      uv_fs_event_cb cb,
Packit Service 7c31a4
                      const char* path,
Packit Service 7c31a4
                      unsigned int flags) {
Packit Service 7c31a4
  int fd;
Packit Service 7c31a4
#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
Packit Service 7c31a4
  struct stat statbuf;
Packit Service 7c31a4
#endif
Packit Service 7c31a4
Packit Service 7c31a4
  if (uv__is_active(handle))
Packit Service 7c31a4
    return UV_EINVAL;
Packit Service 7c31a4
Packit Service 7c31a4
  handle->cb = cb;
Packit Service 7c31a4
  handle->path = uv__strdup(path);
Packit Service 7c31a4
  if (handle->path == NULL)
Packit Service 7c31a4
    return UV_ENOMEM;
Packit Service 7c31a4
Packit Service 7c31a4
  /* TODO open asynchronously - but how do we report back errors? */
Packit Service 7c31a4
  fd = open(handle->path, O_RDONLY);
Packit Service 7c31a4
  if (fd == -1) {
Packit Service 7c31a4
    uv__free(handle->path);
Packit Service 7c31a4
    handle->path = NULL;
Packit Service 7c31a4
    return UV__ERR(errno);
Packit Service 7c31a4
  }
Packit Service 7c31a4
Packit Service 7c31a4
#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
Packit Service 7c31a4
  /* Nullify field to perform checks later */
Packit Service 7c31a4
  handle->cf_cb = NULL;
Packit Service 7c31a4
  handle->realpath = NULL;
Packit Service 7c31a4
  handle->realpath_len = 0;
Packit Service 7c31a4
  handle->cf_flags = flags;
Packit Service 7c31a4
Packit Service 7c31a4
  if (fstat(fd, &statbuf))
Packit Service 7c31a4
    goto fallback;
Packit Service 7c31a4
  /* FSEvents works only with directories */
Packit Service 7c31a4
  if (!(statbuf.st_mode & S_IFDIR))
Packit Service 7c31a4
    goto fallback;
Packit Service 7c31a4
Packit Service e2ebee
  if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) {
Packit Service 7c31a4
    int r;
Packit Service 7c31a4
    /* The fallback fd is no longer needed */
Packit Service 7c31a4
    uv__close_nocheckstdio(fd);
Packit Service 7c31a4
    handle->event_watcher.fd = -1;
Packit Service 7c31a4
    r = uv__fsevents_init(handle);
Packit Service 7c31a4
    if (r == 0) {
Packit Service 7c31a4
      uv__handle_start(handle);
Packit Service 7c31a4
    } else {
Packit Service 7c31a4
      uv__free(handle->path);
Packit Service 7c31a4
      handle->path = NULL;
Packit Service 7c31a4
    }
Packit Service 7c31a4
    return r;
Packit Service 7c31a4
  }
Packit Service 7c31a4
fallback:
Packit Service 7c31a4
#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
Packit Service 7c31a4
Packit Service 7c31a4
  uv__handle_start(handle);
Packit Service 7c31a4
  uv__io_init(&handle->event_watcher, uv__fs_event, fd);
Packit Service 7c31a4
  uv__io_start(handle->loop, &handle->event_watcher, POLLIN);
Packit Service 7c31a4
Packit Service 7c31a4
  return 0;
Packit Service 7c31a4
}
Packit Service 7c31a4
Packit Service 7c31a4
Packit Service 7c31a4
int uv_fs_event_stop(uv_fs_event_t* handle) {
Packit Service 7c31a4
  int r;
Packit Service 7c31a4
  r = 0;
Packit Service 7c31a4
Packit Service 7c31a4
  if (!uv__is_active(handle))
Packit Service 7c31a4
    return 0;
Packit Service 7c31a4
Packit Service 7c31a4
  uv__handle_stop(handle);
Packit Service 7c31a4
Packit Service 7c31a4
#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
Packit Service e2ebee
  if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop))
Packit Service e2ebee
    if (handle->cf_cb != NULL)
Packit Service e2ebee
      r = uv__fsevents_close(handle);
Packit Service 7c31a4
#endif
Packit Service 7c31a4
Packit Service 7c31a4
  if (handle->event_watcher.fd != -1) {
Packit Service 7c31a4
    uv__io_close(handle->loop, &handle->event_watcher);
Packit Service 7c31a4
    uv__close(handle->event_watcher.fd);
Packit Service 7c31a4
    handle->event_watcher.fd = -1;
Packit Service 7c31a4
  }
Packit Service 7c31a4
Packit Service 7c31a4
  uv__free(handle->path);
Packit Service 7c31a4
  handle->path = NULL;
Packit Service 7c31a4
Packit Service 7c31a4
  return r;
Packit Service 7c31a4
}
Packit Service 7c31a4
Packit Service 7c31a4
Packit Service 7c31a4
void uv__fs_event_close(uv_fs_event_t* handle) {
Packit Service 7c31a4
  uv_fs_event_stop(handle);
Packit Service 7c31a4
}