Skip to content

Make Condition to not use ConditionalWeakTable#129083

Open
VSadov wants to merge 20 commits into
dotnet:mainfrom
VSadov:cobReg
Open

Make Condition to not use ConditionalWeakTable#129083
VSadov wants to merge 20 commits into
dotnet:mainfrom
VSadov:cobReg

Conversation

@VSadov
Copy link
Copy Markdown
Member

@VSadov VSadov commented Jun 6, 2026

Main motivation is that ManualResetEventSlim uses Monitor.Wait to implement Wait that is:

  • cancellable
  • interruptible (in Thread.Interrupt sense) and
  • aware of synchronization context

Monitor.Wait is a good fit to implement such pattern and should generally perform well enough.

ManualResetEventSlim in turn is used in Task.Wait and some scenarios can wait on Tasks relatively frequently.

In such scenarios using ConditionalWaitTable for Lock->Condition association may have two inconveniences:

  • it may result in quite a few dependent handles being alive and that can have impact on GC.
    In particular because dependent handles currently do not age and need to be revisited in every Gen0, even if both objects referred from the handle may be Gen2 objects.
    (we should probably address Consider aging dependent handles the same way as the other kinds of handles. #79062, regardless of this PR)

  • Allocating an entry in ConditionalWaitTable acquires table-wide lock and on large enough core count may contend.

It seems there is a relatively simple way to arrange Lock->Condition link without involving ConditionalWaitTable, thus why not.

The change also enables Wait/Pulse/PulseAll functionality on Lock, but only internally.
It can be exposed as a public API, but it would be a separate discussion.

Copilot AI review requested due to automatic review settings June 6, 2026 22:04
@VSadov VSadov added this to the 11.0.0 milestone Jun 6, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors how Monitor.Wait/Pulse are implemented by removing the ConditionalWeakTable<object, Condition> mapping and instead associating a Condition directly with the managed Lock used by a sync block. It also updates diagnostics code (DAC/DBI) to discover monitor waiters via the Lock rather than via Monitor’s static table, and updates ManualResetEventSlim to use Lock for its wait/pulse coordination.

Changes:

  • Replace Monitor’s object→Condition table with Lock-based Wait/Pulse/PulseAll helpers.
  • Update Lock to store either an AutoResetEvent or a Condition in a single field and lazily create the Condition when needed.
  • Update ManualResetEventSlim to use Lock and revise waiter-count/state handling; update DAC monitor-wait enumeration accordingly.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/libraries/System.Private.CoreLib/src/System/Threading/Monitor.cs Routes Wait/Pulse/PulseAll through the sync-block Lock instead of a global ConditionalWeakTable.
src/libraries/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs Switches internal wait/pulse lock to Lock and rewrites signaled/waiter state manipulation.
src/libraries/System.Private.CoreLib/src/System/Threading/Lock.cs Introduces _waitEventOrCondition union field and adds internal Wait/Pulse/PulseAll via Condition.
src/libraries/System.Private.CoreLib/src/System/Threading/Condition.cs Refactors waiter bookkeeping and signaling strategy; adds storage for a Lock’s wait event.
src/coreclr/vm/corelib.h Updates binder field list to remove Monitor.s_conditionTable and add Lock._waitEventOrCondition.
src/coreclr/debug/daccess/dacdbiimpl.cpp Changes monitor-wait enumeration to locate Condition via the sync-block Lock field.

Comment thread src/libraries/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs Outdated
Comment thread src/libraries/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs Outdated
Copilot AI review requested due to automatic review settings June 7, 2026 00:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment on lines +675 to 678
using (mre.m_lock.EnterScope())
{
int oldState = m_combinedState; // cache the old value for testing in CAS

// Procedure:(1) zero the updateBits. eg oldState = [11111111] flag= [00111000] newState = [11000111]
// then (2) map in the newBits. eg [11000111] newBits=00101000, newState=[11101111]
int newState = (oldState & ~updateBitsMask) | newBits;

if (Interlocked.CompareExchange(ref m_combinedState, newState, oldState) == oldState)
{
return;
}

sw.SpinOnce(sleep1Threshold: -1);
mre.m_lock.PulseAll(); // awaken all waiters
}
Copy link
Copy Markdown
Member Author

@VSadov VSadov Jun 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would not be a new issue for ManualResetEventSlim. The previous implementation would do the same just via ConditionalWeakTable indirection.

On the other hand making disposal of the m_lock more eager has a small potential of breaking some existing code that, for example, disposes an ManualResetEventSlim while some waiters are still waking up.

@VSadov VSadov marked this pull request as ready for review June 7, 2026 01:23
Copilot AI review requested due to automatic review settings June 7, 2026 01:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

@VSadov VSadov requested a review from jkotas June 7, 2026 04:34
@jkotas
Copy link
Copy Markdown
Member

jkotas commented Jun 7, 2026

Is there a micro-benchmark that demonstrates the improvement?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants