-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
[Bug]: Duplicate deactivation of ViewModel, from ViewModelActivator. #3635
Comments
Do you need to dispose is the first reaction. Observables often do not. |
Strictly speaking, probably not. This still seems like a violation of the class's apparent contract. And to me, there's value in Disposing the activator, in that if something DOES try to activate it again, I get an exception, and a bug in my own code is revealed. |
Observables aren't honoring that contract anyway. They just adopted the IDisposable to get advantages of some of the mechanics. It's a mistake to think you have to dispose all of them. Depends on ownership of the handle. |
I meant more the "contract" regarding the Actually, scratch what I said earlier. I think an appropriate change would be from... public void Deactivate(bool ignoreRefCount = false)
{
if (Interlocked.Decrement(ref _refCount) == 0 || ignoreRefCount)
{
Interlocked.Exchange(ref _activationHandle, Disposable.Empty).Dispose();
_deactivated.OnNext(Unit.Default);
}
} ...to... public void Deactivate(bool ignoreRefCount = false)
{
var wasActive = ignoreRefCount
? (Interlocked.Exchange(ref _refCount, 0) != 0)
: (Interlocked.Decrement(ref _refCount) == 0);
if(wasActive)
{
Interlocked.Exchange(ref _activationHandle, Disposable.Empty).Dispose();
_deactivated.OnNext(Unit.Default);
}
} |
Describe the bug 🐞
I encountered this via an
ObjectDisposedException
coming out ofViewForMixins.HandleViewModelActivation
. This was the result of the following sequence of events:ViewModelActivator
within that ViewModel is disposed, along with it.Unloaded
event, on a future dispatcher frame.ViewModelActivator.Deactivate()
is called via disposal of a previous call to.Activate()
_deactivated.OnNext()
is called, after_deactivated
has been disposed.I initially tried to solve this by calling
.Deactivate(ignoreRefCount: true)
on the activator before disposing it, but this did not end up preventing thedeactivated.OnNext()
call within.Deactivate()
. This is because I actually have 5 View consumers that are activating this ViewModel, and.Deactivate()
does not reset_refCount
to wipe these out. In other words, it's possible forViewModelActivator.Deactivated
to fire more times thanViewModelActivator.Activated
, which is what_refCount
seems to be intended to prevent from happening.https://github.com/reactiveui/ReactiveUI/blob/11a314090295bc6f53038ffe22aab5687a6df94d/src/ReactiveUI/Activation/ViewModelActivator.cs#L93C43-L93C43
Step to reproduce
For this snippet,
ObjectDisposedException
throws at theactivation3.Dispose()
call.If
activator.Dispose()
is removed, then you can observe two firings of theDeactivated
event in the console, despiteActivated
only firing once.Reproduction repository
No response
Expected behavior
ViewModelActivator
that has is inactive should be safe to dispose, so long as no one tries to activate it again.ViewModelActivator.Deactivate(ignoreRefCount: true)
should render allIDisposable
s returned by previous calls to.Activate()
inert, and disposing those should not trigger an exception.ViewModelActivator.Deactivated
event to fire more times than `ViewModelActivator.Activated.Screenshots 🖼️
No response
IDE
No response
Operating system
Windows
Version
10 22H2
Device
N/A
ReactiveUI Version
19.4.1
Additional information ℹ️
I would happily submit a PR to fix this, if it would be welcome.
The text was updated successfully, but these errors were encountered: