In der Lyx-Standardbibliothek ist fast jeder Ressourcentyp ein int64. Eine TCP-Verbindung ist ein int64. Ein PDF-Dokument ist ein int64. Eine PostgreSQL-Verbindung ist ein int64. Ein SQLite-Statement ist ein int64. Ein epoll-Instanz-Deskriptor ist ein int64.
Das ist kein Zufall und keine Vereinfachung. Es ist ein bewusstes Designprinzip, das direkt aus dem POSIX-Modell abgeleitet ist.
→ Rohspeicher: alloc & peek/poke · Fehler-Konventionen · Standard Library
Lyx unterscheidet intern zwei Kategorien, die beide als int64 erscheinen:
Ein von Linux vergebener int64-Wert (typischerweise klein: 3, 4, 5 …). Der Kernel verwaltet die zugehörige Ressource. Gültige fds sind ≥ 0, Fehler sind < 0.
Beispiele: open(), socket(), epoll_create1(), signalfd(), memfd_create(), inotify_init(), mq_open()
import std.fs;
import std.net.epoll;
var fileFd: int64 := open("/etc/hosts", 0, 0); // Kernel-fd, typisch 3–1023
var epollFd: int64 := EpollCreate(); // Kernel-fd
var netFd: int64 := sca_socket(2, 1, 0); // Kernel-fd
// Prüfung: fd < 0 → Fehler
if (fileFd < 0) { /* open fehlgeschlagen */ }
Kernel-fds müssen mit close(fd) geschlossen werden. Der Kernel gibt die zugehörigen Ressourcen frei.
Eine von alloc(n) zurückgegebene Adresse — also ein normaler Pointer, der als int64 dargestellt wird. Die zugehörige Datenstruktur lebt im Heap des Prozesses. Gültige Handles sind ≠ 0, Fehler sind 0.
Beispiele: SQLiteOpen, PGConnect, PdfCreate, RedisConnect
import std.db.sqlite;
import std.db.postgres;
import std.pdf.builder;
var db: int64 := SQLiteOpen("/data/app.db"); // Zeiger auf SQLiteDB (24 Bytes)
var pg: int64 := PGConnect(...); // Zeiger auf PGConn (120 Bytes)
var doc: int64 := PdfCreate(); // Zeiger auf PDF-Dokument-Struct
// Prüfung: Handle == 0 → Fehler
if (db == 0) { /* SQLite-Fehler */ }
Userspace-Handles müssen mit der entsprechenden Close/Free/Destroy-Funktion freigegeben werden.
Eine naheliegende Frage: Warum kein type SqliteDB = int64 oder gar ein eigener Struct-Typ? Drei Gründe:
1. Uniformität mit dem Kernel-API
Linux modelliert alles als fd: Dateien, Sockets, Pipes, Timer, Signale, epoll-Instanzen. Lyx übernimmt dieses Modell konsequent auf Userspace-Handles aus. Eine einheitliche Schnittstelle reduziert mentalen Overhead.
2. Kein Type-System-Overhead
Typisierte Handles würden zusätzliche Wrapper-Typen, Konvertierungsfunktionen und Compiler-Komplexität erzeugen. Für den Einsatz in Embedded- und Safety-Kontexten ist Einfachheit ein explizites Designziel.
3. Composability
Da alle Handles int64 sind, können sie in Arrays, Structs und als Rückgabewerte ohne Konvertierung verwendet werden:
// Array von Verbindungs-Handles — kein generischer Typ nötig
var conns: int64[64];
var count: int64 := 0;
fn AddConn(fd: int64): void {
if (count < 64) {
conns[count] := fd;
count := count + 1;
}
}
Die Konvention in der Standardbibliothek ist konsistent:
| Muster | Art | Fehlerindikator | Freigabe |
|---|---|---|---|
open, socket, EpollCreate, InotifyCreate, MqOpen | Kernel-fd | < 0 | close(fd) |
SQLiteOpen, PGConnect, PdfCreate, RedisConnect | Userspace | == 0 | SQLiteClose / PGClose / PdfFree / RedisClose |
SQLiteStmtPrepare, PGStmtPrepare | Userspace (Sub-Handle) | == 0 | Finalize / Close |
PdfBeginXObject | Userspace (negativer Index) | == 0 | keine (wird vom doc verwaltet) |
Da Handles nur int64 sind, können sie wie normale Werte weitergegeben werden. Die Ownership-Konvention (wer gibt frei?) ist durch den Funktionsnamen erkennbar:
import std.db.postgres;
// Erstellt Handle — Aufrufer ist verantwortlich für PGClose
fn OpenDB(): int64 {
return PGConnect("127.0.0.1", 5432, "mydb", "user", "pass");
}
// Verwendet Handle — gibt ihn NICHT frei
fn QueryUsers(conn: int64): int64 {
return PGQuery(conn, "SELECT COUNT(*) FROM users");
}
// Schließt Handle — danach nicht mehr verwenden
fn CloseDB(conn: int64): void {
PGClose(conn);
}
fn main(): int64 {
var conn: int64 := OpenDB();
if (conn == 0 || PGIsConnected(conn) == 0) { return 1; }
var res: int64 := QueryUsers(conn);
PGFetchRow(res, 0);
PrintLn(IntToStr(PGGetInt(res, 0)));
PGFreeResult(res);
CloseDB(conn);
return 0;
}
Handles können direkt in eigenen Structs (als alloc'd Byte-Puffer) gespeichert werden:
import std.alloc;
import std.db.postgres;
import std.net.epoll;
// Eigene Verbindungs-Verwaltungsstruktur
pub con APP_SIZE: int64 := 24;
pub con APP_PG_CONN: int64 := 0; // int64 → PGConn-Handle
pub con APP_EPOLL: int64 := 8; // int64 → epoll-fd
pub con APP_FLAGS: int64 := 16; // int64 → Bit-Flags
fn AppCreate(pgConn: int64, epollFd: int64): int64 {
var app: int64 := alloc(APP_SIZE);
if (app == 0) { return 0; }
poke64(app + APP_PG_CONN, pgConn);
poke64(app + APP_EPOLL, epollFd);
poke64(app + APP_FLAGS, 0);
return app;
}
fn AppDestroy(app: int64): void {
PGClose(peek64(app + APP_PG_CONN));
close(peek64(app + APP_EPOLL));
free(app, APP_SIZE);
}
Immer prüfen, bevor ein Handle verwendet wird — besonders nach Funktionen, die fehlschlagen können:
// Kernel-fd: < 0 bedeutet Fehler
var fd: int64 := open("/tmp/test.txt", 1, 0644);
if (fd < 0) {
PrintLn("Fehler beim Öffnen");
return -1;
}
// Userspace-Handle: 0 bedeutet Fehler
var db: int64 := SQLiteOpen("/data/app.db");
if (db == 0) {
PrintLn("SQLite-Fehler");
return -1;
}
// Sonderfall PostgreSQL: PGConnect gibt fast nie 0 zurück (nur bei TCP-Fehler)
// Zusätzlich PGIsConnected prüfen (Auth-Fehler, falsches Passwort):
var pg: int64 := PGConnect("127.0.0.1", 5432, "db", "user", "pass");
if (pg == 0 || PGIsConnected(pg) == 0) {
PrintLn("PG-Verbindung fehlgeschlagen");
return -1;
}
| Frage | Antwort |
|---|---|
| Was ist ein Handle? | Ein int64 — entweder ein Kernel-fd oder eine Userspace-Heap-Adresse |
| Kernel-fd oder Userspace? | Kernel: von OS vergeben (klein, ≥ 0). Userspace: von alloc() (groß, Pointer-Bereich) |
| Fehlerindikator Kernel-fd | < 0 |
| Fehlerindikator Userspace | == 0 (Ausnahme: PdfBeginXObject = negativ) |
| Wer gibt frei? | Wer Open/Create/Connect aufgerufen hat — erkennbar an Close/Free/Destroy |
| Kernel-fd freigeben | close(fd) |
| Userspace-Handle freigeben | Passende *Close / *Free / *Destroy-Funktion der Unit |
→ Rohspeicher: alloc & peek/poke
→ Fehler-Konventionen der Standardbibliothek
→ std.fs_ext · std.net.epoll · std.db.postgres
Letzte Aktualisierung: 2026-06-05