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

Random InvalidOperationException from DisposableCallback #24

Closed
JEnrightDev opened this issue Apr 27, 2020 · 8 comments · Fixed by #27
Closed

Random InvalidOperationException from DisposableCallback #24

JEnrightDev opened this issue Apr 27, 2020 · 8 comments · Fixed by #27

Comments

@JEnrightDev
Copy link

Unfortunately, I don't have a lot of information available here, as the exception isn't really all that useful. At random times, we will receive an InvalidOperationException after executing an effect multiple times. It seems as though it's after the 2nd, 3rd, or 4th invocation of the same effect. (NOTE: the effect makes a couple of database calls, so everything is async using the new ConfigureAwait(false) fix)

I'm trying to come up with a reproduction case, and will update accordingly when I have a simplified version of one.

Exception Details:

System.InvalidOperationException
  HResult=0x80131509
  Message=DisposableCallback was not disposed
  Source=Fluxor
  StackTrace:
   at Fluxor.DisposableCallback.Finalize()

Using Blazor Web (Server) and the 3.1.1 preview build.

@mrpmorris
Copy link
Owner

@JEnrightDev Could you check out my source code and run against that instead of the DLLs?

I'd like you to change the code like so

  1. In DisposableCallback's constructor create an instance of StackTrace
  2. Store it in a private member.
    3: In the ~DisposableCallback finalizer could you add ThatStackTrace.ToString() into the exception description, and then send me the call stack?

That would help a lot!

@JEnrightDev
Copy link
Author

JEnrightDev commented Apr 27, 2020

The Disposable callback is being created from here: https://github.com/mrpmorris/Fluxor/blob/master/Source/Fluxor.Blazor.Web/Components/FluxorComponent.cs#L29

https://github.com/mrpmorris/Fluxor/blob/master/Source/Fluxor.Blazor.Web/Components/StateSubscriber.cs#L36

Stack trace is as follows:

 at Fluxor.DisposableCallback..ctor(Action action) in D:\source\repos\Fluxor\Source\Fluxor\DisposableCallback.cs:line 24
   at Fluxor.Blazor.Web.Components.StateSubscriber.Subscribe(Object subject, Action`1 callback) in D:\source\repos\Fluxor\Source\Fluxor.Blazor.Web\Components\StateSubscriber.cs:line 37
   at Fluxor.Blazor.Web.Components.FluxorComponent.OnInitialized() in D:\source\repos\Fluxor\Source\Fluxor.Blazor.Web\Components\FluxorComponent.cs:line 29
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
   at Microsoft.AspNetCore.Components.ComponentBase.SetParametersAsync(ParameterView parameters)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(DiffContext& diffContext, Int32 frameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(DiffContext& diffContext, Int32 frameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange`1 oldTree, ArrayRange`1 newTree)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessPendingRender()
   at Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer.ProcessPendingRender()
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(Int32 componentId, RenderFragment renderFragment)
   at Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged()
   at Microsoft.AspNetCore.Components.ComponentBase.CallOnParametersSetAsync()
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
   at Microsoft.AspNetCore.Components.ComponentBase.SetParametersAsync(ParameterView parameters)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(Int32 componentId, ParameterView initialParameters)
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(Int32 componentId, ParameterView initialParameters)
   at Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer.AddComponentAsync(Type componentType, ParameterView parameters, String domElementSelector)
   at Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost.<>c__DisplayClass35_0.<<InitializeAsync>b__0>d.MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost.<>c__DisplayClass35_0.<InitializeAsync>b__0()
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c.<<InvokeAsync>b__9_0>d.MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c.<InvokeAsync>b__9_0(Object state)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteSynchronously(TaskCompletionSource`1 completion, SendOrPostCallback d, Object state)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteSynchronouslyIfPossible(SendOrPostCallback d, Object state)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.InvokeAsync(Func`1 asyncAction)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContextDispatcher.InvokeAsync(Func`1 workItem)
   at Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost.InitializeAsync(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Components.Server.ComponentHub.StartCircuit(String baseUri, String uri, String serializedComponentRecords)
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.AspNetCore.Components.Server.ComponentHub.StartCircuit(String baseUri, String uri, String serializedComponentRecords)
   at lambda_method(Closure , Object , Object[] )
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.ExecuteAsync(Object target, Object[] parameters)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.ExecuteHubMethod(ObjectMethodExecutor methodExecutor, THub hub, Object[] arguments)
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.ExecuteHubMethod(ObjectMethodExecutor methodExecutor, THub hub, Object[] arguments)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.<>c__DisplayClass13_0.<<Invoke>g__ExecuteInvocation|0>d.MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.<>c__DisplayClass13_0.<Invoke>g__ExecuteInvocation|0()
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.Invoke(HubMethodDescriptor descriptor, HubConnectionContext connection, HubMethodInvocationMessage hubMethodInvocationMessage, Boolean isStreamResponse, Boolean isStreamCall)
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.Invoke(HubMethodDescriptor descriptor, HubConnectionContext connection, HubMethodInvocationMessage hubMethodInvocationMessage, Boolean isStreamResponse, Boolean isStreamCall)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.ProcessInvocation(HubConnectionContext connection, HubMethodInvocationMessage hubMethodInvocationMessage, Boolean isStreamResponse)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.DispatchMessageAsync(HubConnectionContext connection, HubMessage hubMessage)
   at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.DispatchMessagesAsync(HubConnectionContext connection)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.ExecutionContextCallback(Object s)
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.ExecuteFromThreadPool(Thread threadPoolThread)
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

@JEnrightDev
Copy link
Author

After further digging, this is 100% a PEBKAC issue.

In my "Index", I was overriding the OnIntitializedAsync method, but calling base.OnInitialized() instead of OnInitializedAsync, which skips the component initialization.

I only managed to catch this by looking through the source and slowly piecing it together. Maybe a mechanism should be added to make this obvious, or a change in documentation?

@mrpmorris
Copy link
Owner

Ah, well done!

What improvements do you have in mind?

@JEnrightDev
Copy link
Author

I'm not quite sure. But maybe passing along something like a "tag" or something to the DisposableCallback so that the exception message can be made clearer about where the "owner" of the callback is. That, plus a little extra documentation about that exception in particular would probably help quite a bit.

@mrpmorris
Copy link
Owner

It's really just a helper class that I didn't expect anyone to use. Your tag idea is pretty good though. Shame I can't have it create a StackTrace only when compiling in Debug.

@JEnrightDev
Copy link
Author

Well, that idea works if you're willing to compile from source to debug.

#if DEBUG
_stackTrace = new StackTrace();
#endif

@mrpmorris
Copy link
Owner

Yeah, but I don't release debug binaries. That's why it's a shame :)

mrpmorris added a commit that referenced this issue May 2, 2020
Add Id to help identify the origin of any instance of DisposableCallback that is not disposed of.
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 a pull request may close this issue.

2 participants