std.net.epoll

epoll & I/O-Multiplexing (WP-5): Überwacht beliebig viele Dateideskriptoren auf Ereignisse (lesbar, schreibbar, Fehler) ohne Polling. Ein einzelner EpollWait-Aufruf blockiert bis mindestens ein fd bereit ist und gibt nur die aktiven Events zurück. Enthält außerdem eventfd — eine thread-sichere fd-basierte Signalisierung.

Standard Library · std.net.socket · std.inotify


Konstanten

epoll_event-Struktur

Konstante Wert Beschreibung
EPOLL_EVENT_SIZE 12 Größe eines epoll_event-Eintrags in Bytes (+0: events uint32, +4: data uint64)

Event-Bits (events-Feld)

Konstante Wert Beschreibung
EPOLLIN 1 fd ist lesbar
EPOLLPRI 2 Dringende Daten vorhanden (OOB)
EPOLLOUT 4 fd ist schreibbar
EPOLLERR 8 Fehler aufgetreten (immer aktiv, kein explizites Setzen nötig)
EPOLLHUP 16 Hangup — Gegenseite hat Verbindung getrennt (immer aktiv)
EPOLLRDHUP 8192 Peer hat Schreib-Ende geschlossen (EPOLLIN gefolgt von EOF)
EPOLLWAKEUP 536870912 Verhindert Systemsuspend während des Wartens
EPOLLONESHOT 1073741824 fd nach erstem Event automatisch aus epoll entfernen
EPOLLET 2147483648 Edge-Triggered: Event nur bei Zustandsänderung, nicht dauerhaft

epoll_ctl-Operationen

Konstante Wert Beschreibung
EPOLL_CTL_ADD 1 fd zur epoll-Instanz hinzufügen
EPOLL_CTL_DEL 2 fd aus epoll-Instanz entfernen
EPOLL_CTL_MOD 3 Überwachte Events für fd ändern

Flags

Konstante Wert Beschreibung
EPOLL_CLOEXEC 524288 epoll-fd bei exec() schließen
EFD_CLOEXEC 524288 eventfd-fd bei exec() schließen
EFD_NONBLOCK 2048 eventfd nicht-blockierend
EFD_SEMAPHORE 1 eventfd im Semaphore-Modus: EventFdRead dekrementiert um 1 statt Reset

Funktionen

Instanz erstellen

Signatur Beschreibung
EpollCreate(): int64 Erstellt eine neue epoll-Instanz mit EPOLL_CLOEXEC. Gibt den epoll-fd zurück, < 0 bei Fehler

fd registrieren, ändern, entfernen

Signatur Beschreibung
EpollAdd(epFd: int64, fd: int64, events: int64, data: int64): int64 Registriert fd am epoll-fd. events: Kombination aus EPOLLIN, EPOLLOUT, EPOLLET etc. data: beliebiger uint64-Wert der bei Events zurückgeliefert wird (oft = fd selbst)
EpollMod(epFd: int64, fd: int64, events: int64, data: int64): int64 Ändert die überwachten Events für einen bereits registrierten fd
EpollDel(epFd: int64, fd: int64): int64 Entfernt fd aus der epoll-Instanz

Auf Events warten

Signatur Beschreibung
EpollWait(epFd: int64, evBuf: int64, maxEvents: int64, timeoutMs: int64): int64 Wartet auf Events. evBuf: alloc(EPOLL_EVENT_SIZE * maxEvents). timeoutMs: -1 = blockieren, 0 = sofort zurück, >0 = Millisekunden. Gibt Anzahl aufgetretener Events zurück

epoll_event-Felder auslesen

Signatur Beschreibung
EpollEventFlags(evBuf: int64, idx: int64): int64 Gibt das events-Feld (uint32) des idx-ten Events zurück
EpollEventData(evBuf: int64, idx: int64): int64 Gibt das 8-Byte-Datenfeld des idx-ten Events zurück
EpollEventFd(evBuf: int64, idx: int64): int64 Gibt den fd aus dem Datenfeld zurück (untere 32 Bit von data)

eventfd — thread-sichere Signalisierung

Signatur Beschreibung
EventFdCreate(initVal: int64, flags: int64): int64 Erstellt einen eventfd-Zähler mit Startwert initVal. flags: EFD_CLOEXEC EFD_NONBLOCK EFD_SEMAPHORE (oder 0)
EventFdSignal(fd: int64): int64 Erhöht den Zähler um 1 — weckt alle wartenden EpollWait- oder EventFdRead-Aufrufe
EventFdRead(fd: int64): int64 Liest und resettet den Zähler (blockiert bei 0). Gibt den Zählerwert zurück

