pub struct FlowHandle { /* private fields */ }Expand description
A reference to a spawned flow, returned by
super::runtime::Runtime::spawn.
Holding a FlowHandle does not keep the flow alive — it is a way to
(a) read its FlowId for addressing with Send, and (b) block the
calling native thread until it finishes via FlowHandle::join.
Dropping without joining is fine (fire-and-forget).
Implementations§
Source§impl FlowHandle
impl FlowHandle
pub fn id(&self) -> FlowId
Sourcepub fn join(self) -> FlowOutcome
pub fn join(self) -> FlowOutcome
Block the current (native) thread until the flow terminates. Never call from inside a worker / from bytecode.
Returns FlowOutcome::Failed rather than blocking forever if the
flow was destroyed without producing an outcome — most commonly
Runtime::shutdown while the flow was suspended, since shutdown does
not drain flows out of the timer or the worker deques. The message
comes from super::error::RuntimeError::Abandoned, so it is
distinguishable from a flow that genuinely faulted.
Examples found in repository?
22fn run_once(jit: bool) -> Result<(), Box<dyn std::error::Error>> {
23 let chunk = loop_chunk(1_000_000);
24 let rt = Runtime::with_config(
25 chunk,
26 RuntimeConfig {
27 workers: 1,
28 quantum: 50_000_000,
29 jit: JitConfig {
30 enabled: jit,
31 hot_threshold: 1,
32 },
33 ..Default::default()
34 },
35 )?;
36 let start = Instant::now();
37 let outcome = rt.spawn(0, &[])?.join();
38 let snapshot = rt.metrics();
39 rt.shutdown();
40 let elapsed = start.elapsed();
41 match outcome {
42 byteflow::FlowOutcome::Completed(Value::Int(n)) => {
43 println!("jit={jit} result={n} elapsed={elapsed:?} metrics={snapshot}");
44 Ok(())
45 }
46 other => Err(format!("unexpected outcome: {other:?}").into()),
47 }
48}More examples
9fn main() {
10 let chunk = samples::ping_pong();
11 let rt = match Runtime::with_natives_and_config(
12 chunk,
13 std_native_table(),
14 RuntimeConfig {
15 workers: 1,
16 quantum: 10_000,
17 mailbox: byteflow::MailboxConfig::DEFAULT,
18 ..Default::default()
19 },
20 ) {
21 Ok(rt) => rt,
22 Err(e) => {
23 eprintln!("runtime: {e}");
24 std::process::exit(1);
25 }
26 };
27 let Some(main) = rt.function_index("main") else {
28 eprintln!("missing main");
29 std::process::exit(1);
30 };
31 let handle = match rt.spawn(main, &[]) {
32 Ok(h) => h,
33 Err(e) => {
34 eprintln!("spawn: {e}");
35 std::process::exit(1);
36 }
37 };
38 let outcome = handle.join();
39 let metrics = rt.metrics();
40 rt.shutdown();
41
42 match outcome {
43 FlowOutcome::Completed(Value::Int(2)) => {
44 println!("pong replied 2 (Atomic Hop)");
45 println!("{metrics}");
46 }
47 other => {
48 eprintln!("unexpected {other:?}");
49 std::process::exit(1);
50 }
51 }
52}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 mailbox: byteflow::MailboxConfig::DEFAULT,
30 ..Default::default()
31 },
32 ) {
33 Ok(rt) => rt,
34 Err(e) => {
35 eprintln!("runtime: {e}");
36 std::process::exit(1);
37 }
38 };
39 let Some(main) = rt.function_index("main") else {
40 eprintln!("missing main");
41 std::process::exit(1);
42 };
43 let handle = match rt.spawn(main, &[]) {
44 Ok(h) => h,
45 Err(e) => {
46 eprintln!("spawn: {e}");
47 std::process::exit(1);
48 }
49 };
50 let outcome = handle.join();
51 let metrics = rt.metrics();
52 rt.shutdown();
53
54 match outcome {
55 FlowOutcome::Completed(Value::Int(42)) => {
56 println!("atomic request-reply ok: payload=42");
57 println!("{metrics}");
58 }
59 other => {
60 eprintln!("unexpected {other:?}");
61 std::process::exit(1);
62 }
63 }
64}20fn main() {
21 let n: u32 = match std::env::args().nth(1) {
22 Some(s) => match s.parse() {
23 Ok(v) => v,
24 Err(_) => 50_000,
25 },
26 None => 50_000,
27 };
28
29 let workers = match std::thread::available_parallelism() {
30 Ok(p) => p.get(),
31 Err(_) => 1,
32 };
33
34 let rt = match Runtime::with_config(
35 trivial_chunk(),
36 RuntimeConfig {
37 workers,
38 quantum: 10_000,
39 mailbox: byteflow::MailboxConfig::DEFAULT,
40 ..Default::default()
41 },
42 ) {
43 Ok(rt) => rt,
44 Err(e) => {
45 eprintln!("runtime: {e}");
46 std::process::exit(1);
47 }
48 };
49 let Some(worker_fn) = rt.function_index("worker") else {
50 eprintln!("missing worker");
51 std::process::exit(1);
52 };
53
54 let start = Instant::now();
55 let mut handles = Vec::with_capacity(n as usize);
56 for _ in 0..n {
57 match rt.spawn(worker_fn, &[]) {
58 Ok(h) => handles.push(h),
59 Err(e) => {
60 eprintln!("spawn: {e}");
61 std::process::exit(1);
62 }
63 }
64 }
65 let mut ok = 0u32;
66 for h in handles {
67 if matches!(h.join(), FlowOutcome::Completed(Value::Int(1))) {
68 ok += 1;
69 }
70 }
71 let elapsed = start.elapsed();
72 let metrics = rt.metrics();
73 rt.shutdown();
74
75 let secs = elapsed.as_secs_f64().max(1e-9);
76 println!("processes={ok}/{n}");
77 println!("workers={workers}");
78 println!("elapsed_ms={:.2}", elapsed.as_secs_f64() * 1000.0);
79 println!("spawns_per_sec={:.0}", ok as f64 / secs);
80 println!("{metrics}");
81}Sourcepub fn try_join(&self) -> Option<FlowOutcome>
pub fn try_join(&self) -> Option<FlowOutcome>
The outcome if the flow has already terminated, None if it is still
running. Never waits.
For embedders that drive their own loop and cannot surrender a thread
to FlowHandle::join. Takes &self, so it can be polled until it
answers; note that the outcome is handed out exactly once, and a
further poll after that reports
super::error::RuntimeError::AlreadyCollected as
FlowOutcome::Failed rather than repeating it.
Sourcepub fn join_timeout(&self, timeout: Duration) -> Option<FlowOutcome>
pub fn join_timeout(&self, timeout: Duration) -> Option<FlowOutcome>
Wait up to timeout for the flow to terminate; None if it is still
running when the bound elapses.
This is the variant to reach for in anything with a deadline — a control loop, a watchdog, a test harness — since it is the only join whose worst-case duration the caller chooses.
Sourcepub fn join_deadline(&self, deadline: Instant) -> Option<FlowOutcome>
pub fn join_deadline(&self, deadline: Instant) -> Option<FlowOutcome>
FlowHandle::join_timeout against an absolute deadline, for callers
that already track one and must not have it drift across repeated
waits.