/* * As documented in the kernel source fs/kernfs/file.c #780 * poll will return POLLERR|POLLPRI in case of sysfs * polling. This does not happen in case of out-of-band * TCP messages. * * The above is the case on (at least) FreeBSD and Linux. * * So to properly determine a POLLPRI or a POLLERR we need * to check for both. */ if ((events & POLLERR) && !(events & UV__POLLPRI)) { uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); uv__handle_stop(handle); handle->poll_cb(handle, UV_EBADF, 0); return; }
pevents = 0; if (events & POLLIN) pevents |= UV_READABLE; if (events & UV__POLLPRI) pevents |= UV_PRIORITIZED; if (events & POLLOUT) pevents |= UV_WRITABLE; if (events & UV__POLLRDHUP) pevents |= UV_DISCONNECT;
#if !defined(__sun) /* The event ports backend needs to rearm all file descriptors on each and * every tick of the event loop but the other backends allow us to * short-circuit here if the event mask is unchanged. */ if (w->events == w->pevents) return; #endif
if (QUEUE_EMPTY(&w->watcher_queue)) QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
voiduv__io_poll(uv_loop_t* loop, int timeout) { /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes * effectively infinite on 32 bits architectures. To avoid blocking * indefinitely, we cap the timeout and poll again if necessary. * * Note that "30 minutes" is a simplification because it depends on * the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200, * that being the largest value I have seen in the wild (and only once.) */ staticconstint max_safe_timeout = 1789569; structepoll_eventevents[1024]; structepoll_event* pe; structepoll_evente; int real_timeout; QUEUE* q; uv__io_t* w; sigset_t sigset; sigset_t* psigset; uint64_t base; int have_signals; int nevents; int count; int nfds; int fd; int op; int i;
// 如果没有任何观察者,直接返回 if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); return; }
if (w->events == 0) op = EPOLL_CTL_ADD; else op = EPOLL_CTL_MOD;
// 向 epoll 注册文件描述符及需要监控的IO事件 /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching * events, skip the syscall and squelch the events after epoll_wait(). */ if (epoll_ctl(loop->backend_fd, op, w->fd, &e)) { if (errno != EEXIST) abort();
assert(op == EPOLL_CTL_ADD);
// loop->backend_fd 在事件循环初始化时也就是在 `uv_loop_init` 中 通过 `epoll_create` 创建 /* We've reactivated a file descriptor that's been watched before. */ if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, w->fd, &e)) abort(); }
/* 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));
// 如果`timeout`为`0`函数直接返回 if (timeout == 0) return;
/* We may have been inside the system call for longer than |timeout| * milliseconds so we need to update the timestamp to avoid drift. */ // 减少下次 `epoll_pwait` 的 `timeout` 时间 goto update_timeout; }
// `epoll_wait` 返回错误 if (nfds == -1) { if (errno != EINTR) abort();
// 如果`timeout`为`-1`则继续循环 if (timeout == -1) continue;
// 如果`timeout`为`0`函数直接返回 if (timeout == 0) return;
/* Interrupted by a signal. Update timeout and poll again. */ // 减少下次 `epoll_pwait` 的 `timeout` 时间 goto update_timeout; }
// 如果IO观察者已经被移除,则停止轮询这个文件描述符上的IO事件 // 在一次事件循环中,同一IO观察者上可能出现多次IO事件 // 继而调用多次回调函数,某次回调函数中,有可能移除了`w`自己 if (w == NULL) { /* File descriptor that we've stopped watching, disarm it. * * Ignore all errors because we may be racing with another thread * when the file descriptor is closed. */ epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, pe); continue; }
// 进入事件处理
/* Give users only events they're interested in. Prevents spurious * callbacks when previous callback invocation in this loop has stopped * the current watcher. Also, filters out events that users has not * requested us to watch. */ pe->events &= w->pevents | POLLERR | POLLHUP;
/* Work around an epoll quirk where it sometimes reports just the * EPOLLERR or EPOLLHUP event. In order to force the event loop to * move forward, we merge in the read/write events that the watcher * is interested in; uv__read() and uv__write() will then deal with * the error or hangup in the usual fashion. * * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user * reads the available data, calls uv_read_stop(), then sometime later * calls uv_read_start() again. By then, libuv has forgotten about the * hangup and the kernel won't report EPOLLIN again because there's * nothing left to read. If anything, libuv is to blame here. The * current hack is just a quick bandaid; to properly fix it, libuv * needs to remember the error/hangup event. We should get that for * free when we switch over to edge-triggered I/O. */ if (pe->events == POLLERR || pe->events == POLLHUP) pe->events |= w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
// 如果存在有效事件 if (pe->events != 0) { /* Run signal watchers last. This also affects child process watchers * because those are implemented in terms of signal watchers. */ // 如果 `w` 是 `loop->signal_IOWatcher` 在循环之外调用回调,避免重复触发回调 if (w == &loop->signal_IOWatcher) have_signals = 1; else w->cb(loop, w, pe->events); // `w->cb` 是 `uv__io_cb` 类型的函数指针,对应的实现函数如`uv__async_io`已经在上文介绍 // 这个回调函数指针由 libuv 内部实现的统一入口,在 `cb` 中再进行事件分发,交由特定逻辑处理
nevents++; } }
// 如果信号事件触发 if (have_signals != 0) loop->signal_IOWatcher.cb(loop, &loop->signal_IOWatcher, POLLIN);