Verwendung

Einfache Event-Loop (TCP-Server)

import std.net.epoll;
import std.net.socket;
import std.alloc;
import std.io;

fn RunLoop(listenFd: int64): void {
    var ep := EpollCreate();
    EpollAdd(ep, listenFd, EPOLLIN, listenFd);

    var buf: int64 := alloc(EPOLL_EVENT_SIZE * 64);

    var running: bool := true;
    while (running) {
        var n := EpollWait(ep, buf, 64, -1);
        var i: int64 := 0;
        while (i < n) {
            var fd    := EpollEventFd(buf, i);
            var flags := EpollEventFlags(buf, i);

            if (fd == listenFd) {
                var clientFd := Accept(listenFd, 0, 0);
                EpollAdd(ep, clientFd, EPOLLIN, clientFd);
            } else {
                if ((flags & EPOLLIN) != 0) {
                    // Daten vom Client lesen...
                }
                if ((flags & EPOLLHUP) != 0) {
                    EpollDel(ep, fd);
                    close(fd);
                }
            }
            i := i + 1;
        }
    }

    free(buf, EPOLL_EVENT_SIZE * 64);
    close(ep);
}

inotify + epoll kombinieren

import std.net.epoll;
import std.inotify;
import std.alloc;

fn WatchWithEpoll(path: pchar): void {
    var ep  := EpollCreate();
    var ifd := InotifyCreate();
    InotifyWatch(ifd, path, IN_CREATE | IN_MODIFY | IN_DELETE);

    // inotify-fd in epoll registrieren
    EpollAdd(ep, ifd, EPOLLIN, ifd);

    var evBuf:  int64 := alloc(EPOLL_EVENT_SIZE * 8);
    var inBuf:  int64 := alloc(4096);

    var n := EpollWait(ep, evBuf, 8, 5000);  // max. 5 Sekunden warten
    if (n > 0) {
        var bytes := InotifyRead(ifd, inBuf, 4096);
        // Events verarbeiten...
    }

    free(evBuf, EPOLL_EVENT_SIZE * 8);
    free(inBuf, 4096);
    close(ifd);
    close(ep);
}

eventfd: Thread wecken

import std.net.epoll;
import std.thread;

// Thread A: wartet auf Signal
fn Worker(efd: int64): void {
    var ep := EpollCreate();
    EpollAdd(ep, efd, EPOLLIN, efd);

    var buf: int64 := alloc(EPOLL_EVENT_SIZE);
    EpollWait(ep, buf, 1, -1);   // blockiert bis Signal
    EventFdRead(efd);             // Zähler zurücksetzen
    free(buf, EPOLL_EVENT_SIZE);
    close(ep);
}

// Thread B: sendet Signal
fn Notifier(efd: int64): void {
    // ... Arbeit erledigen ...
    EventFdSignal(efd);   // weckt Worker
}

fn Main(): void {
    var efd := EventFdCreate(0, EFD_CLOEXEC);
    // Threads starten, efd übergeben...
    close(efd);
}

EPOLLONESHOT: einmaliges Event

import std.net.epoll;
import std.alloc;

// fd wird nach erstem Event automatisch aus epoll entfernt.
// Für erneutes Warten: EpollMod mit EPOLLIN | EPOLLONESHOT aufrufen.
fn WaitOnce(fd: int64): void {
    var ep := EpollCreate();
    EpollAdd(ep, fd, EPOLLIN | EPOLLONESHOT, fd);

    var buf: int64 := alloc(EPOLL_EVENT_SIZE);
    EpollWait(ep, buf, 1, -1);
    free(buf, EPOLL_EVENT_SIZE);
    close(ep);
}


Hinweise

  • Im Edge-Triggered-Modus (EPOLLET) muss der fd bis zum EAGAIN vollständig geleert werden — sonst werden keine weiteren Events gemeldet.
  • EPOLLERR und EPOLLHUP sind immer aktiv und müssen nicht explizit in events gesetzt werden.
  • EPOLLONESHOT erfordert nach jedem Event ein EpollMod, um den fd wieder zu aktivieren.
  • Ein eventfd-fd kann selbst in eine epoll-Instanz eingehängt werden und wird bei EPOLLIN angezeigt sobald der Zähler > 0 ist.
  • EpollEventData und EpollEventFd lesen denselben Offset — EpollEventFd maskiert nur auf 32 Bit.

Verwandte Units

Letzte Aktualisierung: 2026-06-05