Repository navigation
Expand file tree
/
Copy pathevent_loop.cpp
More file actions
112 lines (94 loc) · 3.43 KB
/
Copy pathevent_loop.cpp
File metadata and controls
112 lines (94 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include "event_loop.h"
#include <array>
#include <cerrno>
#include <fcntl.h>
#include <system_error>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#include <unordered_map>
EventLoop::EventLoop() : queue_fd(kqueue()) {
if (queue_fd == -1) {
throw std::system_error(errno, std::generic_category(), "kqueue");
}
int flags;
do {
flags = fcntl(queue_fd, F_GETFD);
} while (flags == -1 && errno == EINTR);
int result = -1;
if (flags != -1) {
do {
result = fcntl(queue_fd, F_SETFD, flags | FD_CLOEXEC);
} while (result == -1 && errno == EINTR);
}
if (result == -1) {
const int error = errno;
close(queue_fd);
throw std::system_error(error, std::generic_category(), "fcntl kqueue");
}
}
EventLoop::~EventLoop() {
close(queue_fd);
}
void EventLoop::watch(int fd, bool readable, bool writable) {
struct kevent changes[2];
EV_SET(&changes[0], fd, EVFILT_READ,
EV_ADD | (readable ? EV_ENABLE : EV_DISABLE), 0, 0, nullptr);
EV_SET(&changes[1], fd, EVFILT_WRITE,
EV_ADD | (writable ? EV_ENABLE : EV_DISABLE), 0, 0, nullptr);
int result;
do {
result = kevent(queue_fd, changes, 2, nullptr, 0, nullptr);
} while (result == -1 && errno == EINTR);
if (result == -1) {
throw std::system_error(errno, std::generic_category(), "kevent watch");
}
}
void EventLoop::remove(int fd) {
for (const short filter : {EVFILT_READ, EVFILT_WRITE}) {
struct kevent change;
EV_SET(&change, fd, filter, EV_DELETE, 0, 0, nullptr);
int result;
do {
result = kevent(queue_fd, &change, 1, nullptr, 0, nullptr);
} while (result == -1 && errno == EINTR);
if (result == -1 && errno != ENOENT && errno != EBADF) {
throw std::system_error(errno, std::generic_category(), "kevent remove");
}
}
}
std::vector<SocketEvent> EventLoop::wait(int timeout_ms) {
std::array<struct kevent, 128> events;
struct timespec timeout {};
if (timeout_ms >= 0) {
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_nsec = (timeout_ms % 1000) * 1000000L;
}
const int count = kevent(queue_fd, nullptr, 0, events.data(),
static_cast<int>(events.size()),
timeout_ms < 0 ? nullptr : &timeout);
if (count == -1) {
if (errno == EINTR) {
return {};
}
throw std::system_error(errno, std::generic_category(), "kevent wait");
}
std::vector<SocketEvent> result;
std::unordered_map<int, std::size_t> positions;
result.reserve(static_cast<std::size_t>(count));
positions.reserve(static_cast<std::size_t>(count));
for (int index = 0; index < count; ++index) {
const auto& event = events[static_cast<std::size_t>(index)];
const int fd = static_cast<int>(event.ident);
const auto entry = positions.emplace(fd, result.size());
if (entry.second) {
result.push_back({fd, false, false, false});
}
auto& socket = result[entry.first->second];
socket.readable = socket.readable || event.filter == EVFILT_READ;
socket.writable = socket.writable || event.filter == EVFILT_WRITE;
socket.failed = socket.failed || (event.flags & EV_ERROR) != 0 ||
(event.filter == EVFILT_WRITE && (event.flags & EV_EOF) != 0);
}
return result;
}