Skip to content

Commit 1a2db03

Browse files
author
carpentry-heartbeat[bot]
committed
Implement POSIX poll(2) fallback for non-epoll/kqueue platforms
The #else branch in poll.h (triggered on platforms without epoll or kqueue — e.g. older POSIX, Haiku, Cygwin) was completely empty, causing compilation failures. This implements the full Poll API backed by a dynamically-resized pollfd array: - Poll struct holds a heap-allocated pollfd array with count/capacity - create/add/modify/remove/wait/close match epoll/kqueue semantics - Poll_copy performs a deep copy of the fd array - Poll_close frees the heap allocation - Test helper updated for the new backend Closes #4.
1 parent 4a754cc commit 1a2db03

3 files changed

Lines changed: 124 additions & 1 deletion

File tree

src/poll.carp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ Check `readable`, `writable`, and `error?` to see what happened on `fd`.")
3737
(register-type Poll)
3838

3939
(doc Poll "is an I/O event multiplexer that monitors multiple file descriptors
40-
for readiness. Uses kqueue on macOS/BSD and epoll on Linux.
40+
for readiness. Uses kqueue on macOS/BSD, epoll on Linux, and POSIX poll(2)
41+
as a fallback on other platforms.
4142

4243
## Usage
4344
```

src/poll.h

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,115 @@ void Poll_close(Poll p) {
233233

234234
#endif /* CARP_USE_EPOLL */
235235

236+
#ifdef CARP_USE_POLL
237+
238+
typedef struct {
239+
struct pollfd *fds;
240+
int count;
241+
int capacity;
242+
} Poll;
243+
244+
Poll Poll_create_() {
245+
Poll p;
246+
p.capacity = 16;
247+
p.count = 0;
248+
p.fds = (struct pollfd*)CARP_MALLOC(p.capacity * sizeof(struct pollfd));
249+
return p;
250+
}
251+
252+
int Poll_fd_(Poll* p) {
253+
/* No kernel fd — return 0 if valid, -1 if not */
254+
return (p->fds != NULL) ? 0 : -1;
255+
}
256+
257+
int Poll_add_(Poll* p, int fd, int interest) {
258+
if (p->count >= p->capacity) {
259+
int new_cap = p->capacity * 2;
260+
struct pollfd *new_fds = (struct pollfd*)CARP_MALLOC(new_cap * sizeof(struct pollfd));
261+
if (!new_fds) return -1;
262+
memcpy(new_fds, p->fds, (size_t)p->count * sizeof(struct pollfd));
263+
CARP_FREE(p->fds);
264+
p->fds = new_fds;
265+
p->capacity = new_cap;
266+
}
267+
struct pollfd *pfd = &p->fds[p->count];
268+
pfd->fd = fd;
269+
pfd->events = 0;
270+
if (interest & POLL_READ) pfd->events |= POLLIN;
271+
if (interest & POLL_WRITE) pfd->events |= POLLOUT;
272+
pfd->revents = 0;
273+
p->count++;
274+
return 0;
275+
}
276+
277+
int Poll_modify_(Poll* p, int fd, int interest) {
278+
for (int i = 0; i < p->count; i++) {
279+
if (p->fds[i].fd == fd) {
280+
p->fds[i].events = 0;
281+
if (interest & POLL_READ) p->fds[i].events |= POLLIN;
282+
if (interest & POLL_WRITE) p->fds[i].events |= POLLOUT;
283+
return 0;
284+
}
285+
}
286+
errno = ENOENT;
287+
return -1;
288+
}
289+
290+
int Poll_remove_(Poll* p, int fd) {
291+
for (int i = 0; i < p->count; i++) {
292+
if (p->fds[i].fd == fd) {
293+
p->fds[i] = p->fds[p->count - 1];
294+
p->count--;
295+
return 0;
296+
}
297+
}
298+
errno = ENOENT;
299+
return -1;
300+
}
301+
302+
Array Poll_wait_(Poll* p, int timeout_ms) {
303+
Array result;
304+
if (!p->fds) {
305+
result.len = 0; result.capacity = 0; result.data = NULL;
306+
return result;
307+
}
308+
309+
int n = poll(p->fds, (nfds_t)p->count, timeout_ms);
310+
if (n < 0) {
311+
result.len = 0; result.capacity = 0; result.data = NULL;
312+
return result;
313+
}
314+
315+
int ready = 0;
316+
for (int i = 0; i < p->count; i++) {
317+
if (p->fds[i].revents != 0) ready++;
318+
}
319+
320+
result.len = ready;
321+
result.capacity = ready > 0 ? ready : 1;
322+
result.data = CARP_MALLOC(result.capacity * sizeof(PollEvent));
323+
324+
int j = 0;
325+
for (int i = 0; i < p->count && j < ready; i++) {
326+
if (p->fds[i].revents == 0) continue;
327+
PollEvent* e = &((PollEvent*)result.data)[j];
328+
e->fd = p->fds[i].fd;
329+
e->readable = (p->fds[i].revents & POLLIN) != 0;
330+
e->writable = (p->fds[i].revents & POLLOUT) != 0;
331+
e->error = (p->fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) != 0;
332+
j++;
333+
}
334+
335+
result.len = j;
336+
return result;
337+
}
338+
339+
void Poll_close(Poll p) {
340+
if (p.fds) CARP_FREE(p.fds);
341+
}
342+
343+
#endif /* CARP_USE_POLL */
344+
236345
bool Poll_wait_failed_(Array* a) {
237346
return a->data == NULL;
238347
}
@@ -243,6 +352,15 @@ Poll Poll_copy(Poll* p) {
243352
c.kq = p->kq;
244353
#elif defined(CARP_USE_EPOLL)
245354
c.epfd = p->epfd;
355+
#elif defined(CARP_USE_POLL)
356+
c.count = p->count;
357+
c.capacity = p->capacity;
358+
if (p->fds && p->capacity > 0) {
359+
c.fds = (struct pollfd*)CARP_MALLOC((size_t)p->capacity * sizeof(struct pollfd));
360+
memcpy(c.fds, p->fds, (size_t)p->count * sizeof(struct pollfd));
361+
} else {
362+
c.fds = NULL;
363+
}
246364
#endif
247365
return c;
248366
}

test/poll_test_helpers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Poll Poll_make_invalid_() {
1010
p.kq = -1;
1111
#elif defined(CARP_USE_EPOLL)
1212
p.epfd = -1;
13+
#elif defined(CARP_USE_POLL)
14+
p.fds = NULL;
15+
p.count = 0;
16+
p.capacity = 0;
1317
#endif
1418
return p;
1519
}

0 commit comments

Comments
 (0)