Understanding Async gem and its OS primitives

Today I read Async Ruby is the future article about Async Ruby and the event loop. It gave a great overview of how Ruby's async gem uses Fibers and an event loop to handle I/O without blocking.

The article got me curious about how the operating system actually notifies the Ruby runtime when a Fiber is ready to resume. I dug deeper and learned about epoll_ctl / epoll_wait on Linux, and their macOS equivalent, kqueue and kevent.

With some help from ChatGPT, I wrote a small C demo using kevent to wait for STDIN input asynchronously. It was a good exercise to understand how these lower-level primitives work under the hood.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/event.h>
#include <sys/time.h>
#include <fcntl.h>
 
int main() {
    int kq = kqueue();
    if (kq == -1) {
        perror("kqueue");
        exit(1);
    }
 
    struct kevent change;
    EV_SET(&change, STDIN_FILENO, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
 
    printf("Waiting for input (kqueue)...\n");
 
    struct kevent event;
    int nev = kevent(kq, &change, 1, &event, 1, NULL);  // NULL = block forever
 
    if (nev == -1) {
        perror("kevent");
        exit(1);
    }
 
    if (event.filter == EVFILT_READ) {
        char buffer[1024];
        ssize_t n = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
        if (n > 0) {
            buffer[n] = '\0';
            printf("Read from STDIN: %s\n", buffer);
        }
    }
 
    close(kq);
    return 0;
}

This gave me a deeper appreciation for the abstractions that the OS kernel provides to user-space programs—and how libraries like async in Ruby build on top of that.