diff --git a/src/lib-csharp/UpdateManager.cs b/src/lib-csharp/UpdateManager.cs index 020500b4..b2b480d0 100644 --- a/src/lib-csharp/UpdateManager.cs +++ b/src/lib-csharp/UpdateManager.cs @@ -458,21 +458,13 @@ namespace Velopack /// /// Acquires a globally unique mutex/lock for the current application, to avoid concurrent install/uninstall/update operations. /// - protected virtual Mutex AcquireUpdateLock() + protected virtual async Task AcquireUpdateLock() { - var mutexId = $"velopack-{AppId}"; - bool created = false; - Mutex? mutex = null; - try { - mutex = new Mutex(false, mutexId, out created); - } catch (Exception ex) { - Log.Warn(ex, "Unable to acquire global mutex/lock."); - created = false; - } - if (mutex == null || !created) { - throw new Exception("Cannot perform this operation while another install/unistall operation is in progress."); - } - return mutex; + var dir = Directory.CreateDirectory(Locator.PackagesDir!); + var lockPath = Path.Combine(dir.FullName, ".velopack_lock"); + var fsLock = new FileLock(lockPath); + await fsLock.LockAsync().ConfigureAwait(false); + return fsLock; } private static IUpdateSource CreateSimpleSource(string urlOrPath) diff --git a/src/lib-csharp/Util/FileLock.cs b/src/lib-csharp/Util/FileLock.cs new file mode 100644 index 00000000..c7e179ab --- /dev/null +++ b/src/lib-csharp/Util/FileLock.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Velopack.Util +{ + internal class FileLock : IDisposable + { + private readonly string _filePath; + private FileStream? _fileStream; + private bool _locked; + + public FileLock(string path) + { + _filePath = path; + } + + public async Task LockAsync() + { + if (_locked) { + return; + } + + await IoUtil.RetryAsync( + () => { + _fileStream = new FileStream(_filePath, FileMode.Create, FileAccess.Read, FileShare.None, bufferSize: 1, FileOptions.DeleteOnClose); + _locked = true; + return Task.CompletedTask; + }); + } + + public void Dispose() + { + Interlocked.Exchange(ref this._fileStream, null)?.Dispose(); + } + } +} \ No newline at end of file