Commit c50dd602 authored by Kenton Varda's avatar Kenton Varda

Use edge-triggered epoll on Linux.

parent 50b9a529
...@@ -110,8 +110,7 @@ class AsyncStreamFd: public OwnedFileDescriptor, public AsyncIoStream { ...@@ -110,8 +110,7 @@ class AsyncStreamFd: public OwnedFileDescriptor, public AsyncIoStream {
public: public:
AsyncStreamFd(UnixEventPort& eventPort, int fd, uint flags) AsyncStreamFd(UnixEventPort& eventPort, int fd, uint flags)
: OwnedFileDescriptor(fd, flags), : OwnedFileDescriptor(fd, flags),
readObserver(eventPort, fd), observer(eventPort, fd, UnixEventPort::FdObserver::OBSERVE_READ_WRITE) {}
writeObserver(eventPort, fd) {}
virtual ~AsyncStreamFd() noexcept(false) {} virtual ~AsyncStreamFd() noexcept(false) {}
Promise<size_t> read(void* buffer, size_t minBytes, size_t maxBytes) override { Promise<size_t> read(void* buffer, size_t minBytes, size_t maxBytes) override {
...@@ -157,7 +156,7 @@ public: ...@@ -157,7 +156,7 @@ public:
buffer = reinterpret_cast<const byte*>(buffer) + n; buffer = reinterpret_cast<const byte*>(buffer) + n;
size -= n; size -= n;
return writeObserver.whenBecomesWritable().then([=]() { return observer.whenBecomesWritable().then([=]() {
return write(buffer, size); return write(buffer, size);
}); });
} }
...@@ -192,7 +191,7 @@ public: ...@@ -192,7 +191,7 @@ public:
if (pollResult == 0) { if (pollResult == 0) {
// Not ready yet. We can safely use the edge-triggered observer. // Not ready yet. We can safely use the edge-triggered observer.
return readObserver.whenBecomesReadable(); return observer.whenBecomesReadable();
} else { } else {
// Ready now. // Ready now.
return kj::READY_NOW; return kj::READY_NOW;
...@@ -200,8 +199,7 @@ public: ...@@ -200,8 +199,7 @@ public:
} }
private: private:
UnixEventPort::ReadObserver readObserver; UnixEventPort::FdObserver observer;
UnixEventPort::WriteObserver writeObserver;
Promise<size_t> tryReadInternal(void* buffer, size_t minBytes, size_t maxBytes, Promise<size_t> tryReadInternal(void* buffer, size_t minBytes, size_t maxBytes,
size_t alreadyRead) { size_t alreadyRead) {
...@@ -226,7 +224,7 @@ private: ...@@ -226,7 +224,7 @@ private:
if (n < 0) { if (n < 0) {
// Read would block. // Read would block.
return readObserver.whenBecomesReadable().then([=]() { return observer.whenBecomesReadable().then([=]() {
return tryReadInternal(buffer, minBytes, maxBytes, alreadyRead); return tryReadInternal(buffer, minBytes, maxBytes, alreadyRead);
}); });
} else if (n == 0) { } else if (n == 0) {
...@@ -243,7 +241,7 @@ private: ...@@ -243,7 +241,7 @@ private:
maxBytes -= n; maxBytes -= n;
alreadyRead += n; alreadyRead += n;
KJ_IF_MAYBE(atEnd, readObserver.atEndHint()) { KJ_IF_MAYBE(atEnd, observer.atEndHint()) {
if (*atEnd) { if (*atEnd) {
// We've already received an indication that the next read() will return EOF, so there's // We've already received an indication that the next read() will return EOF, so there's
// nothing to wait for. // nothing to wait for.
...@@ -256,7 +254,7 @@ private: ...@@ -256,7 +254,7 @@ private:
// that even if it was received since then, whenBecomesReadable() will catch that. So, // that even if it was received since then, whenBecomesReadable() will catch that. So,
// let's go ahead and skip calling read() here and instead go straight to waiting for // let's go ahead and skip calling read() here and instead go straight to waiting for
// more input. // more input.
return readObserver.whenBecomesReadable().then([=]() { return observer.whenBecomesReadable().then([=]() {
return tryReadInternal(buffer, minBytes, maxBytes, alreadyRead); return tryReadInternal(buffer, minBytes, maxBytes, alreadyRead);
}); });
} }
...@@ -304,7 +302,7 @@ private: ...@@ -304,7 +302,7 @@ private:
if (n < firstPiece.size()) { if (n < firstPiece.size()) {
// Only part of the first piece was consumed. Wait for buffer space and then write again. // Only part of the first piece was consumed. Wait for buffer space and then write again.
firstPiece = firstPiece.slice(n, firstPiece.size()); firstPiece = firstPiece.slice(n, firstPiece.size());
return writeObserver.whenBecomesWritable().then([=]() { return observer.whenBecomesWritable().then([=]() {
return writeInternal(firstPiece, morePieces); return writeInternal(firstPiece, morePieces);
}); });
} else if (morePieces.size() == 0) { } else if (morePieces.size() == 0) {
...@@ -731,7 +729,8 @@ Promise<Array<SocketAddress>> SocketAddress::lookupHost( ...@@ -731,7 +729,8 @@ Promise<Array<SocketAddress>> SocketAddress::lookupHost(
class FdConnectionReceiver final: public ConnectionReceiver, public OwnedFileDescriptor { class FdConnectionReceiver final: public ConnectionReceiver, public OwnedFileDescriptor {
public: public:
FdConnectionReceiver(UnixEventPort& eventPort, int fd, uint flags) FdConnectionReceiver(UnixEventPort& eventPort, int fd, uint flags)
: OwnedFileDescriptor(fd, flags), eventPort(eventPort), observer(eventPort, fd) {} : OwnedFileDescriptor(fd, flags), eventPort(eventPort),
observer(eventPort, fd, UnixEventPort::FdObserver::OBSERVE_READ) {}
Promise<Own<AsyncIoStream>> accept() override { Promise<Own<AsyncIoStream>> accept() override {
int newFd; int newFd;
...@@ -785,7 +784,7 @@ public: ...@@ -785,7 +784,7 @@ public:
public: public:
UnixEventPort& eventPort; UnixEventPort& eventPort;
UnixEventPort::ReadObserver observer; UnixEventPort::FdObserver observer;
}; };
class TimerImpl final: public Timer { class TimerImpl final: public Timer {
......
...@@ -86,6 +86,27 @@ TEST_F(AsyncUnixTest, SignalWithValue) { ...@@ -86,6 +86,27 @@ TEST_F(AsyncUnixTest, SignalWithValue) {
EXPECT_SI_CODE(SI_QUEUE, info.si_code); EXPECT_SI_CODE(SI_QUEUE, info.si_code);
EXPECT_EQ(123, info.si_value.sival_int); EXPECT_EQ(123, info.si_value.sival_int);
} }
TEST_F(AsyncUnixTest, SignalWithPointerValue) {
// This tests that if we use sigqueue() to attach a value to the signal, that value is received
// correctly. Note that this only works on platforms that support real-time signals -- even
// though the signal we're sending is SIGURG, the sigqueue() system call is introduced by RT
// signals. Hence this test won't run on e.g. Mac OSX.
UnixEventPort port;
EventLoop loop(port);
WaitScope waitScope(loop);
union sigval value;
memset(&value, 0, sizeof(value));
value.sival_ptr = &port;
sigqueue(getpid(), SIGURG, value);
siginfo_t info = port.onSignal(SIGURG).wait(waitScope);
EXPECT_EQ(SIGURG, info.si_signo);
EXPECT_SI_CODE(SI_QUEUE, info.si_code);
EXPECT_EQ(&port, info.si_value.sival_ptr);
}
#endif #endif
TEST_F(AsyncUnixTest, SignalsMultiListen) { TEST_F(AsyncUnixTest, SignalsMultiListen) {
...@@ -207,14 +228,14 @@ TEST_F(AsyncUnixTest, ReadObserver) { ...@@ -207,14 +228,14 @@ TEST_F(AsyncUnixTest, ReadObserver) {
KJ_SYSCALL(pipe(pipefds)); KJ_SYSCALL(pipe(pipefds));
kj::AutoCloseFd infd(pipefds[0]), outfd(pipefds[1]); kj::AutoCloseFd infd(pipefds[0]), outfd(pipefds[1]);
UnixEventPort::ReadObserver readObserver(port, infd); UnixEventPort::FdObserver observer(port, infd, UnixEventPort::FdObserver::OBSERVE_READ);
KJ_SYSCALL(write(outfd, "foo", 3)); KJ_SYSCALL(write(outfd, "foo", 3));
readObserver.whenBecomesReadable().wait(waitScope); observer.whenBecomesReadable().wait(waitScope);
#if __linux__ // platform known to support POLLRDHUP #if __linux__ // platform known to support POLLRDHUP
EXPECT_FALSE(KJ_ASSERT_NONNULL(readObserver.atEndHint())); EXPECT_FALSE(KJ_ASSERT_NONNULL(observer.atEndHint()));
char buffer[4096]; char buffer[4096];
ssize_t n; ssize_t n;
...@@ -224,9 +245,9 @@ TEST_F(AsyncUnixTest, ReadObserver) { ...@@ -224,9 +245,9 @@ TEST_F(AsyncUnixTest, ReadObserver) {
KJ_SYSCALL(write(outfd, "bar", 3)); KJ_SYSCALL(write(outfd, "bar", 3));
outfd = nullptr; outfd = nullptr;
readObserver.whenBecomesReadable().wait(waitScope); observer.whenBecomesReadable().wait(waitScope);
EXPECT_TRUE(KJ_ASSERT_NONNULL(readObserver.atEndHint())); EXPECT_TRUE(KJ_ASSERT_NONNULL(observer.atEndHint()));
#endif #endif
} }
...@@ -237,10 +258,12 @@ TEST_F(AsyncUnixTest, ReadObserverMultiListen) { ...@@ -237,10 +258,12 @@ TEST_F(AsyncUnixTest, ReadObserverMultiListen) {
int bogusPipefds[2]; int bogusPipefds[2];
KJ_SYSCALL(pipe(bogusPipefds)); KJ_SYSCALL(pipe(bogusPipefds));
UnixEventPort::ReadObserver bogusReadObserver(port, bogusPipefds[0]);
KJ_DEFER({ close(bogusPipefds[1]); close(bogusPipefds[0]); }); KJ_DEFER({ close(bogusPipefds[1]); close(bogusPipefds[0]); });
bogusReadObserver.whenBecomesReadable().then([]() { UnixEventPort::FdObserver bogusObserver(port, bogusPipefds[0],
UnixEventPort::FdObserver::OBSERVE_READ);
bogusObserver.whenBecomesReadable().then([]() {
ADD_FAILURE() << "Received wrong poll."; ADD_FAILURE() << "Received wrong poll.";
}).detach([](kj::Exception&& exception) { }).detach([](kj::Exception&& exception) {
ADD_FAILURE() << kj::str(exception).cStr(); ADD_FAILURE() << kj::str(exception).cStr();
...@@ -250,10 +273,11 @@ TEST_F(AsyncUnixTest, ReadObserverMultiListen) { ...@@ -250,10 +273,11 @@ TEST_F(AsyncUnixTest, ReadObserverMultiListen) {
KJ_SYSCALL(pipe(pipefds)); KJ_SYSCALL(pipe(pipefds));
KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); }); KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); });
UnixEventPort::ReadObserver readObserver(port, pipefds[0]); UnixEventPort::FdObserver observer(port, pipefds[0],
UnixEventPort::FdObserver::OBSERVE_READ);
KJ_SYSCALL(write(pipefds[1], "foo", 3)); KJ_SYSCALL(write(pipefds[1], "foo", 3));
readObserver.whenBecomesReadable().wait(waitScope); observer.whenBecomesReadable().wait(waitScope);
} }
TEST_F(AsyncUnixTest, ReadObserverMultiReceive) { TEST_F(AsyncUnixTest, ReadObserverMultiReceive) {
...@@ -265,18 +289,22 @@ TEST_F(AsyncUnixTest, ReadObserverMultiReceive) { ...@@ -265,18 +289,22 @@ TEST_F(AsyncUnixTest, ReadObserverMultiReceive) {
KJ_SYSCALL(pipe(pipefds)); KJ_SYSCALL(pipe(pipefds));
KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); }); KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); });
UnixEventPort::ReadObserver readObserver(port, pipefds[0]); UnixEventPort::FdObserver observer(port, pipefds[0],
UnixEventPort::FdObserver::OBSERVE_READ);
KJ_SYSCALL(write(pipefds[1], "foo", 3)); KJ_SYSCALL(write(pipefds[1], "foo", 3));
int pipefds2[2]; int pipefds2[2];
KJ_SYSCALL(pipe(pipefds2)); KJ_SYSCALL(pipe(pipefds2));
KJ_DEFER({ close(pipefds2[1]); close(pipefds2[0]); }); KJ_DEFER({ close(pipefds2[1]); close(pipefds2[0]); });
UnixEventPort::ReadObserver readObserver2(port, pipefds2[0]); UnixEventPort::FdObserver observer2(port, pipefds2[0],
UnixEventPort::FdObserver::OBSERVE_READ);
KJ_SYSCALL(write(pipefds2[1], "bar", 3)); KJ_SYSCALL(write(pipefds2[1], "bar", 3));
readObserver.whenBecomesReadable().wait(waitScope); auto promise1 = observer.whenBecomesReadable();
readObserver2.whenBecomesReadable().wait(waitScope); auto promise2 = observer2.whenBecomesReadable();
promise1.wait(waitScope);
promise2.wait(waitScope);
} }
TEST_F(AsyncUnixTest, ReadObserverAsync) { TEST_F(AsyncUnixTest, ReadObserverAsync) {
...@@ -288,7 +316,8 @@ TEST_F(AsyncUnixTest, ReadObserverAsync) { ...@@ -288,7 +316,8 @@ TEST_F(AsyncUnixTest, ReadObserverAsync) {
int pipefds[2]; int pipefds[2];
KJ_SYSCALL(pipe(pipefds)); KJ_SYSCALL(pipe(pipefds));
KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); }); KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); });
UnixEventPort::ReadObserver readObserver(port, pipefds[0]); UnixEventPort::FdObserver observer(port, pipefds[0],
UnixEventPort::FdObserver::OBSERVE_READ);
Thread thread([&]() { Thread thread([&]() {
delay(); delay();
...@@ -296,7 +325,7 @@ TEST_F(AsyncUnixTest, ReadObserverAsync) { ...@@ -296,7 +325,7 @@ TEST_F(AsyncUnixTest, ReadObserverAsync) {
}); });
// Wait for the event in this thread. // Wait for the event in this thread.
readObserver.whenBecomesReadable().wait(waitScope); observer.whenBecomesReadable().wait(waitScope);
} }
TEST_F(AsyncUnixTest, ReadObserverNoWait) { TEST_F(AsyncUnixTest, ReadObserverNoWait) {
...@@ -309,18 +338,20 @@ TEST_F(AsyncUnixTest, ReadObserverNoWait) { ...@@ -309,18 +338,20 @@ TEST_F(AsyncUnixTest, ReadObserverNoWait) {
int pipefds[2]; int pipefds[2];
KJ_SYSCALL(pipe(pipefds)); KJ_SYSCALL(pipe(pipefds));
KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); }); KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); });
UnixEventPort::ReadObserver readObserver(port, pipefds[0]); UnixEventPort::FdObserver observer(port, pipefds[0],
UnixEventPort::FdObserver::OBSERVE_READ);
int pipefds2[2]; int pipefds2[2];
KJ_SYSCALL(pipe(pipefds2)); KJ_SYSCALL(pipe(pipefds2));
KJ_DEFER({ close(pipefds2[1]); close(pipefds2[0]); }); KJ_DEFER({ close(pipefds2[1]); close(pipefds2[0]); });
UnixEventPort::ReadObserver readObserver2(port, pipefds2[0]); UnixEventPort::FdObserver observer2(port, pipefds2[0],
UnixEventPort::FdObserver::OBSERVE_READ);
int receivedCount = 0; int receivedCount = 0;
readObserver.whenBecomesReadable().then([&]() { observer.whenBecomesReadable().then([&]() {
receivedCount++; receivedCount++;
}).detach([](Exception&& e) { ADD_FAILURE() << str(e).cStr(); }); }).detach([](Exception&& e) { ADD_FAILURE() << str(e).cStr(); });
readObserver2.whenBecomesReadable().then([&]() { observer2.whenBecomesReadable().then([&]() {
receivedCount++; receivedCount++;
}).detach([](Exception&& e) { ADD_FAILURE() << str(e).cStr(); }); }).detach([](Exception&& e) { ADD_FAILURE() << str(e).cStr(); });
...@@ -360,7 +391,7 @@ TEST_F(AsyncUnixTest, WriteObserver) { ...@@ -360,7 +391,7 @@ TEST_F(AsyncUnixTest, WriteObserver) {
kj::AutoCloseFd infd(pipefds[0]), outfd(pipefds[1]); kj::AutoCloseFd infd(pipefds[0]), outfd(pipefds[1]);
setNonblocking(outfd); setNonblocking(outfd);
UnixEventPort::WriteObserver writeObserver(port, outfd); UnixEventPort::FdObserver observer(port, outfd, UnixEventPort::FdObserver::OBSERVE_WRITE);
// Fill buffer. // Fill buffer.
ssize_t n; ssize_t n;
...@@ -369,7 +400,7 @@ TEST_F(AsyncUnixTest, WriteObserver) { ...@@ -369,7 +400,7 @@ TEST_F(AsyncUnixTest, WriteObserver) {
} while (n >= 0); } while (n >= 0);
bool writable = false; bool writable = false;
auto promise = writeObserver.whenBecomesWritable() auto promise = observer.whenBecomesWritable()
.then([&]() { writable = true; }).eagerlyEvaluate(nullptr); .then([&]() { writable = true; }).eagerlyEvaluate(nullptr);
loop.run(); loop.run();
......
This diff is collapsed.
...@@ -29,8 +29,12 @@ ...@@ -29,8 +29,12 @@
#include "async.h" #include "async.h"
#include "time.h" #include "time.h"
#include "vector.h" #include "vector.h"
#include "io.h"
#include <signal.h> #include <signal.h>
#include <pthread.h>
#if __linux__ && !defined(KJ_USE_EPOLL)
#define KJ_USE_EPOLL 1
#endif
namespace kj { namespace kj {
...@@ -51,9 +55,8 @@ public: ...@@ -51,9 +55,8 @@ public:
UnixEventPort(); UnixEventPort();
~UnixEventPort() noexcept(false); ~UnixEventPort() noexcept(false);
class ReadObserver; class FdObserver;
class WriteObserver; // Class that watches an fd for readability or writability. See definition below.
// Classes that watch an fd for readability or writability. See definitions below.
Promise<siginfo_t> onSignal(int signum); Promise<siginfo_t> onSignal(int signum);
// When the given signal is delivered to this thread, return the corresponding siginfo_t. // When the given signal is delivered to this thread, return the corresponding siginfo_t.
...@@ -91,34 +94,43 @@ public: ...@@ -91,34 +94,43 @@ public:
void poll() override; void poll() override;
private: private:
#if KJ_USE_EPOLL
// TODO(soon): epoll implementation
#else
class PollPromiseAdapter;
class SignalPromiseAdapter;
class TimerPromiseAdapter;
class PollContext;
struct TimerSet; // Defined in source file to avoid STL include. struct TimerSet; // Defined in source file to avoid STL include.
class TimerPromiseAdapter;
class SignalPromiseAdapter;
PollPromiseAdapter* pollHead = nullptr;
PollPromiseAdapter** pollTail = &pollHead;
SignalPromiseAdapter* signalHead = nullptr;
SignalPromiseAdapter** signalTail = &signalHead;
Own<TimerSet> timers; Own<TimerSet> timers;
TimePoint frozenSteadyTime; TimePoint frozenSteadyTime;
void gotSignal(const siginfo_t& siginfo); SignalPromiseAdapter* signalHead = nullptr;
SignalPromiseAdapter** signalTail = &signalHead;
TimePoint currentSteadyTime(); TimePoint currentSteadyTime();
void processTimers(); void processTimers();
void gotSignal(const siginfo_t& siginfo);
friend class TimerPromiseAdapter; friend class TimerPromiseAdapter;
#if KJ_USE_EPOLL
AutoCloseFd epollFd;
AutoCloseFd signalFd;
AutoCloseFd eventFd; // Used for cross-thread wakeups.
sigset_t signalFdSigset;
// Signal mask as currently set on the signalFd. Tracked so we can detect whether or not it
// needs updating.
void doEpollWait(int timeout);
#else
class PollContext;
FdObserver* observersHead = nullptr;
FdObserver** observersTail = &observersHead;
#endif #endif
}; };
class UnixEventPort::ReadObserver { class UnixEventPort::FdObserver {
// Object which watches a file descriptor to determine when it is readable. // Object which watches a file descriptor to determine when it is readable or writable.
// //
// For listen sockets, "readable" means that there is a connection to accept(). For everything // For listen sockets, "readable" means that there is a connection to accept(). For everything
// else, it means that read() (or recv()) will return data. // else, it means that read() (or recv()) will return data.
...@@ -133,11 +145,19 @@ class UnixEventPort::ReadObserver { ...@@ -133,11 +145,19 @@ class UnixEventPort::ReadObserver {
// behavior. If at all possible, use the higher-level AsyncInputStream interface instead. // behavior. If at all possible, use the higher-level AsyncInputStream interface instead.
public: public:
ReadObserver(UnixEventPort& eventPort, int fd): eventPort(eventPort), fd(fd) {} enum Flags {
OBSERVE_READ = 1,
OBSERVE_WRITE = 2,
OBSERVE_READ_WRITE = OBSERVE_READ | OBSERVE_WRITE
};
FdObserver(UnixEventPort& eventPort, int fd, uint flags);
// Begin watching the given file descriptor for readability. Only one ReadObserver may exist // Begin watching the given file descriptor for readability. Only one ReadObserver may exist
// for a given file descriptor at a time. // for a given file descriptor at a time.
KJ_DISALLOW_COPY(ReadObserver); ~FdObserver() noexcept(false);
KJ_DISALLOW_COPY(FdObserver);
Promise<void> whenBecomesReadable(); Promise<void> whenBecomesReadable();
// Resolves the next time the file descriptor transitions from having no data to read to having // Resolves the next time the file descriptor transitions from having no data to read to having
...@@ -177,37 +197,6 @@ public: ...@@ -177,37 +197,6 @@ public:
// //
// This hint may be useful as an optimization to avoid an unnecessary system call. // This hint may be useful as an optimization to avoid an unnecessary system call.
private:
UnixEventPort& eventPort;
int fd;
#if KJ_USE_EPOLL
// TODO(soon): epoll implementation
Own<PromiseFulfiller<void>> fulfiller;
// Replaced each time `whenBecomesReadable()` is called.
#else
#endif
Maybe<bool> atEnd;
friend class UnixEventPort;
};
class UnixEventPort::WriteObserver {
// Object which watches a file descriptor to determine when it is writable.
//
// WARNING: The exact behavior of this class differs across systems, since event interfaces
// vary wildly. Be sure to read the documentation carefully and avoid depending on unspecified
// behavior. If at all possible, use the higher-level AsyncOutputStream interface instead.
public:
WriteObserver(UnixEventPort& eventPort, int fd): eventPort(eventPort), fd(fd) {}
// Begin watching the given file descriptor for writability. Only one WriteObserver may exist
// for a given file descriptor at a time.
KJ_DISALLOW_COPY(WriteObserver);
Promise<void> whenBecomesWritable(); Promise<void> whenBecomesWritable();
// Resolves the next time the file descriptor transitions from having no space available in the // Resolves the next time the file descriptor transitions from having no space available in the
// write buffer to having some space available. // write buffer to having some space available.
...@@ -232,11 +221,24 @@ public: ...@@ -232,11 +221,24 @@ public:
private: private:
UnixEventPort& eventPort; UnixEventPort& eventPort;
int fd; int fd;
uint flags;
#if KJ_USE_EPOLL kj::Maybe<Own<PromiseFulfiller<void>>> readFulfiller;
Own<PromiseFulfiller<void>> fulfiller; kj::Maybe<Own<PromiseFulfiller<void>>> writeFulfiller;
// Replaced each time `whenBecomesReadable()` is called. // Replaced each time `whenBecomesReadable()` or `whenBecomesWritable()` is called. Reverted to
#else // null every time an event is fired.
Maybe<bool> atEnd;
void fire(short events);
#if !KJ_USE_EPOLL
FdObserver* next;
FdObserver** prev;
// Linked list of observers which currently have a non-null readFulfiller or writeFulfiller.
// If `prev` is null then the observer is not currently in the list.
short getEventMask();
#endif #endif
friend class UnixEventPort; friend class UnixEventPort;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment