turnloop 0.1.0-alpha.9

An embeddable event-loop driver, turned by its host
Documentation
# Windows IOCP backend

One completion port belongs to each loop. Normal turns have no driver thread.
Requests use core generational IDs, per-direction FIFO queues, fixed operation
storage and a separate pinned kernel slab. Cancellation retains buffers, accepted
sockets and OVERLAPPED storage until acknowledgement; destruction drains pending
I/O before releasing caller memory.

- TCP uses provider-specific AcceptEx/ConnectEx and overlapped receive/send.
  Pooled idle reads arm zero-byte receives, then acquire a lease at readiness.
  Supported IFS handles skip success packets; other providers keep normal IOCP
  notifications. UDP preserves datagram boundaries and source addresses.
- Timed waits use high-resolution waitable timers and dynamically resolved NT
  completion packets. Missing NT exports return an error. GQCSEx is nonalertable
  and has no competing coarse timeout when an exact timer is armed. No global
  timer-period change is made. See the measured decision in
  [WINDOWS_RESULTS.md]../../../../../spikes/iocp/WINDOWS_RESULTS.md.
- Named pipes reserve `max(backlog, 1)` overlapped, byte-mode, local-only server
  instances at listen, and replenish their fixed slots on accept. Kernel storage
  stays separate from mutable metadata. Listener teardown cancels and joins private
  connects; never-reused port keys discard any packets arriving after release.
  Busy client opens submit NPFS `FSCTL_PIPE_WAIT` through `NtFsControlFile`, the overlapped
  availability operation underlying `WaitNamedPipeW`; no worker or retry tick is
  used. `pipe_connect_until` sets an absolute connection deadline. Expiry closes
  out the pending wait through normal cancellation acknowledgement before TimedOut;
  ordinary `pipe_connect` remains unbounded and cancellable.
- Socket passing is
  a private cooperative control protocol: WSADuplicateSocket targets the actual
  peer PID queried from the OS, and send acceptance retains independent socket
  ownership. Do not mix payload reads with receive-handle operations on a control
  stream. This is not a general-purpose untrusted wire protocol.
- IOCP association survives duplication. Known same-port accepted/reattached pipes
  use native IOCP directly; unassociated imports join the receiving port. Only a
  foreign association needs the event bridge. Quiescent transferred sockets and pipe
  endpoints suppress their old port's notifications with the OVERLAPPED event's
  low bit; one-shot registered event waits forward completion to the new port.
  Callbacks are joined before slot reuse. Named-pipe listeners and pending busy
  connections cannot be detached;
  socket listeners and connected pipe endpoints can. Reuse-port is unsupported.
- Child creation uses an explicit inherited handle list, correctly quoted argv,
  Unicode environment and suspended creation. `windows_hide` defaults false;
  true selects SW_HIDE and, without inherited stdio, CREATE_NO_WINDOW, as in libuv.
  Non-detached children join a process-lifetime, non-inheritable kill-on-close job
  with silent breakaway. Optional tree-control jobs (`new_process_group` or
  `detached`) have no kill-on-close limit: normal leader exit/release preserves
  grandchildren. `detached` also selects DETACHED_PROCESS/CREATE_NEW_PROCESS_GROUP
  and excludes the child from the lifetime job. Host jobs can restrict breakaway,
  as with libuv; access denied on lifetime assignment is tolerated. Parent stdio
  ends are overlapped; child ends are synchronous.
  Direct `.bat`/`.cmd` programs are rejected with InvalidInput, including PATH
  resolution; callers must explicitly select a shell for batch scripts.
  One-shot process waits publish to the owning notifier. Close terminates live
  owned children, including detached ones, then acknowledges exit before Closed.
  Loop Drop retains the same explicit ownership rule. uid/gid and non-Kill
  process signals return Unsupported; console signal subscriptions are separate.
