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
//! Dynamic-linking (`Dl*`) synchronization for the WASIX [`super::Linker`].
//!
//! Instance groups run on different OS threads and share one [`super::LinkerState`] behind an
//! [`std::sync::RwLock`]. Operations that change “who exists” or “what every group must agree on”
//! must coordinate with both that lock and a stop-the-world style broadcast of concrete mutations
//! ([`DlOperation`]). This module holds the primitives that make that safe.
//!
//! # Locks and responsibilities
//!
//! - **Instance-group mutex** ([`lock_instance_group_state!`], `Linker::instance_group_state`):
//! Per-[`super::Linker`] handle to this thread’s [`InstanceGroupState`]. Many linker entry points
//! take it first so they can call into group-local state and, when needed, run cooperative DL
//! helpers with the right `Store` / [`FunctionEnv`].
//!
//! - **Topology lease** ([`LinkerShared`](LinkerShared) holds [`topology_lock::TopologyCoordinator`]
//! privately): A single-writer gate for *topology-changing* work (new instance groups, module loads,
//! export resolution that can allocate shared slots, etc.). The lease is acquired in a cooperative loop
//! with backoff and pending-DL cooperation (see [`LinkerShared::acquire_topology_token`],
//! [`LinkerShared::write_linker_state_with_topology`]). [`TopologyToken`] may move to another thread (spawn handoff).
//!
//! - **Shared linker state** (inside [`LinkerShared`], not exposed as a field): Global module
//! table, symbol records, and the buses used to broadcast [`DlOperation`] and barriers. Writers
//! must follow the cooperative patterns below—not raw lock calls.
//!
//! - **Pending-DL handshake** (`dl_operation_pending`, barriers, wakeup signals): While an
//! instigator runs [`LinkerShared::synchronize_link_operation`], follower threads must enter
//! [`Linker::do_pending_link_operations`] (or helpers) so everyone rendezvouses. That is why
//! contended access to [`LinkerState`] cannot spin blindly.
//!
//! # Lock ordering (intended)
//!
//! When topology applies: **topology token first**, then lock [`LinkerState`] for write via the APIs
//! in this module—not the inverse. Never try to acquire a topology lease from inside code that
//! already holds [`LinkerState`] for write without a deliberate, reviewed plan (easy deadlock).
//!
//! # Why you must never lock `LinkerState` directly
//!
//! **Do not call `linker_state.read()`, `write()`, or `try_write()` on [`Linker`]’s [`RwLock`] from
//! normal instance-group linker paths.** Doing so skips the cooperative path and can deadlock the
//! whole process: another thread may hold the write lock while waiting at a DL barrier for *this*
//! thread to execute [`LinkerShared::do_pending_link_operations_internal`], which requires the same group
//! context and cannot run if this thread is stuck in a naive blocking `write()`.
//!
//! Use instead:
//!
//! - [`LinkerShared::write_linker_state`] — `try_write` loop + [`LinkerStateWriteBackoff`] + pending-DL draining.
//! - [`LinkerShared::write_linker_state_with_topology`] — topology lease + draining + blocking write when
//! topology must be serialized before grabbing [`LinkerState`].
//! - [`LinkerShared::write_linker_state_blocking_holding_topology`] — blocking write only while already
//! holding [`TopologyToken`], after topology was leased on another thread/step.
//!
//! Narrow exceptions (e.g. one-off bootstrap in [`super::Linker::new`] before other groups exist)
//! belong in tightly scoped code and should still avoid contending paths that overlap DL sync.
pub
pub use LinkerShared;
pub use TopologyToken;
use Duration;
use ModuleHandle;
/// Spin, then yield, then capped exponential sleep — for cooperative linker-state retries.
pub
pub use lock_instance_group_state;
// Used to communicate the result of an operation that happened in one
// instance group to all others
pub