private bool IsLockAvailable(bool checkExternalHolderOnly, out NamedPipeMessages.LockData existingExternalHolder) { lock (this.acquisitionLock) { if (!checkExternalHolderOnly && this.currentLockHolder.IsGSD) { 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); } }
/// <summary> /// Allow GSD to acquire the lock. /// </summary> /// <returns>True if GSD was able to acquire the lock or if it already held it. False othwerwise.</returns> public bool TryAcquireLockForGSD() { EventMetadata metadata = new EventMetadata(); try { lock (this.acquisitionLock) { if (this.currentLockHolder.IsGSD) { return(true); } NamedPipeMessages.LockData existingExternalHolder = this.GetExternalHolder(); if (existingExternalHolder != null) { metadata.Add("CurrentLockHolder", existingExternalHolder.ToString()); metadata.Add("Result", "Denied"); return(false); } this.currentLockHolder.AcquireForGSD(); metadata.Add("Result", "Accepted"); return(true); } } finally { this.tracer.RelatedEvent(EventLevel.Verbose, "TryAcquireLockInternal", metadata); } }
public bool IsReadyForExternalAcquireLockRequests(NamedPipeMessages.LockData requester, out string denyMessage) { if (!this.IsMounted) { denyMessage = "Waiting for mount to complete"; return(false); } if (this.BackgroundOperationCount != 0) { denyMessage = "Waiting for GVFS to release the lock"; return(false); } if (!this.GitIndexProjection.IsProjectionParseComplete()) { denyMessage = "Waiting for GVFS to parse index and update placeholder files"; return(false); } if (!this.gitStatusCache.IsReadyForExternalAcquireLockRequests(requester, out denyMessage)) { return(false); } // Even though we're returning true and saying it's safe to ask for the lock // there is no guarantee that the lock will be acquired, because GVFS itself // could obtain the lock before the external holder gets it. Setting up an // appropriate error message in case that happens denyMessage = "Waiting for GVFS to release the lock"; return(true); }
public string GetLockedGitCommand() { NamedPipeMessages.LockData currentHolder = this.lockHolder; if (currentHolder != null) { return(currentHolder.ParsedCommand); } return(null); }
public void AcquireForExternalRequestor(NamedPipeMessages.LockData externalLockHolder) { if (this.IsGSD || this.externalLockHolder != null) { throw new InvalidOperationException("Cannot acquire a lock that is already held"); } this.externalLockHolder = externalLockHolder; }
/// <summary> /// The GitStatusCache gets a chance to approve / deny requests for a /// command to take the GVFS lock. The GitStatusCache will only block /// if the command is a status command and there is a blocking error /// that might affect the correctness of the result. /// </summary> public virtual bool IsReadyForExternalAcquireLockRequests( NamedPipeMessages.LockData requester, out string infoMessage) { infoMessage = null; if (!this.isInitialized) { return(true); } GitCommandLineParser gitCommand = new GitCommandLineParser(requester.ParsedCommand); if (!gitCommand.IsVerb(GitCommandLineParser.Verbs.Status) || gitCommand.IsSerializedStatus()) { return(true); } bool shouldAllowExternalRequest = true; bool isCacheReady = false; lock (this.cacheFileLock) { if (this.IsCacheReadyAndUpToDate()) { isCacheReady = true; } else { if (!this.TryDeleteStatusCacheFile()) { shouldAllowExternalRequest = false; infoMessage = string.Format("Unable to delete stale status cache file at: {0}", this.serializedGitStatusFilePath); } } } if (isCacheReady) { this.statistics.RecordCacheReady(); } else { this.statistics.RecordCacheNotReady(); } if (!shouldAllowExternalRequest) { this.statistics.RecordBlockedRequest(); } this.context.Tracer.RelatedInfo("GitStatusCache.IsReadyForExternalAcquireLockRequests: isCacheReady: {0}, shouldAllowRequest: {1}", isCacheReady, shouldAllowExternalRequest); return(shouldAllowExternalRequest); }
private void HandleLockRequest(string messageBody, NamedPipeServer.Connection connection) { NamedPipeMessages.AcquireLock.Response response; NamedPipeMessages.LockRequest request = new NamedPipeMessages.LockRequest(messageBody); NamedPipeMessages.LockData requester = request.RequestData; if (this.currentState == MountState.Unmounting) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.UnmountInProgressResult); EventMetadata metadata = new EventMetadata(); metadata.Add("LockRequest", requester.ToString()); metadata.Add(TracingConstants.MessageKey.InfoMessage, "Request denied, unmount in progress"); this.tracer.RelatedEvent(EventLevel.Informational, "HandleLockRequest_UnmountInProgress", metadata); } else if (this.currentState != MountState.Ready) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.MountNotReadyResult); } else { bool lockAcquired = false; NamedPipeMessages.LockData existingExternalHolder = null; string denyGVFSMessage = null; bool lockAvailable = this.context.Repository.GVFSLock.IsLockAvailableForExternalRequestor(out existingExternalHolder); bool isReadyForExternalLockRequests = this.fileSystemCallbacks.IsReadyForExternalAcquireLockRequests(requester, out denyGVFSMessage); if (!requester.CheckAvailabilityOnly && isReadyForExternalLockRequests) { lockAcquired = this.context.Repository.GVFSLock.TryAcquireLockForExternalRequestor(requester, out existingExternalHolder); } if (requester.CheckAvailabilityOnly && lockAvailable && isReadyForExternalLockRequests) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.AvailableResult); } else if (lockAcquired) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.AcceptResult); this.tracer.SetGitCommandSessionId(requester.GitCommandSessionId); } else if (existingExternalHolder == null) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.DenyGVFSResult, responseData: null, denyGVFSMessage: denyGVFSMessage); } else { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.DenyGitResult, existingExternalHolder); } } connection.TrySendResponse(response.CreateMessage()); }
private void HandleLockRequest(string messageBody, NamedPipeServer.Connection connection) { NamedPipeMessages.AcquireLock.Response response; NamedPipeMessages.LockData externalHolder; NamedPipeMessages.LockRequest request = new NamedPipeMessages.LockRequest(messageBody); NamedPipeMessages.LockData requester = request.RequestData; if (request == null) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.UnknownRequest, requester); } else if (this.currentState == MountState.Unmounting) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.UnmountInProgressResult); EventMetadata metadata = new EventMetadata(); metadata.Add("LockRequest", requester.ToString()); metadata.Add(TracingConstants.MessageKey.InfoMessage, "Request denied, unmount in progress"); this.tracer.RelatedEvent(EventLevel.Informational, "HandleLockRequest_UnmountInProgress", metadata); } else if (this.currentState != MountState.Ready) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.MountNotReadyResult); } else { bool lockAcquired = false; string denyMessage; if (this.gvfltCallbacks.IsReadyForExternalAcquireLockRequests(requester, out denyMessage)) { lockAcquired = this.context.Repository.GVFSLock.TryAcquireLock(requester, out externalHolder); } else { // There might be an external lock holder, and it should be reported to the user externalHolder = this.context.Repository.GVFSLock.GetExternalLockHolder(); } if (lockAcquired) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.AcceptResult); } else if (externalHolder == null) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.DenyGVFSResult, responseData: null, denyGVFSMessage: denyMessage); } else { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.DenyGitResult, externalHolder); } } connection.TrySendResponse(response.CreateMessage()); }
public void TryAcquireLockForExternalRequestor_WhenExternalLock() { MockPlatform mockPlatform = (MockPlatform)GVFSPlatform.Instance; GVFSLock gvfsLock = this.AcquireDefaultLock(mockPlatform); NamedPipeMessages.LockData newLockData = new NamedPipeMessages.LockData(4321, false, false, "git new"); NamedPipeMessages.LockData existingExternalHolder; gvfsLock.TryAcquireLockForExternalRequestor(newLockData, out existingExternalHolder).ShouldBeFalse(); this.ValidateLockHeld(gvfsLock, DefaultLockData); this.ValidateExistingExternalHolder(DefaultLockData, existingExternalHolder); mockPlatform.ActiveProcesses.Remove(DefaultLockData.PID); }
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); }
/// <summary> /// Allows external callers (non-GVFS) to acquire the lock. /// </summary> /// <param name="requester">The data for the external acquisition request.</param> /// <param name="holder">The current holder of the lock if the acquisition fails.</param> /// <returns>True if the lock was acquired, false otherwise.</returns> public bool TryAcquireLock( NamedPipeMessages.LockData requester, out NamedPipeMessages.LockData holder) { EventMetadata metadata = new EventMetadata(); EventLevel eventLevel = EventLevel.Verbose; metadata.Add("LockRequest", requester.ToString()); metadata.Add("IsElevated", requester.IsElevated); holder = null; try { lock (this.acquisitionLock) { if (this.IsLockedByGVFS) { metadata.Add("CurrentLockHolder", "GVFS"); metadata.Add("Result", "Denied"); return(false); } if (this.IsExternalLockHolderAlive() && this.lockHolder.PID != requester.PID) { metadata.Add("CurrentLockHolder", this.lockHolder.ToString()); metadata.Add("Result", "Denied"); holder = this.lockHolder; return(false); } metadata.Add("Result", "Accepted"); eventLevel = EventLevel.Informational; this.lockHolder = requester; this.externalLockReleased.Reset(); this.stats = new Stats(); return(true); } } finally { this.tracer.RelatedEvent(eventLevel, "TryAcquireLockExternal", metadata); } }
public string GetStatus() { if (this.IsLockedByGVFS) { return("Held by GVFS."); } NamedPipeMessages.LockData currentHolder = this.lockHolder; if (currentHolder != null) { return(string.Format("Held by {0} (PID:{1})", currentHolder.ParsedCommand, currentHolder.PID)); } return("Free"); }
private void ValidateLock( GVFSLock gvfsLock, NamedPipeMessages.LockData expected, string expectedStatus, string expectedGitCommand, bool expectedIsAvailable) { gvfsLock.GetStatus().ShouldEqual(expectedStatus); NamedPipeMessages.LockData existingHolder; gvfsLock.IsLockAvailableForExternalRequestor(out existingHolder).ShouldEqual(expectedIsAvailable); this.ValidateExistingExternalHolder(expected, existingHolder); gvfsLock.GetLockedGitCommand().ShouldEqual(expectedGitCommand); NamedPipeMessages.LockData externalHolder = gvfsLock.GetExternalHolder(); this.ValidateExistingExternalHolder(expected, externalHolder); }
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. bool externalHolderTerminatedWithoutReleasingLock; NamedPipeMessages.LockData previousExternalHolder = this.currentLockHolder.GetExternalHolder( out externalHolderTerminatedWithoutReleasingLock); 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 ValidateExistingExternalHolder(NamedPipeMessages.LockData expected, NamedPipeMessages.LockData actual) { if (actual != null) { expected.ShouldNotBeNull(); actual.ShouldNotBeNull(); actual.PID.ShouldEqual(expected.PID); actual.IsElevated.ShouldEqual(expected.IsElevated); actual.CheckAvailabilityOnly.ShouldEqual(expected.CheckAvailabilityOnly); actual.ParsedCommand.ShouldEqual(expected.ParsedCommand); } else { expected.ShouldBeNull(); } }
/// <summary> /// Allows external callers (non-GSD) 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.IsGSD) { metadata.Add("CurrentLockHolder", "GSD"); 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); } }
public void TryAcquireLockForExternalRequestor_WhenExternalLock() { Mock <ITracer> mockTracer = new Mock <ITracer>(MockBehavior.Strict); mockTracer.Setup(x => x.RelatedEvent(EventLevel.Informational, "TryAcquireLockExternal", It.IsAny <EventMetadata>())); mockTracer.Setup(x => x.RelatedEvent(EventLevel.Verbose, "TryAcquireLockExternal", It.IsAny <EventMetadata>())); MockPlatform mockPlatform = (MockPlatform)GVFSPlatform.Instance; GVFSLock gvfsLock = this.AcquireDefaultLock(mockPlatform, mockTracer.Object); NamedPipeMessages.LockData newLockData = new NamedPipeMessages.LockData(4321, false, false, "git new", "123"); NamedPipeMessages.LockData existingExternalHolder; gvfsLock.TryAcquireLockForExternalRequestor(newLockData, out existingExternalHolder).ShouldBeFalse(); this.ValidateLockHeld(gvfsLock, DefaultLockData); this.ValidateExistingExternalHolder(DefaultLockData, existingExternalHolder); mockPlatform.ActiveProcesses.Remove(DefaultLockData.PID); mockTracer.VerifyAll(); }
public string GetStatus() { lock (this.acquisitionLock) { if (this.currentLockHolder.IsGSD) { return("Held by GSD."); } NamedPipeMessages.LockData externalHolder = this.GetExternalHolder(); if (externalHolder != null) { return(string.Format("Held by {0} (PID:{1})", externalHolder.ParsedCommand, externalHolder.PID)); } } return("Free"); }
private void HandleLockRequest(string messageBody, NamedPipeServer.Connection connection) { NamedPipeMessages.AcquireLock.Response response; NamedPipeMessages.LockData externalHolder; NamedPipeMessages.LockRequest request = new NamedPipeMessages.LockRequest(messageBody); NamedPipeMessages.LockData requester = request.RequestData; if (request == null) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.UnknownRequest, requester); } else if (this.currentState != MountState.Ready) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.MountNotReadyResult); } else { bool lockAcquired = false; if (this.gvfltCallbacks.IsReadyForExternalAcquireLockRequests()) { lockAcquired = this.gvfsLock.TryAcquireLock(requester, out externalHolder); } else { // There might be an external lock holder, and it should be reported to the user externalHolder = this.gvfsLock.GetExternalLockHolder(); } if (lockAcquired) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.AcceptResult); } else if (externalHolder == null) { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.DenyGVFSResult); } else { response = new NamedPipeMessages.AcquireLock.Response(NamedPipeMessages.AcquireLock.DenyGitResult, externalHolder); } } connection.TrySendResponse(response.CreateMessage()); }
private bool ReleaseExternalLock(int pid, string eventName) { lock (this.acquisitionLock) { EventMetadata metadata = new EventMetadata(); try { if (this.isLockedByGVFS) { metadata.Add("IsLockedByGVFS", "true"); return(false); } if (this.externalLockHolder == null) { metadata.Add("Result", "Failed (no current holder, requested PID=" + pid + ")"); return(false); } metadata.Add("CurrentLockHolder", this.externalLockHolder.ToString()); metadata.Add("IsElevated", this.externalLockHolder.IsElevated); metadata.Add(nameof(RepoMetadata.Instance.EnlistmentId), RepoMetadata.Instance.EnlistmentId); if (this.externalLockHolder.PID != pid) { metadata.Add("pid", pid); metadata.Add("Result", "Failed (wrong PID)"); return(false); } this.externalLockHolder = null; metadata.Add("Result", "Released"); this.Stats.AddStatsToTelemetry(metadata); return(true); } finally { this.tracer.RelatedEvent(EventLevel.Informational, eventName, metadata, Keywords.Telemetry); } } }
private bool ReleaseExternalLock(int pid, string eventName) { lock (this.acquisitionLock) { EventMetadata metadata = new EventMetadata(); try { if (this.IsLockedByGVFS) { metadata.Add("IsLockedByGVFS", "true"); return(false); } if (this.lockHolder == null) { metadata.Add("Result", "Failed (no current holder, requested PID=" + pid + ")"); return(false); } metadata.Add("CurrentLockHolder", this.lockHolder.ToString()); metadata.Add("IsElevated", this.lockHolder.IsElevated); if (this.lockHolder.PID != pid) { metadata.Add("pid", pid); metadata.Add("Result", "Failed (wrong PID)"); return(false); } this.lockHolder = null; this.processWatcher.StopWatching(pid); this.externalLockReleased.Set(); metadata.Add("Result", "Released"); return(true); } finally { this.tracer.RelatedEvent(EventLevel.Informational, eventName, metadata); } } }
public void TryAcquireLockForExternalRequestor_WhenExternalHolderTerminated() { Mock <ITracer> mockTracer = new Mock <ITracer>(MockBehavior.Strict); mockTracer.Setup(x => x.RelatedEvent(EventLevel.Informational, "TryAcquireLockExternal", It.IsAny <EventMetadata>())); mockTracer.Setup(x => x.RelatedEvent(EventLevel.Informational, "ExternalLockHolderExited", It.IsAny <EventMetadata>(), Keywords.Telemetry)); mockTracer.Setup(x => x.SetGitCommandSessionId(string.Empty)); MockPlatform mockPlatform = (MockPlatform)GVFSPlatform.Instance; GVFSLock gvfsLock = this.AcquireDefaultLock(mockPlatform, mockTracer.Object); mockPlatform.ActiveProcesses.Remove(DefaultLockData.PID); NamedPipeMessages.LockData newLockData = new NamedPipeMessages.LockData(4321, false, false, "git new", "123"); mockPlatform.ActiveProcesses.Add(newLockData.PID); NamedPipeMessages.LockData existingExternalHolder; gvfsLock.TryAcquireLockForExternalRequestor(newLockData, out existingExternalHolder).ShouldBeTrue(); existingExternalHolder.ShouldBeNull(); this.ValidateLockHeld(gvfsLock, newLockData); mockTracer.VerifyAll(); }
/// <summary> /// Allows external callers (non-GVFS) to acquire the lock. /// </summary> /// <param name="requester">The data for the external acquisition request.</param> /// <param name="holder"> /// The current holder of the lock if the acquisition fails, or /// the input request if it succeeds. /// </param> /// <returns>True if the lock was acquired, false otherwise.</returns> public bool TryAcquireLock( NamedPipeMessages.LockData requester, out NamedPipeMessages.LockData holder) { EventMetadata metadata = new EventMetadata(); EventLevel eventLevel = EventLevel.Verbose; metadata.Add("LockRequest", requester.ToString()); try { lock (this.acquisitionLock) { if (this.IsLockedByGVFS) { holder = null; metadata.Add("CurrentLockHolder", "GVFS"); metadata.Add("Result", "Denied"); return(false); } if (this.lockHolder != null && this.lockHolder.PID != requester.PID) { holder = this.lockHolder; metadata.Add("CurrentLockHolder", this.lockHolder.ToString()); metadata.Add("Result", "Denied"); return(false); } metadata.Add("Result", "Accepted"); eventLevel = EventLevel.Informational; Process process; if (ProcessHelper.TryGetProcess(requester.PID, out process)) { this.processWatcher.WatchForTermination(requester.PID, GVFSConstants.CommandParentExecutableNames); process.Dispose(); this.lockHolder = requester; holder = requester; this.externalLockReleased.Reset(); return(true); } else { // Process is no longer running so let it // succeed since the process non-existence // signals the lock release. if (process != null) { process.Dispose(); } holder = null; return(true); } } } finally { this.tracer.RelatedEvent(eventLevel, "TryAcquireLockExternal", metadata); } }
public override bool IsReadyForExternalAcquireLockRequests(NamedPipeMessages.LockData requester, out string infoMessage) { infoMessage = string.Empty; return(true); }
private void ValidateLockHeld(GVFSLock gvfsLock, NamedPipeMessages.LockData expected) { this.ValidateLock(gvfsLock, expected, expectedStatus: $"Held by {expected.ParsedCommand} (PID:{expected.PID})", expectedGitCommand: expected.ParsedCommand, expectedIsAvailable: false); }
public bool IsLockAvailableForExternalRequestor(out NamedPipeMessages.LockData existingExternalHolder) { return(this.IsLockAvailable(checkExternalHolderOnly: false, existingExternalHolder: out existingExternalHolder)); }
public void Release() { this.IsGSD = false; this.externalLockHolder = null; }