1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! Spawn facility trait — how BIFs request process creation.
//!
//! The spawn facility is an abstraction that allows process creation BIFs
//! (spawn/3, spawn_link/3) to request new processes without direct access
//! to the scheduler. The scheduler provides an implementation that uses its
//! internal spawn machinery; tests can provide mock implementations.
use std::fmt;
use crate::atom::Atom;
use crate::native::CapabilitySet;
use crate::process::Priority;
use crate::term::Term;
/// Error returned when a spawn request fails.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SpawnError {
/// The requested module/function/arity could not be resolved.
UnresolvedMfa,
}
impl fmt::Display for SpawnError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnresolvedMfa => f.write_str("unresolved module/function/arity for spawn"),
}
}
}
impl std::error::Error for SpawnError {}
/// Trait for requesting process creation from BIFs.
///
/// Implementations are provided by the scheduler (or test mocks) and injected
/// into [`super::ProcessContext`] before BIF execution.
pub trait SpawnFacility: Send + Sync {
/// Request creation of a new process at the given Module:Function entry
/// point with the supplied arguments.
///
/// If `link_to` is `Some(parent_pid)`, a bidirectional link between the
/// parent and child is established atomically before the child starts.
///
/// Returns the new process PID on success, or an error when the
/// module/function cannot be resolved or process creation fails.
fn spawn(
&self,
caller_pid: u64,
module: Atom,
function: Atom,
args: Vec<Term>,
link_to: Option<u64>,
) -> Result<u64, SpawnError>;
/// Request creation of a new process and atomically establish a monitor
/// from `caller_pid` to the child before the child can execute.
fn spawn_monitor(
&self,
caller_pid: u64,
module: Atom,
function: Atom,
args: Vec<Term>,
) -> Result<SpawnMonitorResult, SpawnError>;
/// Spawn a process from a lambda (FunT entry) by module and lambda index.
///
/// The scheduler looks up the module's lambda table to find the entry
/// label and starts the child process there. Used by `erlang:spawn/1`
/// and `erlang:spawn_link/1` which receive a closure term.
fn spawn_lambda(
&self,
caller_pid: u64,
module: Atom,
lambda_index: u32,
link_to: Option<u64>,
) -> Result<u64, SpawnError>;
/// Spawn a process from a lambda and atomically establish a monitor from
/// `caller_pid` to the child before the child can execute.
fn spawn_lambda_monitor(
&self,
caller_pid: u64,
module: Atom,
lambda_index: u32,
) -> Result<SpawnMonitorResult, SpawnError>;
/// Request creation of a new process with spawn options applied atomically
/// before the child can execute.
fn spawn_with_options(
&self,
caller_pid: u64,
module: Atom,
function: Atom,
args: Vec<Term>,
options: SpawnOptions,
) -> Result<SpawnOptionsResult, SpawnError>;
/// Spawn a process from a lambda with spawn options applied atomically
/// before the child can execute.
fn spawn_lambda_with_options(
&self,
caller_pid: u64,
module: Atom,
lambda_index: u32,
options: SpawnOptions,
) -> Result<SpawnOptionsResult, SpawnError>;
}
/// Options accepted by `erlang:spawn_opt/2,4`.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct SpawnOptions {
pub link: bool,
pub monitor: bool,
pub priority: Option<Priority>,
pub min_heap_size: Option<usize>,
pub capabilities: Option<CapabilitySet>,
}
/// Successful spawn result that may include a monitor reference.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct SpawnOptionsResult {
pub pid: u64,
pub reference: Option<u64>,
}
/// Successful atomic spawn-monitor result.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct SpawnMonitorResult {
/// PID assigned to the child process.
pub pid: u64,
/// Monitor reference owned by the caller.
pub reference: u64,
}
/// Record of a spawn request, used by test mocks to verify BIF behavior.
#[derive(Clone, Debug)]
pub struct SpawnRecord {
/// Calling process PID requesting the spawn.
pub caller_pid: u64,
/// Module atom for the entry point.
pub module: Atom,
/// Function atom for the entry point.
pub function: Atom,
/// Arguments to pass to the new process.
pub args: Vec<Term>,
/// Parent PID to link to, if spawn_link was used.
pub link_to: Option<u64>,
}