@solidjs/signals 2.0.0-rc.10 → 2.0.0-rc.11
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/dist/dev-shared.js +28 -11
- package/dist/dev.js +79 -10
- package/dist/observe/core/optimistic.js +60 -18
- package/dist/observe/core/scheduler.js +20 -6
- package/dist/observe/map.js +241 -218
- package/dist/prod/core/optimistic.js +62 -20
- package/dist/prod/core/owner.js +2 -2
- package/dist/prod/core/scheduler.js +29 -15
- package/dist/prod/core/verdict.js +1 -1
- package/dist/prod/map.js +200 -177
- package/dist/prod/store/next/optimistic.js +2 -2
- package/dist/types/core/scheduler.d.ts +5 -1
- package/package.json +1 -1
package/dist/dev-shared.js
CHANGED
|
@@ -1932,7 +1932,11 @@ class GlobalQueue extends Queue {
|
|
|
1932
1932
|
* commit whatever its relation to the committed value — a landing equal to
|
|
1933
1933
|
* committed still differs from the override — then _supersedeOverride
|
|
1934
1934
|
* decides. Installed with the optimistic engine; only reachable on a node
|
|
1935
|
-
* that has an override
|
|
1935
|
+
* that has an override — or, from `mapArray` once a lane pass has run over
|
|
1936
|
+
* the map, on a per-slot signal (never CONFIG_OPTIMISTIC): the slot arm
|
|
1937
|
+
* publishes a lane pass's write as the slot's derived override, lands a
|
|
1938
|
+
* plain pass's write over one, and is the plain `setSignal` otherwise (F1,
|
|
1939
|
+
* see optimistic.ts landOnOverride). */
|
|
1936
1940
|
static _landOnOverride = null;
|
|
1937
1941
|
static _trackOptimisticStore = null;
|
|
1938
1942
|
flush() {
|
|
@@ -2073,8 +2077,16 @@ class GlobalQueue extends Queue {
|
|
|
2073
2077
|
// sweep or recompute wrote a node it owns): effects computed under it
|
|
2074
2078
|
// since are its to apply, not this flush's — runEffect leaves them queued
|
|
2075
2079
|
// and the next pass parks them with it (#3319). Everything computed
|
|
2076
|
-
// mainline applies now.
|
|
2077
|
-
|
|
2080
|
+
// mainline applies now. A write the finalize staged in the ambient
|
|
2081
|
+
// batch with no subscriber to dirty (an optimistic store settle's
|
|
2082
|
+
// keyset bump under a reader that never tracks the key set) is work
|
|
2083
|
+
// too — the fast drain and the park exit already count it — so the
|
|
2084
|
+
// next round commits it and the woken re-entry below does not adopt
|
|
2085
|
+
// it into a parked transaction it never belonged to (matrix F6).
|
|
2086
|
+
scheduled =
|
|
2087
|
+
dirtyQueue._max >= dirtyQueue._min ||
|
|
2088
|
+
activeTransition !== null ||
|
|
2089
|
+
this._batch._pendingNodes.length !== 0;
|
|
2078
2090
|
// Run lane effects first (for ready lanes), then regular effects
|
|
2079
2091
|
activeLanes.size && GlobalQueue._runLaneEffects(EFFECT_RENDER);
|
|
2080
2092
|
this.run(EFFECT_RENDER);
|
|
@@ -2105,9 +2117,14 @@ class GlobalQueue extends Queue {
|
|
|
2105
2117
|
// idle pass: entering adopts the ambient batch, and staged or dirty
|
|
2106
2118
|
// ambient work would be held behind flights it never read. `scheduled`
|
|
2107
2119
|
// is that test here — after the park exit as well as the normal one:
|
|
2108
|
-
// it was recomputed from the heap
|
|
2109
|
-
//
|
|
2110
|
-
//
|
|
2120
|
+
// it was recomputed from the heap and the ambient batch's staged nodes
|
|
2121
|
+
// this pass, every write since re-armed it, and optimistic ambient
|
|
2122
|
+
// nodes reverted with the finalize — so a wake in a pass with work
|
|
2123
|
+
// simply falls to the next. (A staged node with no subscriber — the
|
|
2124
|
+
// finalize's keyset bump under a length-only reader — used to be
|
|
2125
|
+
// missed here: the wake adopted it, stamped it, and a later ambient
|
|
2126
|
+
// write to the same node joined the parked transaction and never
|
|
2127
|
+
// reverted; matrix F6.) Entering re-arms
|
|
2111
2128
|
// it itself; a dead (completed) wake is a bare return in
|
|
2112
2129
|
// initTransition, and the loop moves on to the next.
|
|
2113
2130
|
while (!scheduled && !activeTransition && wokenTransitions.length)
|
|
@@ -6967,13 +6984,13 @@ export {
|
|
|
6967
6984
|
currentTransition as M,
|
|
6968
6985
|
NoOwnerError as N,
|
|
6969
6986
|
OVERRIDE_UNDEFINED as O,
|
|
6970
|
-
|
|
6971
|
-
|
|
6987
|
+
assignOrMergeLane as P,
|
|
6988
|
+
queuePendingNode as Q,
|
|
6972
6989
|
REACTIVE_DIRTY as R,
|
|
6973
6990
|
STATUS_UNINITIALIZED as S,
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6991
|
+
latestReadActive as T,
|
|
6992
|
+
REACTIVE_MANUAL_WRITE as U,
|
|
6993
|
+
REACTIVE_OPTIMISTIC_DIRTY as V,
|
|
6977
6994
|
LANE_RUN as W,
|
|
6978
6995
|
attrHooks as X,
|
|
6979
6996
|
EFFECT_USER as Y,
|
package/dist/dev.js
CHANGED
|
@@ -42,11 +42,11 @@ import {
|
|
|
42
42
|
K as currentOptimisticLane,
|
|
43
43
|
L as enterStagedRead,
|
|
44
44
|
M as currentTransition,
|
|
45
|
-
P as
|
|
46
|
-
Q as
|
|
47
|
-
T as
|
|
48
|
-
U as
|
|
49
|
-
V as
|
|
45
|
+
P as assignOrMergeLane,
|
|
46
|
+
Q as queuePendingNode,
|
|
47
|
+
T as latestReadActive,
|
|
48
|
+
U as REACTIVE_MANUAL_WRITE,
|
|
49
|
+
V as REACTIVE_OPTIMISTIC_DIRTY,
|
|
50
50
|
W as LANE_RUN,
|
|
51
51
|
X as attrHooks,
|
|
52
52
|
Y as EFFECT_USER,
|
|
@@ -750,8 +750,53 @@ function overrideRead(el, c) {
|
|
|
750
750
|
* action provenance, or an A17-silent confirmation (#3331). Before this the
|
|
751
751
|
* landing took setSignal's plain path: a differing truth staged silently
|
|
752
752
|
* under the override and the graph never moved until the commit.
|
|
753
|
+
*
|
|
754
|
+
* Slot arm (list-matrix F1): `mapArray`'s writes to its per-slot signals —
|
|
755
|
+
* the row accessors of index mode, the index accessors of keyed mode; never
|
|
756
|
+
* CONFIG_OPTIMISTIC, which is how the arm tells them from the store landings
|
|
757
|
+
* above — once a lane pass has run over the map. The list's frame lives in
|
|
758
|
+
* these writes as much as in the computed's result. Under a LANE pass the
|
|
759
|
+
* write is the lane's frame, as the computed's result is (lanes stage,
|
|
760
|
+
* `laneOverride`): the slot joins the lane carrying a DERIVED override —
|
|
761
|
+
* `_value` stays the committed row, the lane's readers and untracked reads
|
|
762
|
+
* see the override (A17), an off-lane render effect sees the committed frame
|
|
763
|
+
* (#3460), the revert promotes or drops it with the lane's transaction. The
|
|
764
|
+
* gate compares against the slot the pass publishes to (INV-11): the override
|
|
765
|
+
* when armed, the committed row otherwise. A plain `setSignal` here staged
|
|
766
|
+
* the write into the ACTION's transaction: `<For>` without `keyed` showed the
|
|
767
|
+
* pre-action list for the whole action while the keyed modes showed the
|
|
768
|
+
* optimistic one. A PLAIN pass over a slot still carrying the lane's frame —
|
|
769
|
+
* the landing, the reversion, a mainline re-pass — is the truth arriving
|
|
770
|
+
* under an override and takes the landing path below: a differing truth
|
|
771
|
+
* supersedes, an equal one confirms and the revert promotes. Every pass
|
|
772
|
+
* rewrites every slot whose value differs from the previous frame's (index
|
|
773
|
+
* mode rewrites every surviving slot), so a slot the landing does NOT write
|
|
774
|
+
* holds, by construction, what the truth yields. A slot with neither a lane
|
|
775
|
+
* nor an override is the plain write.
|
|
753
776
|
*/
|
|
754
777
|
function landOnOverride(el, v) {
|
|
778
|
+
if (!(el._config & CONFIG_OPTIMISTIC)) {
|
|
779
|
+
const lane = currentOptimisticLane;
|
|
780
|
+
if (lane === null) {
|
|
781
|
+
if (!hasActiveOverride(el)) return setSignal(el, v);
|
|
782
|
+
} else {
|
|
783
|
+
// INV-11: the gate compares against the slot this pass publishes to —
|
|
784
|
+
// the override when one is armed, the committed row otherwise.
|
|
785
|
+
if (
|
|
786
|
+
!el._equals ||
|
|
787
|
+
!el._equals(hasActiveOverride(el) ? unwrapOverride(el._x._overrideValue) : el._value, v)
|
|
788
|
+
) {
|
|
789
|
+
// Membership before the publish: `laneOverride` files the slot under
|
|
790
|
+
// the lane's transaction, and the read path routes a tracked reader
|
|
791
|
+
// of a lane member through `overrideRead` (CONFIG_HAS_LANE).
|
|
792
|
+
assignOrMergeLane(el, lane);
|
|
793
|
+
laneOverride(el, v, lane);
|
|
794
|
+
insertSubs(el, true);
|
|
795
|
+
schedule();
|
|
796
|
+
}
|
|
797
|
+
return v;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
755
800
|
const currentValue = el._pendingValue === NOT_PENDING ? el._value : el._pendingValue;
|
|
756
801
|
if (typeof v === "function") v = v(currentValue);
|
|
757
802
|
if (attrHooks !== null) attrHooks.write(el, currentValue, v);
|
|
@@ -8960,6 +9005,30 @@ function updateKeyedMap() {
|
|
|
8960
9005
|
// entered rows, for the list-identity census.
|
|
8961
9006
|
removedItems,
|
|
8962
9007
|
createdItems;
|
|
9008
|
+
// The pass's write to a per-slot signal (a row accessor in index mode,
|
|
9009
|
+
// an index accessor in keyed mode). The list's frame lives in these
|
|
9010
|
+
// writes as much as in the computed's result, so under a LANE pass they
|
|
9011
|
+
// are the lane's frame too (F1): a plain `setSignal` staged them into
|
|
9012
|
+
// the action's transaction and the row readers were served the
|
|
9013
|
+
// committed value until the action landed — `<For>` without `keyed`
|
|
9014
|
+
// showed the pre-action list for the whole action while the keyed modes
|
|
9015
|
+
// showed the optimistic one. The engine publishes the write as a
|
|
9016
|
+
// derived override on the slot (lanes stage, A17) and lands or
|
|
9017
|
+
// supersedes it when a plain pass later writes a slot still carrying
|
|
9018
|
+
// one (A18) — `landOnOverride`'s slot arm. Decided once per pass, not
|
|
9019
|
+
// per slot: a lane pass marks the map (`_laneSlots`: its slots may
|
|
9020
|
+
// carry overrides), a marked map routes every slot write through the
|
|
9021
|
+
// engine while any lane is live — a slot's override resolves with its
|
|
9022
|
+
// lane's transaction, or with the batch for an orphan lane
|
|
9023
|
+
// (resolveOptimisticNodes, then cleanupCompletedLanes, both before the
|
|
9024
|
+
// completion wakes any recompute), so a pass that finds no lane live
|
|
9025
|
+
// knows the slots are clean and drops the mark — and an unmarked map
|
|
9026
|
+
// takes `setSignal` directly. The engine is installed whenever a lane
|
|
9027
|
+
// is active.
|
|
9028
|
+
const write = (this._laneSlots =
|
|
9029
|
+
currentOptimisticLane !== null || (this._laneSlots && activeLanes.size !== 0))
|
|
9030
|
+
? GlobalQueue._landOnOverride
|
|
9031
|
+
: setSignal;
|
|
8963
9032
|
// skip common prefix
|
|
8964
9033
|
for (
|
|
8965
9034
|
start = 0, end = Math.min(this._len, newLen);
|
|
@@ -8968,7 +9037,7 @@ function updateKeyedMap() {
|
|
|
8968
9037
|
(this._rows && compare(this._key, this._items[start], newItems[start])));
|
|
8969
9038
|
start++
|
|
8970
9039
|
) {
|
|
8971
|
-
if (this._rows)
|
|
9040
|
+
if (this._rows) write(this._rows[start], newItems[start]);
|
|
8972
9041
|
}
|
|
8973
9042
|
// skip common suffix — counted only; retained entries land in one pass
|
|
8974
9043
|
// at commit instead of being staged and copied twice
|
|
@@ -9069,19 +9138,19 @@ function updateKeyedMap() {
|
|
|
9069
9138
|
indexes && (indexes[i] = this._indexes[i]);
|
|
9070
9139
|
}
|
|
9071
9140
|
for (j = start; j <= newEnd; j++) {
|
|
9072
|
-
if (rows)
|
|
9073
|
-
if (indexes)
|
|
9141
|
+
if (rows) write(rows[j], newItems[j]);
|
|
9142
|
+
if (indexes) write(indexes[j], j);
|
|
9074
9143
|
}
|
|
9075
9144
|
for (j = newEnd + 1; j < newLen; j++) {
|
|
9076
9145
|
temp[j] = this._mappings[j - dif];
|
|
9077
9146
|
tempNodes[j] = this._nodes[j - dif];
|
|
9078
9147
|
if (rows) {
|
|
9079
9148
|
rows[j] = this._rows[j - dif];
|
|
9080
|
-
|
|
9149
|
+
write(rows[j], newItems[j]);
|
|
9081
9150
|
}
|
|
9082
9151
|
if (indexes) {
|
|
9083
9152
|
indexes[j] = this._indexes[j - dif];
|
|
9084
|
-
if (dif !== 0)
|
|
9153
|
+
if (dif !== 0) write(indexes[j], j);
|
|
9085
9154
|
}
|
|
9086
9155
|
}
|
|
9087
9156
|
this._mappings = temp;
|
|
@@ -2,7 +2,7 @@ import { NOT_PENDING, unwrapOverride, STATUS_UNINITIALIZED, REACTIVE_DIRTY, REAC
|
|
|
2
2
|
|
|
3
3
|
import { attrHooks } from "./attribution-hooks.js";
|
|
4
4
|
|
|
5
|
-
import { ext, hasActiveOverride, stale, currentOptimisticLane, enterStagedRead, latestReadActive } from "./core.js";
|
|
5
|
+
import { ext, hasActiveOverride, stale, currentOptimisticLane, enterStagedRead, setSignal, latestReadActive } from "./core.js";
|
|
6
6
|
|
|
7
7
|
import { NotReadyError } from "./error.js";
|
|
8
8
|
|
|
@@ -27,17 +27,17 @@ import { GlobalQueue, activeTransition, globalQueue, origin, clock, insertSubs,
|
|
|
27
27
|
*/
|
|
28
28
|
/** The optimistic half of setSignal, fired when `_overrideValue !== undefined`. */ function optimisticWrite(e, n) {
|
|
29
29
|
const i = e.o?.we !== NOT_PENDING;
|
|
30
|
-
const
|
|
31
|
-
if (typeof n === "function") n = n(
|
|
32
|
-
const
|
|
30
|
+
const r = i ? unwrapOverride(e.o?.we) : e.ce;
|
|
31
|
+
if (typeof n === "function") n = n(r);
|
|
32
|
+
const t = !!(e.S & STATUS_UNINITIALIZED) ||
|
|
33
33
|
// A dirty node's _value is stale (its queued recompute hasn't run — e.g.
|
|
34
34
|
// a latest() shadow marked by the previous landing's companion snap), so
|
|
35
35
|
// equality against it must not swallow the write. Without this, a sync
|
|
36
36
|
// push returning the shadow to that stale value was dropped, the snap
|
|
37
37
|
// recompute then committed the parent's old value, and the banner showed
|
|
38
38
|
// the previous transition's target (#3041 follow-up).
|
|
39
|
-
!!((e.oe ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.Fe || !e.Fe(
|
|
40
|
-
if (!
|
|
39
|
+
!!((e.oe ?? 0) & (REACTIVE_DIRTY | REACTIVE_CHECK)) || !e.Fe || !e.Fe(r, n);
|
|
40
|
+
if (!t) {
|
|
41
41
|
// Same-value write with an active override still entangles the current
|
|
42
42
|
// action's transition — the hold must outlast all overlapping actions —
|
|
43
43
|
// and renews the override's PROVENANCE: the newer action re-asks the
|
|
@@ -164,7 +164,7 @@ function resolveOptimisticNodes(e) {
|
|
|
164
164
|
// override-covered authoritative values hold in _pendingValue and
|
|
165
165
|
// elevate on their OWN transition's schedule (A18 as re-ruled 2026-07-07).
|
|
166
166
|
if (!(n.S & STATUS_PENDING)) n.S &= ~STATUS_UNINITIALIZED;
|
|
167
|
-
const
|
|
167
|
+
const r = n.o?.we;
|
|
168
168
|
// A derived override (lanes stage, #3479) has no truth of its own: the
|
|
169
169
|
// slot disarms — the memo is plain again — and the override PROMOTES to
|
|
170
170
|
// `_value`. Not superseded, nothing it derives from told it otherwise
|
|
@@ -174,18 +174,18 @@ function resolveOptimisticNodes(e) {
|
|
|
174
174
|
// the override IS what a recompute from the truth yields. Re-deriving
|
|
175
175
|
// instead re-asked an async member's flight and held the transaction on
|
|
176
176
|
// it — a waterfall after the reveal.
|
|
177
|
-
const
|
|
178
|
-
ext(n).we =
|
|
177
|
+
const t = n.C & CONFIG_DERIVED_OVERRIDE;
|
|
178
|
+
ext(n).we = t && !(n.C & CONFIG_OPTIMISTIC) ? undefined : NOT_PENDING;
|
|
179
179
|
// A superseded override's subscribers already re-derived from the truth
|
|
180
180
|
// when it arrived (#3331) — the drop changes nothing they read. Everyone
|
|
181
181
|
// else learns of the correction here: this drop IS their notification.
|
|
182
182
|
const u = (n.C & CONFIG_OVERRIDE_SUPERSEDED) !== 0;
|
|
183
183
|
n.C &= -8912897;
|
|
184
|
-
if (!u &&
|
|
185
|
-
if (
|
|
184
|
+
if (!u && r !== NOT_PENDING && n.ce !== unwrapOverride(r)) {
|
|
185
|
+
if (t) n.ce = unwrapOverride(r); else {
|
|
186
186
|
// The guess lifts and what was beneath it differs: the screen
|
|
187
187
|
// changes from the override to the committed value.
|
|
188
|
-
if (attrHooks !== null) attrHooks.optimisticReverted(n, unwrapOverride(
|
|
188
|
+
if (attrHooks !== null) attrHooks.optimisticReverted(n, unwrapOverride(r), n.ce, "reverted");
|
|
189
189
|
insertSubs(n, true);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
@@ -198,8 +198,8 @@ function resolveOptimisticNodes(e) {
|
|
|
198
198
|
for (let i = 0; i < n; i++) {
|
|
199
199
|
const n = e[i];
|
|
200
200
|
if (n.o?.xe || n.o?.Me) GlobalQueue.Qn(n);
|
|
201
|
-
const
|
|
202
|
-
if (
|
|
201
|
+
const r = n.o?.$n;
|
|
202
|
+
if (r && (r.o?.xe === n || r.o?.Me === n)) GlobalQueue.Qn(r);
|
|
203
203
|
}
|
|
204
204
|
e.splice(0, n);
|
|
205
205
|
}
|
|
@@ -257,14 +257,14 @@ function resolveOptimisticNodes(e) {
|
|
|
257
257
|
const i = e.o?.Ce;
|
|
258
258
|
if (i) {
|
|
259
259
|
const n = findLane(i);
|
|
260
|
-
const
|
|
261
|
-
while (
|
|
262
|
-
const e =
|
|
260
|
+
const r = [ e ];
|
|
261
|
+
while (r.length) {
|
|
262
|
+
const e = r.pop();
|
|
263
263
|
const i = e.o?.Ce;
|
|
264
264
|
if (!i || findLane(i) !== n) continue;
|
|
265
265
|
e.o.Ce = undefined;
|
|
266
266
|
n.ke.delete(e);
|
|
267
|
-
for (let n = e.u; n !== null; n = n.Pe)
|
|
267
|
+
for (let n = e.u; n !== null; n = n.Pe) r.push(n.Ae);
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
}
|
|
@@ -403,7 +403,49 @@ function resolveOptimisticNodes(e) {
|
|
|
403
403
|
* action provenance, or an A17-silent confirmation (#3331). Before this the
|
|
404
404
|
* landing took setSignal's plain path: a differing truth staged silently
|
|
405
405
|
* under the override and the graph never moved until the commit.
|
|
406
|
+
*
|
|
407
|
+
* Slot arm (list-matrix F1): `mapArray`'s writes to its per-slot signals —
|
|
408
|
+
* the row accessors of index mode, the index accessors of keyed mode; never
|
|
409
|
+
* CONFIG_OPTIMISTIC, which is how the arm tells them from the store landings
|
|
410
|
+
* above — once a lane pass has run over the map. The list's frame lives in
|
|
411
|
+
* these writes as much as in the computed's result. Under a LANE pass the
|
|
412
|
+
* write is the lane's frame, as the computed's result is (lanes stage,
|
|
413
|
+
* `laneOverride`): the slot joins the lane carrying a DERIVED override —
|
|
414
|
+
* `_value` stays the committed row, the lane's readers and untracked reads
|
|
415
|
+
* see the override (A17), an off-lane render effect sees the committed frame
|
|
416
|
+
* (#3460), the revert promotes or drops it with the lane's transaction. The
|
|
417
|
+
* gate compares against the slot the pass publishes to (INV-11): the override
|
|
418
|
+
* when armed, the committed row otherwise. A plain `setSignal` here staged
|
|
419
|
+
* the write into the ACTION's transaction: `<For>` without `keyed` showed the
|
|
420
|
+
* pre-action list for the whole action while the keyed modes showed the
|
|
421
|
+
* optimistic one. A PLAIN pass over a slot still carrying the lane's frame —
|
|
422
|
+
* the landing, the reversion, a mainline re-pass — is the truth arriving
|
|
423
|
+
* under an override and takes the landing path below: a differing truth
|
|
424
|
+
* supersedes, an equal one confirms and the revert promotes. Every pass
|
|
425
|
+
* rewrites every slot whose value differs from the previous frame's (index
|
|
426
|
+
* mode rewrites every surviving slot), so a slot the landing does NOT write
|
|
427
|
+
* holds, by construction, what the truth yields. A slot with neither a lane
|
|
428
|
+
* nor an override is the plain write.
|
|
406
429
|
*/ function landOnOverride(e, n) {
|
|
430
|
+
if (!(e.C & CONFIG_OPTIMISTIC)) {
|
|
431
|
+
const i = currentOptimisticLane;
|
|
432
|
+
if (i === null) {
|
|
433
|
+
if (!hasActiveOverride(e)) return setSignal(e, n);
|
|
434
|
+
} else {
|
|
435
|
+
// INV-11: the gate compares against the slot this pass publishes to —
|
|
436
|
+
// the override when one is armed, the committed row otherwise.
|
|
437
|
+
if (!e.Fe || !e.Fe(hasActiveOverride(e) ? unwrapOverride(e.o.we) : e.ce, n)) {
|
|
438
|
+
// Membership before the publish: `laneOverride` files the slot under
|
|
439
|
+
// the lane's transaction, and the read path routes a tracked reader
|
|
440
|
+
// of a lane member through `overrideRead` (CONFIG_HAS_LANE).
|
|
441
|
+
assignOrMergeLane(e, i);
|
|
442
|
+
laneOverride(e, n, i);
|
|
443
|
+
insertSubs(e, true);
|
|
444
|
+
schedule();
|
|
445
|
+
}
|
|
446
|
+
return n;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
407
449
|
const i = e._e === NOT_PENDING ? e.ce : e._e;
|
|
408
450
|
if (typeof n === "function") n = n(i);
|
|
409
451
|
if (attrHooks !== null) attrHooks.write(e, i, n);
|
|
@@ -631,7 +631,11 @@ class GlobalQueue extends Queue {
|
|
|
631
631
|
* commit whatever its relation to the committed value — a landing equal to
|
|
632
632
|
* committed still differs from the override — then _supersedeOverride
|
|
633
633
|
* decides. Installed with the optimistic engine; only reachable on a node
|
|
634
|
-
* that has an override
|
|
634
|
+
* that has an override — or, from `mapArray` once a lane pass has run over
|
|
635
|
+
* the map, on a per-slot signal (never CONFIG_OPTIMISTIC): the slot arm
|
|
636
|
+
* publishes a lane pass's write as the slot's derived override, lands a
|
|
637
|
+
* plain pass's write over one, and is the plain `setSignal` otherwise (F1,
|
|
638
|
+
* see optimistic.ts landOnOverride). */
|
|
635
639
|
static Vn=null;
|
|
636
640
|
static Jn=null;
|
|
637
641
|
flush() {
|
|
@@ -793,8 +797,13 @@ class GlobalQueue extends Queue {
|
|
|
793
797
|
// sweep or recompute wrote a node it owns): effects computed under it
|
|
794
798
|
// since are its to apply, not this flush's — runEffect leaves them queued
|
|
795
799
|
// and the next pass parks them with it (#3319). Everything computed
|
|
796
|
-
// mainline applies now.
|
|
797
|
-
|
|
800
|
+
// mainline applies now. A write the finalize staged in the ambient
|
|
801
|
+
// batch with no subscriber to dirty (an optimistic store settle's
|
|
802
|
+
// keyset bump under a reader that never tracks the key set) is work
|
|
803
|
+
// too — the fast drain and the park exit already count it — so the
|
|
804
|
+
// next round commits it and the woken re-entry below does not adopt
|
|
805
|
+
// it into a parked transaction it never belonged to (matrix F6).
|
|
806
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.st || activeTransition !== null || this.m.In.length !== 0;
|
|
798
807
|
// Run lane effects first (for ready lanes), then regular effects
|
|
799
808
|
activeLanes.size && GlobalQueue.Kn(EFFECT_RENDER);
|
|
800
809
|
this.run(EFFECT_RENDER);
|
|
@@ -808,9 +817,14 @@ class GlobalQueue extends Queue {
|
|
|
808
817
|
// idle pass: entering adopts the ambient batch, and staged or dirty
|
|
809
818
|
// ambient work would be held behind flights it never read. `scheduled`
|
|
810
819
|
// is that test here — after the park exit as well as the normal one:
|
|
811
|
-
// it was recomputed from the heap
|
|
812
|
-
//
|
|
813
|
-
//
|
|
820
|
+
// it was recomputed from the heap and the ambient batch's staged nodes
|
|
821
|
+
// this pass, every write since re-armed it, and optimistic ambient
|
|
822
|
+
// nodes reverted with the finalize — so a wake in a pass with work
|
|
823
|
+
// simply falls to the next. (A staged node with no subscriber — the
|
|
824
|
+
// finalize's keyset bump under a length-only reader — used to be
|
|
825
|
+
// missed here: the wake adopted it, stamped it, and a later ambient
|
|
826
|
+
// write to the same node joined the parked transaction and never
|
|
827
|
+
// reverted; matrix F6.) Entering re-arms
|
|
814
828
|
// it itself; a dead (completed) wake is a bare return in
|
|
815
829
|
// initTransition, and the loop moves on to the next.
|
|
816
830
|
while (!scheduled && !activeTransition && wokenTransitions.length) this.initTransition(wokenTransitions.pop());
|