diff options
author | jhorv <38920027+jhorv@users.noreply.github.com> | 2023-05-26 17:57:43 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-26 23:57:43 +0200 |
commit | 42b9c1e8fede88880454154f8c3683f1f8424ed9 (patch) | |
tree | 7299252239f3809a5fbd4637c34624052c3c933f /src | |
parent | 3b375525fbaded422fb2f9a9984a5770a3779fcb (diff) |
Ryujinx.Ava: fixes for random hangs on exit (#4827)1.1.826
* Attempt at fixing hang on exit by ending the WindowNotificationManager notification loop, so that the Thread running it can exit.
* explicitly apply the NotificationManager template to allow the notification loop to begin
* NotificationHelper - remove explicity call to ApplyTemplate(). Change to ManualResetEventSlim so we can cancel the Wait on it.
* add a timeout to AudioRenderSystem.Stop()'s waiting for the termination signal, log a warning if this timeout occurs, and continue execution
* NotifiationHelper - cancel first, the CompleteAdding()
* Remove AudioRenderSystem._terminationEvent, redundant
* NotificationHelper - use host.Closing event to trigger cancellation instead of _notifationManager.DetachedFromLogicalTree
* Change NotificationHelper to use an explicit Thread for background work. Wait on the cancellationToken's WaitHandle so the Thread doesn't have to deal with async. Wrap foreach in try/catch (OperationCanceledException) to swallow the escaping exception from the GetConsumingEnumerable().
* adjust formatting of AsyncWorkQueue constructor to use object initializers consistently
* use AsyncWorkQueue to do everything I added in SetNotificationManager()
* Revert "use AsyncWorkQueue to do everything I added in SetNotificationManager()"
This reverts commit f0e78366b8776ec8e2fef8ab023c0db1833155d3.
* use AsyncWorkQueue to handle the Thread-related changes previously made to NotificationHelper.SetNotificationHelper(). Wrap it in Lazy<T> and force instantiation in the TemplateApplied event handler to accomodate for the fact that AsyncWorkQueue starts immediately, and the notification dispatch loop was being delayed by _templateAppliedEvent.
* impl changes suggested by AcK77
* impl changes suggested by AcK77 (more)
Diffstat (limited to 'src')
-rw-r--r-- | src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs | 14 | ||||
-rw-r--r-- | src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs | 35 | ||||
-rw-r--r-- | src/Ryujinx.Common/AsyncWorkQueue.cs | 8 |
3 files changed, 25 insertions, 32 deletions
diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 53570243..1ad7b859 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -31,7 +31,6 @@ namespace Ryujinx.Audio.Renderer.Server private AudioRendererRenderingDevice _renderingDevice; private AudioRendererExecutionMode _executionMode; private IWritableEvent _systemEvent; - private ManualResetEvent _terminationEvent; private MemoryPoolState _dspMemoryPoolState; private VoiceContext _voiceContext; private MixContext _mixContext; @@ -83,7 +82,6 @@ namespace Ryujinx.Audio.Renderer.Server public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent) { _manager = manager; - _terminationEvent = new ManualResetEvent(false); _dspMemoryPoolState = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp); _voiceContext = new VoiceContext(); _mixContext = new MixContext(); @@ -387,11 +385,6 @@ namespace Ryujinx.Audio.Renderer.Server _isActive = false; } - if (_executionMode == AudioRendererExecutionMode.Auto) - { - _terminationEvent.WaitOne(); - } - Logger.Info?.Print(LogClass.AudioRenderer, $"Stopped renderer id {_sessionId}"); } @@ -668,8 +661,6 @@ namespace Ryujinx.Audio.Renderer.Server { if (_isActive) { - _terminationEvent.Reset(); - if (!_manager.Processor.HasRemainingCommands(_sessionId)) { GenerateCommandList(out CommandList commands); @@ -686,10 +677,6 @@ namespace Ryujinx.Audio.Renderer.Server _isDspRunningBehind = true; } } - else - { - _terminationEvent.Set(); - } } } @@ -857,7 +844,6 @@ namespace Ryujinx.Audio.Renderer.Server } _manager.Unregister(this); - _terminationEvent.Dispose(); _workBufferMemoryPin.Dispose(); if (MemoryManager is IRefCounted rc) diff --git a/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs b/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs index 7e2afb8b..f207c5fb 100644 --- a/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs +++ b/src/Ryujinx.Ava/UI/Helpers/NotificationHelper.cs @@ -3,21 +3,20 @@ using Avalonia.Controls; using Avalonia.Controls.Notifications; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Common; using System; using System.Collections.Concurrent; using System.Threading; -using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Helpers { public static class NotificationHelper { - private const int MaxNotifications = 4; + private const int MaxNotifications = 4; private const int NotificationDelayInMs = 5000; private static WindowNotificationManager _notificationManager; - private static readonly ManualResetEvent _templateAppliedEvent = new(false); private static readonly BlockingCollection<Notification> _notifications = new(); public static void SetNotificationManager(Window host) @@ -29,25 +28,31 @@ namespace Ryujinx.Ava.UI.Helpers Margin = new Thickness(0, 0, 15, 40) }; + var maybeAsyncWorkQueue = new Lazy<AsyncWorkQueue<Notification>>( + () => new AsyncWorkQueue<Notification>(notification => + { + Dispatcher.UIThread.Post(() => + { + _notificationManager.Show(notification); + }); + }, + "UI.NotificationThread", + _notifications), + LazyThreadSafetyMode.ExecutionAndPublication); + _notificationManager.TemplateApplied += (sender, args) => { - _templateAppliedEvent.Set(); + // NOTE: Force creation of the AsyncWorkQueue. + _ = maybeAsyncWorkQueue.Value; }; - Task.Run(async () => + host.Closing += (sender, args) => { - _templateAppliedEvent.WaitOne(); - - foreach (var notification in _notifications.GetConsumingEnumerable()) + if (maybeAsyncWorkQueue.IsValueCreated) { - Dispatcher.UIThread.Post(() => - { - _notificationManager.Show(notification); - }); - - await Task.Delay(NotificationDelayInMs / MaxNotifications); + maybeAsyncWorkQueue.Value.Dispose(); } - }); + }; } public static void Show(string title, string text, NotificationType type, bool waitingExit = false, Action onClick = null, Action onClose = null) diff --git a/src/Ryujinx.Common/AsyncWorkQueue.cs b/src/Ryujinx.Common/AsyncWorkQueue.cs index 80f8dcfe..746ef4ca 100644 --- a/src/Ryujinx.Common/AsyncWorkQueue.cs +++ b/src/Ryujinx.Common/AsyncWorkQueue.cs @@ -22,9 +22,11 @@ namespace Ryujinx.Common _cts = new CancellationTokenSource(); _queue = collection; _workerAction = callback; - _workerThread = new Thread(DoWork) { Name = name }; - - _workerThread.IsBackground = true; + _workerThread = new Thread(DoWork) + { + Name = name, + IsBackground = true + }; _workerThread.Start(); } |