Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dead GVFSLock and GitStatusCache code #29

Merged
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
5 changes: 0 additions & 5 deletions GVFS/GVFS.Common/GVFSConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,6 @@ public static class LocalGVFSConfig
public const string OrgInfoServerUrl = "upgrade.orgInfoServerUrl";
}

public static class GitStatusCache
{
public const string EnableGitStatusCacheTokenFile = "EnableGitStatusCacheToken.dat";
}

public static class Service
{
public const string ServiceName = "GVFS.Service";
Expand Down
308 changes: 1 addition & 307 deletions GVFS/GVFS.Common/GVFSLock.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
using GVFS.Common.NamedPipes;
using GVFS.Common.Tracing;
using System;
using System.Diagnostics;
using System.Threading;

namespace GVFS.Common
{
public partial class GVFSLock
public class GVFSLock
{
private readonly object acquisitionLock = new object();
private readonly ITracer tracer;
private readonly LockHolder currentLockHolder = new LockHolder();

public GVFSLock(ITracer tracer)
{
Expand All @@ -24,238 +21,6 @@ public ActiveGitCommandStats Stats
private set;
}

/// <summary>
/// Allows external callers (non-GVFS) to acquire the lock.
/// </summary>
/// <param name="requestor">The data for the external acquisition request.</param>
/// <param name="existingExternalHolder">The current holder of the lock if the acquisition fails.</param>
/// <returns>True if the lock was acquired, false otherwise.</returns>
public bool TryAcquireLockForExternalRequestor(
NamedPipeMessages.LockData requestor,
out NamedPipeMessages.LockData existingExternalHolder)
{
EventMetadata metadata = new EventMetadata();
EventLevel eventLevel = EventLevel.Verbose;
metadata.Add("LockRequest", requestor.ToString());
metadata.Add("IsElevated", requestor.IsElevated);

existingExternalHolder = null;

try
{
lock (this.acquisitionLock)
{
if (this.currentLockHolder.IsGVFS)
{
metadata.Add("CurrentLockHolder", "GVFS");
metadata.Add("Result", "Denied");

return false;
}

existingExternalHolder = this.GetExternalHolder();
if (existingExternalHolder != null)
{
metadata.Add("CurrentLockHolder", existingExternalHolder.ToString());
metadata.Add("Result", "Denied");

return false;
}

metadata.Add("Result", "Accepted");
eventLevel = EventLevel.Informational;

this.currentLockHolder.AcquireForExternalRequestor(requestor);
this.Stats = new ActiveGitCommandStats();

return true;
}
}
finally
{
this.tracer.RelatedEvent(eventLevel, "TryAcquireLockExternal", metadata);
}
}

/// <summary>
/// Allow GVFS to acquire the lock.
/// </summary>
/// <returns>True if GVFS was able to acquire the lock or if it already held it. False othwerwise.</returns>
public bool TryAcquireLockForGVFS()
{
EventMetadata metadata = new EventMetadata();
try
{
lock (this.acquisitionLock)
{
if (this.currentLockHolder.IsGVFS)
{
return true;
}

NamedPipeMessages.LockData existingExternalHolder = this.GetExternalHolder();
if (existingExternalHolder != null)
{
metadata.Add("CurrentLockHolder", existingExternalHolder.ToString());
metadata.Add("Result", "Denied");
return false;
}

this.currentLockHolder.AcquireForGVFS();
metadata.Add("Result", "Accepted");
return true;
}
}
finally
{
this.tracer.RelatedEvent(EventLevel.Verbose, "TryAcquireLockInternal", metadata);
}
}

public void ReleaseLockHeldByGVFS()
{
lock (this.acquisitionLock)
{
if (!this.currentLockHolder.IsGVFS)
{
throw new InvalidOperationException("Cannot release lock that is not held by GVFS");
}

this.tracer.RelatedEvent(EventLevel.Verbose, nameof(this.ReleaseLockHeldByGVFS), new EventMetadata());
this.currentLockHolder.Release();
}
}

public bool ReleaseLockHeldByExternalProcess(int pid)
{
return this.ReleaseExternalLock(pid, nameof(this.ReleaseLockHeldByExternalProcess));
}

public NamedPipeMessages.LockData GetExternalHolder()
{
NamedPipeMessages.LockData externalHolder;
this.IsLockAvailable(checkExternalHolderOnly: true, existingExternalHolder: out externalHolder);

return externalHolder;
}

public bool IsLockAvailableForExternalRequestor(out NamedPipeMessages.LockData existingExternalHolder)
{
return this.IsLockAvailable(checkExternalHolderOnly: false, existingExternalHolder: out existingExternalHolder);
}

public string GetLockedGitCommand()
{
// In this code path, we don't care if the process terminated without releasing the lock. The calling code
// is asking us about this lock so that it can determine if git was the cause of certain IO events. Even
// if the git process has terminated, the answer to that question does not change.
NamedPipeMessages.LockData currentHolder = this.currentLockHolder.GetExternalHolder();

if (currentHolder != null)
{
return currentHolder.ParsedCommand;
}

return null;
}

public string GetStatus()
{
lock (this.acquisitionLock)
{
if (this.currentLockHolder.IsGVFS)
{
return "Held by GVFS.";
}

NamedPipeMessages.LockData externalHolder = this.GetExternalHolder();
if (externalHolder != null)
{
return string.Format("Held by {0} (PID:{1})", externalHolder.ParsedCommand, externalHolder.PID);
}
}

return "Free";
}

private bool IsLockAvailable(bool checkExternalHolderOnly, out NamedPipeMessages.LockData existingExternalHolder)
{
lock (this.acquisitionLock)
{
if (!checkExternalHolderOnly &&
this.currentLockHolder.IsGVFS)
{
existingExternalHolder = null;
return false;
}

bool externalHolderTerminatedWithoutReleasingLock;
existingExternalHolder = this.currentLockHolder.GetExternalHolder(
out externalHolderTerminatedWithoutReleasingLock);

if (externalHolderTerminatedWithoutReleasingLock)
{
this.ReleaseLockForTerminatedProcess(existingExternalHolder.PID);
this.tracer.SetGitCommandSessionId(string.Empty);
existingExternalHolder = null;
}

return existingExternalHolder == null;
}
}

private bool ReleaseExternalLock(int pid, string eventName)
{
lock (this.acquisitionLock)
{
EventMetadata metadata = new EventMetadata();

try
{
if (this.currentLockHolder.IsGVFS)
{
metadata.Add("IsLockedByGVFS", "true");
return false;
}

// We don't care if the process has already terminated. We're just trying to record the info for the last holder.
NamedPipeMessages.LockData previousExternalHolder = this.currentLockHolder.GetExternalHolder();

if (previousExternalHolder == null)
{
metadata.Add("Result", "Failed (no current holder, requested PID=" + pid + ")");
return false;
}

metadata.Add("CurrentLockHolder", previousExternalHolder.ToString());
metadata.Add("IsElevated", previousExternalHolder.IsElevated);
metadata.Add(nameof(RepoMetadata.Instance.EnlistmentId), RepoMetadata.Instance.EnlistmentId);

if (previousExternalHolder.PID != pid)
{
metadata.Add("pid", pid);
metadata.Add("Result", "Failed (wrong PID)");
return false;
}

this.currentLockHolder.Release();
metadata.Add("Result", "Released");
this.Stats.AddStatsToTelemetry(metadata);

return true;
}
finally
{
this.tracer.RelatedEvent(EventLevel.Informational, eventName, metadata, Keywords.Telemetry);
}
}
}

private void ReleaseLockForTerminatedProcess(int pid)
{
this.ReleaseExternalLock(pid, "ExternalLockHolderExited");
}

// The lock release event is a convenient place to record stats about things that happened while a git command was running,
// such as duration/count of object downloads during a git command, cache hits during a git command, etc.
public class ActiveGitCommandStats
Expand Down Expand Up @@ -364,76 +129,5 @@ public void AddStatsToTelemetry(EventMetadata metadata)
metadata.Add("SizeQueryTimeMS", this.sizeQueryTimeMs);
}
}

/// <summary>
/// This class manages the state of which process currently owns the GVFS lock. This code is complicated because
/// the lock can be held by us or by an external process, and because the external process that holds the lock
/// can terminate without releasing the lock. If that happens, we implicitly release the lock the next time we
/// check to see who is holding it.
///
/// The goal of this class is to make it impossible for the rest of GVFSLock to read the external holder without being
/// aware of the fact that it could have terminated.
///
/// This class assumes that the caller is handling all synchronization.
/// </summary>
private class LockHolder
{
private NamedPipeMessages.LockData externalLockHolder;

public bool IsFree
{
get { return !this.IsGVFS && this.externalLockHolder == null; }
}

public bool IsGVFS
{
get; private set;
}

public void AcquireForGVFS()
{
if (this.externalLockHolder != null)
{
throw new InvalidOperationException("Cannot acquire for GVFS because there is an external holder");
}

this.IsGVFS = true;
}

public void AcquireForExternalRequestor(NamedPipeMessages.LockData externalLockHolder)
{
if (this.IsGVFS ||
this.externalLockHolder != null)
{
throw new InvalidOperationException("Cannot acquire a lock that is already held");
}

this.externalLockHolder = externalLockHolder;
}

public void Release()
{
this.IsGVFS = false;
this.externalLockHolder = null;
}

public NamedPipeMessages.LockData GetExternalHolder()
{
return this.externalLockHolder;
}

public NamedPipeMessages.LockData GetExternalHolder(out bool externalHolderTerminatedWithoutReleasingLock)
{
externalHolderTerminatedWithoutReleasingLock = false;

if (this.externalLockHolder != null)
{
int pid = this.externalLockHolder.PID;
externalHolderTerminatedWithoutReleasingLock = !GVFSPlatform.Instance.IsProcessActive(pid);
}

return this.externalLockHolder;
}
}
}
}
2 changes: 0 additions & 2 deletions GVFS/GVFS.Common/GVFSPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ public static void Register(GVFSPlatform platform)
public abstract bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage);
public abstract bool TryGetDefaultLocalCacheRoot(string enlistmentRoot, out string localCacheRoot, out string localCacheRootError);

public abstract bool IsGitStatusCacheSupported();

public abstract FileBasedLock CreateFileBasedLock(
PhysicalFileSystem fileSystem,
ITracer tracer,
Expand Down
Loading