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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
//! Core data structures for the reactive runtime.
//!
//! The runtime is a single thread-local `ReactiveRuntime` holding two
//! generational slot maps (`owners` and `nodes`) plus a bit of
//! transient bookkeeping for the currently-running effect and the
//! pending-effects queue.
//!
//! All public reactive primitives (`ReadSignal`, `WriteSignal`,
//! `RwSignal`) are `Copy` newtypes around a `NodeId`. They look their
//! value up through the runtime on every operation. Cloning a handle
//! is just an integer copy; the lifetime of the underlying state is
//! bounded by the owning [`Scope`] (looked up via its [`Owner`] handle),
//! not by the handle. `computed()` returns a `ReadSignal<T>` that happens
//! to be backed by a `NodeData::Computed` node — externally
//! indistinguishable from a primitive signal.
//!
//! This module defines the types only. The thread-local instance and
//! the orchestration logic live in `mod.rs` and the sibling files.
use ;
use RefCell;
use ;
use Rc;
use ;
new_key_type!
/// Kind discriminator for [`ReactiveNode`]. Carried separately from
/// [`NodeData`] so dependency-graph walks can branch on kind without
/// matching the data variant (the variants carry mutable state that we
/// generally don't want to touch during graph walks).
/// The mutable payload of a [`ReactiveNode`]. Signals carry a value,
/// effects carry a compute closure, computed values carry both.
///
/// The compute closure is wrapped in `Rc<RefCell<…>>` so the
/// scheduler can grab a clone of the handle before invoking it,
/// keeping the runtime borrow short-lived. User code inside the
/// closure can then re-borrow the runtime freely.
/// One node in the reactive graph.
///
/// `sources` records what this node read in its last run (downstream
/// dependencies); `subscribers` records who reads us. Both sets are
/// kept in sync by the effect/computed runner — on each re-run, the runner
/// re-derives `sources` by tracking signal reads during the closure,
/// then sets `subscribers` on the new sources symmetrically.
///
/// `arc_sources` is the equivalent for `ArcSignal`-family reads: Arc-
/// backed signals don't live in the arena, so they can't be referenced
/// by `NodeId`. Instead, each Arc signal whose value this node read
/// hands the node a `Rc<dyn ArcSubscription>` clone of its inner; on
/// re-run / disposal, the node iterates this list to tell each signal
/// "drop me from your subscriber list" via [`ArcSubscription::unsubscribe`].
/// The signal itself stays alive (Arc refcount) regardless.
/// Cleanup interface that Arc-backed signals expose so the scheduler
/// and owner-disposal code can detach a node from a signal's subscriber
/// list without knowing the signal's concrete type.
///
/// The signal owns a `Vec<NodeId>` of subscribers; when a node either
/// (a) re-runs and rebuilds its source set or (b) gets disposed with
/// its owner, the runtime walks the node's `arc_sources` and calls
/// `unsubscribe(node_id)` on each so the signal's bookkeeping stays in
/// sync. The signal's value lives on as long as someone holds an Arc
/// to it — disposal here only severs the back-reference.
/// A scope record in the reactive tree. Created when a component
/// mounts, disposed when the component unmounts. Tracks the reactive
/// nodes allocated inside it (so they can be freed on disposal) and
/// the child scopes (so disposal cascades).
///
/// The public-facing API surface is the [`super::owner::Owner`]
/// handle (a `Copy` slotmap key); `Scope` is the data record that
/// handle dereferences to via the runtime's `owners` slotmap. Users
/// (and even framework extension authors) never name `Scope`
/// directly.
///
/// `contexts` is the per-scope context bag for `provide_context` /
/// `use_context`. `cleanups` is the LIFO callback queue from
/// `on_cleanup`.
/// The reactive runtime itself. One per thread (held in a
/// `thread_local!` slot in `mod.rs`).
///
/// All public reactive operations route through here. The pattern is
/// always:
///
/// 1. Open a short `with_borrow_mut` to read or mutate `owners` /
/// `nodes` / `current_*`.
/// 2. If user code needs to run (effect / computed closure), drop the
/// borrow first by cloning the necessary handles out, then call
/// the closure.
/// 3. Re-open a short borrow to restore book-keeping.
///
/// This keeps the `RefCell` borrow window narrow enough that user code
/// running inside a closure can re-enter the runtime (read signals,
/// write signals, register new effects) without panicking.