- Standard streams are duplicated, preserving the host originals. Synchronous
  handles reserve two workers at adoption, one per direction. Reads and writes
  have independent FIFOs and cancellation state. Workers reuse the console
  classification captured at quiescent adoption; no worker allocation or
  classification syscall is needed per operation.
  Windows serializes all I/O on one synchronous file object: WriteFile on the
  same pipe endpoint waits, with no kernel request of its own and so beyond
  CancelSynchronousIo, until an idle ReadFile returns. For synchronous pipes the
  write worker therefore preempts an idle read with CancelSynchronousIo, and the
  read worker reissues the same request after the write. A cancelled pipe read
  has consumed no bytes, so read FIFO order, exactly-once completion and data are
  preserved, and duplex writes progress before the peer replies. A write waiting
  for the peer to drain its buffer still owns the object: reads on that endpoint
  complete only after it finishes or is cancelled. Writes are never preempted,
  because a cancelled partially consumed pipe write reports zero bytes.
  ReOpenFile cannot obtain an independent overlapped object for pipe ends
  (ERROR_PIPE_BUSY), and FSCTL_PIPE_ASSIGN_EVENT is not supported. libuv's
  non-overlapped pipes are subject to the same kernel constraint.
  Anonymous pipe ends are one-way; console input and screen output use separate
  native objects and are not preempted. Synchronous regular files preserve their
  shared file position, with no cross-direction ordering promise; reads at EOF
  complete rather than waiting for appended data. Other synchronous character
  devices use ReadFile/WriteFile with their native driver's serialization and
  cancellation semantics, without preemption. NUL exercises the ordinary
  character-device path; serial-port and third-party hardware-driver runtime
  behavior is not covered by that test.
  See [sem-fix2 evidence]../../../../../docs/lanes/iocp-semantics.md#sem-fix2.
  Imported handles are classified by native file mode before submission;
  overlapped pipes join this IOCP or route an existing foreign association, and
  other overlapped files are unsupported.
  One process-wide cancellation helper handles the race before a worker enters
  ReadFile, without blocking turns or polling idle handles. A cancellation that
  reports success just after kernel entry can be lost; the helper and the write
  preemption re-issue it after at most 10 ms while the worker is still inside I/O. Console input workers
  translate key records to UTF-8 and dispatch resize records to WinCh subscribers.
  Resize delivery requires the console input reader to be active. Captured console
  modes are restored on close/drop; input and output VT modes are distinguished.
- Console handlers fan out Int/Break/Hup to independent loop subscriptions.
  Unsupported Unix signals fail explicitly. The console handler is installed
  with the first subscription and never removed, as in libuv:
  SetConsoleCtrlHandler blocks while any control handler runs, so removing it
  beside a held close handler would deadlock the host's cleanup. Without a
  matching subscription it returns FALSE, as if absent. Unsubscribed CTRL_CLOSE
  therefore preserves older host handlers. With Hup subscribed, dispatch completes
  before Sleep(INFINITE), matching libuv's cleanup window (Windows normally
  terminates after about five seconds). Sleeping retains no subscription access.
  Windows invokes handlers newest first; this subscribed case prevents older host
  handlers from running. Windows provides no supported way to both continue that
  chain and retain the handler thread. Hup remains best-effort; the host must turn
  and exit within the OS budget. See [the decision and real-close tests]../../../../../docs/lanes/iocp-semantics.md.
- Integration::Event opts into a sole-consumer helper with a bounded queue and
  an auto-reset event. Timer resets carry generations so old forwarded packets
  cannot complete a later deadline. Hosts call turn(Now) after their GUI wait.
  A pump error remains observable on every subsequent turn/integration call,
  including turns with core completions already queued. Drop joins the helper,
  recovers retained packets and drains cancellation using blocking IOCP waits.
  An unrecoverable port/teardown error aborts, preserving buffer safety without spin.
- Core provides timer semantics, liveness, pooled buffers, the parking handshake,
  shared blocking jobs, external waits and executor behavior.

The tests in `turnloop-contract/tests/windows*.rs` instantiate the same shared
scenarios as Unix, plus native console, GUI, cancellation and handle-count checks.
The shared allocation and executor suites also execute on Windows. Run contracts
serially because console state, process resources and allocation counters are
part of the subjects being measured.