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