#1851 - threading bug

This commit is contained in:
rmcrackan
2026-06-04 09:59:12 -04:00
parent d532e40fe9
commit 869917e0ce
3 changed files with 33 additions and 8 deletions

View File

@@ -1,4 +1,5 @@
using ApplicationServices;
using Avalonia.Threading;
using DataLayer;
using LibationFileManager;
using ReactiveUI;
@@ -77,6 +78,9 @@ partial class MainVM
if (Configuration.Instance.AutoDownloadEpisodes
&& stats.PendingBooks + stats.pdfsNotDownloaded > 0)
await BackupAllBooksAsync(stats.LibraryBooks);
{
// RunWorkerCompleted has no SynchronizationContext; queue items require the UI thread.
await Dispatcher.UIThread.InvokeAsync(async () => await BackupAllBooksAsync(stats.LibraryBooks));
}
}
}

View File

@@ -255,7 +255,16 @@ public class ProcessQueueViewModel : ReactiveObject
&& entry.Status is ProcessBookStatus.Completed
&& Queue.RemoveCompleted(entry);
/// <summary>
/// ProcessBookViewModel requires a captured UI SynchronizationContext. Callers may resume on a
/// thread-pool thread after await (e.g. auto-download after BackgroundWorker).
/// </summary>
private void RunOnQueueUiThread(Action action) => Invoke(action);
private void AddDownloadPdf(IList<LibraryBook> entries, Configuration config)
=> RunOnQueueUiThread(() => addDownloadPdfCore(entries, config));
private void addDownloadPdfCore(IList<LibraryBook> entries, Configuration config)
{
var procs = entries.Where(e => !IsBookInQueue(e)).Select(Create).ToArray();
Serilog.Log.Logger.Information("Queueing {count} books for PDF-only download", procs.Length);
@@ -266,6 +275,9 @@ public class ProcessQueueViewModel : ReactiveObject
}
private void AddDownloadDecrypt(IList<LibraryBook> entries, Configuration config)
=> RunOnQueueUiThread(() => addDownloadDecryptCore(entries, config));
private void addDownloadDecryptCore(IList<LibraryBook> entries, Configuration config)
{
var procs = entries.Where(e => !IsBookInQueue(e)).Select(Create).ToArray();
Serilog.Log.Logger.Information("Queueing {count} books ofr download/decrypt", procs.Length);
@@ -276,6 +288,9 @@ public class ProcessQueueViewModel : ReactiveObject
}
private void AddConvertMp3(IList<LibraryBook> entries, Configuration config)
=> RunOnQueueUiThread(() => addConvertMp3Core(entries, config));
private void addConvertMp3Core(IList<LibraryBook> entries, Configuration config)
{
var procs = entries.Where(e => !IsBookInQueue(e)).Select(Create).ToArray();
Serilog.Log.Logger.Information("Queueing {count} books for mp3 conversion", procs.Length);

View File

@@ -1,5 +1,6 @@
using ApplicationServices;
using AudibleUtilities;
using Dinah.Core.Threading;
using Dinah.Core.WindowsDesktop.Drawing;
using FileManager;
using LibationFileManager;
@@ -26,14 +27,19 @@ public partial class Form1
// wire-up event to automatically download after scan.
// winforms only. this should NOT be allowed in cli
updateCountsBw.RunWorkerCompleted += async (object? sender, System.ComponentModel.RunWorkerCompletedEventArgs e) =>
{
if (!Configuration.Instance.AutoDownloadEpisodes || e.Result is not LibraryCommands.LibraryStats libraryStats)
return;
updateCountsBw.RunWorkerCompleted += (_, e) => tryAutoDownloadAfterCounts(e);
}
if ((libraryStats.PendingBooks + libraryStats.pdfsNotDownloaded) > 0)
await BackupAllBooksAsync(libraryStats.LibraryBooks);
};
private void tryAutoDownloadAfterCounts(System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (!Configuration.Instance.AutoDownloadEpisodes || e.Result is not LibraryCommands.LibraryStats libraryStats)
return;
if (libraryStats.PendingBooks + libraryStats.pdfsNotDownloaded <= 0)
return;
// RunWorkerCompleted has no SynchronizationContext; queue items require the UI thread.
this.UIThreadAsync(() => _ = BackupAllBooksAsync(libraryStats.LibraryBooks));
}
private static object? LoadResourceImage(string resourceName)