@polyengine/runtime 0.1.0-pre.g633468a
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 +202 -0
- package/README.md +23 -0
- package/esm/cabi/async_values.js +162 -0
- package/esm/cabi/bulk_lists.js +198 -0
- package/esm/cabi/context.js +42 -0
- package/esm/cabi/flatten.js +145 -0
- package/esm/cabi/float.js +67 -0
- package/esm/cabi/handles.js +253 -0
- package/esm/cabi/layout.js +149 -0
- package/esm/cabi/lift.js +196 -0
- package/esm/cabi/load.js +146 -0
- package/esm/cabi/lower.js +141 -0
- package/esm/cabi/memory.js +182 -0
- package/esm/cabi/mod.js +22 -0
- package/esm/cabi/store.js +186 -0
- package/esm/cabi/strings.js +336 -0
- package/esm/cabi/trap.js +38 -0
- package/esm/cabi/types.js +264 -0
- package/esm/cabi/values.js +64 -0
- package/esm/cache/core.js +156 -0
- package/esm/cache/dir.js +170 -0
- package/esm/cache/mod.js +4 -0
- package/esm/cache/web.js +136 -0
- package/esm/digest/digest.js +332 -0
- package/esm/digest/mod.js +3 -0
- package/esm/digest/verify.js +129 -0
- package/esm/embedder/casing.js +56 -0
- package/esm/embedder/copy.js +42 -0
- package/esm/embedder/errors.js +26 -0
- package/esm/embedder/imports.js +63 -0
- package/esm/embedder/instantiate.js +978 -0
- package/esm/embedder/mod.js +40 -0
- package/esm/embedder/resources.js +406 -0
- package/esm/embedder/streams.js +770 -0
- package/esm/embedder/values.js +450 -0
- package/esm/embedder/version.js +273 -0
- package/esm/exec/boundary.js +1902 -0
- package/esm/exec/executor.js +1004 -0
- package/esm/exec/host_streams.js +818 -0
- package/esm/exec/mod.js +4 -0
- package/esm/intrinsics/async_builtins.js +510 -0
- package/esm/intrinsics/context.js +90 -0
- package/esm/intrinsics/errors.js +13 -0
- package/esm/intrinsics/fact_calls.js +865 -0
- package/esm/intrinsics/mod.js +564 -0
- package/esm/intrinsics/stream_builtins.js +578 -0
- package/esm/intrinsics/transcode.js +443 -0
- package/esm/jspi/bridge.js +579 -0
- package/esm/jspi/mechanics.js +89 -0
- package/esm/jspi/mod.js +5 -0
- package/esm/jspi/suspending.js +15 -0
- package/esm/jspi/types.js +29 -0
- package/esm/package.json +3 -0
- package/esm/plan/format.js +5 -0
- package/esm/plan/loader.js +657 -0
- package/esm/plan/mod.js +3 -0
- package/esm/shim/mod.js +2 -0
- package/esm/shim/translator.js +94 -0
- package/esm/task/mod.js +483 -0
- package/esm/task/scheduler.js +1028 -0
- package/esm/task/streams.js +786 -0
- package/esm/task/subtask.js +135 -0
- package/esm/task/thread.js +255 -0
- package/esm/task/waitable.js +144 -0
- package/package.json +91 -0
- package/types/cabi/async_values.d.ts +35 -0
- package/types/cabi/bulk_lists.d.ts +18 -0
- package/types/cabi/context.d.ts +59 -0
- package/types/cabi/flatten.d.ts +14 -0
- package/types/cabi/float.d.ts +14 -0
- package/types/cabi/handles.d.ts +70 -0
- package/types/cabi/layout.d.ts +13 -0
- package/types/cabi/lift.d.ts +25 -0
- package/types/cabi/load.d.ts +14 -0
- package/types/cabi/lower.d.ts +10 -0
- package/types/cabi/memory.d.ts +58 -0
- package/types/cabi/mod.d.ts +15 -0
- package/types/cabi/store.d.ts +12 -0
- package/types/cabi/strings.d.ts +23 -0
- package/types/cabi/trap.d.ts +11 -0
- package/types/cabi/types.d.ts +206 -0
- package/types/cabi/values.d.ts +5 -0
- package/types/cache/core.d.ts +97 -0
- package/types/cache/dir.d.ts +6 -0
- package/types/cache/mod.d.ts +3 -0
- package/types/cache/web.d.ts +10 -0
- package/types/digest/digest.d.ts +17 -0
- package/types/digest/mod.d.ts +2 -0
- package/types/digest/verify.d.ts +48 -0
- package/types/embedder/casing.d.ts +40 -0
- package/types/embedder/copy.d.ts +24 -0
- package/types/embedder/errors.d.ts +11 -0
- package/types/embedder/imports.d.ts +47 -0
- package/types/embedder/instantiate.d.ts +88 -0
- package/types/embedder/mod.d.ts +11 -0
- package/types/embedder/resources.d.ts +158 -0
- package/types/embedder/streams.d.ts +202 -0
- package/types/embedder/values.d.ts +70 -0
- package/types/embedder/version.d.ts +85 -0
- package/types/exec/boundary.d.ts +360 -0
- package/types/exec/executor.d.ts +125 -0
- package/types/exec/host_streams.d.ts +165 -0
- package/types/exec/mod.d.ts +3 -0
- package/types/intrinsics/async_builtins.d.ts +69 -0
- package/types/intrinsics/context.d.ts +28 -0
- package/types/intrinsics/errors.d.ts +5 -0
- package/types/intrinsics/fact_calls.d.ts +120 -0
- package/types/intrinsics/mod.d.ts +187 -0
- package/types/intrinsics/stream_builtins.d.ts +113 -0
- package/types/intrinsics/transcode.d.ts +21 -0
- package/types/jspi/bridge.d.ts +227 -0
- package/types/jspi/mechanics.d.ts +50 -0
- package/types/jspi/mod.d.ts +3 -0
- package/types/jspi/suspending.d.ts +1 -0
- package/types/jspi/types.d.ts +26 -0
- package/types/plan/format.d.ts +369 -0
- package/types/plan/loader.d.ts +113 -0
- package/types/plan/mod.d.ts +2 -0
- package/types/shim/mod.d.ts +1 -0
- package/types/shim/translator.d.ts +55 -0
- package/types/task/mod.d.ts +257 -0
- package/types/task/scheduler.d.ts +421 -0
- package/types/task/streams.d.ts +370 -0
- package/types/task/subtask.d.ts +96 -0
- package/types/task/thread.d.ts +73 -0
- package/types/task/waitable.d.ts +67 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/** definitions.py `Cancelled` (line 248). */
|
|
2
|
+
export declare const CANCELLED_FALSE = false;
|
|
3
|
+
export declare const CANCELLED_TRUE = true;
|
|
4
|
+
export type Cancelled = boolean;
|
|
5
|
+
/**
|
|
6
|
+
* What a thread body yields when it wants to stop running. Mirrors the
|
|
7
|
+
* reference's `Thread.wait_until` (`ready_func` + `cancellable`); `suspend`
|
|
8
|
+
* is `wait_until` with no ready condition (`ready_func === null`), and
|
|
9
|
+
* `yield_` is `wait_until(() => true)`.
|
|
10
|
+
*/
|
|
11
|
+
export interface BlockRequest {
|
|
12
|
+
/** Resumable once this returns true; `null` = only an explicit resume. */
|
|
13
|
+
readyFunc: (() => boolean) | null;
|
|
14
|
+
cancellable: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* JSPI seam. When present the thread is not waiting on a scheduler
|
|
17
|
+
* condition at all — it is waiting for a **Promise**, namely the one a
|
|
18
|
+
* `promising`-wrapped wasm entry returned. The driving loop awaits it and
|
|
19
|
+
* resumes the body with the resolved value (or throws the rejection into
|
|
20
|
+
* the body, so a post-resume trap unwinds exactly like a synchronous one —
|
|
21
|
+
* jspi pin (e)).
|
|
22
|
+
*
|
|
23
|
+
* This is what lets one generator body serve both modes: in plain mode the
|
|
24
|
+
* core call returns a value and the body never yields such a request, so
|
|
25
|
+
* the synchronous path is bit-for-bit what it was before JSPI existed.
|
|
26
|
+
*/
|
|
27
|
+
awaitValue?: Promise<unknown>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A thread body: yields block requests, and receives back either the
|
|
31
|
+
* cancelled flag (for a scheduler block point) or the resolved value of an
|
|
32
|
+
* `awaitValue` request.
|
|
33
|
+
*/
|
|
34
|
+
export type ThreadBody = Generator<BlockRequest, void, any>;
|
|
35
|
+
/**
|
|
36
|
+
* Failure raised where the reference genuinely needs to suspend a wasm frame.
|
|
37
|
+
*
|
|
38
|
+
* This is deliberately *not* a `Trap`: the component is not at fault and the
|
|
39
|
+
* program is not ill-formed — our runtime is incomplete. Reporting it as a
|
|
40
|
+
* trap would let a conformance run score a missing capability as a correct
|
|
41
|
+
* rejection, which is the exact failure mode contracts/plan-format.md's
|
|
42
|
+
* error-phase split exists to prevent.
|
|
43
|
+
*/
|
|
44
|
+
export declare class NeedsJspi extends Error {
|
|
45
|
+
constructor(what: string);
|
|
46
|
+
}
|
|
47
|
+
export declare function needsJspi(what: string): never;
|
|
48
|
+
/**
|
|
49
|
+
* Failure raised where a capability scheduled for a later M2 phase is
|
|
50
|
+
* required. Same rationale as `NeedsJspi`: never a `Trap`.
|
|
51
|
+
*/
|
|
52
|
+
export declare class PendingCapability extends Error {
|
|
53
|
+
constructor(what: string);
|
|
54
|
+
}
|
|
55
|
+
/** @internal — see `onInstancePoisoned`; registered once by task/streams.ts. */
|
|
56
|
+
export declare function setOnInstancePoisoned(f: (inst: {
|
|
57
|
+
handles: Iterable<unknown>;
|
|
58
|
+
}, cause: unknown) => void): void;
|
|
59
|
+
/**
|
|
60
|
+
* @internal — invoke the poisoning hook. For the bracket-break sites that
|
|
61
|
+
* live outside this module (`Thread.resumeWith`, exec/boundary.ts `poison`):
|
|
62
|
+
* one seam, all sites.
|
|
63
|
+
*/
|
|
64
|
+
export declare function notifyInstancePoisoned(inst: {
|
|
65
|
+
handles: Iterable<unknown>;
|
|
66
|
+
}, cause: unknown): void;
|
|
67
|
+
export declare function isInstancePoisoned(inst: object): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* May a settled activation tail for parked thread `t` be DISPATCHED now
|
|
70
|
+
* (issue #156)? True iff its instance is host-enterable — `Thread.resumeWith`
|
|
71
|
+
* brackets the resumption with `enterFrom(null)` — or POISONED, in which case
|
|
72
|
+
* `resumeWith`'s early return retires it and deferring would leak forever.
|
|
73
|
+
*
|
|
74
|
+
* CONTRACT: a parked entry without a reachable `task.inst` (the partial
|
|
75
|
+
* thread doubles the host-pump tests park in `Store.awaiting`) holds no
|
|
76
|
+
* reentrance state, so there is nothing to defer on: dispatchable.
|
|
77
|
+
*/
|
|
78
|
+
export declare function dispatchableTail(t: any): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* The recorded cause of an instance's poisoning: the original trap that
|
|
81
|
+
* broke the enter/leave bracket (polyengine#145). `undefined` when the instance
|
|
82
|
+
* is not poisoned — and, degenerately, when the poisoning cause itself was
|
|
83
|
+
* a thrown `undefined`; use `isInstancePoisoned` for the predicate.
|
|
84
|
+
*/
|
|
85
|
+
export declare function instancePoisonCause(inst: object): unknown;
|
|
86
|
+
/**
|
|
87
|
+
* Append the recorded poison cause to an entry-refusal trap message
|
|
88
|
+
* (polyengine#145 ask 1). "cannot enter component instance" covers two states
|
|
89
|
+
* that send an embedder down entirely different debugging paths — a
|
|
90
|
+
* transient reentrance overlap (retry later, look for caller-side call
|
|
91
|
+
* overlap) and a permanently poisoned instance (the corpse of an earlier
|
|
92
|
+
* trap, which this suffix names). Only the poisoned case gets the suffix:
|
|
93
|
+
* the transient message stays byte-identical, and the suffix is
|
|
94
|
+
* conformance-safe because the official suite matches trap messages by
|
|
95
|
+
* substring (harness/src/runner.ts).
|
|
96
|
+
*/
|
|
97
|
+
export declare function withPoisonCause(inst: object, base: string): string;
|
|
98
|
+
/** Test hook: switch policy at runtime. `null` restores FIFO. */
|
|
99
|
+
export declare function schedulerSeedForTesting(value: number | null): void;
|
|
100
|
+
export declare function schedulerPolicy(): "fifo" | "seeded-shuffle";
|
|
101
|
+
/**
|
|
102
|
+
* Pick one candidate. FIFO (index 0 — candidates are supplied in
|
|
103
|
+
* ready-order) unless a seed is configured, in which case a seeded uniform
|
|
104
|
+
* choice, mirroring the reference's `random.choice`.
|
|
105
|
+
*/
|
|
106
|
+
export declare function chooseCandidate<T>(candidates: readonly T[]): T;
|
|
107
|
+
export interface CurrentThreadLike {
|
|
108
|
+
storage: number[];
|
|
109
|
+
task: any;
|
|
110
|
+
}
|
|
111
|
+
export declare function pushCurrentThread(t: CurrentThreadLike): void;
|
|
112
|
+
export declare function popCurrentThread(t: CurrentThreadLike): void;
|
|
113
|
+
/**
|
|
114
|
+
* Run `fn` with `t` as the ambient, for `fn`'s SYNCHRONOUS extent.
|
|
115
|
+
*
|
|
116
|
+
* This is the wasm-entry bracket (`awaitCore`). It is the same `threadStack`
|
|
117
|
+
* the scheduler's own `resume()` bracket uses, deliberately: a wasm entry made
|
|
118
|
+
* from *inside* an engine-driven resumption (a FACT callee reached from a
|
|
119
|
+
* resumed activation — fact_calls.ts) has an empty scheduler bracket, and the
|
|
120
|
+
* entry itself is then the most specific statement of who is running.
|
|
121
|
+
*/
|
|
122
|
+
export declare function withActivation<T>(t: any, fn: () => T): T;
|
|
123
|
+
/**
|
|
124
|
+
* Record that the engine will run `t`'s wasm outside our frames.
|
|
125
|
+
*
|
|
126
|
+
* Idempotent in MEMBERSHIP but not in POSITION: re-claiming MOVES an
|
|
127
|
+
* existing claim to the top. The stack's contract is "top = the innermost
|
|
128
|
+
* activation the engine is running outside our frames", and a re-claim is
|
|
129
|
+
* direct evidence that `t` is running RIGHT NOW (its Suspending import just
|
|
130
|
+
* returned into its wasm). The previous early-return kept stale order: a
|
|
131
|
+
* nested callee's claim whose release edge is a promise reaction
|
|
132
|
+
* (`Store.noteAwaiting` -> `releaseClaimOf`) outlives the callee by a
|
|
133
|
+
* microtask, and an outer activation's continuation chunk that resumed in
|
|
134
|
+
* that window re-claimed itself as a NOOP — leaving the finished callee on
|
|
135
|
+
* top, so every ambient read in the rest of the chunk (the next hop's
|
|
136
|
+
* `owner` capture, and any unsafe intrinsic like `context.set`, which has
|
|
137
|
+
* no hop to re-anchor on) answered the wrong thread. Found as issue #24:
|
|
138
|
+
* wit-bindgen's callback epilogue restored its task pointer into another
|
|
139
|
+
* thread's context slots, and the next disciplined callback invocation
|
|
140
|
+
* panicked on a null slot (async_support.rs:578).
|
|
141
|
+
*
|
|
142
|
+
* A null/undefined activation is "no claim" — the instantiation-time shape
|
|
143
|
+
* that has no thread at all.
|
|
144
|
+
*/
|
|
145
|
+
export declare function claimActivationAmbient(t: any): void;
|
|
146
|
+
export declare function dbgId(t: unknown): string;
|
|
147
|
+
/**
|
|
148
|
+
* Drop `t`'s activation-ambient claim, if it holds one.
|
|
149
|
+
*
|
|
150
|
+
* The two closing edges: the activation PARKS on a fresh suspension
|
|
151
|
+
* (`blockCurrentActivation`), or it FINISHES — its `awaitValue` promise
|
|
152
|
+
* settles, normally or by rejection, and `Store.noteAwaiting`'s eager settle
|
|
153
|
+
* continuation calls this. The `task.implicitThread` indirection covers the
|
|
154
|
+
* second edge for claims taken against a task's implicit thread.
|
|
155
|
+
*/
|
|
156
|
+
export declare function releaseActivationAmbient(t: any): void;
|
|
157
|
+
/** Claim the ambient for `t` across an engine-driven resumption. */
|
|
158
|
+
export declare function setResumingThread(t: any): void;
|
|
159
|
+
/** Is a settled-but-not-yet-run activation holding the ambient? */
|
|
160
|
+
export declare function hasResumingThread(): boolean;
|
|
161
|
+
/** Release the claim; called once we are back in our own continuation. */
|
|
162
|
+
export declare function clearResumingThread(): void;
|
|
163
|
+
/**
|
|
164
|
+
* Release the driver's claim iff its activation is demonstrably RUNNING —
|
|
165
|
+
* i.e. the claim names the same thread the ACTIVATION AMBIENT names for the
|
|
166
|
+
* code calling us. The claim exists to cover the window between settling a
|
|
167
|
+
* suspension and the resumed activation running; once that activation's own
|
|
168
|
+
* code is on the stack the window is closed, and holding the claim would
|
|
169
|
+
* falsely trip the one-claimant assert when the running activation's built-in
|
|
170
|
+
* settles ANOTHER activation's suspension — `subtask.cancel` delivering a
|
|
171
|
+
* cancellation to a parked callee (cancellable.wast) is exactly that shape.
|
|
172
|
+
* When the two disagree (or no ambient is present) the claim stays, and the
|
|
173
|
+
* assert keeps guarding the genuine two-unrun-claimants bug it was built for.
|
|
174
|
+
*
|
|
175
|
+
* The comparison used to be against the async-context store; it is now
|
|
176
|
+
* against `activationOf()`, which is the same statement made explicitly.
|
|
177
|
+
*/
|
|
178
|
+
export declare function consumeClaimIfRunning(): void;
|
|
179
|
+
/**
|
|
180
|
+
* Release the claim iff it names `t` — the settle-side half of the claim
|
|
181
|
+
* discipline: a claim taken when `t`'s suspension was settled dies when `t`'s
|
|
182
|
+
* activation finishes (its `awaitValue` promise settles; `Store.noteAwaiting`
|
|
183
|
+
* calls this from the eager settle continuation) or parks again
|
|
184
|
+
* (`blockCurrentActivation` consumes via `consumeClaimIfRunning`).
|
|
185
|
+
*
|
|
186
|
+
* `t` FINISHING also ends its activation ambient, so both are dropped here.
|
|
187
|
+
*/
|
|
188
|
+
export declare function releaseClaimOf(t: any): void;
|
|
189
|
+
/** Diagnostic (#24 probe): the full ambient state, for tracing. */
|
|
190
|
+
export declare function ambientDebug(): {
|
|
191
|
+
stack: unknown[];
|
|
192
|
+
claims: unknown[];
|
|
193
|
+
resuming: unknown;
|
|
194
|
+
};
|
|
195
|
+
/** Diagnostic: module-scope state that must NOT survive a completed call. */
|
|
196
|
+
export declare function ambientResidue(): {
|
|
197
|
+
stack: number;
|
|
198
|
+
claim: boolean;
|
|
199
|
+
};
|
|
200
|
+
export declare function currentThread<T = CurrentThreadLike>(): T;
|
|
201
|
+
export declare function maybeCurrentThread(): CurrentThreadLike | undefined;
|
|
202
|
+
/** definitions.py `current_task()` (line 309). */
|
|
203
|
+
export declare function currentTask(): any;
|
|
204
|
+
/**
|
|
205
|
+
* The running task, or `null` outside any task — e.g. a core module's start
|
|
206
|
+
* function during instantiation, which the reference has no model for.
|
|
207
|
+
*/
|
|
208
|
+
export declare function maybeCurrentTask(): any | null;
|
|
209
|
+
/** definitions.py `current_instance()` (line 312). */
|
|
210
|
+
export declare function currentInstance(): any;
|
|
211
|
+
/** Structural view of a Thread, as the store's ready queue needs it. */
|
|
212
|
+
export interface SchedulableThread {
|
|
213
|
+
ready(): boolean;
|
|
214
|
+
waiting(): boolean;
|
|
215
|
+
resume(cancelled?: Cancelled): void;
|
|
216
|
+
task: any;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The embedder-visible scheduler state (definitions.py `Store`). One per
|
|
220
|
+
* instantiated component in this runtime — the reference shares one `Store`
|
|
221
|
+
* across component instances of a linked graph, and so do we: `Executor`
|
|
222
|
+
* creates a single `Store` and hands it to every `ComponentInstanceState`.
|
|
223
|
+
*
|
|
224
|
+
* `waiting` is kept as an **array, in insertion order**, which is what makes
|
|
225
|
+
* the default policy FIFO: `readyCandidates()` preserves the order in which
|
|
226
|
+
* threads started waiting.
|
|
227
|
+
*/
|
|
228
|
+
export declare class Store {
|
|
229
|
+
readonly waiting: SchedulableThread[];
|
|
230
|
+
/**
|
|
231
|
+
* Host-import promises this store is waiting on. Non-empty means progress
|
|
232
|
+
* is possible but only after a microtask turn — see `drive` in
|
|
233
|
+
* exec/boundary.ts. (definitions.py has no analogue: its host functions run
|
|
234
|
+
* on real threads.)
|
|
235
|
+
*/
|
|
236
|
+
readonly pendingHostCalls: Set<Promise<unknown>>;
|
|
237
|
+
/**
|
|
238
|
+
* An exception raised by a host import's promise (a rejection, or a trap
|
|
239
|
+
* thrown while lowering its results). It cannot propagate out of the
|
|
240
|
+
* microtask that produced it, so it is parked here and rethrown by whoever
|
|
241
|
+
* is driving the store — which is the call the guest is blocked in.
|
|
242
|
+
*/
|
|
243
|
+
hostFailure: unknown;
|
|
244
|
+
startWaiting(t: SchedulableThread): void;
|
|
245
|
+
stopWaiting(t: SchedulableThread): void;
|
|
246
|
+
/** Ready waiting threads, in wait order (the FIFO of the default policy). */
|
|
247
|
+
readyCandidates(): SchedulableThread[];
|
|
248
|
+
/**
|
|
249
|
+
* Threads parked on a Promise (the jspi `awaitValue` seam). They are not in
|
|
250
|
+
* `waiting` — nothing the scheduler can do makes them ready — so the driving
|
|
251
|
+
* loop tracks them separately and resumes them when their promise settles.
|
|
252
|
+
*/
|
|
253
|
+
readonly awaiting: Set<any>;
|
|
254
|
+
/**
|
|
255
|
+
* Settled-but-unserviced activation tails, in settle order.
|
|
256
|
+
*
|
|
257
|
+
* A settled `awaitValue` is the rest of an activation that already finished
|
|
258
|
+
* its wasm: result shaping, the callback loop, `exit_implicit_thread` (and
|
|
259
|
+
* with it the exclusive-thread release). The reference runs all of that
|
|
260
|
+
* atomically inside `Thread.resume`; under jspi it lands a few engine
|
|
261
|
+
* microtasks after the observable effects of the activation (`task.return`
|
|
262
|
+
* flips `resolved` DURING the wasm, the settle only afterwards — jspi
|
|
263
|
+
* pin (j)). Any scheduling decision taken in that window sees phantom
|
|
264
|
+
* state — a finished callee still "holding" its exclusive slot made
|
|
265
|
+
* cancellable.wast report STARTING for an entry the reference admits. So
|
|
266
|
+
* settlement is recorded EAGERLY (at park time, below), `tick` refuses to
|
|
267
|
+
* run anything while a tail is unserviced, and the driving loop services
|
|
268
|
+
* this queue first.
|
|
269
|
+
*/
|
|
270
|
+
readonly settled: {
|
|
271
|
+
t: any;
|
|
272
|
+
value: unknown;
|
|
273
|
+
failure: {
|
|
274
|
+
error: unknown;
|
|
275
|
+
} | undefined;
|
|
276
|
+
}[];
|
|
277
|
+
/**
|
|
278
|
+
* Park `t` on `promise` (jspi `awaitValue`), with EAGER settle tracking.
|
|
279
|
+
*
|
|
280
|
+
* The `.then` here is also what closes the claim discipline for
|
|
281
|
+
* resumptions the driver did not settle itself (a guest built-in resolving
|
|
282
|
+
* another activation's suspension — `subtask.cancel` delivering a
|
|
283
|
+
* cancellation): the claim taken at settle time must survive until the
|
|
284
|
+
* resumed activation parks again or finishes, and "finished" is exactly
|
|
285
|
+
* this continuation firing. See `releaseClaimOf`.
|
|
286
|
+
*/
|
|
287
|
+
noteAwaiting(t: any, promise: Promise<unknown>): void;
|
|
288
|
+
/**
|
|
289
|
+
* Service settled activation tails. Returns whether anything ran. EVERY
|
|
290
|
+
* driving loop must call this before (and interleaved with) `tick` — the
|
|
291
|
+
* queue gates `tick`, so a driver that never services it wedges the store
|
|
292
|
+
* (observed: host-stream pumping between export calls). A `resumeWith` may
|
|
293
|
+
* throw (trap unwinding); callers propagate or park it exactly as they do
|
|
294
|
+
* for `tick`.
|
|
295
|
+
*
|
|
296
|
+
* A tail whose instance is NOT host-enterable is DEFERRED IN PLACE — left
|
|
297
|
+
* in the queue, skipped here — until the lock releases (issue #156).
|
|
298
|
+
* `resumeWith` brackets the resumption with `enterFrom(null)`, and under
|
|
299
|
+
* the shared synthetic per-instantiation root a host entry into ANY
|
|
300
|
+
* instance of the graph locks the root, so while one instance is entered a
|
|
301
|
+
* sibling's tail cannot be dispatched: dispatching it tripped
|
|
302
|
+
* `resumeWith`'s enterability assert (which, mutating before asserting,
|
|
303
|
+
* also stranded the thread and lost the settle).
|
|
304
|
+
*
|
|
305
|
+
* Deferral is safe because `!inst.mayEnterFrom(null)` is EXACTLY `tick`'s
|
|
306
|
+
* candidate-filter predicate on the same instance: while a tail of `inst`
|
|
307
|
+
* is deferred, `tick` cannot resume any thread of `inst` either, so the
|
|
308
|
+
* phantom-state gate the queue exists to enforce is preserved per-instance
|
|
309
|
+
* by construction.
|
|
310
|
+
*
|
|
311
|
+
* The ordering discipline is therefore per-instance settle order. Cross-
|
|
312
|
+
* instance order relaxes only when enterability defers a tail, which is
|
|
313
|
+
* conforming schedule nondeterminism: in definitions.py the tail runs
|
|
314
|
+
* atomically inside the entered bracket, so a host entry admitted during a
|
|
315
|
+
* park necessarily orders before the parked activation's tail there.
|
|
316
|
+
*
|
|
317
|
+
* A POISONED instance's tail is still dispatched: `resumeWith`'s poison
|
|
318
|
+
* early-return retires it, and deferring it would leak forever — a
|
|
319
|
+
* poisoned leaf keeps its lock permanently.
|
|
320
|
+
*/
|
|
321
|
+
serviceSettled(): boolean;
|
|
322
|
+
/**
|
|
323
|
+
* "Would a `serviceSettled` call make progress right now?" — i.e. some
|
|
324
|
+
* entry is stale (would be removed) or serviceable (would be dispatched).
|
|
325
|
+
* A queue holding ONLY deferred tails (issue #156) answers false: `tick`
|
|
326
|
+
* must not be gated by them, and the driving loops must not spin on them.
|
|
327
|
+
*/
|
|
328
|
+
hasServiceableSettled(): boolean;
|
|
329
|
+
/**
|
|
330
|
+
* "Does component instance `inst` still have runnable work?" — the
|
|
331
|
+
* drain-to-quiescence predicate behind the **deferred entry decision**
|
|
332
|
+
* (issue #43).
|
|
333
|
+
*
|
|
334
|
+
* wasmtime decides an async-lowered call's initial status only after the
|
|
335
|
+
* executor has drained the work queued ahead of it: a queued
|
|
336
|
+
* `GuestCall(StartImplicit)` is popped, and if `is_ready` is false
|
|
337
|
+
* (`do_not_enter || backpressure`) the caller is told STARTING
|
|
338
|
+
* (concurrent.rs :1497-1522, :3040-3160). That formulation is FIFO-order
|
|
339
|
+
* dependent; polyengine uses the order-robust restatement (issue #43): *the
|
|
340
|
+
* call reports STARTING only if the callee is still unstarted after the
|
|
341
|
+
* instance's runnable work has been exhausted* — drain to quiescence, not
|
|
342
|
+
* pop-one. That is what keeps `sync-streams.wast` green under
|
|
343
|
+
* `POLYENGINE_SCHED_SEED` shuffles, which wasmtime's own rule would not be.
|
|
344
|
+
* Adjudicated 2026-08-10 (issue #43): entry-status timing is NOT
|
|
345
|
+
* normative — this predicate implements a scheduler *policy*, picked so
|
|
346
|
+
* the suite's schedule-overfitted STARTED assertion holds under any
|
|
347
|
+
* seed; the hold-rule gate itself is the spec semantics.
|
|
348
|
+
*
|
|
349
|
+
* "Runnable work of `inst`" is, exhaustively:
|
|
350
|
+
*
|
|
351
|
+
* (a) a settled-but-unserviced activation tail (`settled`) — bookkeeping
|
|
352
|
+
* the reference runs atomically inside `Thread.resume`, so the
|
|
353
|
+
* instance is mid-step, not quiescent;
|
|
354
|
+
* (b) a waiting entry (thread or `SuspensionPoint`) of `inst` that is
|
|
355
|
+
* `ready()` — the scheduler will resume it on the next tick. A gate
|
|
356
|
+
* holder parked mid-frame on an un-rendezvous'd operation is NOT
|
|
357
|
+
* ready and therefore contributes nothing: that is the "holder
|
|
358
|
+
* cannot be drained" case, whose answer is STARTING;
|
|
359
|
+
* (c) a thread of `inst` in `awaiting` whose promise is not a scheduler
|
|
360
|
+
* park — i.e. genuinely in flight across an engine microtask hop.
|
|
361
|
+
* A JSPI-parked activation appears in `awaiting` *and* owns a
|
|
362
|
+
* `SuspensionPoint` in `waiting` (`SuspensionPoint.owner`), and is
|
|
363
|
+
* accounted for by (b) instead; counting it here would make the
|
|
364
|
+
* instance permanently non-quiescent.
|
|
365
|
+
*
|
|
366
|
+
* `excludeTask` is the CALLER's task, and is excluded everywhere: the
|
|
367
|
+
* caller cannot be drained — it is the activation asking the question.
|
|
368
|
+
* This is what makes the "only obstacle is the current running activation"
|
|
369
|
+
* shape (a nested lower from inside the gate holder's own invocation)
|
|
370
|
+
* answer STARTING immediately, with no park at all.
|
|
371
|
+
*/
|
|
372
|
+
hasRunnableWork(inst: unknown, excludeTask: unknown): boolean;
|
|
373
|
+
/**
|
|
374
|
+
* definitions.py `Store.tick` (line 597): resume one ready thread, bracketed
|
|
375
|
+
* by the reentrance gate for a host-initiated entry (`enter_from(None)` /
|
|
376
|
+
* `leave_to(None)`).
|
|
377
|
+
*
|
|
378
|
+
* Returns false when no thread was ready, so callers can distinguish
|
|
379
|
+
* "made progress" from "stuck" without inspecting the queue themselves.
|
|
380
|
+
*/
|
|
381
|
+
tick(): boolean;
|
|
382
|
+
}
|
|
383
|
+
/** Mark `p` as an activity arm (exec/host_streams.ts `HostActivity`). */
|
|
384
|
+
export declare function markHostActivityArm(p: Promise<unknown>): void;
|
|
385
|
+
/** Is there host-call work outstanding that is not just an activity arm? */
|
|
386
|
+
export declare function hasRealHostCall(store: Store): boolean;
|
|
387
|
+
/** Every outstanding host call that is real work (not an activity arm). */
|
|
388
|
+
export declare function realHostCalls(store: Store): Promise<unknown>[];
|
|
389
|
+
/**
|
|
390
|
+
* Is there anything left that only a turn of the event loop could advance?
|
|
391
|
+
* Activity arms do not count: they say "the embedder may still act", which is
|
|
392
|
+
* precisely the state in which a between-calls driver should stop and let the
|
|
393
|
+
* operation's promise stay pending (the documented hang, exec/host_streams.ts
|
|
394
|
+
* module header).
|
|
395
|
+
*
|
|
396
|
+
* `store.settled` (settled-but-unserviced activation tails) DOES count: it
|
|
397
|
+
* gates `tick`, so exiting with a tail queued is a lost wakeup — the store is
|
|
398
|
+
* wedged until some other driver appears.
|
|
399
|
+
*/
|
|
400
|
+
export declare function storeQuiescent(store: Store): boolean;
|
|
401
|
+
/**
|
|
402
|
+
* The reference's `canon_lift` sync driving loop (line 2213):
|
|
403
|
+
*
|
|
404
|
+
* ```python
|
|
405
|
+
* while task.state != Task.State.RESOLVED:
|
|
406
|
+
* candidates = { t for t in inst.threads if t.ready() and t is not inst.exclusive_thread }
|
|
407
|
+
* trap_if(not candidates)
|
|
408
|
+
* random.choice(list(candidates)).resume()
|
|
409
|
+
* ```
|
|
410
|
+
*
|
|
411
|
+
* Note the candidate set is `inst.threads` — threads *of the callee instance*
|
|
412
|
+
* — and excludes the exclusive thread, and that an empty set is a **trap**
|
|
413
|
+
* (the spec's deadlock trap), not a hang.
|
|
414
|
+
*/
|
|
415
|
+
export declare function driveSyncLift(task: {
|
|
416
|
+
state: string;
|
|
417
|
+
inst: {
|
|
418
|
+
threads: Iterable<SchedulableThread>;
|
|
419
|
+
exclusiveThread: unknown;
|
|
420
|
+
};
|
|
421
|
+
}): void;
|