Skip to main content

set_forwarded_source

Function set_forwarded_source 

Source
pub fn set_forwarded_source(socket: OwnedFd, sink: ForwardedRecordSink)
Expand description

Registers the receiving end of the socket an in-guest Tool sends its records on (send_forwarded_record) and the sink that writes them to the run’s log.

THE ORDER RULE, one for every backend whose Tool runs in the guest. Each record carries its sending thread (tagged_forwarded_message). Before it handles any guest request the coordinator takes the records waiting on the socket into a queue per sending thread (drain_forwarded_at_request at the top of the request handler). A thread’s queued records are written into the log:

  • when that thread’s own request arrives, if it is the thread the scheduler last committed an ordinary turn to, or, before any turn, the thread whose request arrives first (the root; every other thread is created during some turn). That thread’s request is the event that ends the turn the schedule gave it, so the request arrives at a point the schedule fixes, and its records land where a single-process (ptrace) run writes them, ahead of everything handling that request writes. (A thread still in background IO from an earlier BACKGROUND turn may run at the same time; its records wait for its own COMMIT, by the next rule. Coordinator lines its continuation writes below INFO verbosity, such as TRACE, are not ordered by this rule; the INFO DETLOG and COMMIT lines that verification compares are.)
  • otherwise immediately before the scheduler commits that thread’s next turn (write_forwarded_for, at both kinds of COMMIT): a thread that runs without holding the turn (a forked child doing its setup, a thread in background IO) sends its records while other threads’ turns are being handled, so only its own next COMMIT is a point the schedule fixes;
  • and, for any still queued when the run ends, then, thread by thread in thread-id order.

If a second thread sends a request before the first turn, there is more than one root (a preload constructor that forked before the runtime was installed, say), and which root’s records come first would be the host’s choice. The coordinator then writes FORWARDING_AMBIGUOUS_ORDER_NOTICE, so verification refuses to compare the records rather than compare a host-timed order.

A send on a message socket queues the record on the receiver before it returns, so a guest thread’s records sent before a request are queued by the time that request is handled, and so before the turn it leads to is committed. Writing every thread’s records at any request’s arrival instead put a record wherever the host happened to deliver it: a forked child’s records, sent while its parent’s next request was being handled, landed before or after the scheduler’s records for that request depending on timing.

The records are TAKEN off the socket continuously, by a receiver task that queues each one for its thread as soon as it arrives, so a guest never blocks on a full socket buffer waiting for a request the coordinator would drain at: a single writev of 1024 buffers sends 1024 records before its thread’s next request. Taking a record only queues it; where it is written is still decided by the rule above. (A message without a sender tag is written as it is taken, which verification refuses to compare anyway: see ForwardedOrder::untagged.) The receiver is a task on the coordinator’s tokio runtime, started at the first request (start_forwarded_receiver), not a thread of its own: inside Hermit’s PID namespace a coordinator thread takes an ID from the same allocator as the guest’s tasks, and one more thread shifted every guest ID by one (the root became DetPid 4).