27#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
39typedef union epoll_data {
49} __attribute__((packed));
60#define EPOLLRDHUP 0x2000
61#define EPOLLEXCLUSIVE 0x10000000
62#define EPOLLET (1U << 31)
63#define EPOLLONESHOT (1U << 30)
66#define EPOLL_CTL_ADD 1
67#define EPOLL_CTL_DEL 2
68#define EPOLL_CTL_MOD 3
78static inline int epoll_create1(
int flags) {
86static inline int epoll_create(
int size) {
95static inline int epoll_ctl(
int epfd,
int op,
int fd,
struct epoll_event* event) {
102 uint16_t k_flags = 0;
104 if (op == EPOLL_CTL_ADD) {
105 k_flags = EV_ADD | EV_ENABLE;
106 }
else if (op == EPOLL_CTL_MOD) {
107 k_flags = EV_ADD | EV_ENABLE;
109 }
else if (op == EPOLL_CTL_DEL) {
117 if (event && (event->events & EPOLLET)) {
121 if (event && (event->events & EPOLLONESHOT)) {
122 k_flags |= EV_ONESHOT;
126 if (op == EPOLL_CTL_DEL || (event && (event->events & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)))) {
129 EV_SET(&kev[nchanges++], fd, EVFILT_READ, k_flags, 0, 0, event ? event->data.ptr : NULL);
133 if (op == EPOLL_CTL_DEL || (event && (event->events & EPOLLOUT))) {
134 EV_SET(&kev[nchanges++], fd, EVFILT_WRITE, k_flags, 0, 0, event ? event->data.ptr : NULL);
147 if (op == EPOLL_CTL_MOD && event) {
149 if (!(event->events & EPOLLOUT)) {
150 struct kevent del_ev;
151 EV_SET(&del_ev, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
152 kevent(epfd, &del_ev, 1, NULL, 0, NULL);
154 if (!(event->events & (EPOLLIN | EPOLLPRI))) {
155 struct kevent del_ev;
156 EV_SET(&del_ev, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
157 kevent(epfd, &del_ev, 1, NULL, 0, NULL);
161 if (nchanges == 0)
return 0;
163 return kevent(epfd, kev, nchanges, NULL, 0, NULL);
170static inline int epoll_wait(
int epfd,
struct epoll_event* events,
int maxevents,
int timeout_ms) {
172 struct timespec* ts_ptr = NULL;
174 if (timeout_ms >= 0) {
175 ts.tv_sec = timeout_ms / 1000;
176 ts.tv_nsec = (timeout_ms % 1000) * 1000000;
185 struct kevent* k_events = (
struct kevent*)malloc(
sizeof(
struct kevent) * maxevents);
191 int nfds = kevent(epfd, NULL, 0, k_events, maxevents, ts_ptr);
194 for (
int i = 0; i < nfds; ++i) {
195 uint32_t ev_flags = 0;
197 if (k_events[i].filter == EVFILT_READ) {
199 if (k_events[i].flags & EV_EOF) {
200 ev_flags |= EPOLLRDHUP;
202 }
else if (k_events[i].filter == EVFILT_WRITE) {
203 ev_flags |= EPOLLOUT;
206 if (k_events[i].flags & EV_ERROR) {
207 ev_flags |= EPOLLERR;
209 if (k_events[i].flags & EV_EOF) {
210 ev_flags |= EPOLLHUP;
213 events[i].events = ev_flags;
214 events[i].data.ptr = k_events[i].udata;
223#error "Unsupported platform: This header supports Linux (native) and BSD/macOS (kqueue shim)."
226#include <arpa/inet.h>
229#include <netinet/in.h>
232#include <sys/socket.h>
238static inline int set_nonblocking(
int fd) {
239 int flags = fcntl(fd, F_GETFL, 0);
243 return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
250static inline int create_listen_socket(uint16_t port) {
251 int fd = socket(AF_INET, SOCK_STREAM, 0);
259 if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt,
sizeof(opt)) == -1) {
260 perror(
"setsockopt SO_REUSEPORT");
266 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt,
sizeof(opt)) == -1) {
267 perror(
"setsockopt SO_REUSEADDR");
272 struct sockaddr_in addr = {0};
273 addr.sin_family = AF_INET;
274 addr.sin_addr.s_addr = INADDR_ANY;
275 addr.sin_port = htons(port);
277 if (bind(fd, (
struct sockaddr*)&addr,
sizeof(addr)) == -1) {
283 if (listen(fd, SOMAXCONN) == -1) {
289 if (set_nonblocking(fd) == -1) {
290 perror(
"set_nonblocking");