Skip to main content

Module atomic_hop

Module atomic_hop 

Source
Expand description

Atomic Hop: Message-only Send, FlowCap addressing, Ask, selective receive.

§Atomic Hop (Byteflow core)

Handoff context for humans / other AIs working on this repo only (byteflow-actors). Hardware (byteflow-hw) was removed from the monorepo — do not restore it here.

§Units: flows

Byteflow’s concurrent unit is a flow (Flow, FlowId, FlowHandle, FlowOutcome) — not an “actor” API surface.

Wire identity still uses Value::Pid (FlowId as u64) inside messages. Addressing for bytecode Send / Ask uses Value::Cap (FlowCap, ABI v4). Scalars include Value::Str / Value::Bytes in the constant pool; hops remain Message-only.

§What “Atomic Hop” means

Every Send (bytecode or Runtime::send) must carry a full Message envelope:

Message { sender, reply_cap, request_id, tag, payload }
  • sender — authenticated origin FlowId (stamped by the worker)
  • reply_cap — SEND-only Cap back to the sender (minted at stamp time)
  • request_id — client correlation token (echoed on reply)
  • tag — protocol discriminator (opaque to the VM)
  • payload — small u64 body

Authenticated sender (security S1): bytecode Send / Ask overwrite Message.sender and attach reply_cap before delivery. The make_msg sender argument is untrusted metadata — see security.md.

FlowCap (security S6): bytecode Send / Ask targets must be Value::Cap. SelfPid / Spawn return Caps. Reply with msg_reply_cap, not msg_sender.

Bare scalars (Int, Pid, …) on Send → VM trap / SendError::NotAHop.
Mailbox park/push share one mutex → no lost-wakeup (scheduler/mailbox.rs).

§Selective receive (ReceiveMatch)

Receive takes the next hop. ReceiveMatch / ReceiveMatchImm wait for a hop whose Message.tag matches — earlier non-matching hops stay in the mailbox (FIFO skip, never drop). A parked selective waiter is woken only by a matching hop; junk is queued behind the same lock.

OpcodeForm
ReceiveMatch 0x53ra, rb — tag from r[b] (Int in 0..=u16::MAX)
ReceiveMatchImm 0x54ra, imm — immediate tag

Sample: samples::selective_receive (TAG_JUNK then TAG_REQ).

§Ask — atomic RPC hop (0x55)

Ask ra, rb, rc delivers r[c] (Message) to r[b] (Cap), then parks the caller until a reply matches:

reply.request_id == request.request_id
&& reply.sender  == resolved_FlowId(target_cap)

Implemented via mailbox WaitFilter::Correlation { expect_request_id, expect_sender: Some(flow_id) }. FIFO skip applies: unrelated hops (wrong id or wrong sender) stay queued.

No AskTimeout in this revision. Sample: samples::ask_reply.

That is the deliberate difference vs classic actor runtimes that allow any value on send.

§Make / unpack (std natives)

Stable indices in natives.rs:

IdxNameArgs → result
0printvalues… → Unit (flow-visible log)
1now_msInt
2make_msgsender, request_id, tag, payload → Message
3msg_sendermsg → Pid (identity)
4msg_request_idmsg → Int
5msg_tagmsg → Int
6msg_payloadmsg → Int
7msg_reply_capmsg → Cap (SEND grant)

Runtime must use Runtime::with_natives(chunk, std_native_table()) (or with_natives_and_config).

§Sample + refresh

# unit + sample tests
cargo test -p byteflow-actors

# demos
cargo run -p byteflow-actors --example ping_pong
cargo run -p byteflow-actors --example atomic_actors

# Windows PowerShell — scheduler logs
$env:BYTEFLOW_LOG="info"
cargo run -p byteflow-actors --example atomic_actors

§Hard rules (do not regress)

  1. Opcodes are append-only — never renumber.
  2. Std natives 0–6 frozen; 7 is msg_reply_cap (append-only thereafter).
  3. Fail-closed: no PoisonError::into_inner(); mutex helpers → Result.
  4. Preserve long design comments (mailbox, directory, oneshot, timer, sync_lock).
  5. Clippy: unwrap_used + expect_used = deny (tests may allow).