/// <summary> /// AcquireAsync the lock, waiting as long as it takes or until the configured timeout. /// </summary> public async Task <LockAcquisitionResult> AcquireAsync(Context context, TimeSpan waitTimeout) { _tracer.Info(context, $"Acquiring lock file=[{_lockFilePath}]"); _fileSystem.CreateDirectory(_lockFilePath.GetParent()); DateTime timeOutTime = DateTime.UtcNow + waitTimeout; Exception?lastException = null; int? lastCompetingProcessId = null; while (DateTime.UtcNow < timeOutTime) { try { // Anything other than FileShare.None is effectively ignored in Unix FileShare fileShare = BuildXL.Utilities.OperatingSystemHelper.IsUnixOS ? FileShare.None : FileShare.Read; _lockFile = await _fileSystem.OpenSafeAsync( _lockFilePath, FileAccess.Write, FileMode.OpenOrCreate, fileShare); using (var writer = new StreamWriter(_lockFile, UTF8WithoutBom, bufferSize: 4096, leaveOpen: true)) { await writer.WriteLineAsync( $"Lock acquired at {DateTime.UtcNow:O} by computer [{Environment.MachineName}] running command line [{Environment.CommandLine}] with process id [{Process.GetCurrentProcess().Id}]" ); } _tracer.Info(context, $"Acquired lock file=[{_lockFilePath}]"); await _lockFile.FlushAsync(); return(LockAcquisitionResult.Acquired()); } catch (IOException ioException) { lastException = ioException; } catch (UnauthorizedAccessException accessException) { lastException = accessException; } try { string?contents = await _fileSystem.TryReadFileAsync(_lockFilePath); if (contents != null) { _tracer.Diagnostic(context, $"Lock file=[{_lockFilePath}] contains [{contents}]"); lastCompetingProcessId = TryExtractProcessIdFromLockFilesContent(contents); } } catch (Exception readLockFileException) { string message = readLockFileException is UnauthorizedAccessException ae ? ae.Message : readLockFileException.ToString(); // This is just extra cautious. We shouldn't fail hard being unable to get this diagnostic information. _tracer.Info( context, $"Unable to read contents of lock file=[{_lockFilePath}] because [{message}]"); } await Task.Delay(_pollingInterval); } string lastProcessIdText = lastCompetingProcessId == null ? string.Empty : " Competing process Id: " + lastCompetingProcessId; _tracer.Info( context, $"Timed out trying to acquire lock file=[{_lockFilePath}].{lastProcessIdText} Last exception was=[{lastException}]"); return(LockAcquisitionResult.Failed(waitTimeout, lastCompetingProcessId, TryGetProcessName(lastCompetingProcessId), lastException)); }