From 5eeba9e4a286f450832ecc08f2cd5f106329f20f Mon Sep 17 00:00:00 2001 From: Tyrie Vella Date: Mon, 18 May 2026 13:43:27 -0700 Subject: [PATCH] Replace object locks with System.Threading.Lock .NET 9+ introduced System.Threading.Lock as a dedicated, lightweight synchronization primitive. Migrate all private lock fields from `object` + `new object()` to `Lock` + `new Lock()`. Also replace Monitor.IsEntered() assertions in GitStatusCache with Lock.IsHeldByCurrentThread, since Lock is not compatible with Monitor APIs. 20 fields across 19 files updated. No Monitor.Wait/Pulse usage found in the codebase, so all instances are safe to migrate. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella --- GVFS/GVFS.Common/Database/PlaceholderTable.cs | 5 +++-- GVFS/GVFS.Common/Database/SparseTable.cs | 3 ++- GVFS/GVFS.Common/FileBasedCollection.cs | 2 +- GVFS/GVFS.Common/GVFSLock.cs | 2 +- GVFS/GVFS.Common/Git/GitAuthentication.cs | 3 ++- GVFS/GVFS.Common/Git/GitProcess.cs | 5 +++-- GVFS/GVFS.Common/Git/LibGit2RepoInvoker.cs | 2 +- GVFS/GVFS.Common/GitStatusCache.cs | 6 +++--- GVFS/GVFS.Common/Maintenance/GitMaintenanceQueue.cs | 2 +- GVFS/GVFS.Common/Maintenance/GitMaintenanceStep.cs | 3 ++- GVFS/GVFS.Common/MissingTreeTracker.cs | 3 ++- GVFS/GVFS.Common/Tracing/PrettyConsoleEventListener.cs | 3 ++- GVFS/GVFS.Platform.Windows/WindowsFileBasedLock.cs | 3 ++- GVFS/GVFS.Service/Handlers/EnableAndAttachProjFSHandler.cs | 3 ++- GVFS/GVFS.Service/Handlers/RequestHandler.cs | 2 +- GVFS/GVFS.Service/PendingUpgradeHandler.cs | 2 +- GVFS/GVFS.Service/PendingUpgradeMonitor.cs | 2 +- GVFS/GVFS.Service/RepoRegistry.cs | 3 ++- GVFS/GVFS.UnitTests/Tracing/QueuedPipeStringWriterTests.cs | 2 +- 19 files changed, 33 insertions(+), 23 deletions(-) diff --git a/GVFS/GVFS.Common/Database/PlaceholderTable.cs b/GVFS/GVFS.Common/Database/PlaceholderTable.cs index 0813a1fb1..c0d98d293 100644 --- a/GVFS/GVFS.Common/Database/PlaceholderTable.cs +++ b/GVFS/GVFS.Common/Database/PlaceholderTable.cs @@ -2,16 +2,17 @@ using System.Collections.Generic; using System.Data; using System.IO; +using System.Threading; namespace GVFS.Common.Database { /// - /// This class is for interacting with the Placeholder table in the SQLite database + /// This class is for interacting with the Placeholder tablein the SQLite database /// public class PlaceholderTable : IPlaceholderCollection { private IGVFSConnectionPool connectionPool; - private object writerLock = new object(); + private Lock writerLock = new Lock(); public PlaceholderTable(IGVFSConnectionPool connectionPool) { diff --git a/GVFS/GVFS.Common/Database/SparseTable.cs b/GVFS/GVFS.Common/Database/SparseTable.cs index 5ead9baed..4a7f3db46 100644 --- a/GVFS/GVFS.Common/Database/SparseTable.cs +++ b/GVFS/GVFS.Common/Database/SparseTable.cs @@ -2,13 +2,14 @@ using System.Collections.Generic; using System.Data; using System.IO; +using System.Threading; namespace GVFS.Common.Database { public class SparseTable : ISparseCollection { private IGVFSConnectionPool connectionPool; - private object writerLock = new object(); + private Lock writerLock = new Lock(); public SparseTable(IGVFSConnectionPool connectionPool) { diff --git a/GVFS/GVFS.Common/FileBasedCollection.cs b/GVFS/GVFS.Common/FileBasedCollection.cs index 1956c086e..1ebe09b33 100644 --- a/GVFS/GVFS.Common/FileBasedCollection.cs +++ b/GVFS/GVFS.Common/FileBasedCollection.cs @@ -27,7 +27,7 @@ public abstract class FileBasedCollection : IDisposable /// private readonly bool collectionAppendsDirectlyToFile; - private readonly object fileLock = new object(); + private readonly Lock fileLock = new Lock(); private readonly PhysicalFileSystem fileSystem; private readonly string dataDirectoryPath; diff --git a/GVFS/GVFS.Common/GVFSLock.cs b/GVFS/GVFS.Common/GVFSLock.cs index b7324d74c..5bfaa5976 100644 --- a/GVFS/GVFS.Common/GVFSLock.cs +++ b/GVFS/GVFS.Common/GVFSLock.cs @@ -8,7 +8,7 @@ namespace GVFS.Common { public partial class GVFSLock { - private readonly object acquisitionLock = new object(); + private readonly Lock acquisitionLock = new Lock(); private readonly ITracer tracer; private readonly LockHolder currentLockHolder = new LockHolder(); diff --git a/GVFS/GVFS.Common/Git/GitAuthentication.cs b/GVFS/GVFS.Common/Git/GitAuthentication.cs index fff37ad9a..0a39715e7 100644 --- a/GVFS/GVFS.Common/Git/GitAuthentication.cs +++ b/GVFS/GVFS.Common/Git/GitAuthentication.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Security.Cryptography.X509Certificates; using System.Text; +using System.Threading; namespace GVFS.Common.Git { @@ -13,7 +14,7 @@ public class GitAuthentication { private const double MaxBackoffSeconds = 30; - private readonly object gitAuthLock = new object(); + private readonly Lock gitAuthLock = new Lock(); private readonly ICredentialStore credentialStore; private readonly string repoUrl; diff --git a/GVFS/GVFS.Common/Git/GitProcess.cs b/GVFS/GVFS.Common/Git/GitProcess.cs index a6ac3c748..caca4df64 100644 --- a/GVFS/GVFS.Common/Git/GitProcess.cs +++ b/GVFS/GVFS.Common/Git/GitProcess.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Threading; namespace GVFS.Common.Git { @@ -21,14 +22,14 @@ public class GitProcess : ICredentialStore /// /// Lock taken for duration of running executingProcess. /// - private object executionLock = new object(); + private Lock executionLock = new Lock(); /// /// Lock taken when changing the running state of executingProcess. /// /// Can be taken within executionLock. /// - private object processLock = new object(); + private Lock processLock = new Lock(); private string gitBinPath; private string workingDirectoryRoot; diff --git a/GVFS/GVFS.Common/Git/LibGit2RepoInvoker.cs b/GVFS/GVFS.Common/Git/LibGit2RepoInvoker.cs index 44b084049..f3fdf83b3 100644 --- a/GVFS/GVFS.Common/Git/LibGit2RepoInvoker.cs +++ b/GVFS/GVFS.Common/Git/LibGit2RepoInvoker.cs @@ -8,7 +8,7 @@ public class LibGit2RepoInvoker : IDisposable { private readonly Func createRepo; private readonly ITracer tracer; - private readonly object sharedRepoLock = new object(); + private readonly Lock sharedRepoLock = new Lock(); private volatile bool disposing; private volatile int activeCallers; private LibGit2Repo sharedRepo; diff --git a/GVFS/GVFS.Common/GitStatusCache.cs b/GVFS/GVFS.Common/GitStatusCache.cs index efe13a49d..7323ec209 100644 --- a/GVFS/GVFS.Common/GitStatusCache.cs +++ b/GVFS/GVFS.Common/GitStatusCache.cs @@ -56,7 +56,7 @@ public class GitStatusCache : IDisposable private volatile CacheState cacheState = CacheState.Dirty; - private object cacheFileLock = new object(); + private Lock cacheFileLock = new Lock(); internal static bool? TEST_EnableHydrationSummaryOverride = null; @@ -597,7 +597,7 @@ private bool TryRebuildStatusCache() private bool TryDeleteStatusCacheFile() { - Debug.Assert(Monitor.IsEntered(this.cacheFileLock), "Attempting to delete the git status cache file without the cacheFileLock"); + Debug.Assert(this.cacheFileLock.IsHeldByCurrentThread, "Attempting to delete the git status cache file without the cacheFileLock"); try { @@ -635,7 +635,7 @@ private bool TryDeleteStatusCacheFile() /// True on success, False on failure private bool MoveCacheFileToFinalLocation(string tmpStatusFilePath) { - Debug.Assert(Monitor.IsEntered(this.cacheFileLock), "Attempting to update the git status cache file without the cacheFileLock"); + Debug.Assert(this.cacheFileLock.IsHeldByCurrentThread, "Attempting to update the git status cache file without the cacheFileLock"); try { diff --git a/GVFS/GVFS.Common/Maintenance/GitMaintenanceQueue.cs b/GVFS/GVFS.Common/Maintenance/GitMaintenanceQueue.cs index 5ecfa63ff..26d2f8431 100644 --- a/GVFS/GVFS.Common/Maintenance/GitMaintenanceQueue.cs +++ b/GVFS/GVFS.Common/Maintenance/GitMaintenanceQueue.cs @@ -8,7 +8,7 @@ namespace GVFS.Common.Maintenance { public class GitMaintenanceQueue { - private readonly object queueLock = new object(); + private readonly Lock queueLock = new Lock(); private GVFSContext context; private BlockingCollection queue = new BlockingCollection(); private GitMaintenanceStep currentStep; diff --git a/GVFS/GVFS.Common/Maintenance/GitMaintenanceStep.cs b/GVFS/GVFS.Common/Maintenance/GitMaintenanceStep.cs index 29bb3e9e4..ae14ab4ce 100644 --- a/GVFS/GVFS.Common/Maintenance/GitMaintenanceStep.cs +++ b/GVFS/GVFS.Common/Maintenance/GitMaintenanceStep.cs @@ -4,13 +4,14 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; namespace GVFS.Common.Maintenance { public abstract class GitMaintenanceStep { public const string ObjectCacheLock = "git-maintenance-step.lock"; - private readonly object gitProcessLock = new object(); + private readonly Lock gitProcessLock = new Lock(); public GitMaintenanceStep(GVFSContext context, bool requireObjectCacheLock, GitProcessChecker gitProcessChecker = null) { diff --git a/GVFS/GVFS.Common/MissingTreeTracker.cs b/GVFS/GVFS.Common/MissingTreeTracker.cs index 3d5ca78a1..fe17c9890 100644 --- a/GVFS/GVFS.Common/MissingTreeTracker.cs +++ b/GVFS/GVFS.Common/MissingTreeTracker.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using GVFS.Common.Tracing; namespace GVFS.Common @@ -16,7 +17,7 @@ public class MissingTreeTracker private readonly int treeCapacity; private readonly ITracer tracer; - private readonly object syncLock = new object(); + private readonly Lock syncLock = new Lock(); // Primary storage: commit -> set of missing trees private readonly Dictionary> missingTreesByCommit; diff --git a/GVFS/GVFS.Common/Tracing/PrettyConsoleEventListener.cs b/GVFS/GVFS.Common/Tracing/PrettyConsoleEventListener.cs index 5999d97ee..1cdefe681 100644 --- a/GVFS/GVFS.Common/Tracing/PrettyConsoleEventListener.cs +++ b/GVFS/GVFS.Common/Tracing/PrettyConsoleEventListener.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; namespace GVFS.Common.Tracing { @@ -9,7 +10,7 @@ namespace GVFS.Common.Tracing /// public class PrettyConsoleEventListener : EventListener { - private static object consoleLock = new object(); + private static Lock consoleLock = new Lock(); public PrettyConsoleEventListener(EventLevel maxVerbosity, Keywords keywordFilter, IEventListenerEventSink eventSink) : base(maxVerbosity, keywordFilter, eventSink) diff --git a/GVFS/GVFS.Platform.Windows/WindowsFileBasedLock.cs b/GVFS/GVFS.Platform.Windows/WindowsFileBasedLock.cs index edf1c43a0..5e24711cd 100644 --- a/GVFS/GVFS.Platform.Windows/WindowsFileBasedLock.cs +++ b/GVFS/GVFS.Platform.Windows/WindowsFileBasedLock.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.IO; using System.Text; +using System.Threading; namespace GVFS.Platform.Windows { @@ -16,7 +17,7 @@ public class WindowsFileBasedLock : FileBasedLock private const string EtwArea = nameof(WindowsFileBasedLock); private static readonly Encoding UTF8NoBOM = new UTF8Encoding(false, true); // Default encoding used by StreamWriter - private readonly object deleteOnCloseStreamLock = new object(); + private readonly Lock deleteOnCloseStreamLock = new Lock(); private Stream deleteOnCloseStream; /// diff --git a/GVFS/GVFS.Service/Handlers/EnableAndAttachProjFSHandler.cs b/GVFS/GVFS.Service/Handlers/EnableAndAttachProjFSHandler.cs index f8c9306fe..b8ec2ac5d 100644 --- a/GVFS/GVFS.Service/Handlers/EnableAndAttachProjFSHandler.cs +++ b/GVFS/GVFS.Service/Handlers/EnableAndAttachProjFSHandler.cs @@ -3,6 +3,7 @@ using GVFS.Common.NamedPipes; using GVFS.Common.Tracing; using GVFS.Platform.Windows; +using System.Threading; namespace GVFS.Service.Handlers { @@ -10,7 +11,7 @@ public class EnableAndAttachProjFSHandler : MessageHandler { private const string EtwArea = nameof(EnableAndAttachProjFSHandler); - private static object enablePrjFltLock = new object(); + private static Lock enablePrjFltLock = new Lock(); private NamedPipeServer.Connection connection; private NamedPipeMessages.EnableAndAttachProjFSRequest request; diff --git a/GVFS/GVFS.Service/Handlers/RequestHandler.cs b/GVFS/GVFS.Service/Handlers/RequestHandler.cs index 8f0447b52..72e6e9e08 100644 --- a/GVFS/GVFS.Service/Handlers/RequestHandler.cs +++ b/GVFS/GVFS.Service/Handlers/RequestHandler.cs @@ -29,7 +29,7 @@ public class RequestHandler private ITracer tracer; private IRepoRegistry repoRegistry; private Timer pendingUpgradeTimer; - private readonly object pendingUpgradeTimerLock = new object(); + private readonly Lock pendingUpgradeTimerLock = new Lock(); public RequestHandler(ITracer tracer, string etwArea, IRepoRegistry repoRegistry) { diff --git a/GVFS/GVFS.Service/PendingUpgradeHandler.cs b/GVFS/GVFS.Service/PendingUpgradeHandler.cs index c2701be23..f891402b1 100644 --- a/GVFS/GVFS.Service/PendingUpgradeHandler.cs +++ b/GVFS/GVFS.Service/PendingUpgradeHandler.cs @@ -39,7 +39,7 @@ public static class PendingUpgradeHandler private const string MountProcessName = "GVFS.Mount"; private const string MountExeName = "GVFS.Mount.exe"; - private static readonly object ApplyLock = new object(); + private static readonly Lock ApplyLock = new Lock(); // Executables that users or the service can launch to start new // mount/hook processes. During upgrade these are moved out first diff --git a/GVFS/GVFS.Service/PendingUpgradeMonitor.cs b/GVFS/GVFS.Service/PendingUpgradeMonitor.cs index fd9e07d00..8c65adb81 100644 --- a/GVFS/GVFS.Service/PendingUpgradeMonitor.cs +++ b/GVFS/GVFS.Service/PendingUpgradeMonitor.cs @@ -27,7 +27,7 @@ public sealed class PendingUpgradeMonitor : IDisposable private const int DebouncePeriodMs = 1000; private readonly ITracer tracer; - private readonly object syncLock = new object(); + private readonly Lock syncLock = new Lock(); private List trackedProcesses = new List(); private Timer debounceTimer; private bool disposed; diff --git a/GVFS/GVFS.Service/RepoRegistry.cs b/GVFS/GVFS.Service/RepoRegistry.cs index 422a006f7..b2d371557 100644 --- a/GVFS/GVFS.Service/RepoRegistry.cs +++ b/GVFS/GVFS.Service/RepoRegistry.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; namespace GVFS.Service { @@ -20,7 +21,7 @@ public class RepoRegistry : IRepoRegistry private string registryParentFolderPath; private ITracer tracer; private PhysicalFileSystem fileSystem; - private object repoLock = new object(); + private Lock repoLock = new Lock(); private IRepoMounter repoMounter; private INotificationHandler notificationHandler; diff --git a/GVFS/GVFS.UnitTests/Tracing/QueuedPipeStringWriterTests.cs b/GVFS/GVFS.UnitTests/Tracing/QueuedPipeStringWriterTests.cs index 55ba75dca..e3df9c0c4 100644 --- a/GVFS/GVFS.UnitTests/Tracing/QueuedPipeStringWriterTests.cs +++ b/GVFS/GVFS.UnitTests/Tracing/QueuedPipeStringWriterTests.cs @@ -124,7 +124,7 @@ private class TestPipeReaderWorker : IDisposable private int bufferLength = 0; private byte[] buffer = new byte[16*1024]; - private object bufferLock = new object(); + private Lock bufferLock = new Lock(); private Thread thread; private bool isRunning; private bool isDisposed;