stdlib: document the libc/kernel layering rationale in Net.Sockets

Explain why the unit binds libc (the kernel boundary, the syscall doorway), the
common three-layer pattern shared with Indy/Synapse/the JDK, how Java is itself
not libc-free, and what a pure-Pascal syscall-stub version would and would not
buy.
This commit is contained in:
Graeme Geldenhuys 2026-06-23 17:21:36 +01:00
parent 3c6fbc3184
commit dd635d5140

View file

@ -21,7 +21,41 @@
MakeNonBlocking, and IgnoreSigPipe. MakeNonBlocking, and IgnoreSigPipe.
IPv4 only, no TLS — suitable for a localhost service or a plain TCP server. IPv4 only, no TLS — suitable for a localhost service or a plain TCP server.
Strings are treated as raw bytes for I/O. } Strings are treated as raw bytes for I/O.
--- Why this binds libc (and why that is the normal choice) ---
Opening a socket is not a computation; it is a request to the OS kernel. A
userspace process cannot touch the network card, allocate a port, or run TCP
state itself — only the kernel can. The single doorway from a process into
the kernel is the CPU's `syscall` instruction, and libc (glibc on Linux) is
the canonical wrapper around those syscalls: it knows the syscall numbers and
the register ABI per architecture, handles errno and EINTR restarts, and
gives each call a stable C name (socket/bind/recv/...). `external name
'socket'` simply links to that libc symbol. This is why nearly everything on
Linux — the FPC RTL, Python, Node, even the JVM — goes through libc.
Every networking stack is the same three layers; only the middle "shim"
differs by who wrote it:
protocol logic (HTTP/WS/crypto) - pure Pascal here, pure Java in the JDK,
pure Pascal in Indy/Synapse
socket abstraction - this unit; Indy's TIdStack; the JDK's
sun.nio.ch native methods
OS doorway - libc bindings (us, Indy, FPC, Synapse)
OR hand-written C in a VM (Java JNI ->
still libc/Winsock underneath)
kernel - syscalls (fixed; nobody reimplements it)
Java is NOT libc-free: java.net.Socket calls `native` methods implemented in
C inside the JVM, which call socket()/bind()/recv() in libc exactly as we do.
Its portability comes from writing that C glue once per OS, not from avoiding
libc. A truly "pure Pascal" version would replace libc's job with our own
per-arch `syscall` assembly stubs (the route Go takes) — that removes the
libc dependency but not the kernel boundary, and trades a stable cross-OS C
ABI for per-arch/per-OS assembly we would then maintain. For a single Linux
target it is a day or two of work; as a portability strategy it is MORE work
than the per-platform binding layer noted at the PORTING BOUNDARY below. }
unit Net.Sockets; unit Net.Sockets;