aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/unix/kqueue.c33
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);