diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 6447466e33593d..f993a8a9b62c51 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -16,7 +16,7 @@ namespace System.Threading // public sealed partial class RegisteredWaitHandle : MarshalByRefObject { - private readonly object? _lock; + private readonly Lock? _lock; private bool _unregistering; // Handle to this object to keep it alive @@ -30,7 +30,7 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO { Debug.Assert(ThreadPool.UseWindowsThreadPool); - _lock = new object(); + _lock = new Lock(); waitHandle.DangerousAddRef(); _waitHandle = waitHandle; @@ -194,7 +194,7 @@ private void FinishUnregisteringAsync(object? waitObject) // If this object gets resurrected and another thread calls Unregister, that creates a race condition. // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. // The _lock may be null in case of OOM in the constructor. - if ((_lock != null) && Monitor.TryEnter(_lock)) + if ((_lock != null) && _lock.TryEnter()) { try { @@ -218,7 +218,7 @@ private void FinishUnregisteringAsync(object? waitObject) } finally { - Monitor.Exit(_lock); + _lock.Exit(); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs index aa0c2e4478ca64..0f0eafe3fe0683 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs @@ -51,9 +51,12 @@ public class ThreadLocal : IDisposable // when the instance is disposed. private volatile bool _initialized; - // IdManager assigns and reuses slot IDs. Additionally, the object is also used as a global lock. + // IdManager assigns and reuses slot IDs. private static readonly IdManager s_idManager = new IdManager(); + // Global Lock for the IdManager. + private static readonly Lock s_idManagerLock = new Lock(); + // A linked list of all values associated with this ThreadLocal instance. // We create a dummy head node. That allows us to remove any (non-dummy) node without having to locate the m_linkedSlot field. private LinkedSlot? _linkedSlot = new LinkedSlot(null); @@ -165,7 +168,7 @@ protected virtual void Dispose(bool disposing) { int id; - lock (s_idManager) + lock (s_idManagerLock) { id = ~_idComplement; _idComplement = 0; @@ -384,7 +387,7 @@ private void CreateLinkedSlot(LinkedSlotVolatile[] slotArray, int id, T value) var linkedSlot = new LinkedSlot(slotArray); // Insert the LinkedSlot into the linked list maintained by this ThreadLocal<> instance and into the slot array - lock (s_idManager) + lock (s_idManagerLock) { // Check that the instance has not been disposed. It is important to check this under a lock, since // Dispose also executes under a lock. @@ -525,7 +528,7 @@ private static void GrowTable(ref LinkedSlotVolatile[] table, int minLength) // Dispose could use a stale SlotArray reference and clear out a slot in the old array only, while // the value continues to be referenced from the new (larger) array. // - lock (s_idManager) + lock (s_idManagerLock) { for (int i = 0; i < table.Length; i++) { @@ -614,13 +617,15 @@ private sealed class IdManager // Stores IDs that are used, and if each ID tracksAllValues or not. private readonly Dictionary _usedIdToTracksAllValuesMap = new Dictionary(); - // Stores IDs that were previously used and are now free to reuse. Additionally, the object is also used as a lock - // for the IdManager. + // Stores IDs that were previously used and are now free to reuse. private readonly List _freeIds = new List(); + // Lock for the IdManager. + private readonly Lock _freeIdsLock = new Lock(); + internal int GetId(bool trackAllValues) { - lock (_freeIds) + lock (_freeIdsLock) { int availableId; int freeIdCount = _freeIds.Count; @@ -658,7 +663,7 @@ internal int GetId(bool trackAllValues) // Identify if an allocated id tracks all values or not internal bool IdTracksAllValues(int id) { - lock (_freeIds) + lock (_freeIdsLock) { return _usedIdToTracksAllValuesMap.TryGetValue(id, out bool tracksAllValues) && tracksAllValues; } @@ -669,7 +674,7 @@ internal bool IdTracksAllValues(int id) // Return an ID to the pool internal void ReturnId(int id, bool idTracksAllValues) { - lock (_freeIds) + lock (_freeIdsLock) { if (!idTracksAllValues) _idsThatDoNotTrackAllValues--; @@ -728,7 +733,7 @@ internal FinalizationHelper(LinkedSlotVolatile[] slotArray) { // Remove the LinkedSlot from the linked list. Once the FinalizationHelper is done, all back-references to // the table will be have been removed, and so the table can get GC'd. - lock (s_idManager) + lock (s_idManagerLock) { // If the slot wasn't disposed between reading it above and entering the lock // decrement idsThatDoNotTrackAllValuesCountRemaining diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs index 346259aa412c32..f125dcb4b4de3d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs @@ -20,6 +20,8 @@ internal sealed partial class TimerQueue : IThreadPoolWorkItem /// private static readonly AutoResetEvent s_timerEvent = new AutoResetEvent(false); + private static readonly Lock s_timerEventLock = new Lock(); + // this means that it's in the s_scheduledTimers collection, not that it's the one which would run on the next TimeoutCallback private bool _isScheduled; private long _scheduledDueTimeMs; @@ -50,7 +52,9 @@ private bool SetTimerPortable(uint actualDuration) Debug.Assert((int)actualDuration >= 0); long dueTimeMs = TickCount64 + (int)actualDuration; AutoResetEvent timerEvent = s_timerEvent; - lock (timerEvent) + Lock timerEventLock = s_timerEventLock; + + lock (timerEventLock) { if (!_isScheduled) { @@ -74,9 +78,11 @@ private bool SetTimerPortable(uint actualDuration) private static void TimerThread() { AutoResetEvent timerEvent = s_timerEvent; + Lock timerEventLock = s_timerEventLock; List timersToFire = s_scheduledTimersToFire!; List timers; - lock (timerEvent) + + lock (timerEventLock) { timers = s_scheduledTimers!; } @@ -88,7 +94,7 @@ private static void TimerThread() long currentTimeMs = TickCount64; shortestWaitDurationMs = int.MaxValue; - lock (timerEvent) + lock (timerEventLock) { for (int i = timers.Count - 1; i >= 0; --i) {