diff --git a/docs/release-notes/.VisualStudio/18.vNext.md b/docs/release-notes/.VisualStudio/18.vNext.md index cffa42edc9c..9e49f812bd6 100644 --- a/docs/release-notes/.VisualStudio/18.vNext.md +++ b/docs/release-notes/.VisualStudio/18.vNext.md @@ -7,6 +7,7 @@ * Fixed Rename incorrectly renaming `get` and `set` keywords for properties with explicit accessors. ([Issue #18270](https://github.com/dotnet/fsharp/issues/18270), [PR #19252](https://github.com/dotnet/fsharp/pull/19252)) * Fixed Find All References crash when F# project contains non-F# files like `.cshtml`. ([Issue #16394](https://github.com/dotnet/fsharp/issues/16394), [PR #19252](https://github.com/dotnet/fsharp/pull/19252)) +* Avoid using `cancellableTask` in `DocumentCache`; the editor cache now uses direct `CancellationToken`-aware `task` wrappers, avoiding the background `Task.Run` offload and a larger wrapper closure from the `cancellableTask` builder. ([Issue #20268](https://github.com/dotnet/fsharp/issues/20268)) * Find All References for external DLL symbols now only searches projects that reference the specific assembly. ([Issue #10227](https://github.com/dotnet/fsharp/issues/10227), [PR #19252](https://github.com/dotnet/fsharp/pull/19252)) * Improve static compilation of state machines. ([PR #19297](https://github.com/dotnet/fsharp/pull/19297)) * Make Alt+F1 (momentary toggle) work for inlay hints. ([PR #19421](https://github.com/dotnet/fsharp/pull/19421)) diff --git a/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs b/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs index 6c88bc0f912..9233d95e8f4 100644 --- a/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs +++ b/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs @@ -2,6 +2,8 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Runtime.Caching +open System.Threading +open System.Threading.Tasks open Microsoft.CodeAnalysis open CancellableTasks @@ -16,30 +18,40 @@ type DocumentCache<'Value when 'Value: not struct>(name: string, ?cacheItemPolic let policy = defaultArg cacheItemPolicy (CacheItemPolicy(SlidingExpiration = (TimeSpan.FromSeconds defaultSlidingExpiration))) + static let tryGetCachedValueAsync (doc: Document, cache: MemoryCache, ct: CancellationToken) = + if ct.IsCancellationRequested then + Task.FromCanceled<'Value voption>(ct) + else + task { + let! currentVersion = doc.GetTextVersionAsync ct + + match cache.Get(doc.Id.ToString()) with + | null -> return ValueNone + | :? (VersionStamp * 'Value) as value -> + if fst value = currentVersion then + return ValueSome(snd value) + else + return ValueNone + | _ -> return ValueNone + } + + static let setCacheValueAsync (doc: Document, value: 'Value, cache: MemoryCache, policy: CacheItemPolicy, ct: CancellationToken) = + if ct.IsCancellationRequested then + Task.FromCanceled(ct) + else + task { + let! currentVersion = doc.GetTextVersionAsync ct + do cache.Set(doc.Id.ToString(), (currentVersion, value), policy) + } + new(name: string, slidingExpirationSeconds: float) = new DocumentCache<'Value>(name, CacheItemPolicy(SlidingExpiration = (TimeSpan.FromSeconds slidingExpirationSeconds))) - member _.TryGetValueAsync(doc: Document) = - cancellableTask { - let! ct = CancellableTask.getCancellationToken () - let! currentVersion = doc.GetTextVersionAsync ct - - match cache.Get(doc.Id.ToString()) with - | null -> return ValueNone - | :? (VersionStamp * 'Value) as value -> - if fst value = currentVersion then - return ValueSome(snd value) - else - return ValueNone - | _ -> return ValueNone - } - - member _.SetAsync(doc: Document, value: 'Value) = - cancellableTask { - let! ct = CancellableTask.getCancellationToken () - let! currentVersion = doc.GetTextVersionAsync ct - do cache.Set(doc.Id.ToString(), (currentVersion, value), policy) - } + member _.TryGetValueAsync(doc: Document) : CancellableTask<'Value voption> = + fun ct -> tryGetCachedValueAsync (doc, cache, ct) + + member _.SetAsync(doc: Document, value: 'Value) : CancellableTask = + fun ct -> setCacheValueAsync (doc, value, cache, policy, ct) interface IDisposable with member _.Dispose() = cache.Dispose()