diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/unix/kqueue.c | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index 1762165e..876b7170 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -99,6 +99,39 @@ int uv__io_fork(uv_loop_t* loop) { int uv__io_check_fd(uv_loop_t* loop, int fd) { struct kevent ev; int rc; + struct stat sb; +#ifdef __APPLE__ + char path[MAXPATHLEN]; +#endif + + if (uv__fstat(fd, &sb)) + return UV__ERR(errno); + + /* On FreeBSD, kqueue only supports EVFILT_READ notification for regular files + * and always reports ready events for writing, resulting in busy-looping. + * + * On Darwin, DragonFlyBSD, NetBSD and OpenBSD, kqueue reports ready events for + * regular files as readable and writable only once, acting like an EV_ONESHOT. + * + * Neither of the above cases should be added to the kqueue. + */ + if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) + return UV_EINVAL; + +#ifdef __APPLE__ + /* On Darwin (both macOS and iOS), in addition to regular files, FIFOs also don't + * work properly with kqueue: the disconnection from the last writer won't trigger + * an event for kqueue in spite of what the man pages say. Thus, we also disallow + * the case of S_IFIFO. */ + if (S_ISFIFO(sb.st_mode)) { + /* File descriptors of FIFO, pipe and kqueue share the same type of file, + * therefore there is no way to tell them apart via stat.st_mode&S_IFMT. + * Fortunately, FIFO is the only one that has a persisted file on filesystem, + * from which we're able to make the distinction for it. */ + if (!fcntl(fd, F_GETPATH, path)) + return UV_EINVAL; + } +#endif rc = 0; EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0); |