Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion GVFS/GVFS.Common/FileBasedLock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,23 @@ public FileBasedLock(
protected string LockPath { get; }
protected ITracer Tracer { get; }

public abstract bool TryAcquireLock();
public bool TryAcquireLock()
{
return this.TryAcquireLock(out _);
}

/// <summary>
/// Attempts to acquire the lock, providing the exception that prevented acquisition.
/// </summary>
/// <param name="lockException">
/// When the method returns false, contains the exception that prevented lock acquisition.
/// Callers can pattern-match on the exception type to distinguish lock contention
/// (e.g. <see cref="System.IO.IOException"/> with a sharing violation HResult) from
/// permission errors (<see cref="UnauthorizedAccessException"/>) or other failures.
/// Null when the method returns true.
/// </param>
/// <returns>True if the lock was acquired, false otherwise.</returns>
public abstract bool TryAcquireLock(out Exception lockException);

public abstract void Dispose();
}
Expand Down
1 change: 1 addition & 0 deletions GVFS/GVFS.Common/GVFSConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public static class DotGVFS
{
public const string CorruptObjectsName = "CorruptObjects";
public const string LogName = "logs";
public const string MountLock = "mount.lock";

public static class Databases
{
Expand Down
1 change: 1 addition & 0 deletions GVFS/GVFS.Common/ReturnCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ public enum ReturnCode
NullRequestData = 5,
UnableToRegisterForOfflineIO = 6,
DehydrateFolderFailures = 7,
MountAlreadyRunning = 8,
}
}
29 changes: 28 additions & 1 deletion GVFS/GVFS.Mount/InProcessMount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,28 @@ public void Mount(EventLevel verbosity, Keywords keywords)
{
this.currentState = MountState.Mounting;

string mountLockPath = Path.Combine(this.enlistment.DotGVFSRoot, GVFSConstants.DotGVFS.MountLock);
using (FileBasedLock mountLock = GVFSPlatform.Instance.CreateFileBasedLock(
new PhysicalFileSystem(),
this.tracer,
mountLockPath))
{
if (!mountLock.TryAcquireLock(out Exception lockException))
{
if (lockException is IOException)
{
this.FailMountAndExit(ReturnCode.MountAlreadyRunning, "Mount: Another mount process is already running.");
}

this.FailMountAndExit("Mount: Failed to acquire mount lock: {0}", lockException.Message);
}

this.MountWithLockAcquired(verbosity, keywords);
}
}

private void MountWithLockAcquired(EventLevel verbosity, Keywords keywords)
{
// Start auth + config query immediately — these are network-bound and don't
// depend on repo metadata or cache paths. Every millisecond of network latency
// we can overlap with local I/O is a win.
Expand Down Expand Up @@ -296,6 +318,11 @@ private NamedPipeServer StartNamedPipe()
}

private void FailMountAndExit(string error, params object[] args)
{
this.FailMountAndExit(ReturnCode.GenericError, error, args);
}

private void FailMountAndExit(ReturnCode returnCode, string error, params object[] args)
{
this.currentState = MountState.MountFailed;

Expand All @@ -312,7 +339,7 @@ private void FailMountAndExit(string error, params object[] args)
this.fileSystemCallbacks = null;
}

Environment.Exit((int)ReturnCode.GenericError);
Environment.Exit((int)returnCode);
}

private T CreateOrReportAndExit<T>(Func<T> factory, string reportMessage)
Expand Down
8 changes: 6 additions & 2 deletions GVFS/GVFS.Platform.Windows/WindowsFileBasedLock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ public WindowsFileBasedLock(
{
}

public override bool TryAcquireLock()
public override bool TryAcquireLock(out Exception lockException)
{
lockException = null;
try
{
lock (this.deleteOnCloseStreamLock)
Expand All @@ -63,13 +64,14 @@ public override bool TryAcquireLock()
catch (IOException e)
{
// HResultErrorFileExists is expected when the lock file exists
// HResultErrorSharingViolation is expected when the lock file exists andanother GVFS process has acquired the lock file
// HResultErrorSharingViolation is expected when the lock file exists and another GVFS process has acquired the lock file
if (e.HResult != HResultErrorFileExists && e.HResult != HResultErrorSharingViolation)
{
EventMetadata metadata = this.CreateLockMetadata(e);
this.Tracer.RelatedWarning(metadata, $"{nameof(this.TryAcquireLock)}: IOException caught while trying to acquire lock");
}

lockException = e;
this.DisposeStream();
return false;
}
Expand All @@ -78,6 +80,7 @@ public override bool TryAcquireLock()
EventMetadata metadata = this.CreateLockMetadata(e);
this.Tracer.RelatedWarning(metadata, $"{nameof(this.TryAcquireLock)}: UnauthorizedAccessException caught while trying to acquire lock");

lockException = e;
this.DisposeStream();
return false;
}
Expand All @@ -86,6 +89,7 @@ public override bool TryAcquireLock()
EventMetadata metadata = this.CreateLockMetadata(e);
this.Tracer.RelatedWarning(metadata, $"{nameof(this.TryAcquireLock)}: Win32Exception caught while trying to acquire lock");

lockException = e;
this.DisposeStream();
return false;
}
Expand Down
4 changes: 3 additions & 1 deletion GVFS/GVFS.UnitTests/Mock/Common/MockFileBasedLock.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using GVFS.Common;
using GVFS.Common.FileSystem;
using GVFS.Common.Tracing;
using System;

namespace GVFS.UnitTests.Mock.Common
{
Expand All @@ -14,8 +15,9 @@ public MockFileBasedLock(
{
}

public override bool TryAcquireLock()
public override bool TryAcquireLock(out Exception lockException)
{
lockException = null;
return true;
}

Expand Down
Loading