Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix cancellation crash in adaptive #1184

Merged
merged 1 commit into from
Oct 24, 2023

Conversation

TheAngryByrd
Copy link
Member

@TheAngryByrd TheAngryByrd commented Oct 24, 2023

WHAT

🤖 Generated by Copilot at a32b6e9

This pull request enhances the AdaptiveExtensions module with a new type, better cancellation and disposal logic, caching, and simplification. These changes aim to improve the performance and reliability of async adaptive values, which are used for incremental and reactive computations.

🤖 Generated by Copilot at a32b6e9

No more cancel, no more dispose
We use SafeCancel and SafeDispose
We cache the values, we simplify the maps
We are the masters of AdaptiveExtensions

🛠️🚀🧹

WHY

Got this last week:


[17:27:35.291 ERR] [Startup] Start - LSP mode crashed
System.AggregateException: One or more errors occurred. (Exception has been thrown by the target of an invocation.)
 ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.AggregateException: One or more errors occurred. (One or more errors occurred. (One or more errors occurred. (One or more errors occurred. (One or more errors occurred. (The CancellationTokenSource has been disposed.)))))
 ---> System.AggregateException: One or more errors occurred. (One or more errors occurred. (One or more errors occurred. (One or more errors occurred. (The CancellationTokenSource has been disposed.))))
 ---> System.AggregateException: One or more errors occurred. (One or more errors occurred. (One or more errors occurred. (The CancellationTokenSource has been disposed.)))
 ---> System.AggregateException: One or more errors occurred. (One or more errors occurred. (The CancellationTokenSource has been disposed.))
 ---> System.AggregateException: One or more errors occurred. (The CancellationTokenSource has been disposed.)
 ---> System.ObjectDisposedException: The CancellationTokenSource has been disposed.
   at System.Threading.CancellationTokenSource.Cancel()
   at [email protected](Unit unitVar0) in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 516
   at FsAutoComplete.Adaptive.AdaptiveCancellableTask`1.Cancel() in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 371
   at [email protected]() in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 695
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
   --- End of inner exception stack trace ---
   at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
   at FsAutoComplete.Adaptive.RefCountingTaskCreator`1.RemoveRef() in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 321
   at <StartupCode$FsAutoComplete-Core>[email protected](Unit unitVar)
   at FsAutoComplete.Adaptive.AdaptiveCancellableTask`1.Cancel() in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 371
   at [email protected]()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
   --- End of inner exception stack trace ---
   at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
   at FsAutoComplete.Adaptive.RefCountingTaskCreator`1.RemoveRef() in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 321
   at <StartupCode$FsAutoComplete-Core>[email protected](Unit unitVar)
   at FsAutoComplete.Adaptive.AdaptiveCancellableTask`1.Cancel() in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 371
   at [email protected]()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
   --- End of inner exception stack trace ---
   at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
   at FsAutoComplete.Adaptive.RefCountingTaskCreator`1.RemoveRef() in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 321
   at <StartupCode$FsAutoComplete-Core>[email protected](Unit unitVar)
   at FsAutoComplete.Adaptive.AdaptiveCancellableTask`1.Cancel() in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 371
   at [email protected]()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
   --- End of inner exception stack trace ---
   at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
   at FsAutoComplete.Adaptive.RefCountingTaskCreator`1.RemoveRef() in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 321
   at <StartupCode$FsAutoComplete-Core>[email protected](Unit unitVar)
   at FsAutoComplete.Adaptive.AdaptiveCancellableTask`1.Cancel() in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete.Core\AdaptiveExtensions.fs:line 371
   at [email protected]()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
   --- End of inner exception stack trace ---
   at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
   at StreamJsonRpc.StandardCancellationStrategy.CancelInboundRequest(RequestId id)
   at InvokeStub_StandardCancellationStrategy.CancelInboundRequest(Object, Object, IntPtr*)
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
   --- End of inner exception stack trace ---
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at StreamJsonRpc.TargetMethod.InvokeAsync(CancellationToken cancellationToken)
   at StreamJsonRpc.JsonRpc.DispatchRequestAsync(JsonRpcRequest request, TargetMethod targetMethod, CancellationToken cancellationToken)
   at StreamJsonRpc.JsonRpc.DispatchIncomingRequestAsync(JsonRpcRequest request)
   at StreamJsonRpc.JsonRpc.DispatchIncomingRequestAsync(JsonRpcRequest request)
   at StreamJsonRpc.JsonRpc.HandleRpcAsync(JsonRpcMessage rpc)
   --- End of inner exception stack trace ---
   at Ionide.LanguageServerProtocol.Server.startWithSetup[client](FSharpFunc`2 setupRequestHandlings, Stream input, Stream output, FSharpFunc`2 clientCreator, FSharpFunc`2 customizeRpc) in /_//src/LanguageServerProtocol.fs:line 183
   at [email protected](FSharpFunc`2 customizeRpc) in /_//src/LanguageServerProtocol.fs:line 286
   at FsAutoComplete.Lsp.AdaptiveFSharpLspServerModule.startCore[a,a](a toolsPath, FSharpFunc`2 workspaceLoaderFactory, ISourceTextFactory sourceTextFactory) in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete\LspServers\AdaptiveFSharpLspServer.fs:line 5126
   at [email protected](Unit unitVar0) in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete\Parser.fs:line 156
   at FsAutoComplete.Lsp.AdaptiveFSharpLspServerModule.start(FSharpFunc`2 startCore) in C:\Users\jimmy\Repositories\public\TheAngryByrd\FsAutoComplete\src\FsAutoComplete\LspServers\AdaptiveFSharpLspServer.fs:line 5132

HOW

🤖 Generated by Copilot at a32b6e9

  • Add TryCancel and TryDispose extension methods for CancellationTokenSource to ignore ObjectDisposedException (link)
  • Use TryCancel and TryDispose methods instead of Cancel and Dispose for CancellationTokenSource instances in RefCountingTaskCreator and AMap types (link, link)
  • Add cache field to ofCancellableTask and ofAsync functions in AsyncAVal module to reuse adaptive cancellable tasks if input value has not changed (link)
  • Simplify mapAsync function in AsyncAVal module by reusing map function with a different mapping function (link)
  • Replace explicit disposal of CancellationTokenRegistration with use keyword in map, map2, bind, and inner computation of bind functions in AsyncAVal module (link, link, link, link)
  • Remove empty line at the end of AMapAsync module (link)

cache.Value.New() }
:> asyncaval<_>

map (fun a ct -> Async.StartImmediateAsTask(mapping a, ct)) input
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactoring

@@ -628,17 +626,14 @@ module AsyncAVal =

let! ct = CancellableTask.getCancellationToken ()

let s =
use _s =
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactoring to use

return inner
finally
s.Dispose()
use _s = ct.Register(fun () -> it.Cancel())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactoring to use

s2.Dispose()
finally
s.Dispose()
use _s = ct.Register(fun () -> innerCellTask.Cancel())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactoring to use

@@ -480,43 +497,53 @@ module AsyncAVal =
let ofTask (value: Task<'a>) = ConstantVal(value) :> asyncaval<_>

let ofCancellableTask (value: CancellableTask<'a>) =
let mutable cache: Option<AdaptiveCancellableTask<'a>> = None
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Help cut down on re-evaluations.

@@ -369,7 +386,7 @@ and AdaptiveCancellableTask<'a>(cancel: unit -> unit, real: Task<'a>) =
/// <summary>Will run the cancel function passed into the constructor and set the output Task to cancelled state.</summary>
member x.Cancel() =
cancel ()
cts.Cancel()
cts.TryCancel()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't care if the cancellation can't be cancelled anymore. Alternative approaches would be to swap the cancel function out upon finishing of a task but this seemed easier.

@TheAngryByrd TheAngryByrd force-pushed the fix-failed-cancellations branch from a32b6e9 to 18f6f58 Compare October 24, 2023 03:07
@TheAngryByrd TheAngryByrd merged commit 7b14e31 into ionide:main Oct 24, 2023
8 of 9 checks passed
nojaf pushed a commit to nojaf/FsAutoComplete that referenced this pull request Nov 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant