@@ -7,6 +7,10 @@ let currentObserver: ref<option<Core.observer>> = ref(None)
77// Current dependency tracking version (shared across nested compute/effect runs)
88let currentTrackingVersion : ref <int > = ref (0 )
99
10+ // Per-run dependency cursors (separate from true tail pointers).
11+ let currentComputedDepCursor : ref <option <Core .link >> = ref (None )
12+ let currentObserverDepCursor : ref <option <Core .link >> = ref (None )
13+
1014// Pending effects to execute
1115let pendingEffects : array <Core .observer > = []
1216// Pending computeds to recompute (subs that are dirty)
@@ -44,20 +48,22 @@ let trackDepFromComputed = (computedSubs: Core.subs, sourceSubs: Core.subs): uni
4448 newLink .lastTrackedVersion = currentTrackingVersion .contents
4549 Core .linkToSubsDeps (computedSubs , newLink )
4650 Core .linkToSubs (sourceSubs , newLink )
51+ currentComputedDepCursor := Some (newLink )
4752 } else {
4853 let currentVersion = currentTrackingVersion .contents
49- // Fast path: reuse tail or tail .next to avoid scanning in common cases
54+ // Fast path: reuse run cursor or cursor .next to avoid scanning in common cases.
5055 let fastPathFound = ref (false )
51- switch computedSubs . lastDep {
52- | Some (lastDep ) =>
53- if lastDep .subs === sourceSubs {
54- lastDep .lastTrackedVersion = currentVersion
56+ switch currentComputedDepCursor . contents {
57+ | Some (cursor ) =>
58+ if cursor .subs === sourceSubs {
59+ cursor .lastTrackedVersion = currentVersion
5560 fastPathFound .contents = true
5661 } else {
57- switch lastDep .nextDep {
62+ switch cursor .nextDep {
5863 | Some (nextDep ) =>
5964 if nextDep .subs === sourceSubs {
6065 nextDep .lastTrackedVersion = currentVersion
66+ currentComputedDepCursor := Some (nextDep )
6167 fastPathFound .contents = true
6268 }
6369 | None => ()
@@ -71,6 +77,7 @@ let trackDepFromComputed = (computedSubs: Core.subs, sourceSubs: Core.subs): uni
7177 | Some (lastSubLink ) =>
7278 if lastSubLink .lastTrackedVersion === currentVersion && lastSubLink .observer === computedObserver {
7379 lastSubLink .lastTrackedVersion = currentVersion
80+ currentComputedDepCursor := Some (lastSubLink )
7481 fastPathFound .contents = true
7582 }
7683 | None => ()
@@ -80,12 +87,14 @@ let trackDepFromComputed = (computedSubs: Core.subs, sourceSubs: Core.subs): uni
8087 if ! fastPathFound .contents {
8188 // Fall back to full scan
8289 let found = ref (false )
90+ let foundLink : ref <option <Core .link >> = ref (None )
8391 let link = ref (computedSubs .firstDep )
8492 while link .contents !== None && ! found .contents {
8593 switch link .contents {
8694 | Some (l ) =>
8795 if l .subs === sourceSubs {
8896 l .lastTrackedVersion = currentVersion
97+ foundLink := Some (l )
8998 found := true
9099 } else {
91100 link := l .nextDep
@@ -100,6 +109,9 @@ let trackDepFromComputed = (computedSubs: Core.subs, sourceSubs: Core.subs): uni
100109 newLink .lastTrackedVersion = currentVersion
101110 Core .linkToSubsDeps (computedSubs , newLink )
102111 Core .linkToSubs (sourceSubs , newLink )
112+ currentComputedDepCursor := Some (newLink )
113+ } else {
114+ currentComputedDepCursor := foundLink .contents
103115 }
104116 }
105117 }
@@ -113,20 +125,22 @@ let trackDepFromEffect = (observer: Core.observer, sourceSubs: Core.subs): unit
113125 newLink .lastTrackedVersion = currentTrackingVersion .contents
114126 Core .linkToDeps (observer , newLink )
115127 Core .linkToSubs (sourceSubs , newLink )
128+ currentObserverDepCursor := Some (newLink )
116129 } else {
117130 let currentVersion = currentTrackingVersion .contents
118- // Fast path: reuse tail or tail .next to avoid scanning in common cases
131+ // Fast path: reuse run cursor or cursor .next to avoid scanning in common cases.
119132 let fastPathFound = ref (false )
120- switch observer . lastDep {
121- | Some (lastDep ) =>
122- if lastDep .subs === sourceSubs {
123- lastDep .lastTrackedVersion = currentVersion
133+ switch currentObserverDepCursor . contents {
134+ | Some (cursor ) =>
135+ if cursor .subs === sourceSubs {
136+ cursor .lastTrackedVersion = currentVersion
124137 fastPathFound .contents = true
125138 } else {
126- switch lastDep .nextDep {
139+ switch cursor .nextDep {
127140 | Some (nextDep ) =>
128141 if nextDep .subs === sourceSubs {
129142 nextDep .lastTrackedVersion = currentVersion
143+ currentObserverDepCursor := Some (nextDep )
130144 fastPathFound .contents = true
131145 }
132146 | None => ()
@@ -140,6 +154,7 @@ let trackDepFromEffect = (observer: Core.observer, sourceSubs: Core.subs): unit
140154 | Some (lastSubLink ) =>
141155 if lastSubLink .lastTrackedVersion === currentVersion && lastSubLink .observer === observer {
142156 lastSubLink .lastTrackedVersion = currentVersion
157+ currentObserverDepCursor := Some (lastSubLink )
143158 fastPathFound .contents = true
144159 }
145160 | None => ()
@@ -148,12 +163,14 @@ let trackDepFromEffect = (observer: Core.observer, sourceSubs: Core.subs): unit
148163
149164 if ! fastPathFound .contents {
150165 let found = ref (false )
166+ let foundLink : ref <option <Core .link >> = ref (None )
151167 let link = ref (observer .firstDep )
152168 while link .contents !== None && ! found .contents {
153169 switch link .contents {
154170 | Some (l ) =>
155171 if l .subs === sourceSubs {
156172 l .lastTrackedVersion = currentVersion
173+ foundLink := Some (l )
157174 found := true
158175 } else {
159176 link := l .nextDep
@@ -168,6 +185,9 @@ let trackDepFromEffect = (observer: Core.observer, sourceSubs: Core.subs): unit
168185 newLink .lastTrackedVersion = currentVersion
169186 Core .linkToDeps (observer , newLink )
170187 Core .linkToSubs (sourceSubs , newLink )
188+ currentObserverDepCursor := Some (newLink )
189+ } else {
190+ currentObserverDepCursor := foundLink .contents
171191 }
172192 }
173193 }
@@ -248,7 +268,9 @@ let runComputedCycle = (subs: Core.subs, ~clearPending: bool): unit => {
248268 }
249269
250270 let prev = currentComputedSubs .contents
271+ let prevCursor = currentComputedDepCursor .contents
251272 currentComputedSubs := Some (subs )
273+ currentComputedDepCursor := subs .firstDep
252274
253275 try {
254276 switch subs .compute {
@@ -301,10 +323,12 @@ let runComputedCycle = (subs: Core.subs, ~clearPending: bool): unit => {
301323 }
302324
303325 currentComputedSubs := prev
326+ currentComputedDepCursor := prevCursor
304327 currentTrackingVersion .contents = previousTrackingVersion
305328 } catch {
306329 | exn =>
307330 currentComputedSubs := prev
331+ currentComputedDepCursor := prevCursor
308332 currentTrackingVersion .contents = previousTrackingVersion
309333 throw (exn )
310334 }
@@ -333,7 +357,9 @@ let retrackEffect = (observer: Core.observer): unit => {
333357 Core .clearPending (observer )
334358
335359 let prev = currentObserver .contents
360+ let prevCursor = currentObserverDepCursor .contents
336361 currentObserver := Some (observer )
362+ currentObserverDepCursor := observer .firstDep
337363
338364 try {
339365 observer .run ()
@@ -356,10 +382,12 @@ let retrackEffect = (observer: Core.observer): unit => {
356382
357383 Core .clearDirty (observer )
358384 currentObserver := prev
385+ currentObserverDepCursor := prevCursor
359386 currentTrackingVersion .contents = previousTrackingVersion
360387 } catch {
361388 | exn =>
362389 currentObserver := prev
390+ currentObserverDepCursor := prevCursor
363391 currentTrackingVersion .contents = previousTrackingVersion
364392 throw (exn )
365393 }
@@ -508,17 +536,25 @@ let batch = fn => {
508536let untrack = (fn : unit => 'a ): 'a => {
509537 let prevComputed = currentComputedSubs .contents
510538 let prevObserver = currentObserver .contents
539+ let prevComputedCursor = currentComputedDepCursor .contents
540+ let prevObserverCursor = currentObserverDepCursor .contents
511541 currentComputedSubs := None
512542 currentObserver := None
543+ currentComputedDepCursor := None
544+ currentObserverDepCursor := None
513545 try {
514546 let result = fn ()
515547 currentComputedSubs := prevComputed
516548 currentObserver := prevObserver
549+ currentComputedDepCursor := prevComputedCursor
550+ currentObserverDepCursor := prevObserverCursor
517551 result
518552 } catch {
519553 | exn =>
520554 currentComputedSubs := prevComputed
521555 currentObserver := prevObserver
556+ currentComputedDepCursor := prevComputedCursor
557+ currentObserverDepCursor := prevObserverCursor
522558 throw (exn )
523559 }
524560}
0 commit comments