Skip to content

Replace GCX_COOP with EBR for EEHashTable bucket reclamation#124822

Draft
AaronRobinsonMSFT wants to merge 1 commit intodotnet:mainfrom
AaronRobinsonMSFT:ebr-eehash
Draft

Replace GCX_COOP with EBR for EEHashTable bucket reclamation#124822
AaronRobinsonMSFT wants to merge 1 commit intodotnet:mainfrom
AaronRobinsonMSFT:ebr-eehash

Conversation

@AaronRobinsonMSFT
Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT commented Feb 24, 2026

Replace cooperative GC mode transitions (GCX_COOP_NO_THREAD_BROKEN) with Epoch-Based Reclamation for safe deferred deletion of old EEHashTable bucket arrays during resize.

Changes

  • Add dedicated g_EEHashEbr collector (separate from HashMap's g_HashMapEbr), with initialization in ceemain.cpp and cleanup in f inalizerthread.cpp
  • Add AllocateEEHashBuckets FreeEEHashBuckets helpers in eehash.cpp, following the same pattern as HashMap's AllocateBuckets FreeBuckets
  • Remove SyncClean::AddEEHashTable and associated cleanup infrastructure from syncclean.cpp/.hpp
  • Remove redundant MemoryBarrier() before VolatilePtr::Store() — release semantics already provide the ordering guarantee

Follow-up to #124307 (Replace HashMap COOP transitions with EBR). Specifically see #124307 (comment).

@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

@AaronRobinsonMSFT AaronRobinsonMSFT added this to the 11.0.0 milestone Feb 24, 2026
Copy link
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 replaces the prior GC cooperative-mode based scheme for safely reclaiming obsolete EEHashTable bucket arrays with Epoch-Based Reclamation (EBR), aligning EEHashTable resizing behavior with the earlier HashMap EBR work.

Changes:

  • Add a dedicated global EBR collector (g_EEHashEbr) and initialize it during EE startup; trigger cleanup from the finalizer thread’s “extra work” loop.
  • Refactor EEHashTable bucket allocation/freeing into helpers and use EBR critical regions + deferred deletion during table growth instead of GCX_COOP_NO_THREAD_BROKEN + SyncClean.
  • Simplify SyncClean by removing the EEHashTable-specific cleanup list and related APIs.

Reviewed changes

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

Show a summary per file
File Description
src/coreclr/vm/syncclean.hpp Removes EEHashTable cleanup list API; leaves SyncClean::CleanUp() only.
src/coreclr/vm/syncclean.cpp Removes EEHashTable cleanup list storage and freeing logic.
src/coreclr/vm/finalizerthread.cpp Adds g_EEHashEbr cleanup request detection and processing in finalizer extra-work path.
src/coreclr/vm/eehash.inl Replaces COOP transitions with EBR critical regions; defers old bucket array deletion via EBR during growth.
src/coreclr/vm/eehash.h Declares EEHashTable bucket allocation/free helpers.
src/coreclr/vm/eehash.cpp Implements AllocateEEHashBuckets / FreeEEHashBuckets.
src/coreclr/vm/ebr.h Declares extern EbrCollector g_EEHashEbr.
src/coreclr/vm/ebr.cpp Defines global g_EEHashEbr.
src/coreclr/vm/ceemain.cpp Initializes g_EEHashEbr during EE startup.

Replace cooperative GC mode transitions (GCX_COOP_NO_THREAD_BROKEN) with
Epoch-Based Reclamation for safe deferred deletion of old EEHashTable
bucket arrays during resize.

- Add dedicated g_EEHashEbr collector (init, cleanup, critical regions)
- Add AllocateEEHashBuckets/FreeEEHashBuckets helpers in eehash.cpp
- Remove SyncClean::AddEEHashTable and associated cleanup infrastructure
- Remove redundant MemoryBarrier before VolatilePtr::Store (release semantics)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
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 10 out of 10 changed files in this pull request and generated no new comments.


InterlockedExchange( (LONG *) &m_bGrowing, 0);

// Before EE starts we're single-threaded; delete old buckets immediately.
Copy link
Member

Choose a reason for hiding this comment

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

Is this a correct statement? We do create finalizer thread, debugger threads, tracing threads, ... before g_fEEStarted is set to true. Is it guaranteed that none of those threads cross the path with this?

g_HashMapEbr.Init();

// Initialize EBR for EEHashTable bucket reclamation.
g_EEHashEbr.Init();
Copy link
Member

Choose a reason for hiding this comment

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

I do not see how the single copy of the thread local state works with multiple instances of the Ebr.

Should both HashMap and EEHash use the same instance? Both of them are dealing with byte[] at the end of the day.

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

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants