Skip to content

Commit 297000f

Browse files
committed
fixed ASet.ofSetTree/ofListTree: ignore nested dirty updates
1 parent 1b6d265 commit 297000f

4 files changed

Lines changed: 123 additions & 23 deletions

File tree

RELEASE_NOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
### 1.2.21
2+
- fixed ASet.ofSetTree/ofListTree: ignore nested dirty updates
3+
14
### 1.2.20
25
- added ASet.ofListTree and ASet.ofSetTree
36
- added AList.mapToASet

src/FSharp.Data.Adaptive/CollectionExtensions.fs

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ module CollectionExtensions =
291291
let mutable initial = true
292292
let reader = list.GetReader() // NOTE: need to be held, otherwise it will be collected and no updates can be consumed
293293
let cache = System.Collections.Generic.Dictionary<struct(IIndexListReader<'T> * FSharp.Data.Adaptive.Index), struct('T * IIndexListReader<'T>)>() // TODO: refcounting
294+
let removedDirty = DefaultHashSet.create<IIndexListReader<'T>>()
294295

295296
member x.Invoke(token : AdaptiveToken, r : IIndexListReader<'T>, i : FSharp.Data.Adaptive.Index, n : 'T) =
296297
let mutable delta = HashSetDelta.empty
@@ -316,8 +317,15 @@ module CollectionExtensions =
316317
let mutable delta = HashSetDelta.empty
317318
match cache.TryGetValue (struct(r, i)) with
318319
| (true, struct(n, subReader)) ->
319-
cache.Remove (struct(r, i)) |> ignore
320-
subReader.Outputs.Remove x |> ignore
320+
if not (cache.Remove (struct(r, i))) then
321+
unexpected()
322+
323+
// if subReader is outOfDate it is expected to be also included in "dirty" inputs
324+
// -> prevent updates to be included in delta computation
325+
if subReader.OutOfDate then
326+
removedDirty.Add(subReader) |> ignore
327+
elif not (subReader.Outputs.Remove x) then
328+
unexpected()
321329

322330
delta <- delta.Add (Rem n)
323331
subReader.State |> IndexList.iteri (fun i old ->
@@ -338,12 +346,15 @@ module CollectionExtensions =
338346
| _ -> unexpected()
339347

340348
for d in dirty do
341-
let inner = d.GetChanges token
342-
for c in inner do
343-
match c with
344-
| (i, Set n) -> delta <- delta.Combine (x.Invoke(token, d, i, n))
345-
| (i, Remove) -> delta <- delta.Combine (x.Revoke(d, i))
349+
if not (removedDirty.Contains d) then
350+
let inner = d.GetChanges token
351+
for c in inner do
352+
match c with
353+
| (i, Set n) -> delta <- delta.Combine (x.Invoke(token, d, i, n))
354+
| (i, Remove) -> delta <- delta.Combine (x.Revoke(d, i))
346355

356+
removedDirty.Clear()
357+
347358
delta
348359

349360

@@ -355,18 +366,19 @@ module CollectionExtensions =
355366
let mutable initial = true
356367
let reader = set.GetReader() // NOTE: need to be held, otherwise it will be collected and no updates can be consumed
357368
let cache = DefaultDictionary.create<'T, struct(IHashSetReader<'T> * ref<int>)>()
369+
let removedDirty = DefaultHashSet.create<IHashSetReader<'T>>()
358370

359371
member x.Invoke(token : AdaptiveToken, n : 'T) =
360372
let mutable delta = HashSetDelta.empty
361373
match cache.TryGetValue n with
362374
| (true, (_, refCount)) -> refCount.Value <- refCount.Value + 1
363375
| _ ->
364376
let subNodes = getChildren n
365-
let reader = subNodes.GetReader()
366-
cache[n] <- (reader, ref 1)
377+
let subReader = subNodes.GetReader()
378+
cache[n] <- (subReader, ref 1)
367379

368380
delta <- delta.Add (Add n)
369-
let content = reader.GetChanges token
381+
let content = subReader.GetChanges token
370382
for c in content do
371383
if c.Count <> 1 then unexpected()
372384
delta <- delta.Combine (x.Invoke(token, c.Value))
@@ -376,13 +388,20 @@ module CollectionExtensions =
376388
member x.Revoke(n : 'T) =
377389
let mutable delta = HashSetDelta.empty
378390
match cache.TryGetValue n with
379-
| (true, (reader, refCount)) ->
391+
| (true, (subReader, refCount)) ->
380392
if refCount.Value = 1 then
381-
cache.Remove n |> ignore
382-
reader.Outputs.Remove x |> ignore
393+
if not (cache.Remove n) then
394+
unexpected()
395+
396+
// if subReader is outOfDate it is expected to be also included in "dirty" inputs
397+
// -> prevent updates to be included in delta computation
398+
if subReader.OutOfDate then
399+
removedDirty.Add(subReader) |> ignore
400+
elif not (subReader.Outputs.Remove x) then
401+
unexpected()
383402

384403
delta <- delta.Add (Rem n)
385-
for old in reader.State do
404+
for old in subReader.State do
386405
delta <- delta.Combine (x.Revoke(old))
387406
else
388407
refCount.Value <- refCount.Value - 1
@@ -404,13 +423,17 @@ module CollectionExtensions =
404423
HashSetDelta.empty
405424

406425
for d in dirty do
407-
let inner = d.GetChanges token |> HashSetDelta.collect (fun d ->
408-
let n = d.Value
409-
if d.Count = 1 then x.Invoke(token, n)
410-
elif d.Count = -1 then x.Revoke(n)
411-
else unexpected()
412-
)
413-
deltas <- deltas.Combine inner
426+
if not (removedDirty.Contains d) then
427+
let inner = d.GetChanges token |> HashSetDelta.collect (fun d ->
428+
let n = d.Value
429+
if d.Count = 1 then x.Invoke(token, n)
430+
elif d.Count = -1 then x.Revoke(n)
431+
else unexpected()
432+
)
433+
deltas <- deltas.Combine inner
434+
435+
removedDirty.Clear()
436+
414437
deltas
415438

416439

src/Test/FSharp.Data.Adaptive.Tests/ASet.fs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -784,4 +784,76 @@ let ``[ASet] filterA``() =
784784
takeEven.Value <- true
785785
)
786786

787-
filtered |> ASet.force |> should setequal [0; 1; 2; 3; 4]
787+
filtered |> ASet.force |> should setequal [0; 1; 2; 3; 4]
788+
789+
type SetTreeNode =
790+
{
791+
value : int
792+
nodes : cset<SetTreeNode>
793+
}
794+
override this.ToString() = sprintf "Node %d" this.value
795+
796+
[<Test>]
797+
let ``[ASet] ofSetTree``() =
798+
799+
let roots = cset<SetTreeNode>()
800+
801+
let filter = AVal.init(0)
802+
let set = roots |> ASet.filterA (fun n -> filter |> AVal.map(fun f -> (n.value % 2) = f)) |> ASet.ofSetTree (fun n -> n.nodes |> ASet.filterA (fun n -> filter |> AVal.map(fun f -> (n.value % 2) = f)))
803+
804+
let rec cnt (nodes : cset<SetTreeNode>) (filter : int) =
805+
let mutable sum = 0
806+
for sn in nodes do
807+
if (sn.value % 2) = filter then
808+
sum <- sum + (cnt sn.nodes filter) + 1
809+
sum
810+
811+
let nodes = System.Collections.Generic.List<SetTreeNode>()
812+
813+
let rnd = System.Random(2225)
814+
for i in 0..10000 do
815+
transact (fun () ->
816+
if rnd.NextDouble() < 0.1 then
817+
let newFilter = if filter.Value = 0 then 1 else 0
818+
//printfn "%d: toggle filter %d" i newFilter
819+
filter.Value <- newFilter
820+
else
821+
if roots.Count = 0 || rnd.NextDouble() < 0.45 then
822+
let nv = rnd.Next()
823+
let n = { value = nv; nodes = cset<SetTreeNode>() }
824+
if roots.Count = 0 || rnd.NextDouble() < 0.25 then
825+
roots.Add n |> ignore
826+
//printfn "%d: add root node %d" i nv
827+
else
828+
let index = rnd.Next(nodes.Count)
829+
nodes.[index].nodes.Add(n) |> ignore
830+
//printfn "%d: add node %d to %d" i nv (nodes.[index].value)
831+
nodes.Add n |> ignore
832+
else
833+
if rnd.NextDouble() < 0.2 then
834+
let index = rnd.Next(roots.Count)
835+
let n = roots |> Seq.skip(index) |> Seq.head
836+
nodes.Remove(n) |> ignore
837+
roots.Remove(n) |> ignore
838+
//printfn "%d: rem root node %d" i n.value
839+
else
840+
let index = rnd.Next(nodes.Count)
841+
let n = nodes.[index]
842+
if n.nodes.Count > 0 then
843+
let indexRem = rnd.Next(n.nodes.Count)
844+
let nr = n.nodes |> Seq.skip(indexRem) |> Seq.head
845+
nodes.Remove(nr) |> ignore
846+
n.nodes.Remove(nr) |> ignore
847+
//printfn "%d: rem node %d from %d" i nr.value n.value
848+
)
849+
850+
851+
let refCnt = cnt roots (filter |> AVal.force)
852+
let setCnt = (set |> ASet.force).Count
853+
854+
if refCnt <> setCnt then
855+
printfn "fail: refCnt=%d setCnt=%d" refCnt setCnt
856+
857+
should equal setCnt refCnt
858+
859+
()

src/Test/FSharp.Data.Adaptive.Tests/Program.fs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ let main _args =
5454

5555
//``[AList] sub``();
5656

57+
ASet.``[ASet] ofSetTree``();
58+
5759

5860
//let a = cval [1;2;3;4]
5961
//let b = AVal.cast<seq<int>> a
@@ -92,7 +94,7 @@ let main _args =
9294
//BenchmarkRunner.Run<Benchmarks.HashSetDeltaBench>() |> ignore
9395
//BenchmarkRunner.Run<Benchmarks.IndexListBenchmarks>() |> ignore
9496
//BenchmarkRunner.Run<Benchmarks.IndexEqualsBenchmarks>() |> ignore
95-
BenchmarkRunner.Run<Benchmarks.IndexGarbageBenchmarks>() |> ignore
97+
//BenchmarkRunner.Run<Benchmarks.IndexGarbageBenchmarks>() |> ignore
9698

9799
//let x = Benchmarks.IndexGarbageBenchmarks()
98100
//x.ListCount <- 100

0 commit comments

Comments
 (0)