Skip to content

Commit 0242c14

Browse files
committed
Fixing snapshots/caching
1 parent f3ab7c6 commit 0242c14

11 files changed

+559
-369
lines changed

src/FsAutoComplete.Core/AdaptiveExtensions.fs

Lines changed: 121 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,18 @@ open System.Threading
1212
module AdaptiveExtensions =
1313

1414
type CancellationTokenSource with
15-
16-
/// Communicates a request for cancellation. Ignores ObjectDisposedException
1715
member cts.TryCancel() =
1816
try
1917
cts.Cancel()
20-
with :? ObjectDisposedException ->
21-
()
18+
with
19+
| :? ObjectDisposedException
20+
| :? NullReferenceException -> ()
2221

23-
/// Releases all resources used by the current instance of the System.Threading.CancellationTokenSource class.
2422
member cts.TryDispose() =
25-
try
26-
cts.Dispose()
27-
with _ ->
28-
()
23+
// try
24+
cts.Dispose()
25+
// with _ -> ()
26+
2927

3028
type TaskCompletionSource<'a> with
3129

@@ -148,7 +146,7 @@ module AVal =
148146
/// Creates an observable with the given object and will be executed whenever the object gets marked out-of-date. Note that it does not trigger when the object is currently out-of-date.
149147
/// </summary>
150148
/// <param name="aval">The aval to get out-of-date information from.</param>
151-
let onOutOfDateWeak (aval: #aval<_>) =
149+
let onOutOfDateWeak (aval: #IAdaptiveObject) =
152150
Observable.Create(fun (obs: IObserver<_>) -> aval.AddWeakMarkingCallback(fun _ -> obs.OnNext aval))
153151

154152

@@ -531,53 +529,90 @@ module AsyncAVal =
531529
let ofTask (value: Task<'a>) = ConstantVal(value) :> asyncaval<_>
532530

533531
let ofCancellableTask (value: CancellableTask<'a>) =
534-
let mutable cache: Option<AdaptiveCancellableTask<'a>> = None
535532

536533
{ new AbstractVal<'a>() with
537-
member x.Compute t =
538-
if x.OutOfDate || Option.isNone cache then
539-
let cts = new CancellationTokenSource()
540-
541-
let cancel () =
542-
cts.TryCancel()
543-
cts.TryDispose()
534+
member x.Compute _ =
535+
let cts = new CancellationTokenSource()
536+
537+
let cancel () =
538+
cts.TryCancel()
539+
cts.TryDispose()
540+
541+
let real =
542+
task {
543+
try
544+
return! value cts.Token
545+
finally
546+
cts.TryDispose()
547+
}
548+
549+
AdaptiveCancellableTask(cancel, real) }
550+
:> asyncaval<_>
544551

545-
let real =
546-
task {
547-
try
548-
return! value cts.Token
549-
finally
550-
cts.TryDispose()
551-
}
552552

553-
cache <- Some(AdaptiveCancellableTask(cancel, real))
553+
let ofCancellableValueTask (value: CancellableValueTask<'a>) =
554554

555-
cache.Value }
555+
{ new AbstractVal<'a>() with
556+
member x.Compute _ =
557+
let cts = new CancellationTokenSource()
558+
559+
let cancel () =
560+
cts.TryCancel()
561+
cts.TryDispose()
562+
563+
let real =
564+
task {
565+
try
566+
return! value cts.Token
567+
finally
568+
cts.TryDispose()
569+
}
570+
571+
AdaptiveCancellableTask(cancel, real) }
556572
:> asyncaval<_>
557573

558-
let ofAsync (value: Async<'a>) =
559-
let mutable cache: Option<AdaptiveCancellableTask<'a>> = None
560574

561-
{ new AbstractVal<'a>() with
562-
member x.Compute t =
563-
if x.OutOfDate || Option.isNone cache then
564-
let cts = new CancellationTokenSource()
565575

566-
let cancel () =
567-
cts.TryCancel()
568-
cts.TryDispose()
576+
let _ofAsyncAValSeq (maxDegreeOfParallelism: int) (input: #seq<#asyncaval<'a>>) =
577+
let mutable cache: option<RefCountingTaskCreator<'a array>> = None
569578

570-
let real =
571-
task {
572-
try
573-
return! Async.StartImmediateAsTask(value, cts.Token)
574-
finally
575-
cts.TryDispose()
576-
}
579+
{ new AbstractVal<_>() with
580+
member x.Compute t =
581+
if x.OutOfDate || Option.isNone cache then
582+
let ref =
583+
RefCountingTaskCreator(
584+
cancellableTask {
585+
return!
586+
input
587+
|> Seq.map (fun v -> cancellableTask { return! v.GetValue t })
588+
|> CancellableTask.whenAllThrottled maxDegreeOfParallelism
589+
}
590+
)
577591

578-
cache <- Some(AdaptiveCancellableTask(cancel, real))
592+
cache <- Some ref
593+
ref.New()
594+
else
595+
cache.Value.New() }
596+
:> asyncaval<_>
579597

580-
cache.Value }
598+
let ofAsync (value: Async<'a>) =
599+
{ new AbstractVal<'a>() with
600+
member x.Compute _ =
601+
let cts = new CancellationTokenSource()
602+
603+
let cancel () =
604+
cts.TryCancel()
605+
cts.TryDispose()
606+
607+
let real =
608+
task {
609+
try
610+
return! Async.StartImmediateAsTask(value, cts.Token)
611+
finally
612+
cts.TryDispose()
613+
}
614+
615+
AdaptiveCancellableTask(cancel, real) }
581616
:> asyncaval<_>
582617

583618
/// <summary>
@@ -589,7 +624,11 @@ module AsyncAVal =
589624
else
590625
{ new AbstractVal<'a>() with
591626
member x.Compute t =
592-
let real = Task.FromResult(value.GetValue t)
627+
let real =
628+
// if out of date, assume it needs to run on the threadpool and not the current thread
629+
Task.Run(fun () -> value.GetValue t)
630+
631+
593632
AdaptiveCancellableTask(id, real) }
594633
:> asyncaval<_>
595634

@@ -600,6 +639,7 @@ module AsyncAVal =
600639
/// </summary>
601640
let map (mapping: 'a -> CancellationToken -> Task<'b>) (input: asyncaval<'a>) =
602641
let mutable cache: option<RefCountingTaskCreator<'b>> = None
642+
let mutable dataCache = ValueNone
603643

604644
{ new AbstractVal<'b>() with
605645
member x.Compute t =
@@ -608,7 +648,13 @@ module AsyncAVal =
608648
RefCountingTaskCreator(
609649
cancellableTask {
610650
let! i = input.GetValue t
611-
return! mapping i
651+
match dataCache with
652+
| ValueSome (struct (oa, ob)) when Utils.cheapEqual oa i ->
653+
return ob
654+
| _ ->
655+
let! b = mapping i
656+
dataCache <- ValueSome (struct(i, b))
657+
return b
612658
}
613659
)
614660

@@ -633,9 +679,6 @@ module AsyncAVal =
633679
let mapSync (mapping: 'a -> CancellationToken -> 'b) (input: asyncaval<'a>) =
634680
map
635681
(fun a ct ->
636-
if ct.IsCancellationRequested then
637-
Task.FromCanceled<_>(ct)
638-
else
639682
Task.FromResult(mapping a ct))
640683
input
641684

@@ -645,6 +688,7 @@ module AsyncAVal =
645688
/// </summary>
646689
let map2 (mapping: 'a -> 'b -> CancellationToken -> Task<'c>) (ca: asyncaval<'a>) (cb: asyncaval<'b>) =
647690
let mutable cache: option<RefCountingTaskCreator<'c>> = None
691+
let mutable dataCache = ValueNone
648692

649693
{ new AbstractVal<'c>() with
650694
member x.Compute t =
@@ -662,9 +706,15 @@ module AsyncAVal =
662706
ta.Cancel()
663707
tb.Cancel())
664708

665-
let! va = ta.Task
666-
let! vb = tb.Task
667-
return! mapping va vb
709+
let! ia = ta.Task
710+
let! ib = tb.Task
711+
match dataCache with
712+
| ValueSome (struct (va, vb, vc)) when Utils.cheapEqual va ia && Utils.cheapEqual vb ib ->
713+
return vc
714+
| _ ->
715+
let! vc = mapping ia ib ct
716+
dataCache <- ValueSome (struct (ia, ib, vc))
717+
return vc
668718
}
669719
)
670720

@@ -680,6 +730,7 @@ module AsyncAVal =
680730
let bind (mapping: 'a -> CancellationToken -> asyncaval<'b>) (value: asyncaval<'a>) =
681731
let mutable cache: option<_> = None
682732
let mutable innerCache: option<_> = None
733+
let mutable outerDataCache: option<_> = None
683734
let mutable inputChanged = 0
684735
let inners: ref<HashSet<asyncaval<'b>>> = ref HashSet.empty
685736

@@ -702,9 +753,14 @@ module AsyncAVal =
702753
RefCountingTaskCreator(
703754
cancellableTask {
704755
let! i = value.GetValue t
705-
let! ct = CancellableTask.getCancellationToken ()
706-
let inner = mapping i ct
707-
return inner
756+
match outerDataCache with
757+
| Some (struct (oa, ob)) when Utils.cheapEqual oa i ->
758+
return ob
759+
| _ ->
760+
let! ct = CancellableTask.getCancellationToken ()
761+
let inner = mapping i ct
762+
outerDataCache <- Some (i, inner)
763+
return inner
708764

709765
}
710766
)
@@ -800,7 +856,8 @@ module AsyncAValBuilderExtensions =
800856
member inline x.Source(value: aval<'T>) = AsyncAVal.ofAVal value
801857
member inline x.Source(value: Task<'T>) = AsyncAVal.ofTask value
802858
member inline x.Source(value: Async<'T>) = AsyncAVal.ofAsync value
803-
member inline x.Source(value: CancellableTask<'T>) = AsyncAVal.ofCancellableTask value
859+
member inline x.Source([<InlineIfLambda>] value: CancellableTask<'T>) = AsyncAVal.ofCancellableTask value
860+
member inline x.Source([<InlineIfLambda>] value: CancellableValueTask<'T>) = AsyncAVal.ofCancellableValueTask value
804861

805862
member inline x.BindReturn(value: asyncaval<'T1>, [<InlineIfLambda>] mapping: 'T1 -> CancellationToken -> 'T2) =
806863
AsyncAVal.mapSync (fun data ctok -> mapping data ctok) value
@@ -854,3 +911,10 @@ module AMapAsync =
854911
| Some x -> return! x
855912
| None -> return Error reason
856913
}
914+
915+
916+
let _filterValuesByKey (key: 'Key) (map: amap<'Key, #asyncaval<'Value>>) =
917+
asyncAVal {
918+
let! values = map |> AMap.filter (fun k _ -> k = key) |> AMap.toASetValues |> ASet.toAVal
919+
return! AsyncAVal._ofAsyncAValSeq 1 values
920+
}

src/FsAutoComplete.Core/AdaptiveExtensions.fsi

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ namespace FsAutoComplete.Adaptive
22

33
[<AutoOpen>]
44
module AdaptiveExtensions =
5+
6+
type System.Threading.CancellationTokenSource with
7+
8+
/// Communicates a request for cancellation. Ignores ObjectDisposedException
9+
member TryCancel: unit -> unit
10+
/// Releases all resources used by the current instance of the System.Threading.CancellationTokenSource class.
11+
member TryDispose : unit -> unit
12+
513
type FSharp.Data.Adaptive.ChangeableHashMap<'Key, 'Value> with
614

715
/// <summary>
@@ -63,7 +71,7 @@ module AVal =
6371
/// Creates an observable with the given object and will be executed whenever the object gets marked out-of-date. Note that it does not trigger when the object is currently out-of-date.
6472
/// </summary>
6573
/// <param name="aval">The aval to get out-of-date information from.</param>
66-
val onOutOfDateWeak: aval: 'a -> System.IObservable<'a> when 'a :> FSharp.Data.Adaptive.aval<'b>
74+
val onOutOfDateWeak: aval: 'a -> System.IObservable<'a> when 'a :> FSharp.Data.Adaptive.IAdaptiveObject
6775

6876
/// <summary>Creates an observable on the aval that will be executed whenever the avals value changed.</summary>
6977
/// <param name="aval">The aval to get out-of-date information from.</param>
@@ -268,6 +276,7 @@ module AsyncAVal =
268276
val ofTask: value: System.Threading.Tasks.Task<'a> -> asyncaval<'a>
269277

270278
val ofCancellableTask: value: IcedTasks.CancellableTasks.CancellableTask<'a> -> asyncaval<'a>
279+
val ofCancellableValueTask: value: IcedTasks.CancellableValueTasks.CancellableValueTask<'a> -> asyncaval<'a>
271280

272281
val ofAsync: value: Async<'a> -> asyncaval<'a>
273282

@@ -355,6 +364,7 @@ module AsyncAValBuilderExtensions =
355364
member inline Source: value: System.Threading.Tasks.Task<'T> -> asyncaval<'T>
356365
member inline Source: value: Async<'T> -> asyncaval<'T>
357366
member inline Source: value: CancellableTask<'T> -> asyncaval<'T>
367+
member inline Source: value: CancellableValueTask<'T> -> asyncaval<'T>
358368

359369
member inline BindReturn:
360370
value: asyncaval<'T1> * mapping: ('T1 -> System.Threading.CancellationToken -> 'T2) -> asyncaval<'T2>

0 commit comments

Comments
 (0)