2011-09-22 23:20:33 +00:00
|
|
|
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "uv.h"
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/event.h>
|
2012-08-22 23:14:41 +00:00
|
|
|
#include <sys/time.h>
|
2011-09-27 22:49:08 +00:00
|
|
|
#include <unistd.h>
|
2011-09-22 23:20:33 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
|
2016-09-08 22:42:43 +00:00
|
|
|
/*
|
|
|
|
|
* Required on
|
|
|
|
|
* - Until at least FreeBSD 11.0
|
|
|
|
|
* - Older versions of Mac OS X
|
|
|
|
|
*
|
|
|
|
|
* http://www.boost.org/doc/libs/1_61_0/boost/asio/detail/kqueue_reactor.hpp
|
|
|
|
|
*/
|
|
|
|
|
#ifndef EV_OOBAND
|
|
|
|
|
#define EV_OOBAND EV_FLAG1
|
|
|
|
|
#endif
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags);
|
2011-09-22 23:20:33 +00:00
|
|
|
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
int uv__kqueue_init(uv_loop_t* loop) {
|
|
|
|
|
loop->backend_fd = kqueue();
|
|
|
|
|
if (loop->backend_fd == -1)
|
2018-01-19 02:04:51 +00:00
|
|
|
return UV__ERR(errno);
|
2012-08-22 23:14:41 +00:00
|
|
|
|
|
|
|
|
uv__cloexec(loop->backend_fd, 1);
|
|
|
|
|
|
|
|
|
|
return 0;
|
2011-09-22 23:20:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-09-06 21:10:15 +00:00
|
|
|
#if defined(__APPLE__)
|
unix: make loops and watchers usable after fork()
Added the uv_loop_fork() API that must be called in a child process to
continue using an existing loop. Internally this calls a uv__io_fork
function for each supported platform, similar to the way
uv__platform_loop_init works.
After this call, existing and new IO, async and signal watchers will
contiue working as before on all platforms, as will the
threadpool (although any threads it was using are of course gone).
On Linux and BSDs that use kqueue, existing and new fsevent watchers
will also continue to work as expected. On OS X, though, directory
fsevents will not be able to use the optimized CoreFoundation path if
they had already been used in the parent process, instead falling back
to the kqueue path used on other BSDs.
Existing fsevent watchers will not function on AIX or SunOS. This
could be relatively easily fixed by someone with AIX knowledge in the
future, but SunOS will require some additional work to keep track if
the watchers.
A new test file, test/test-fork.c, was added to contain fork-related
tests to verify functionality in the child process.
PR-URL: https://github.com/libuv/libuv/pull/846
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
2016-04-22 19:16:31 +00:00
|
|
|
static int uv__has_forked_with_cfrunloop;
|
2017-09-06 21:10:15 +00:00
|
|
|
#endif
|
unix: make loops and watchers usable after fork()
Added the uv_loop_fork() API that must be called in a child process to
continue using an existing loop. Internally this calls a uv__io_fork
function for each supported platform, similar to the way
uv__platform_loop_init works.
After this call, existing and new IO, async and signal watchers will
contiue working as before on all platforms, as will the
threadpool (although any threads it was using are of course gone).
On Linux and BSDs that use kqueue, existing and new fsevent watchers
will also continue to work as expected. On OS X, though, directory
fsevents will not be able to use the optimized CoreFoundation path if
they had already been used in the parent process, instead falling back
to the kqueue path used on other BSDs.
Existing fsevent watchers will not function on AIX or SunOS. This
could be relatively easily fixed by someone with AIX knowledge in the
future, but SunOS will require some additional work to keep track if
the watchers.
A new test file, test/test-fork.c, was added to contain fork-related
tests to verify functionality in the child process.
PR-URL: https://github.com/libuv/libuv/pull/846
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
2016-04-22 19:16:31 +00:00
|
|
|
|
|
|
|
|
int uv__io_fork(uv_loop_t* loop) {
|
|
|
|
|
int err;
|
|
|
|
|
loop->backend_fd = -1;
|
|
|
|
|
err = uv__kqueue_init(loop);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
#if defined(__APPLE__)
|
|
|
|
|
if (loop->cf_state != NULL) {
|
|
|
|
|
/* We cannot start another CFRunloop and/or thread in the child
|
|
|
|
|
process; CF aborts if you try or if you try to touch the thread
|
|
|
|
|
at all to kill it. So the best we can do is ignore it from now
|
|
|
|
|
on. This means we can't watch directories in the same way
|
|
|
|
|
anymore (like other BSDs). It also means we cannot properly
|
|
|
|
|
clean up the allocated resources; calling
|
|
|
|
|
uv__fsevents_loop_delete from uv_loop_close will crash the
|
|
|
|
|
process. So we sidestep the issue by pretending like we never
|
|
|
|
|
started it in the first place.
|
|
|
|
|
*/
|
|
|
|
|
uv__has_forked_with_cfrunloop = 1;
|
|
|
|
|
uv__free(loop->cf_state);
|
|
|
|
|
loop->cf_state = NULL;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-12-20 01:29:11 +00:00
|
|
|
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
|
|
|
|
struct kevent ev;
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
|
|
|
|
|
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
2018-01-19 02:04:51 +00:00
|
|
|
rc = UV__ERR(errno);
|
2015-12-20 01:29:11 +00:00
|
|
|
|
|
|
|
|
EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
|
|
|
|
if (rc == 0)
|
|
|
|
|
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
|
|
|
|
abort();
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
|
|
|
|
struct kevent events[1024];
|
|
|
|
|
struct kevent* ev;
|
|
|
|
|
struct timespec spec;
|
|
|
|
|
unsigned int nevents;
|
|
|
|
|
unsigned int revents;
|
2013-03-26 20:11:29 +00:00
|
|
|
QUEUE* q;
|
2014-11-27 07:09:10 +00:00
|
|
|
uv__io_t* w;
|
|
|
|
|
sigset_t* pset;
|
|
|
|
|
sigset_t set;
|
2012-08-22 23:14:41 +00:00
|
|
|
uint64_t base;
|
|
|
|
|
uint64_t diff;
|
2015-11-06 23:26:44 +00:00
|
|
|
int have_signals;
|
2012-08-22 23:14:41 +00:00
|
|
|
int filter;
|
|
|
|
|
int fflags;
|
|
|
|
|
int count;
|
|
|
|
|
int nfds;
|
|
|
|
|
int fd;
|
|
|
|
|
int op;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (loop->nfds == 0) {
|
2013-03-26 20:11:29 +00:00
|
|
|
assert(QUEUE_EMPTY(&loop->watcher_queue));
|
2012-08-22 23:14:41 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nevents = 0;
|
|
|
|
|
|
2013-03-26 20:11:29 +00:00
|
|
|
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
|
|
|
|
|
q = QUEUE_HEAD(&loop->watcher_queue);
|
|
|
|
|
QUEUE_REMOVE(q);
|
|
|
|
|
QUEUE_INIT(q);
|
2012-08-22 23:14:41 +00:00
|
|
|
|
2013-03-26 20:11:29 +00:00
|
|
|
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
|
2012-08-22 23:14:41 +00:00
|
|
|
assert(w->pevents != 0);
|
|
|
|
|
assert(w->fd >= 0);
|
|
|
|
|
assert(w->fd < (int) loop->nwatchers);
|
|
|
|
|
|
2016-04-09 08:36:41 +00:00
|
|
|
if ((w->events & POLLIN) == 0 && (w->pevents & POLLIN) != 0) {
|
2012-08-22 23:14:41 +00:00
|
|
|
filter = EVFILT_READ;
|
|
|
|
|
fflags = 0;
|
|
|
|
|
op = EV_ADD;
|
|
|
|
|
|
|
|
|
|
if (w->cb == uv__fs_event) {
|
|
|
|
|
filter = EVFILT_VNODE;
|
|
|
|
|
fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME
|
|
|
|
|
| NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
|
|
|
|
|
op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0);
|
|
|
|
|
|
|
|
|
|
if (++nevents == ARRAY_SIZE(events)) {
|
|
|
|
|
if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
|
|
|
|
|
abort();
|
|
|
|
|
nevents = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-09 08:36:41 +00:00
|
|
|
if ((w->events & POLLOUT) == 0 && (w->pevents & POLLOUT) != 0) {
|
2012-08-22 23:14:41 +00:00
|
|
|
EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
if (++nevents == ARRAY_SIZE(events)) {
|
|
|
|
|
if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
|
|
|
|
|
abort();
|
|
|
|
|
nevents = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-08 22:42:43 +00:00
|
|
|
if ((w->events & UV__POLLPRI) == 0 && (w->pevents & UV__POLLPRI) != 0) {
|
|
|
|
|
EV_SET(events + nevents, w->fd, EV_OOBAND, EV_ADD, 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
if (++nevents == ARRAY_SIZE(events)) {
|
|
|
|
|
if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
|
|
|
|
|
abort();
|
|
|
|
|
nevents = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
w->events = w->pevents;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 07:09:10 +00:00
|
|
|
pset = NULL;
|
|
|
|
|
if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
|
|
|
|
|
pset = &set;
|
|
|
|
|
sigemptyset(pset);
|
|
|
|
|
sigaddset(pset, SIGPROF);
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
assert(timeout >= -1);
|
|
|
|
|
base = loop->time;
|
|
|
|
|
count = 48; /* Benchmarks suggest this gives the best throughput. */
|
|
|
|
|
|
|
|
|
|
for (;; nevents = 0) {
|
|
|
|
|
if (timeout != -1) {
|
|
|
|
|
spec.tv_sec = timeout / 1000;
|
|
|
|
|
spec.tv_nsec = (timeout % 1000) * 1000000;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 07:09:10 +00:00
|
|
|
if (pset != NULL)
|
|
|
|
|
pthread_sigmask(SIG_BLOCK, pset, NULL);
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
nfds = kevent(loop->backend_fd,
|
|
|
|
|
events,
|
|
|
|
|
nevents,
|
|
|
|
|
events,
|
|
|
|
|
ARRAY_SIZE(events),
|
|
|
|
|
timeout == -1 ? NULL : &spec);
|
|
|
|
|
|
2014-11-27 07:09:10 +00:00
|
|
|
if (pset != NULL)
|
|
|
|
|
pthread_sigmask(SIG_UNBLOCK, pset, NULL);
|
|
|
|
|
|
2013-01-06 16:55:51 +00:00
|
|
|
/* Update loop->time unconditionally. It's tempting to skip the update when
|
|
|
|
|
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
|
|
|
|
|
* operating system didn't reschedule our process while in the syscall.
|
|
|
|
|
*/
|
|
|
|
|
SAVE_ERRNO(uv__update_time(loop));
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
if (nfds == 0) {
|
|
|
|
|
assert(timeout != -1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nfds == -1) {
|
|
|
|
|
if (errno != EINTR)
|
|
|
|
|
abort();
|
|
|
|
|
|
|
|
|
|
if (timeout == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (timeout == -1)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Interrupted by a signal. Update timeout and poll again. */
|
|
|
|
|
goto update_timeout;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-06 23:26:44 +00:00
|
|
|
have_signals = 0;
|
2012-08-22 23:14:41 +00:00
|
|
|
nevents = 0;
|
|
|
|
|
|
2013-10-25 08:56:37 +00:00
|
|
|
assert(loop->watchers != NULL);
|
|
|
|
|
loop->watchers[loop->nwatchers] = (void*) events;
|
|
|
|
|
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
|
2012-08-22 23:14:41 +00:00
|
|
|
for (i = 0; i < nfds; i++) {
|
|
|
|
|
ev = events + i;
|
|
|
|
|
fd = ev->ident;
|
2013-10-25 08:56:37 +00:00
|
|
|
/* Skip invalidated events, see uv__platform_invalidate_fd */
|
|
|
|
|
if (fd == -1)
|
|
|
|
|
continue;
|
osx: Fix a possible segfault in uv__io_poll
In our build infrastructure, I've seen a lot of segfaults recently that
were all only happening on OSX. Upon inspecting the coredumps, it
appearded that all segfaults happened at the same instruction, and upon
translating the assembly back to the source, I found that an array could
be indexed with a -1 index before the index was checked to be not -1.
As concrete evidence, here is the situation that I found caused the
segfault. The instruction in question along with the relevant register
values was:
mov (%r8,%r15,8),%r12
r8 = 0x7fb0ba800000
r15 = 0xffffffffffffffff
r8 + r15 * 8 == 0x7fb0ba7ffff8
It appears that the base of loop->watchers was page aligned, and by
going back one word I guess that the page wasn't mapped, causing our
segfaults.
2013-12-22 04:12:35 +00:00
|
|
|
w = loop->watchers[fd];
|
2013-10-25 08:56:37 +00:00
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
if (w == NULL) {
|
|
|
|
|
/* File descriptor that we've stopped watching, disarm it. */
|
|
|
|
|
/* TODO batch up */
|
|
|
|
|
struct kevent events[1];
|
|
|
|
|
|
|
|
|
|
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
|
|
|
|
|
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
|
|
|
|
|
if (errno != EBADF && errno != ENOENT)
|
|
|
|
|
abort();
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ev->filter == EVFILT_VNODE) {
|
2016-04-09 08:36:41 +00:00
|
|
|
assert(w->events == POLLIN);
|
|
|
|
|
assert(w->pevents == POLLIN);
|
2012-08-22 23:14:41 +00:00
|
|
|
w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */
|
|
|
|
|
nevents++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
revents = 0;
|
|
|
|
|
|
|
|
|
|
if (ev->filter == EVFILT_READ) {
|
2016-04-09 08:36:41 +00:00
|
|
|
if (w->pevents & POLLIN) {
|
|
|
|
|
revents |= POLLIN;
|
2013-02-08 10:48:52 +00:00
|
|
|
w->rcount = ev->data;
|
|
|
|
|
} else {
|
2012-08-22 23:14:41 +00:00
|
|
|
/* TODO batch up */
|
|
|
|
|
struct kevent events[1];
|
|
|
|
|
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
|
2012-12-17 13:03:44 +00:00
|
|
|
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
|
|
|
|
|
if (errno != ENOENT)
|
|
|
|
|
abort();
|
2012-08-22 23:14:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-08 22:42:43 +00:00
|
|
|
if (ev->filter == EV_OOBAND) {
|
|
|
|
|
if (w->pevents & UV__POLLPRI) {
|
|
|
|
|
revents |= UV__POLLPRI;
|
|
|
|
|
w->rcount = ev->data;
|
|
|
|
|
} else {
|
|
|
|
|
/* TODO batch up */
|
|
|
|
|
struct kevent events[1];
|
|
|
|
|
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
|
|
|
|
|
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
|
|
|
|
|
if (errno != ENOENT)
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
if (ev->filter == EVFILT_WRITE) {
|
2016-04-09 08:36:41 +00:00
|
|
|
if (w->pevents & POLLOUT) {
|
|
|
|
|
revents |= POLLOUT;
|
2013-02-08 10:48:52 +00:00
|
|
|
w->wcount = ev->data;
|
|
|
|
|
} else {
|
2012-08-22 23:14:41 +00:00
|
|
|
/* TODO batch up */
|
|
|
|
|
struct kevent events[1];
|
|
|
|
|
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
|
2012-12-17 13:03:44 +00:00
|
|
|
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
|
|
|
|
|
if (errno != ENOENT)
|
|
|
|
|
abort();
|
2012-08-22 23:14:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ev->flags & EV_ERROR)
|
2016-04-09 08:36:41 +00:00
|
|
|
revents |= POLLERR;
|
2012-08-22 23:14:41 +00:00
|
|
|
|
2016-01-11 22:00:57 +00:00
|
|
|
if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP))
|
|
|
|
|
revents |= UV__POLLRDHUP;
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
if (revents == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2015-11-06 23:26:44 +00:00
|
|
|
/* Run signal watchers last. This also affects child process watchers
|
|
|
|
|
* because those are implemented in terms of signal watchers.
|
|
|
|
|
*/
|
|
|
|
|
if (w == &loop->signal_io_watcher)
|
|
|
|
|
have_signals = 1;
|
|
|
|
|
else
|
|
|
|
|
w->cb(loop, w, revents);
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
nevents++;
|
|
|
|
|
}
|
2015-11-06 23:26:44 +00:00
|
|
|
|
|
|
|
|
if (have_signals != 0)
|
|
|
|
|
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
|
|
|
|
|
|
2013-10-25 08:56:37 +00:00
|
|
|
loop->watchers[loop->nwatchers] = NULL;
|
|
|
|
|
loop->watchers[loop->nwatchers + 1] = NULL;
|
2012-08-22 23:14:41 +00:00
|
|
|
|
2015-11-06 23:26:44 +00:00
|
|
|
if (have_signals != 0)
|
|
|
|
|
return; /* Event loop should cycle now so don't poll again. */
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
if (nevents != 0) {
|
|
|
|
|
if (nfds == ARRAY_SIZE(events) && --count != 0) {
|
|
|
|
|
/* Poll for more events but don't block this time. */
|
|
|
|
|
timeout = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timeout == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (timeout == -1)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
update_timeout:
|
|
|
|
|
assert(timeout > 0);
|
|
|
|
|
|
2013-01-06 16:55:51 +00:00
|
|
|
diff = loop->time - base;
|
2012-08-22 23:14:41 +00:00
|
|
|
if (diff >= (uint64_t) timeout)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
timeout -= diff;
|
|
|
|
|
}
|
2011-09-22 23:20:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-11-13 11:20:07 +00:00
|
|
|
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
|
|
|
|
struct kevent* events;
|
|
|
|
|
uintptr_t i;
|
|
|
|
|
uintptr_t nfds;
|
|
|
|
|
|
|
|
|
|
assert(loop->watchers != NULL);
|
|
|
|
|
|
|
|
|
|
events = (struct kevent*) loop->watchers[loop->nwatchers];
|
|
|
|
|
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
|
|
|
|
|
if (events == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Invalidate events with same file descriptor */
|
|
|
|
|
for (i = 0; i < nfds; i++)
|
|
|
|
|
if ((int) events[i].ident == fd)
|
|
|
|
|
events[i].ident = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
|
2011-09-22 23:20:33 +00:00
|
|
|
uv_fs_event_t* handle;
|
2012-08-22 23:14:41 +00:00
|
|
|
struct kevent ev;
|
2011-09-22 23:20:33 +00:00
|
|
|
int events;
|
2013-04-10 17:29:16 +00:00
|
|
|
const char* path;
|
|
|
|
|
#if defined(F_GETPATH)
|
|
|
|
|
/* MAXPATHLEN == PATH_MAX but the former is what XNU calls it internally. */
|
|
|
|
|
char pathbuf[MAXPATHLEN];
|
|
|
|
|
#endif
|
2011-09-22 23:20:33 +00:00
|
|
|
|
|
|
|
|
handle = container_of(w, uv_fs_event_t, event_watcher);
|
|
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
if (fflags & (NOTE_ATTRIB | NOTE_EXTEND))
|
2011-09-22 23:20:33 +00:00
|
|
|
events = UV_CHANGE;
|
|
|
|
|
else
|
|
|
|
|
events = UV_RENAME;
|
|
|
|
|
|
2013-04-10 17:29:16 +00:00
|
|
|
path = NULL;
|
|
|
|
|
#if defined(F_GETPATH)
|
|
|
|
|
/* Also works when the file has been unlinked from the file system. Passing
|
|
|
|
|
* in the path when the file has been deleted is arguably a little strange
|
|
|
|
|
* but it's consistent with what the inotify backend does.
|
|
|
|
|
*/
|
|
|
|
|
if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0)
|
|
|
|
|
path = uv__basename_r(pathbuf);
|
|
|
|
|
#endif
|
|
|
|
|
handle->cb(handle, path, events, 0);
|
2011-09-22 23:20:33 +00:00
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
if (handle->event_watcher.fd == -1)
|
2012-02-29 14:55:05 +00:00
|
|
|
return;
|
2011-09-22 23:20:33 +00:00
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
/* Watcher operates in one-shot mode, re-arm it. */
|
|
|
|
|
fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME
|
|
|
|
|
| NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
|
2011-09-22 23:20:33 +00:00
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
EV_SET(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0);
|
2011-09-22 23:20:33 +00:00
|
|
|
|
2012-08-22 23:14:41 +00:00
|
|
|
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
|
|
|
|
abort();
|
2011-09-22 23:20:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-09-23 22:08:31 +00:00
|
|
|
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
|
|
|
|
|
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int uv_fs_event_start(uv_fs_event_t* handle,
|
|
|
|
|
uv_fs_event_cb cb,
|
2014-02-23 23:09:41 +00:00
|
|
|
const char* path,
|
2013-09-23 22:08:31 +00:00
|
|
|
unsigned int flags) {
|
2012-08-13 12:37:31 +00:00
|
|
|
#if defined(__APPLE__)
|
|
|
|
|
struct stat statbuf;
|
|
|
|
|
#endif /* defined(__APPLE__) */
|
2012-08-22 23:14:41 +00:00
|
|
|
int fd;
|
2011-09-22 23:20:33 +00:00
|
|
|
|
2013-09-23 22:08:31 +00:00
|
|
|
if (uv__is_active(handle))
|
2018-01-19 02:04:51 +00:00
|
|
|
return UV_EINVAL;
|
2013-09-23 22:08:31 +00:00
|
|
|
|
2011-09-22 23:20:33 +00:00
|
|
|
/* TODO open asynchronously - but how do we report back errors? */
|
2014-02-23 23:09:41 +00:00
|
|
|
fd = open(path, O_RDONLY);
|
2013-06-06 21:10:50 +00:00
|
|
|
if (fd == -1)
|
2018-01-19 02:04:51 +00:00
|
|
|
return UV__ERR(errno);
|
2011-09-22 23:20:33 +00:00
|
|
|
|
2013-09-23 22:08:31 +00:00
|
|
|
uv__handle_start(handle);
|
2012-08-22 23:14:41 +00:00
|
|
|
uv__io_init(&handle->event_watcher, uv__fs_event, fd);
|
2015-05-26 08:51:10 +00:00
|
|
|
handle->path = uv__strdup(path);
|
2011-09-22 23:20:33 +00:00
|
|
|
handle->cb = cb;
|
2012-08-13 12:37:31 +00:00
|
|
|
|
|
|
|
|
#if defined(__APPLE__)
|
unix: make loops and watchers usable after fork()
Added the uv_loop_fork() API that must be called in a child process to
continue using an existing loop. Internally this calls a uv__io_fork
function for each supported platform, similar to the way
uv__platform_loop_init works.
After this call, existing and new IO, async and signal watchers will
contiue working as before on all platforms, as will the
threadpool (although any threads it was using are of course gone).
On Linux and BSDs that use kqueue, existing and new fsevent watchers
will also continue to work as expected. On OS X, though, directory
fsevents will not be able to use the optimized CoreFoundation path if
they had already been used in the parent process, instead falling back
to the kqueue path used on other BSDs.
Existing fsevent watchers will not function on AIX or SunOS. This
could be relatively easily fixed by someone with AIX knowledge in the
future, but SunOS will require some additional work to keep track if
the watchers.
A new test file, test/test-fork.c, was added to contain fork-related
tests to verify functionality in the child process.
PR-URL: https://github.com/libuv/libuv/pull/846
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
2016-04-22 19:16:31 +00:00
|
|
|
if (uv__has_forked_with_cfrunloop)
|
|
|
|
|
goto fallback;
|
|
|
|
|
|
2012-08-13 12:37:31 +00:00
|
|
|
/* Nullify field to perform checks later */
|
2013-08-14 13:14:35 +00:00
|
|
|
handle->cf_cb = NULL;
|
2012-09-17 20:38:40 +00:00
|
|
|
handle->realpath = NULL;
|
|
|
|
|
handle->realpath_len = 0;
|
|
|
|
|
handle->cf_flags = flags;
|
2012-08-13 12:37:31 +00:00
|
|
|
|
|
|
|
|
if (fstat(fd, &statbuf))
|
|
|
|
|
goto fallback;
|
|
|
|
|
/* FSEvents works only with directories */
|
|
|
|
|
if (!(statbuf.st_mode & S_IFDIR))
|
|
|
|
|
goto fallback;
|
|
|
|
|
|
2015-06-29 21:39:44 +00:00
|
|
|
/* The fallback fd is no longer needed */
|
|
|
|
|
uv__close(fd);
|
|
|
|
|
handle->event_watcher.fd = -1;
|
|
|
|
|
|
2012-08-13 12:37:31 +00:00
|
|
|
return uv__fsevents_init(handle);
|
|
|
|
|
|
|
|
|
|
fallback:
|
|
|
|
|
#endif /* defined(__APPLE__) */
|
|
|
|
|
|
2016-04-09 08:36:41 +00:00
|
|
|
uv__io_start(handle->loop, &handle->event_watcher, POLLIN);
|
2011-09-22 23:20:33 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-09-23 22:08:31 +00:00
|
|
|
int uv_fs_event_stop(uv_fs_event_t* handle) {
|
|
|
|
|
if (!uv__is_active(handle))
|
2014-05-12 21:46:56 +00:00
|
|
|
return 0;
|
2013-09-23 22:08:31 +00:00
|
|
|
|
|
|
|
|
uv__handle_stop(handle);
|
|
|
|
|
|
2012-08-13 12:37:31 +00:00
|
|
|
#if defined(__APPLE__)
|
unix: make loops and watchers usable after fork()
Added the uv_loop_fork() API that must be called in a child process to
continue using an existing loop. Internally this calls a uv__io_fork
function for each supported platform, similar to the way
uv__platform_loop_init works.
After this call, existing and new IO, async and signal watchers will
contiue working as before on all platforms, as will the
threadpool (although any threads it was using are of course gone).
On Linux and BSDs that use kqueue, existing and new fsevent watchers
will also continue to work as expected. On OS X, though, directory
fsevents will not be able to use the optimized CoreFoundation path if
they had already been used in the parent process, instead falling back
to the kqueue path used on other BSDs.
Existing fsevent watchers will not function on AIX or SunOS. This
could be relatively easily fixed by someone with AIX knowledge in the
future, but SunOS will require some additional work to keep track if
the watchers.
A new test file, test/test-fork.c, was added to contain fork-related
tests to verify functionality in the child process.
PR-URL: https://github.com/libuv/libuv/pull/846
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
2016-04-22 19:16:31 +00:00
|
|
|
if (uv__has_forked_with_cfrunloop || uv__fsevents_close(handle))
|
2012-08-13 12:37:31 +00:00
|
|
|
#endif /* defined(__APPLE__) */
|
2014-03-10 21:44:44 +00:00
|
|
|
{
|
|
|
|
|
uv__io_close(handle->loop, &handle->event_watcher);
|
|
|
|
|
}
|
2012-08-13 12:37:31 +00:00
|
|
|
|
2015-05-26 08:51:10 +00:00
|
|
|
uv__free(handle->path);
|
2014-02-23 23:09:41 +00:00
|
|
|
handle->path = NULL;
|
2012-08-22 23:14:41 +00:00
|
|
|
|
2015-06-29 21:39:44 +00:00
|
|
|
if (handle->event_watcher.fd != -1) {
|
|
|
|
|
/* When FSEvents is used, we don't use the event_watcher's fd under certain
|
|
|
|
|
* confitions. (see uv_fs_event_start) */
|
|
|
|
|
uv__close(handle->event_watcher.fd);
|
|
|
|
|
handle->event_watcher.fd = -1;
|
|
|
|
|
}
|
2013-09-23 22:08:31 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void uv__fs_event_close(uv_fs_event_t* handle) {
|
|
|
|
|
uv_fs_event_stop(handle);
|
2011-09-22 23:20:33 +00:00
|
|
|
}
|