@@ -6,6 +6,9 @@ let flag_dirty = 1
66let flag_pending = 2
77let flag_running = 4
88
9+ // Global tracking version counter for O(1) duplicate detection
10+ let trackingVersion : ref <int > = ref (0 )
11+
912// Observer kind tag
1013type kind = [#Effect | #Computed ]
1114
@@ -21,6 +24,8 @@ type rec link = {
2124 // Links in the signal's subscriber chain
2225 mutable nextSub : option <link >,
2326 mutable prevSub : option <link >,
27+ // Version stamp for O(1) duplicate detection within a compute cycle
28+ mutable lastTrackedVersion : int ,
2429}
2530
2631// Signal subscriber list (head/tail of linked list)
@@ -36,6 +41,10 @@ and subs = {
3641 mutable lastDep : option <link >,
3742 mutable flags : int ,
3843 mutable level : int ,
44+ // Current tracking version for this compute cycle (for link reuse)
45+ mutable currentTrackingVersion : int ,
46+ // Whether the last recompute changed the value (for equality short-circuit)
47+ mutable valueChanged : bool ,
3948}
4049
4150// Observer for effects only (computeds use subs directly)
@@ -50,6 +59,8 @@ and observer = {
5059 name : option <string >,
5160 // For computed observers: direct reference to backing subs (the combined object)
5261 mutable backingSubs : option <subs >,
62+ // Current tracking version for this run cycle (for link reuse)
63+ mutable currentTrackingVersion : int ,
5364}
5465
5566// Create empty subscriber list (for plain signals)
@@ -62,6 +73,8 @@ let makeSubs = (): subs => {
6273 lastDep : None ,
6374 flags : 0 ,
6475 level : 0 ,
76+ currentTrackingVersion : 0 ,
77+ valueChanged : true , // Plain signals always "changed"
6578}
6679
6780// Create subs for a computed (with compute function)
@@ -74,6 +87,8 @@ let makeComputedSubs = (compute: unit => unit): subs => {
7487 lastDep : None ,
7588 flags : flag_dirty , // start dirty
7689 level : 0 ,
90+ currentTrackingVersion : 0 ,
91+ valueChanged : true , // Start as changed for initial computation
7792}
7893
7994// Create observer
@@ -93,6 +108,7 @@ let makeObserver = (
93108 level : 0 ,
94109 name ,
95110 backingSubs ,
111+ currentTrackingVersion : 0 ,
96112}
97113
98114// Flag operations for observer (using Int.Bitwise module)
@@ -126,6 +142,7 @@ let makeLink = (subs: subs, observer: observer): link => {
126142 prevDep : None ,
127143 nextSub : None ,
128144 prevSub : None ,
145+ lastTrackedVersion : 0 ,
129146}
130147
131148// Add link to signal's subscriber list
@@ -179,6 +196,20 @@ let unlinkFromDeps = (observer: observer, link: link): unit => {
179196 link .nextDep = None
180197}
181198
199+ // Remove link from subs's dependency list (for computeds - subs IS the observer)
200+ let unlinkFromSubsDeps = (s : subs , link : link ): unit => {
201+ switch link .prevDep {
202+ | Some (prev ) => prev .nextDep = link .nextDep
203+ | None => s .firstDep = link .nextDep
204+ }
205+ switch link .nextDep {
206+ | Some (next ) => next .prevDep = link .prevDep
207+ | None => s .lastDep = link .prevDep
208+ }
209+ link .prevDep = None
210+ link .nextDep = None
211+ }
212+
182213// Clear all dependencies from observer (unlinks from all signals)
183214let clearDeps = (observer : observer ): unit => {
184215 let link = ref (observer .firstDep )
0 commit comments