From 4ba463ddeccddd64135c2856e9d8ae8454425aa8 Mon Sep 17 00:00:00 2001 From: Ben Olden-Cooligan Date: Thu, 29 Feb 2024 16:59:21 -0800 Subject: [PATCH] Sdk: Keep SANE initialized for multiple operations --- NAPS2.Lib/Scan/ScanPerformer.cs | 4 ++++ .../Scan/Internal/Sane/Native/SaneClient.cs | 17 ++++++++++++++--- NAPS2.Sdk/Scan/Internal/Sane/SaneScanDriver.cs | 4 ++-- NAPS2.Sdk/Scan/SaneOptions.cs | 6 ++++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/NAPS2.Lib/Scan/ScanPerformer.cs b/NAPS2.Lib/Scan/ScanPerformer.cs index eb5836ca08..b0714f0524 100644 --- a/NAPS2.Lib/Scan/ScanPerformer.cs +++ b/NAPS2.Lib/Scan/ScanPerformer.cs @@ -238,6 +238,10 @@ private ScanOptions BuildOptions(ScanProfile scanProfile, ScanParams scanParams, IncludeWiaDevices = false // TODO: Consider adding a user option for TwainOptions.ShowProgress instead of our progress window }, + SaneOptions = + { + KeepInitialized = false + }, KeyValueOptions = scanProfile.KeyValueOptions != null ? new KeyValueScanOptions(scanProfile.KeyValueOptions) : new KeyValueScanOptions(), diff --git a/NAPS2.Sdk/Scan/Internal/Sane/Native/SaneClient.cs b/NAPS2.Sdk/Scan/Internal/Sane/Native/SaneClient.cs index edbeb6a412..daa89051df 100644 --- a/NAPS2.Sdk/Scan/Internal/Sane/Native/SaneClient.cs +++ b/NAPS2.Sdk/Scan/Internal/Sane/Native/SaneClient.cs @@ -6,6 +6,9 @@ namespace NAPS2.Scan.Internal.Sane.Native; internal class SaneClient : SaneNativeObject { private static readonly object SaneLock = new(); + private static bool _isInitialized; + + private readonly bool _keepInitialized; private static SaneNativeLibrary GetNativeLibrary(ISaneInstallation saneInstallation) { @@ -15,10 +18,17 @@ private static SaneNativeLibrary GetNativeLibrary(ISaneInstallation saneInstalla } } - public SaneClient(ISaneInstallation saneInstallation) : base(GetNativeLibrary(saneInstallation), IntPtr.Zero) + public SaneClient(ISaneInstallation saneInstallation, bool keepInitialized) + : base(GetNativeLibrary(saneInstallation), IntPtr.Zero) { + _keepInitialized = keepInitialized; + Monitor.Enter(SaneLock); - Native.sane_init(out _, IntPtr.Zero); + if (!_isInitialized) + { + Native.sane_init(out _, IntPtr.Zero); + _isInitialized = true; + } } public IEnumerable GetDevices() @@ -56,9 +66,10 @@ public SaneDevice OpenDevice(string deviceName) protected override void Dispose(bool disposing) { - if (disposing) + if (disposing && !_keepInitialized) { Native.sane_exit(); + _isInitialized = false; } Monitor.Exit(SaneLock); } diff --git a/NAPS2.Sdk/Scan/Internal/Sane/SaneScanDriver.cs b/NAPS2.Sdk/Scan/Internal/Sane/SaneScanDriver.cs index 5adee29f45..4d805f1a64 100644 --- a/NAPS2.Sdk/Scan/Internal/Sane/SaneScanDriver.cs +++ b/NAPS2.Sdk/Scan/Internal/Sane/SaneScanDriver.cs @@ -50,7 +50,7 @@ void MaybeCallback(SaneDeviceInfo device) { // TODO: This is crashing after a delay for no apparent reason. // That's okay because we're in a worker process, but ideally we could fix it in SANE. - using var client = new SaneClient(Installation); + using var client = new SaneClient(Installation, options.SaneOptions.KeepInitialized); // TODO: We can use device.type and .vendor to help pick an icon etc. // https://sane-project.gitlab.io/standard/api.html#device-descriptor-type if (Installation.CanStreamDevices) @@ -133,7 +133,7 @@ public Task Scan(ScanOptions options, CancellationToken cancelToken, IScanEvents try { Installation.Initialize(); - using var client = new SaneClient(Installation); + using var client = new SaneClient(Installation, options.SaneOptions.KeepInitialized); if (cancelToken.IsCancellationRequested) return; _scanningContext.Logger.LogDebug("Opening SANE Device \"{ID}\"", options.Device!.ID); using var device = client.OpenDevice(options.Device.ID); diff --git a/NAPS2.Sdk/Scan/SaneOptions.cs b/NAPS2.Sdk/Scan/SaneOptions.cs index 7ed1ce6a49..a46e077157 100644 --- a/NAPS2.Sdk/Scan/SaneOptions.cs +++ b/NAPS2.Sdk/Scan/SaneOptions.cs @@ -9,4 +9,10 @@ public class SaneOptions /// Limit the devices queried by GetDevices/GetDeviceList to the given SANE backend. /// public string? Backend { get; set; } + + /// + /// Whether to keep SANE initialized in memory after the operation is complete. This improves stability when + /// doing multiple operations in a single process. Defaults to true. + /// + public bool KeepInitialized { get; set; } = true; } \ No newline at end of file