You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It looks like the implementation in #93 was not fully correct. I'm seeing the following problems related to calling TaskbarIcon.Dispose() explicitly during application shutdown:
Explicit dispose may deadlock itself with the one triggered by TaskbarIcons Application.Exit handler.
Dispose may throw TaskCanceledException when called explicitly during application shutdown. Note that this second problem already happened before the mentioned PR as well.
Both of these only happen sporadically. Unfortunately I am neither able to reproduce them in a minimal example nor am I able to share the code of the actual application. So I've written down my observations below..
In my own code, I've now removed the explicit TaskbarIcon.Dispose() entirely, relying on the dispose from Application.Exit, and suppressing the dispose-releated analyzer warning instead. This works for me because application and tray icon lifetime are coupled in my case. But that may not be the case for other applications, so a general solution to the problem still makes sense I think.
Deadlock
Here is a screenshot from Visual Studios Parallel Stacks view showing the problem. From what I can tell, the following happens:
A thread other than the UI-Thread calls Application.Current.Dispatcher.BeginInvoke(() => Application.Current.Shutdown()) (or similar). This will queue the shutdown operation on the UI thread.
The same or yet another threads calls TaskbarIcon.Dispose().
TaskbarIcon.Dispose() queues the unsubscribe from Application.Exit on the dispatcher and waits until it finished executing (Application.Current.Dispatcher.Invoke(() => Application.Current.Exit -= OnExit)).
The dispatcher executes the Application.Current.Shutdown() from earlier on the UI thread and invokes Application.Exit handlers as part of that procedure
TaskbarIcon.OnExit() gets called and invokes TaskbarIcon.Dispose().
The second TaskbarIcon.Dispose() call tries to acquire the lock which is currently held by the other thread that caused the first invocation.
The UI thread is blocked, will never get to the unsubscribe from Application.Exit and so the other thread will never return form the Dispatcher.Invoke() call.
TaskCanceledException
Here I don't really have any concrete information as to why this exception happens. My best guess is that the application shutdown has already progressed far enough that the application dispatcher is not functional anymore and cannot be called.
Exception Info: System.Threading.Tasks.TaskCanceledException: A task was canceled.
at System.Windows.Threading.DispatcherOperation.Wait(TimeSpan timeout)
at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherOperation operation, CancellationToken cancellationToken, TimeSpan timeout)
at System.Windows.Threading.Dispatcher.Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout)
at System.Windows.Threading.Dispatcher.Invoke(Action callback)
at Hardcodet.Wpf.TaskbarNotification.TaskbarIcon.Dispose(Boolean disposing) in D:\a\1\s\src\NotifyIconWpf\TaskbarIcon.cs:line 1113
at Hardcodet.Wpf.TaskbarNotification.TaskbarIcon.Dispose() in D:\a\1\s\src\NotifyIconWpf\TaskbarIcon.cs:line 1074
The text was updated successfully, but these errors were encountered:
It looks like the implementation in #93 was not fully correct. I'm seeing the following problems related to calling
TaskbarIcon.Dispose()
explicitly during application shutdown:TaskbarIcon
sApplication.Exit
handler.TaskCanceledException
when called explicitly during application shutdown. Note that this second problem already happened before the mentioned PR as well.Both of these only happen sporadically. Unfortunately I am neither able to reproduce them in a minimal example nor am I able to share the code of the actual application. So I've written down my observations below..
In my own code, I've now removed the explicit
TaskbarIcon.Dispose()
entirely, relying on the dispose fromApplication.Exit
, and suppressing the dispose-releated analyzer warning instead. This works for me because application and tray icon lifetime are coupled in my case. But that may not be the case for other applications, so a general solution to the problem still makes sense I think.Deadlock
Here is a screenshot from Visual Studios Parallel Stacks view showing the problem. From what I can tell, the following happens:
Application.Current.Dispatcher.BeginInvoke(() => Application.Current.Shutdown())
(or similar). This will queue the shutdown operation on the UI thread.TaskbarIcon.Dispose()
.TaskbarIcon.Dispose()
takes the lock in TaskbarIcon.cs#L1105TaskbarIcon.Dispose()
queues the unsubscribe fromApplication.Exit
on the dispatcher and waits until it finished executing (Application.Current.Dispatcher.Invoke(() => Application.Current.Exit -= OnExit)
).Application.Current.Shutdown()
from earlier on the UI thread and invokesApplication.Exit
handlers as part of that procedureTaskbarIcon.OnExit()
gets called and invokesTaskbarIcon.Dispose()
.TaskbarIcon.Dispose()
call tries to acquire the lock which is currently held by the other thread that caused the first invocation.Application.Exit
and so the other thread will never return form theDispatcher.Invoke()
call.TaskCanceledException
Here I don't really have any concrete information as to why this exception happens. My best guess is that the application shutdown has already progressed far enough that the application dispatcher is not functional anymore and cannot be called.
The text was updated successfully, but these errors were encountered: