@trioforge/crucible 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/client/package.json +4 -0
- package/emit/package.json +4 -0
- package/out/all.d.ts +7 -0
- package/out/all.luau +2 -0
- package/out/attribute.d.ts +7 -0
- package/out/attribute.luau +7 -0
- package/out/client/index.d.ts +10 -0
- package/out/client/init.luau +55 -0
- package/out/configs.d.ts +37 -0
- package/out/configs.luau +46 -0
- package/out/crucible-behaviour.d.ts +16 -0
- package/out/crucible-behaviour.luau +78 -0
- package/out/crucible-object.d.ts +10 -0
- package/out/crucible-object.luau +69 -0
- package/out/data.d.ts +224 -0
- package/out/data.luau +97 -0
- package/out/emit/configs.d.ts +4 -0
- package/out/emit/configs.luau +22 -0
- package/out/emit/data.d.ts +6 -0
- package/out/emit/data.luau +19 -0
- package/out/emit/index.d.ts +45 -0
- package/out/emit/init.luau +101 -0
- package/out/emit/network.d.ts +6 -0
- package/out/emit/network.luau +36 -0
- package/out/emit/replicated.d.ts +4 -0
- package/out/emit/replicated.luau +13 -0
- package/out/index.d.ts +20 -0
- package/out/init.luau +34 -0
- package/out/inject.d.ts +1 -0
- package/out/inject.luau +7 -0
- package/out/internal/binding.d.ts +1 -0
- package/out/internal/binding.luau +615 -0
- package/out/internal/boot.d.ts +1 -0
- package/out/internal/boot.luau +562 -0
- package/out/internal/configs/limits.d.ts +10 -0
- package/out/internal/configs/limits.luau +25 -0
- package/out/internal/configs/port.d.ts +29 -0
- package/out/internal/configs/port.luau +99 -0
- package/out/internal/configs/runtime.d.ts +21 -0
- package/out/internal/configs/runtime.luau +981 -0
- package/out/internal/construction-claim.d.ts +1 -0
- package/out/internal/construction-claim.luau +26 -0
- package/out/internal/data/admission.d.ts +1 -0
- package/out/internal/data/admission.luau +673 -0
- package/out/internal/data/declarations.d.ts +1 -0
- package/out/internal/data/declarations.luau +384 -0
- package/out/internal/data/envelope.d.ts +1 -0
- package/out/internal/data/envelope.luau +318 -0
- package/out/internal/data/handles.d.ts +1 -0
- package/out/internal/data/handles.luau +255 -0
- package/out/internal/data/inbox-transforms.d.ts +1 -0
- package/out/internal/data/inbox-transforms.luau +458 -0
- package/out/internal/data/inbox.d.ts +1 -0
- package/out/internal/data/inbox.luau +437 -0
- package/out/internal/data/lease.d.ts +1 -0
- package/out/internal/data/lease.luau +304 -0
- package/out/internal/data/limits.d.ts +1 -0
- package/out/internal/data/limits.luau +234 -0
- package/out/internal/data/messages.d.ts +1 -0
- package/out/internal/data/messages.luau +69 -0
- package/out/internal/data/operation-id.d.ts +1 -0
- package/out/internal/data/operation-id.luau +61 -0
- package/out/internal/data/paths.d.ts +2 -0
- package/out/internal/data/paths.luau +323 -0
- package/out/internal/data/port.d.ts +1 -0
- package/out/internal/data/port.luau +290 -0
- package/out/internal/data/profiles.d.ts +1 -0
- package/out/internal/data/profiles.luau +862 -0
- package/out/internal/data/receipts.d.ts +1 -0
- package/out/internal/data/receipts.luau +190 -0
- package/out/internal/data/record-bindings.d.ts +1 -0
- package/out/internal/data/record-bindings.luau +92 -0
- package/out/internal/data/record-declarations.d.ts +1 -0
- package/out/internal/data/record-declarations.luau +140 -0
- package/out/internal/data/records.d.ts +1 -0
- package/out/internal/data/records.luau +400 -0
- package/out/internal/data/runtime.d.ts +1 -0
- package/out/internal/data/runtime.luau +918 -0
- package/out/internal/data/scheduler.d.ts +1 -0
- package/out/internal/data/scheduler.luau +507 -0
- package/out/internal/data/session.d.ts +1 -0
- package/out/internal/data/session.luau +234 -0
- package/out/internal/data/shutdown.d.ts +1 -0
- package/out/internal/data/shutdown.luau +91 -0
- package/out/internal/data/waiters.d.ts +1 -0
- package/out/internal/data/waiters.luau +65 -0
- package/out/internal/dense-list.d.ts +1 -0
- package/out/internal/dense-list.luau +155 -0
- package/out/internal/diagnostics.d.ts +1 -0
- package/out/internal/diagnostics.luau +29 -0
- package/out/internal/hooks.d.ts +1 -0
- package/out/internal/hooks.luau +53 -0
- package/out/internal/implementers.d.ts +1 -0
- package/out/internal/implementers.luau +119 -0
- package/out/internal/lifecycle.d.ts +1 -0
- package/out/internal/lifecycle.luau +912 -0
- package/out/internal/log-output.d.ts +1 -0
- package/out/internal/log-output.luau +106 -0
- package/out/internal/network/catalog.d.ts +26 -0
- package/out/internal/network/catalog.luau +182 -0
- package/out/internal/network/claims.d.ts +1 -0
- package/out/internal/network/claims.luau +154 -0
- package/out/internal/network/client-session.d.ts +1 -0
- package/out/internal/network/client-session.luau +403 -0
- package/out/internal/network/codec.d.ts +63 -0
- package/out/internal/network/codec.luau +1183 -0
- package/out/internal/network/envelope.d.ts +1 -0
- package/out/internal/network/envelope.luau +247 -0
- package/out/internal/network/limits.d.ts +26 -0
- package/out/internal/network/limits.luau +55 -0
- package/out/internal/network/port.d.ts +1 -0
- package/out/internal/network/port.luau +19 -0
- package/out/internal/network/roblox-network-port.d.ts +1 -0
- package/out/internal/network/roblox-network-port.luau +105 -0
- package/out/internal/network/schema.d.ts +72 -0
- package/out/internal/network/schema.luau +386 -0
- package/out/internal/network/server-session.d.ts +1 -0
- package/out/internal/network/server-session.luau +393 -0
- package/out/internal/network/session.d.ts +1 -0
- package/out/internal/network/session.luau +266 -0
- package/out/internal/port.d.ts +1 -0
- package/out/internal/port.luau +12 -0
- package/out/internal/reactive/context.d.ts +1 -0
- package/out/internal/reactive/context.luau +149 -0
- package/out/internal/reactive/flush.d.ts +1 -0
- package/out/internal/reactive/flush.luau +355 -0
- package/out/internal/reactive/graph.d.ts +1 -0
- package/out/internal/reactive/graph.luau +626 -0
- package/out/internal/reactive/limits.d.ts +1 -0
- package/out/internal/reactive/limits.luau +54 -0
- package/out/internal/reactive/ownership.d.ts +1 -0
- package/out/internal/reactive/ownership.luau +626 -0
- package/out/internal/registry.d.ts +1 -0
- package/out/internal/registry.luau +455 -0
- package/out/internal/roblox-port.d.ts +1 -0
- package/out/internal/roblox-port.luau +117 -0
- package/out/internal/runner.d.ts +1 -0
- package/out/internal/runner.luau +86 -0
- package/out/internal/scheduler.d.ts +1 -0
- package/out/internal/scheduler.luau +207 -0
- package/out/internal/state.d.ts +1 -0
- package/out/internal/state.luau +41 -0
- package/out/internal/sync/client-sync.d.ts +1 -0
- package/out/internal/sync/client-sync.luau +157 -0
- package/out/internal/sync/declarations.d.ts +1 -0
- package/out/internal/sync/declarations.luau +276 -0
- package/out/internal/sync/frame.d.ts +1 -0
- package/out/internal/sync/frame.luau +91 -0
- package/out/internal/sync/server-sync.d.ts +1 -0
- package/out/internal/sync/server-sync.luau +502 -0
- package/out/internal/sync/values.d.ts +1 -0
- package/out/internal/sync/values.luau +930 -0
- package/out/log-level.d.ts +7 -0
- package/out/log-level.luau +21 -0
- package/out/logging.d.ts +34 -0
- package/out/logging.luau +233 -0
- package/out/network.d.ts +66 -0
- package/out/network.luau +24 -0
- package/out/reactive.d.ts +76 -0
- package/out/reactive.luau +136 -0
- package/out/replicated.d.ts +38 -0
- package/out/replicated.luau +45 -0
- package/out/server/data.d.ts +54 -0
- package/out/server/data.luau +44 -0
- package/out/server/index.d.ts +18 -0
- package/out/server/init.luau +62 -0
- package/out/server/publish.d.ts +20 -0
- package/out/server/publish.luau +24 -0
- package/out/subsystem.d.ts +4 -0
- package/out/subsystem.luau +30 -0
- package/out/vide/index.d.ts +7 -0
- package/out/vide/init.luau +50 -0
- package/package.json +52 -5
- package/server/package.json +4 -0
- package/transformer/data.cjs +72 -0
- package/transformer/declarations.cjs +153 -0
- package/transformer/diagnostics.cjs +19 -0
- package/transformer/index.js +741 -0
- package/transformer/network.cjs +961 -0
- package/transformer/type-id.cjs +80 -0
- package/vide/package.json +4 -0
- package/README.md +0 -3
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _runner = TS.import(script, script.Parent, "runner")
|
|
4
|
+
local call = _runner.call
|
|
5
|
+
local disown = _runner.disown
|
|
6
|
+
local own = _runner.own
|
|
7
|
+
local ownerOf = _runner.ownerOf
|
|
8
|
+
--[[
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
* Pure transition function. The drain calls it until it returns undefined. It reads only the current state,
|
|
12
|
+
* so re-running it after a request converges on the same end state, and asking twice for the state an object
|
|
13
|
+
* already has finds no step.
|
|
14
|
+
|
|
15
|
+
]]
|
|
16
|
+
local function nextStep(s)
|
|
17
|
+
local ending = s.fault ~= nil or s.want == 2
|
|
18
|
+
local _exp = s.phase
|
|
19
|
+
repeat
|
|
20
|
+
if _exp == 0 then
|
|
21
|
+
if ending then
|
|
22
|
+
return 10
|
|
23
|
+
end
|
|
24
|
+
return if s.gate >= 1 then 0 else nil
|
|
25
|
+
end
|
|
26
|
+
if _exp == 1 then
|
|
27
|
+
if ending then
|
|
28
|
+
return 10
|
|
29
|
+
end
|
|
30
|
+
return if s.gate >= 2 then 1 else nil
|
|
31
|
+
end
|
|
32
|
+
if _exp == 2 then
|
|
33
|
+
if ending then
|
|
34
|
+
return 9
|
|
35
|
+
end
|
|
36
|
+
if s.gate < 3 then
|
|
37
|
+
return nil
|
|
38
|
+
end
|
|
39
|
+
return if s.want == 0 then 2 else 3
|
|
40
|
+
end
|
|
41
|
+
if _exp == 3 then
|
|
42
|
+
if s.fault ~= nil or s.want ~= 0 then
|
|
43
|
+
return if s.joined or s.ticking then 7 else 8
|
|
44
|
+
end
|
|
45
|
+
if not s.joined then
|
|
46
|
+
return 4
|
|
47
|
+
end
|
|
48
|
+
if s.start == 0 then
|
|
49
|
+
return if s.gate >= 4 then 5 else nil
|
|
50
|
+
end
|
|
51
|
+
return if s.start == 2 and not s.ticking then 6 else nil
|
|
52
|
+
end
|
|
53
|
+
if _exp == 4 then
|
|
54
|
+
if ending then
|
|
55
|
+
return 9
|
|
56
|
+
end
|
|
57
|
+
return if s.want == 0 then 2 else nil
|
|
58
|
+
end
|
|
59
|
+
until true
|
|
60
|
+
return nil
|
|
61
|
+
end
|
|
62
|
+
local installed
|
|
63
|
+
local states = {}
|
|
64
|
+
--* @internal
|
|
65
|
+
local function useEffects(effects)
|
|
66
|
+
installed = effects
|
|
67
|
+
end
|
|
68
|
+
local function effects()
|
|
69
|
+
return installed or error("Crucible lifecycle used before boot")
|
|
70
|
+
end
|
|
71
|
+
--* @internal
|
|
72
|
+
local function stateOf(object)
|
|
73
|
+
local _object = object
|
|
74
|
+
return states[_object]
|
|
75
|
+
end
|
|
76
|
+
local enablingNow
|
|
77
|
+
--* @internal The generation whose Enable step is running. What it creates belongs to its enable interval.
|
|
78
|
+
local function enabling()
|
|
79
|
+
return enablingNow
|
|
80
|
+
end
|
|
81
|
+
--* @internal Awake, not faulted, and not asked to go. `get` and queries see only live objects.
|
|
82
|
+
local function isLive(s)
|
|
83
|
+
return s.fault == nil and s.want ~= 2 and (s.phase == 2 or s.phase == 3 or s.phase == 4)
|
|
84
|
+
end
|
|
85
|
+
--* @internal Faulted or Destroyed.
|
|
86
|
+
local function hasEnded(s)
|
|
87
|
+
return s.phase == 5 or s.phase == 6
|
|
88
|
+
end
|
|
89
|
+
--* @internal A new generation, before its constructor runs. `admit` gives it a batch.
|
|
90
|
+
local function track(record, instance, attributes)
|
|
91
|
+
return {
|
|
92
|
+
work = 0,
|
|
93
|
+
phase = 0,
|
|
94
|
+
start = 0,
|
|
95
|
+
gate = 0,
|
|
96
|
+
want = 0,
|
|
97
|
+
joined = false,
|
|
98
|
+
ticking = false,
|
|
99
|
+
record = record,
|
|
100
|
+
instance = instance,
|
|
101
|
+
attributes = attributes,
|
|
102
|
+
queued = false,
|
|
103
|
+
transitions = 0,
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
--[[
|
|
107
|
+
*
|
|
108
|
+
* @internal
|
|
109
|
+
* The object whose code is running: the owner of a runner worker during a hook or callback, or of a start
|
|
110
|
+
* thread for its whole life. A thread made with `task.spawn` inside a hook has no owner.
|
|
111
|
+
|
|
112
|
+
]]
|
|
113
|
+
local function current()
|
|
114
|
+
return ownerOf(coroutine.running())
|
|
115
|
+
end
|
|
116
|
+
local function note(s, hook, err)
|
|
117
|
+
if s.fault == nil then
|
|
118
|
+
s.fault = {
|
|
119
|
+
hook = hook,
|
|
120
|
+
error = err,
|
|
121
|
+
cleanup = {},
|
|
122
|
+
}
|
|
123
|
+
else
|
|
124
|
+
local _cleanup = s.fault.cleanup
|
|
125
|
+
local _arg0 = {
|
|
126
|
+
hook = hook,
|
|
127
|
+
error = err,
|
|
128
|
+
}
|
|
129
|
+
table.insert(_cleanup, _arg0)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
--* A teardown failure attaches to the fault, or is diagnosed without faulting a clean generation.
|
|
133
|
+
local function cleanup(s, hook, err)
|
|
134
|
+
if s.fault ~= nil then
|
|
135
|
+
local _cleanup = s.fault.cleanup
|
|
136
|
+
local _arg0 = {
|
|
137
|
+
hook = hook,
|
|
138
|
+
error = err,
|
|
139
|
+
}
|
|
140
|
+
table.insert(_cleanup, _arg0)
|
|
141
|
+
else
|
|
142
|
+
effects().diagnose(s, hook, err)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
local subsystemLacks = {
|
|
146
|
+
[2] = true,
|
|
147
|
+
[4] = true,
|
|
148
|
+
[5] = true,
|
|
149
|
+
}
|
|
150
|
+
local function run(s, hook)
|
|
151
|
+
local _condition = s.record.kind == 0
|
|
152
|
+
if _condition then
|
|
153
|
+
local _hook = hook
|
|
154
|
+
_condition = subsystemLacks[_hook] ~= nil
|
|
155
|
+
end
|
|
156
|
+
if _condition then
|
|
157
|
+
return nil
|
|
158
|
+
end
|
|
159
|
+
local caller = effects().hook(s, hook)
|
|
160
|
+
if caller == nil then
|
|
161
|
+
return nil
|
|
162
|
+
end
|
|
163
|
+
local result, value = call(caller, s, nil, s)
|
|
164
|
+
if result ~= 0 then
|
|
165
|
+
note(s, hook, value)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
local function construct(s)
|
|
169
|
+
return effects().construct(s)
|
|
170
|
+
end
|
|
171
|
+
local function release(s)
|
|
172
|
+
if s.object ~= nil then
|
|
173
|
+
local _object = s.object
|
|
174
|
+
states[_object] = nil
|
|
175
|
+
end
|
|
176
|
+
local disabled = effects().disabled
|
|
177
|
+
if disabled ~= nil then
|
|
178
|
+
pcall(disabled, s)
|
|
179
|
+
end
|
|
180
|
+
local subscriptions = s.subscriptions
|
|
181
|
+
s.subscriptions = nil
|
|
182
|
+
if subscriptions == nil then
|
|
183
|
+
return nil
|
|
184
|
+
end
|
|
185
|
+
for _, disconnect in subscriptions do
|
|
186
|
+
local ok, err = pcall(disconnect)
|
|
187
|
+
if not ok then
|
|
188
|
+
cleanup(s, 11, err)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
local function finish(s)
|
|
193
|
+
s.phase = if s.fault == nil then 6 else 5
|
|
194
|
+
release(s)
|
|
195
|
+
effects().ended(s)
|
|
196
|
+
end
|
|
197
|
+
local function traceback(err)
|
|
198
|
+
return debug.traceback(tostring(err), 2)
|
|
199
|
+
end
|
|
200
|
+
local finishStart
|
|
201
|
+
local function runStart(s)
|
|
202
|
+
local ok, err = xpcall(effects().start, traceback, s)
|
|
203
|
+
disown(coroutine.running())
|
|
204
|
+
finishStart(s, ok, err)
|
|
205
|
+
end
|
|
206
|
+
local function cancelSuspended(thread)
|
|
207
|
+
disown(thread)
|
|
208
|
+
return pcall(effects().cancel, thread)
|
|
209
|
+
end
|
|
210
|
+
--[[
|
|
211
|
+
*
|
|
212
|
+
* Only a suspended thread can be closed. A start thread that is running, or normal because it resumed the
|
|
213
|
+
* thread now draining, keeps its owner, so `current()` sees an ended object and `observe` subscribes nothing.
|
|
214
|
+
* A deferred check cancels it at its next yield, and `finishStart` ignores it if it returns first.
|
|
215
|
+
|
|
216
|
+
]]
|
|
217
|
+
local function stopStart(s, thread)
|
|
218
|
+
if coroutine.status(thread) == "suspended" then
|
|
219
|
+
local ok, err = cancelSuspended(thread)
|
|
220
|
+
if not ok then
|
|
221
|
+
cleanup(s, 10, err)
|
|
222
|
+
end
|
|
223
|
+
return nil
|
|
224
|
+
end
|
|
225
|
+
local fx = effects()
|
|
226
|
+
fx.defer(function()
|
|
227
|
+
if coroutine.status(thread) ~= "suspended" then
|
|
228
|
+
return nil
|
|
229
|
+
end
|
|
230
|
+
local ok, err = cancelSuspended(thread)
|
|
231
|
+
if not ok then
|
|
232
|
+
fx.diagnose(s, 10, err)
|
|
233
|
+
end
|
|
234
|
+
end)
|
|
235
|
+
end
|
|
236
|
+
local function perform(s, step)
|
|
237
|
+
local fx = effects()
|
|
238
|
+
repeat
|
|
239
|
+
if step == 0 then
|
|
240
|
+
local result, value = call(construct, s, nil, s)
|
|
241
|
+
if result ~= 0 then
|
|
242
|
+
note(s, 0, value)
|
|
243
|
+
else
|
|
244
|
+
local object = value
|
|
245
|
+
s.object = object
|
|
246
|
+
local _s = s
|
|
247
|
+
states[object] = _s
|
|
248
|
+
s.phase = 1
|
|
249
|
+
end
|
|
250
|
+
return nil
|
|
251
|
+
end
|
|
252
|
+
if step == 1 then
|
|
253
|
+
s.phase = 2
|
|
254
|
+
run(s, 1)
|
|
255
|
+
return nil
|
|
256
|
+
end
|
|
257
|
+
if step == 2 then
|
|
258
|
+
s.phase = 3
|
|
259
|
+
local previous = enablingNow
|
|
260
|
+
enablingNow = s
|
|
261
|
+
run(s, 2)
|
|
262
|
+
enablingNow = previous
|
|
263
|
+
return nil
|
|
264
|
+
end
|
|
265
|
+
if step == 3 then
|
|
266
|
+
s.phase = 4
|
|
267
|
+
return nil
|
|
268
|
+
end
|
|
269
|
+
if step == 4 then
|
|
270
|
+
s.joined = true
|
|
271
|
+
fx.join(s)
|
|
272
|
+
return nil
|
|
273
|
+
end
|
|
274
|
+
if step == 5 then
|
|
275
|
+
s.start = 1
|
|
276
|
+
local thread = coroutine.create(runStart)
|
|
277
|
+
s.startThread = thread
|
|
278
|
+
own(thread, s)
|
|
279
|
+
local ok, err = coroutine.resume(thread, s)
|
|
280
|
+
if not ok then
|
|
281
|
+
note(s, 3, err)
|
|
282
|
+
end
|
|
283
|
+
return nil
|
|
284
|
+
end
|
|
285
|
+
if step == 6 then
|
|
286
|
+
s.ticking = true
|
|
287
|
+
fx.tick(s)
|
|
288
|
+
return nil
|
|
289
|
+
end
|
|
290
|
+
if step == 7 then
|
|
291
|
+
if s.ticking then
|
|
292
|
+
s.ticking = false
|
|
293
|
+
fx.untick(s)
|
|
294
|
+
end
|
|
295
|
+
if s.joined then
|
|
296
|
+
s.joined = false
|
|
297
|
+
fx.leave(s)
|
|
298
|
+
end
|
|
299
|
+
return nil
|
|
300
|
+
end
|
|
301
|
+
if step == 8 then
|
|
302
|
+
s.phase = 4
|
|
303
|
+
run(s, 4)
|
|
304
|
+
local _result = fx.disabled
|
|
305
|
+
if _result ~= nil then
|
|
306
|
+
_result(s)
|
|
307
|
+
end
|
|
308
|
+
return nil
|
|
309
|
+
end
|
|
310
|
+
if step == 9 then
|
|
311
|
+
local thread = s.startThread
|
|
312
|
+
s.startThread = nil
|
|
313
|
+
if s.start == 1 and thread ~= nil then
|
|
314
|
+
stopStart(s, thread)
|
|
315
|
+
end
|
|
316
|
+
run(s, 5)
|
|
317
|
+
finish(s)
|
|
318
|
+
return nil
|
|
319
|
+
end
|
|
320
|
+
if step == 10 then
|
|
321
|
+
finish(s)
|
|
322
|
+
return nil
|
|
323
|
+
end
|
|
324
|
+
until true
|
|
325
|
+
end
|
|
326
|
+
--* Ends a generation with no user hooks, after its own teardown failed again. Every effect is protected.
|
|
327
|
+
local function forceEnd(s)
|
|
328
|
+
local fx = effects()
|
|
329
|
+
local thread = s.startThread
|
|
330
|
+
s.startThread = nil
|
|
331
|
+
if s.start == 1 and thread ~= nil then
|
|
332
|
+
pcall(stopStart, s, thread)
|
|
333
|
+
end
|
|
334
|
+
if s.ticking then
|
|
335
|
+
s.ticking = false
|
|
336
|
+
pcall(fx.untick, s)
|
|
337
|
+
end
|
|
338
|
+
if s.joined then
|
|
339
|
+
s.joined = false
|
|
340
|
+
pcall(fx.leave, s)
|
|
341
|
+
end
|
|
342
|
+
s.phase = if s.fault == nil then 6 else 5
|
|
343
|
+
pcall(release, s)
|
|
344
|
+
pcall(fx.ended, s)
|
|
345
|
+
end
|
|
346
|
+
--* @internal Steps one causal chain may take on one object before it faults. Fault cleanup runs past it.
|
|
347
|
+
local MAX_TRANSITIONS = 64
|
|
348
|
+
--* @internal Creation that never settles is a failure after this many awake waves in one batch.
|
|
349
|
+
local MAX_AWAKE_WAVES = 1024
|
|
350
|
+
--* @internal Batches one drain may start. A chain of objects creating objects in `start` stops here.
|
|
351
|
+
local MAX_BATCHES = 1024
|
|
352
|
+
--[[
|
|
353
|
+
*
|
|
354
|
+
* @internal
|
|
355
|
+
* Requests that steps may make in one drain. C5 bounds an object that undoes itself, not objects that undo each
|
|
356
|
+
* other; this cap ends such a cycle. Like the batch cap it is a failure, and its value waits on a Studio measurement.
|
|
357
|
+
|
|
358
|
+
]]
|
|
359
|
+
local MAX_CHAINED_REQUESTS = 65_536
|
|
360
|
+
--* @internal Reconcile rounds one drain may run. Binding that queues pairs on every round stops here.
|
|
361
|
+
local MAX_RECONCILE_ROUNDS = 1024
|
|
362
|
+
--* @internal Consecutive internal errors that fault or force-end nothing before the drain gives up on its work.
|
|
363
|
+
local MAX_RECOVERIES = 1
|
|
364
|
+
local function awaking(batch)
|
|
365
|
+
return batch.sweep == 0 or batch.sweep == 1
|
|
366
|
+
end
|
|
367
|
+
local function settle(reconciler)
|
|
368
|
+
reconciler:settle()
|
|
369
|
+
end
|
|
370
|
+
--[[
|
|
371
|
+
*
|
|
372
|
+
* The single reconciler. Every lifecycle and binding change happens in a drain, and one drain runs at a time.
|
|
373
|
+
* A drain starts at an outermost entry point: an engine signal, boot, a frame dispatch, or a public call made
|
|
374
|
+
* outside a drain. A request made inside a drain only enqueues, so no hook ever runs while another object's
|
|
375
|
+
* hook is on the stack. The stack holds objects to settle and batches to advance. What one step asks for
|
|
376
|
+
* collects in `fresh` and moves onto the stack when the step ends, first request on top. So requests run in
|
|
377
|
+
* the order they were made, after the hook that made them returns and before its object takes its next step.
|
|
378
|
+
|
|
379
|
+
]]
|
|
380
|
+
local request, fault
|
|
381
|
+
local Reconciler
|
|
382
|
+
do
|
|
383
|
+
Reconciler = setmetatable({}, {
|
|
384
|
+
__tostring = function()
|
|
385
|
+
return "Reconciler"
|
|
386
|
+
end,
|
|
387
|
+
})
|
|
388
|
+
Reconciler.__index = Reconciler
|
|
389
|
+
function Reconciler.new(...)
|
|
390
|
+
local self = setmetatable({}, Reconciler)
|
|
391
|
+
return self:constructor(...) or self
|
|
392
|
+
end
|
|
393
|
+
function Reconciler:constructor()
|
|
394
|
+
self.stack = {}
|
|
395
|
+
self.fresh = {}
|
|
396
|
+
self.depth = 0
|
|
397
|
+
self.pairsQueued = false
|
|
398
|
+
self.rounds = 0
|
|
399
|
+
self.batches = 0
|
|
400
|
+
self.requests = 0
|
|
401
|
+
end
|
|
402
|
+
function Reconciler:isDraining()
|
|
403
|
+
return self.depth > 0
|
|
404
|
+
end
|
|
405
|
+
function Reconciler:open()
|
|
406
|
+
self.depth += 1
|
|
407
|
+
end
|
|
408
|
+
function Reconciler:leave()
|
|
409
|
+
self.depth -= 1
|
|
410
|
+
return self.depth == 0 and (#self.stack > 0 or #self.fresh > 0 or self.pairsQueued)
|
|
411
|
+
end
|
|
412
|
+
function Reconciler:enter(callback, a, b, owner)
|
|
413
|
+
self.depth += 1
|
|
414
|
+
local result, value = call(callback, a, b, owner)
|
|
415
|
+
self.depth -= 1
|
|
416
|
+
return result, value
|
|
417
|
+
end
|
|
418
|
+
function Reconciler:fail(message)
|
|
419
|
+
if self.failure == nil then
|
|
420
|
+
self.failure = message
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
function Reconciler:notePairs()
|
|
424
|
+
self.pairsQueued = true
|
|
425
|
+
end
|
|
426
|
+
function Reconciler:push(s)
|
|
427
|
+
if s.queued then
|
|
428
|
+
return nil
|
|
429
|
+
end
|
|
430
|
+
s.queued = true
|
|
431
|
+
local _fresh = self.fresh
|
|
432
|
+
local _s = s
|
|
433
|
+
table.insert(_fresh, _s)
|
|
434
|
+
end
|
|
435
|
+
function Reconciler:chain(s)
|
|
436
|
+
local stepping = self.stepping
|
|
437
|
+
if stepping ~= s then
|
|
438
|
+
s.transitions = 0
|
|
439
|
+
end
|
|
440
|
+
if stepping == nil then
|
|
441
|
+
return nil
|
|
442
|
+
end
|
|
443
|
+
self.requests += 1
|
|
444
|
+
if self.requests <= MAX_CHAINED_REQUESTS or stepping.fault ~= nil then
|
|
445
|
+
return nil
|
|
446
|
+
end
|
|
447
|
+
-- A cycle's members keep requesting while a passive target never does, so the requester is the one to end.
|
|
448
|
+
self.requests = 0
|
|
449
|
+
local message = `Crucible: objects kept changing each other through {MAX_CHAINED_REQUESTS} requests in one drain`
|
|
450
|
+
self:fail(message)
|
|
451
|
+
note(stepping, 9, message)
|
|
452
|
+
end
|
|
453
|
+
function Reconciler:raise(s, gate)
|
|
454
|
+
if s.gate < gate then
|
|
455
|
+
s.gate = gate
|
|
456
|
+
end
|
|
457
|
+
self:push(s)
|
|
458
|
+
end
|
|
459
|
+
function Reconciler:innermostBatch()
|
|
460
|
+
for i = #self.fresh - 1, 0, -1 do
|
|
461
|
+
local entry = self.fresh[i + 1]
|
|
462
|
+
if entry.work == 1 then
|
|
463
|
+
return entry
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
for i = #self.stack - 1, 0, -1 do
|
|
467
|
+
local entry = self.stack[i + 1]
|
|
468
|
+
if entry.work == 1 then
|
|
469
|
+
return entry
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
return nil
|
|
473
|
+
end
|
|
474
|
+
function Reconciler:admit(created)
|
|
475
|
+
if #created == 0 then
|
|
476
|
+
return nil
|
|
477
|
+
end
|
|
478
|
+
local open = self:innermostBatch()
|
|
479
|
+
if open ~= nil and awaking(open) then
|
|
480
|
+
for _, s in created do
|
|
481
|
+
local _exp = open.pending
|
|
482
|
+
table.insert(_exp, s)
|
|
483
|
+
end
|
|
484
|
+
return nil
|
|
485
|
+
end
|
|
486
|
+
if self.batches == MAX_BATCHES then
|
|
487
|
+
for _, s in created do
|
|
488
|
+
request(s, 2)
|
|
489
|
+
end
|
|
490
|
+
self:fail(`Crucible: objects kept creating objects for {MAX_BATCHES} batches in one drain; the rest were dropped`)
|
|
491
|
+
return nil
|
|
492
|
+
end
|
|
493
|
+
self.batches += 1
|
|
494
|
+
local _fresh = self.fresh
|
|
495
|
+
local _object = {
|
|
496
|
+
work = 1,
|
|
497
|
+
members = {},
|
|
498
|
+
wave = {},
|
|
499
|
+
}
|
|
500
|
+
local _left = "pending"
|
|
501
|
+
local _array = {}
|
|
502
|
+
local _length = #_array
|
|
503
|
+
table.move(created, 1, #created, _length + 1, _array)
|
|
504
|
+
_object[_left] = _array
|
|
505
|
+
_object.sweep = 1
|
|
506
|
+
_object.cursor = 0
|
|
507
|
+
_object.waves = 0
|
|
508
|
+
table.insert(_fresh, _object)
|
|
509
|
+
self:drain()
|
|
510
|
+
end
|
|
511
|
+
function Reconciler:abandon()
|
|
512
|
+
local _array = {}
|
|
513
|
+
local _length = #_array
|
|
514
|
+
local _array_1 = self.stack
|
|
515
|
+
table.move(_array_1, 1, #_array_1, _length + 1, _array)
|
|
516
|
+
local _array_2 = {}
|
|
517
|
+
local _length_1 = #_array_2
|
|
518
|
+
local _array_3 = self.fresh
|
|
519
|
+
table.move(_array_3, 1, #_array_3, _length_1 + 1, _array_2)
|
|
520
|
+
for _, list in { _array, _array_2 } do
|
|
521
|
+
for _1, entry in list do
|
|
522
|
+
if entry.work ~= 1 then
|
|
523
|
+
continue
|
|
524
|
+
end
|
|
525
|
+
for _2, s in entry.members do
|
|
526
|
+
request(s, 2)
|
|
527
|
+
end
|
|
528
|
+
for _2, s in entry.pending do
|
|
529
|
+
request(s, 2)
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
function Reconciler:advance(batch)
|
|
535
|
+
local _exp = batch.sweep
|
|
536
|
+
repeat
|
|
537
|
+
local _fallthrough = false
|
|
538
|
+
if _exp == 0 then
|
|
539
|
+
_fallthrough = true
|
|
540
|
+
end
|
|
541
|
+
if _fallthrough or _exp == 1 then
|
|
542
|
+
if batch.cursor < #batch.wave then
|
|
543
|
+
local s = batch.wave[batch.cursor + 1]
|
|
544
|
+
batch.cursor += 1
|
|
545
|
+
self:raise(s, if batch.sweep == 0 then 1 else 2)
|
|
546
|
+
return true
|
|
547
|
+
end
|
|
548
|
+
batch.cursor = 0
|
|
549
|
+
if batch.sweep == 0 then
|
|
550
|
+
batch.sweep = 1
|
|
551
|
+
return true
|
|
552
|
+
end
|
|
553
|
+
local wave = batch.pending
|
|
554
|
+
batch.pending = {}
|
|
555
|
+
if #wave == 0 then
|
|
556
|
+
batch.sweep = 2
|
|
557
|
+
elseif batch.waves == MAX_AWAKE_WAVES then
|
|
558
|
+
for _, s in wave do
|
|
559
|
+
request(s, 2)
|
|
560
|
+
end
|
|
561
|
+
self:fail(`Crucible: objects kept creating objects during awake for {MAX_AWAKE_WAVES} waves; the last wave was dropped`)
|
|
562
|
+
batch.sweep = 2
|
|
563
|
+
else
|
|
564
|
+
batch.waves += 1
|
|
565
|
+
batch.wave = wave
|
|
566
|
+
for _, s in wave do
|
|
567
|
+
local _exp_1 = batch.members
|
|
568
|
+
table.insert(_exp_1, s)
|
|
569
|
+
end
|
|
570
|
+
batch.sweep = 0
|
|
571
|
+
end
|
|
572
|
+
return true
|
|
573
|
+
end
|
|
574
|
+
if _exp == 2 then
|
|
575
|
+
_fallthrough = true
|
|
576
|
+
end
|
|
577
|
+
if _fallthrough or _exp == 3 then
|
|
578
|
+
if batch.cursor < #batch.members then
|
|
579
|
+
local s = batch.members[batch.cursor + 1]
|
|
580
|
+
batch.cursor += 1
|
|
581
|
+
self:raise(s, if batch.sweep == 2 then 3 else 4)
|
|
582
|
+
return true
|
|
583
|
+
end
|
|
584
|
+
if batch.sweep == 3 then
|
|
585
|
+
return false
|
|
586
|
+
end
|
|
587
|
+
batch.cursor = 0
|
|
588
|
+
batch.sweep = 3
|
|
589
|
+
return true
|
|
590
|
+
end
|
|
591
|
+
until true
|
|
592
|
+
return false
|
|
593
|
+
end
|
|
594
|
+
function Reconciler:settleStep(s)
|
|
595
|
+
local step = nextStep(s)
|
|
596
|
+
if step == nil then
|
|
597
|
+
return false
|
|
598
|
+
end
|
|
599
|
+
s.transitions += 1
|
|
600
|
+
if s.fault == nil then
|
|
601
|
+
if s.transitions > MAX_TRANSITIONS then
|
|
602
|
+
note(s, 9, `did not settle after {MAX_TRANSITIONS} transitions since a request from outside it`)
|
|
603
|
+
return true
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
self.stepping = s
|
|
607
|
+
perform(s, step)
|
|
608
|
+
self.stepping = nil
|
|
609
|
+
return true
|
|
610
|
+
end
|
|
611
|
+
function Reconciler:settle()
|
|
612
|
+
while true do
|
|
613
|
+
while #self.fresh > 0 do
|
|
614
|
+
local _stack = self.stack
|
|
615
|
+
local _exp = self.fresh
|
|
616
|
+
-- ▼ Array.pop ▼
|
|
617
|
+
local _length = #_exp
|
|
618
|
+
local _result = _exp[_length]
|
|
619
|
+
_exp[_length] = nil
|
|
620
|
+
-- ▲ Array.pop ▲
|
|
621
|
+
table.insert(_stack, _result)
|
|
622
|
+
end
|
|
623
|
+
local top = self.stack[#self.stack]
|
|
624
|
+
if top == nil or top.work == 1 then
|
|
625
|
+
if self.pairsQueued then
|
|
626
|
+
self.pairsQueued = false
|
|
627
|
+
if self.rounds == MAX_RECONCILE_ROUNDS then
|
|
628
|
+
self:fail(`Crucible: binding kept queueing pairs for {MAX_RECONCILE_ROUNDS} reconcile rounds in one drain; the rest wait for the next drain`)
|
|
629
|
+
else
|
|
630
|
+
self.rounds += 1
|
|
631
|
+
self:admit(effects().reconcile())
|
|
632
|
+
end
|
|
633
|
+
continue
|
|
634
|
+
end
|
|
635
|
+
if top == nil then
|
|
636
|
+
return nil
|
|
637
|
+
end
|
|
638
|
+
if not self:advance(top) then
|
|
639
|
+
local _exp = self.stack
|
|
640
|
+
_exp[#_exp] = nil
|
|
641
|
+
end
|
|
642
|
+
continue
|
|
643
|
+
end
|
|
644
|
+
if not self:settleStep(top) then
|
|
645
|
+
local _exp = self.stack
|
|
646
|
+
_exp[#_exp] = nil
|
|
647
|
+
top.queued = false
|
|
648
|
+
end
|
|
649
|
+
end
|
|
650
|
+
end
|
|
651
|
+
function Reconciler:unwind()
|
|
652
|
+
local stranded = {}
|
|
653
|
+
local rest = {}
|
|
654
|
+
local _array = {}
|
|
655
|
+
local _length = #_array
|
|
656
|
+
local _array_1 = self.fresh
|
|
657
|
+
table.move(_array_1, 1, #_array_1, _length + 1, _array)
|
|
658
|
+
local order = _array
|
|
659
|
+
for i = #self.stack - 1, 0, -1 do
|
|
660
|
+
local _arg0 = self.stack[i + 1]
|
|
661
|
+
table.insert(order, _arg0)
|
|
662
|
+
end
|
|
663
|
+
table.clear(self.fresh)
|
|
664
|
+
table.clear(self.stack)
|
|
665
|
+
for _, entry in order do
|
|
666
|
+
if entry.work == 0 then
|
|
667
|
+
entry.queued = false
|
|
668
|
+
table.insert(rest, entry)
|
|
669
|
+
continue
|
|
670
|
+
end
|
|
671
|
+
for _1, s in entry.members do
|
|
672
|
+
if s.gate < 4 then
|
|
673
|
+
table.insert(stranded, s)
|
|
674
|
+
end
|
|
675
|
+
end
|
|
676
|
+
for _1, s in entry.pending do
|
|
677
|
+
if s.gate < 4 then
|
|
678
|
+
table.insert(stranded, s)
|
|
679
|
+
end
|
|
680
|
+
end
|
|
681
|
+
end
|
|
682
|
+
return stranded, rest
|
|
683
|
+
end
|
|
684
|
+
function Reconciler:drain()
|
|
685
|
+
if self.depth > 0 or (#self.stack == 0 and #self.fresh == 0 and not self.pairsQueued) then
|
|
686
|
+
return nil
|
|
687
|
+
end
|
|
688
|
+
local errors = {}
|
|
689
|
+
local faulted = {}
|
|
690
|
+
local idle = 0
|
|
691
|
+
local result, err = self:enter(settle, self, nil)
|
|
692
|
+
while result ~= 0 do
|
|
693
|
+
local _arg0 = tostring(err)
|
|
694
|
+
table.insert(errors, _arg0)
|
|
695
|
+
local culprit = self.stepping
|
|
696
|
+
self.stepping = nil
|
|
697
|
+
local stranded, rest = self:unwind()
|
|
698
|
+
local progressed = false
|
|
699
|
+
if culprit ~= nil and not hasEnded(culprit) then
|
|
700
|
+
if faulted[culprit] ~= nil then
|
|
701
|
+
forceEnd(culprit)
|
|
702
|
+
else
|
|
703
|
+
faulted[culprit] = true
|
|
704
|
+
note(culprit, 11, err)
|
|
705
|
+
end
|
|
706
|
+
progressed = true
|
|
707
|
+
end
|
|
708
|
+
for _, s in stranded do
|
|
709
|
+
if hasEnded(s) or faulted[s] ~= nil then
|
|
710
|
+
continue
|
|
711
|
+
end
|
|
712
|
+
faulted[s] = true
|
|
713
|
+
note(s, 11, err)
|
|
714
|
+
progressed = true
|
|
715
|
+
end
|
|
716
|
+
idle = if progressed then 0 else idle + 1
|
|
717
|
+
local _array = {}
|
|
718
|
+
local _length = #_array
|
|
719
|
+
local _strandedLength = #stranded
|
|
720
|
+
table.move(stranded, 1, _strandedLength, _length + 1, _array)
|
|
721
|
+
_length += _strandedLength
|
|
722
|
+
table.move(rest, 1, #rest, _length + 1, _array)
|
|
723
|
+
-- ▼ ReadonlyArray.filter ▼
|
|
724
|
+
local _newValue = {}
|
|
725
|
+
local _callback = function(s)
|
|
726
|
+
return not hasEnded(s)
|
|
727
|
+
end
|
|
728
|
+
local _length_1 = 0
|
|
729
|
+
for _k, _v in _array do
|
|
730
|
+
if _callback(_v, _k - 1, _array) == true then
|
|
731
|
+
_length_1 += 1
|
|
732
|
+
_newValue[_length_1] = _v
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
-- ▲ ReadonlyArray.filter ▲
|
|
736
|
+
local left = _newValue
|
|
737
|
+
if idle > MAX_RECOVERIES then
|
|
738
|
+
self.pairsQueued = false
|
|
739
|
+
for _, s in left do
|
|
740
|
+
if hasEnded(s) then
|
|
741
|
+
continue
|
|
742
|
+
end
|
|
743
|
+
if not (faulted[s] ~= nil) then
|
|
744
|
+
note(s, 11, err)
|
|
745
|
+
end
|
|
746
|
+
forceEnd(s)
|
|
747
|
+
end
|
|
748
|
+
break
|
|
749
|
+
end
|
|
750
|
+
for _, s in left do
|
|
751
|
+
self:push(s)
|
|
752
|
+
end
|
|
753
|
+
result, err = self:enter(settle, self, nil)
|
|
754
|
+
end
|
|
755
|
+
self.rounds = 0
|
|
756
|
+
self.batches = 0
|
|
757
|
+
self.requests = 0
|
|
758
|
+
self.stepping = nil
|
|
759
|
+
local failure = self.failure
|
|
760
|
+
self.failure = nil
|
|
761
|
+
local fx = effects()
|
|
762
|
+
for _, e in errors do
|
|
763
|
+
fx.failed(`Crucible: internal error; the reconciler recovered: {e}`)
|
|
764
|
+
end
|
|
765
|
+
if failure ~= nil then
|
|
766
|
+
fx.failed(failure)
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
function Reconciler:within(owner, blame, hook, callback, a, b)
|
|
770
|
+
local outer = self.depth > 0
|
|
771
|
+
local result, value = self:enter(callback, a, b, owner)
|
|
772
|
+
if result ~= 0 and blame ~= nil then
|
|
773
|
+
fault(blame, hook, value)
|
|
774
|
+
end
|
|
775
|
+
if not outer then
|
|
776
|
+
self:drain()
|
|
777
|
+
end
|
|
778
|
+
return result, value
|
|
779
|
+
end
|
|
780
|
+
end
|
|
781
|
+
local reconciler = Reconciler.new()
|
|
782
|
+
--* @internal True while a drain runs, including inside every hook and callback it calls.
|
|
783
|
+
local function isDraining()
|
|
784
|
+
return reconciler:isDraining()
|
|
785
|
+
end
|
|
786
|
+
--[[
|
|
787
|
+
*
|
|
788
|
+
* @internal
|
|
789
|
+
* Opens a scope like `within` for a caller that runs its own worker: requests made until `closeScope` wait for it.
|
|
790
|
+
* Pair the two, with nothing between them that can throw.
|
|
791
|
+
|
|
792
|
+
]]
|
|
793
|
+
local function openScope()
|
|
794
|
+
reconciler:open()
|
|
795
|
+
end
|
|
796
|
+
--* @internal Closes the scope `openScope` opened. True when it was the outermost and requests wait: call `drain`.
|
|
797
|
+
local function closeScope()
|
|
798
|
+
return reconciler:leave()
|
|
799
|
+
end
|
|
800
|
+
--[[
|
|
801
|
+
*
|
|
802
|
+
* @internal
|
|
803
|
+
* Runs every queued change to rest. Returns at once when a drain is already running: that drain processes the
|
|
804
|
+
* queue before it returns. Never throws. A cap or an internal error goes to `effects.failed` after recovery.
|
|
805
|
+
|
|
806
|
+
]]
|
|
807
|
+
local function drain()
|
|
808
|
+
reconciler:drain()
|
|
809
|
+
end
|
|
810
|
+
--[[
|
|
811
|
+
*
|
|
812
|
+
* @internal
|
|
813
|
+
* Binding has queued pairs. The drain reconciles them when its top is a batch or it is empty. Past the round
|
|
814
|
+
* cap the drain drops the note, so binding calls this again whenever it queues.
|
|
815
|
+
|
|
816
|
+
]]
|
|
817
|
+
local function notePairs()
|
|
818
|
+
reconciler:notePairs()
|
|
819
|
+
end
|
|
820
|
+
--[[
|
|
821
|
+
*
|
|
822
|
+
* @internal
|
|
823
|
+
* Runs one callback as its own scope: a frame hook, an `All<T>` visit, or an observer callback. Requests it
|
|
824
|
+
* makes apply after it returns. A failure faults `blame`; with no blame the caller logs the returned failure.
|
|
825
|
+
* Outside a drain, the scope drains when it ends, so the next frame hook in a lane sees the effects of the one
|
|
826
|
+
* before.
|
|
827
|
+
|
|
828
|
+
]]
|
|
829
|
+
local function within(owner, blame, hook, callback, a, b)
|
|
830
|
+
return reconciler:within(owner, blame, hook, callback, a, b)
|
|
831
|
+
end
|
|
832
|
+
--* @internal Updates `want` and queues the object. Called by `setEnabled`, `remove()` and binding. Ignored once Destroyed is wanted.
|
|
833
|
+
function request(s, want)
|
|
834
|
+
if s.want == 2 then
|
|
835
|
+
return nil
|
|
836
|
+
end
|
|
837
|
+
s.want = want
|
|
838
|
+
reconciler:chain(s)
|
|
839
|
+
reconciler:push(s)
|
|
840
|
+
reconciler:drain()
|
|
841
|
+
end
|
|
842
|
+
--* @internal Faults the object from outside a lifecycle step: a frame hook, `accepts`, or an `All<T>` callback.
|
|
843
|
+
function fault(s, hook, err)
|
|
844
|
+
if hasEnded(s) then
|
|
845
|
+
return nil
|
|
846
|
+
end
|
|
847
|
+
note(s, hook, err)
|
|
848
|
+
reconciler:chain(s)
|
|
849
|
+
reconciler:push(s)
|
|
850
|
+
reconciler:drain()
|
|
851
|
+
end
|
|
852
|
+
--* @internal Called by the start thread when `start` returns or throws. A start that outlived its generation is ignored.
|
|
853
|
+
function finishStart(s, ok, err)
|
|
854
|
+
if hasEnded(s) then
|
|
855
|
+
return nil
|
|
856
|
+
end
|
|
857
|
+
s.startThread = nil
|
|
858
|
+
s.start = 2
|
|
859
|
+
if not ok then
|
|
860
|
+
note(s, 3, err)
|
|
861
|
+
end
|
|
862
|
+
reconciler:chain(s)
|
|
863
|
+
reconciler:push(s)
|
|
864
|
+
reconciler:drain()
|
|
865
|
+
end
|
|
866
|
+
--[[
|
|
867
|
+
*
|
|
868
|
+
* @internal
|
|
869
|
+
* Gives new generations a batch. Inside the awake closure of the innermost batch they join its next wave, so
|
|
870
|
+
* every object a batch creates during awake is awake before any of the batch is enabled. Otherwise they form
|
|
871
|
+
* a new batch, which runs to rest before the code that created them continues.
|
|
872
|
+
|
|
873
|
+
]]
|
|
874
|
+
local function admit(created)
|
|
875
|
+
reconciler:admit(created)
|
|
876
|
+
end
|
|
877
|
+
--* @internal Asks every generation in an unfinished batch to end. Boot calls it when a Subsystem faults.
|
|
878
|
+
local function abandon()
|
|
879
|
+
reconciler:abandon()
|
|
880
|
+
end
|
|
881
|
+
--* @internal Forwards to the installed effects' `defer`, so networking defers ClientReady cleanups as lifecycle does.
|
|
882
|
+
local function defer(callback)
|
|
883
|
+
effects().defer(callback)
|
|
884
|
+
end
|
|
885
|
+
return {
|
|
886
|
+
nextStep = nextStep,
|
|
887
|
+
useEffects = useEffects,
|
|
888
|
+
stateOf = stateOf,
|
|
889
|
+
enabling = enabling,
|
|
890
|
+
isLive = isLive,
|
|
891
|
+
hasEnded = hasEnded,
|
|
892
|
+
track = track,
|
|
893
|
+
current = current,
|
|
894
|
+
isDraining = isDraining,
|
|
895
|
+
openScope = openScope,
|
|
896
|
+
closeScope = closeScope,
|
|
897
|
+
drain = drain,
|
|
898
|
+
notePairs = notePairs,
|
|
899
|
+
within = within,
|
|
900
|
+
request = request,
|
|
901
|
+
fault = fault,
|
|
902
|
+
finishStart = finishStart,
|
|
903
|
+
admit = admit,
|
|
904
|
+
abandon = abandon,
|
|
905
|
+
defer = defer,
|
|
906
|
+
MAX_TRANSITIONS = MAX_TRANSITIONS,
|
|
907
|
+
MAX_AWAKE_WAVES = MAX_AWAKE_WAVES,
|
|
908
|
+
MAX_BATCHES = MAX_BATCHES,
|
|
909
|
+
MAX_CHAINED_REQUESTS = MAX_CHAINED_REQUESTS,
|
|
910
|
+
MAX_RECONCILE_ROUNDS = MAX_RECONCILE_ROUNDS,
|
|
911
|
+
MAX_RECOVERIES = MAX_RECOVERIES,
|
|
912
|
+
}
|