weida-runtime 0.1.0-alpha.1

Reactor ownership, the task/timer/DNS surface and the local-transport OS hygiene shared by weida and the standalone protocol libraries
Documentation

The reactor, the resolver and the OS plumbing a messaging library needs, with no protocol in it.

This crate is the answer to one question asked twice: where does the async runtime live? A library that opens sockets needs tasks, timers and name resolution, and its users must not have to be standing in a reactor to call it. Everything here exists so that a library can own that reactor instead of demanding one, and so that the same discipline does not have to be written twice for two protocols (decisions/0013 §4.2).

Nothing in this crate knows a wire format. It depends on weida-core for one error vocabulary and on tokio for the reactor, and on nothing else; it MUST NOT depend on weida-protocol or weida. A consumer with no weida in the picture — a ZeroMQ or nanomsg implementation, say — is the case every doc comment here is written for.

What is here

  • [Exec] — the whole surface onto the async runtime: [Exec::spawn], [Exec::sleep], [Exec::within], [Exec::enter] and [Exec::resolve]. A consumer holds one and never touches tokio directly, which is what makes "our library works on any executor" a checkable claim rather than an intention.
  • Three ways to get one, differing only in where the reactor comes from: [Exec::current] borrows the ambient reactor, [Exec::from_handle] takes a handle to somebody else's, and [Exec::owned] creates one and hands back the [OwnedReactor] that owns it — with the background-shutdown discipline that dropping a reactor from inside its own worker thread requires.
  • [CloseBudget] — a finite budget spanning the phases of a shutdown, so that no close waits on a peer's behaviour forever. ZeroMQ's ZMQ_LINGER defaults to infinite and zmq_ctx_term() can therefore block for as long as a peer likes; this type is what a finite default is built from.
  • [NameRegistry] — a process- or context-scoped namespace of bound names with a byte budget, generic over what a bound name hands its acceptor. weida's weida+inproc:// buses and ZeroMQ's inproc:// endpoints are the same object under two names.
  • On unix: BoundUnixSocket, which binds an AF_UNIX socket with the hygiene a filesystem endpoint needs — socket-type check, unlink before bind, an explicit mode rather than whatever umask allowed, a sun_path budget, and removal of the node on drop — and peer_credentials, the kernel's answer to who is on the other end (decisions/0010 §4.5).
  • On Windows: BoundPipe, which creates the instances of a named pipe with an owner-only DACL, local clients only and the first-instance flag, connect_pipe, which opens the client end and waits out ERROR_PIPE_BUSY, and client_principal / server_principal, the kernel's answer to the same question in SID form (0010 §4.4, §4.5).

The grep

Exec is the enforcement point, and the enforcement is mechanical. Two greps, one per crate:

grep -rn 'tokio::spawn\|tokio::time\|lookup_host' crates/weida/src   # no call outside #[cfg(test)]
grep -rn 'tokio::spawn\|tokio::time\|lookup_host' crates/runtime/src # only exec.rs calls them

Every task, timer and name lookup in weida goes through an Exec from this crate, and the same grep over a consumer's own src is the same check for it (docs/ARCHITECTURE.md §5). The one place those three names are allowed to appear is [Exec]'s own module, which is the entire point of moving them here.