pub fn atomic_request_reply() -> ChunkExpand description
Atomic request-reply with crate::Value::Message (one envelope per hop).
Requires crate::std_native_table (make_msg / msg_* / print).
§Why “Atomic Hop”
Correlation (request_id) and reply routing (sender) travel in a
single mailbox value. Classic actor runtimes often allow any scalar
on Send; Byteflow rejects that — every hop is a typed envelope.
§Protocol
mainspawnsserver, buildsMessage { id=1, tag=REQ, payload=41 }(sender placeholder ignored)serverreceives, logs viaprint, repliestag=REP, payload=42viamsg_reply_cap(echoingrequest_id)mainreturns the reply payload42
§Register discipline (CallNative clobbers r[a])
CallNative ra, …, nc reads args from r[a..a+nc] and writes the
result into r[a]. Keeping the original Message in r0 therefore
means every unpack is Move ri, r0 then CallNative ri, msg_*, 1.
make_msg needs four contiguous arg registers; the server rearranges
into r4..r7 before the call.
Examples found in repository?
examples/atomic_actors.rs (line 22)
21fn main() {
22 let chunk = samples::atomic_request_reply();
23 let rt = match Runtime::with_natives_and_config(
24 chunk,
25 std_native_table(),
26 RuntimeConfig {
27 workers: 2,
28 quantum: 10_000,
29 },
30 ) {
31 Ok(rt) => rt,
32 Err(e) => {
33 eprintln!("runtime: {e}");
34 std::process::exit(1);
35 }
36 };
37 let Some(main) = rt.function_index("main") else {
38 eprintln!("missing main");
39 std::process::exit(1);
40 };
41 let handle = match rt.spawn(main, &[]) {
42 Ok(h) => h,
43 Err(e) => {
44 eprintln!("spawn: {e}");
45 std::process::exit(1);
46 }
47 };
48 let outcome = handle.join();
49 let metrics = rt.metrics();
50 rt.shutdown();
51
52 match outcome {
53 FlowOutcome::Completed(Value::Int(42)) => {
54 println!("atomic request-reply ok: payload=42");
55 println!("{metrics}");
56 }
57 other => {
58 eprintln!("unexpected {other:?}");
59 std::process::exit(1);
60 }
61 }
62}