You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Check unsynchronized counters and recycle probes in the large-allocation path #178
The large-allocation path still has shared state accessed without consistent synchronization in src/hx/gc/Immix.cpp.
mRecycleSize is declared volatile int. AllocLarge reads it before taking mLargeListLock, while other allocating threads update it under that lock. volatile does not make that concurrent access atomic in C++.
AllocLarge reads mLargeAllocated to decide whether to collect before acquiring mLargeListLock. Other threads change the same size_t under that lock in AllocLarge, FreeLarge and onMemoryChange.
mLargeAllocSpace is read and conditionally written before taking the lock in AllocLarge and onMemoryChange. Concurrent allocations can access it together, and racing updates can lose a larger budget.
FreeLarge checks largeObjectRecycle.hasExtraCapacity(1) before taking mLargeListLock. The vector can be changed by another allocating thread under the lock.
The lock protects list mutations, but it does not synchronize these readers. The recycle-hint comment explicitly describes an unlocked read, so that hint needs a C++ synchronization mechanism even if a stale value is acceptable.
This is a source-level finding. I have not reproduced a crash or collected a TSan report for these accesses. The consequences need checking rather than assuming that every race corrupts the heap.
I'd start with a focused TSan workload that allocates and grows large arrays on several threads, exercising both recycling and cpp.vm.Gc.changeManagedMemory. Keep application-owned buffers thread-local so the workload does not introduce its own races. Check the current checkout, since several nearby races have already been fixed.
Possible fixes include a relaxed atomic recycle hint, synchronized counter snapshots and a monotonic atomic update or locked update for the budget. Keep vector inspection under the mutex. Do not hold mLargeListLock across CollectFromThisThread, since collection can need the same allocator state. Measure throughput and pauses before choosing a locking strategy.
Acceptance: the focused workload has no reports for these fields, the existing GC stress modes pass, and large-buffer allocation throughput does not regress materially. Related: #148 for sanitizer coverage and #177 for the separate budget-decay question.
Activity
Synchronize the large-allocation counters