@@ -254,34 +254,42 @@ class CodeCache {
254254class CodeCacheArray {
255255private:
256256 CodeCache *_libs[MAX_NATIVE_LIBS ];
257- volatile int _count;
257+ volatile int _reserved; // next slot to reserve (CAS by writers)
258+ volatile int _count; // published count (all indices < _count have non-NULL pointers)
258259 volatile size_t _used_memory;
259260
260261public:
261- CodeCacheArray () : _count(0 ), _used_memory(0 ) {
262+ CodeCacheArray () : _reserved( 0 ), _count(0 ), _used_memory(0 ) {
262263 memset (_libs, 0 , MAX_NATIVE_LIBS * sizeof (CodeCache *));
263264 }
264265
265266 CodeCache *operator [](int index) const { return __atomic_load_n (&_libs[index], __ATOMIC_ACQUIRE); }
266267
267- // Returns a count that may include indices whose pointer has not yet been
268- // stored by a concurrent add(). Callers using operator[] must handle NULL.
268+ // All indices < count() are guaranteed to have a non-NULL pointer.
269269 int count () const { return __atomic_load_n (&_count, __ATOMIC_ACQUIRE); }
270270
271- // Two-phase add: _count is CAS-incremented first, then the pointer is stored.
272- // This creates a window where operator[]/at() may return NULL for valid indices.
271+ // Pointer-first add: reserve a slot via CAS on _reserved, store the
272+ // pointer with RELEASE, then advance _count. Readers see count() grow
273+ // only after the pointer is visible, so indices < count() never yield NULL.
273274 void add (CodeCache *lib) {
274- int old = __atomic_load_n (&_count , __ATOMIC_RELAXED);
275+ int slot = __atomic_load_n (&_reserved , __ATOMIC_RELAXED);
275276 do {
276- if (old >= MAX_NATIVE_LIBS ) return ;
277- } while (!__atomic_compare_exchange_n (&_count , &old, old + 1 ,
277+ if (slot >= MAX_NATIVE_LIBS ) return ;
278+ } while (!__atomic_compare_exchange_n (&_reserved , &slot, slot + 1 ,
278279 true , __ATOMIC_RELAXED, __ATOMIC_RELAXED));
279- assert (__atomic_load_n (&_libs[old ], __ATOMIC_RELAXED) == nullptr );
280+ assert (__atomic_load_n (&_libs[slot ], __ATOMIC_RELAXED) == nullptr );
280281 __atomic_fetch_add (&_used_memory, lib->memoryUsage (), __ATOMIC_RELAXED);
281- __atomic_store_n (&_libs[old], lib, __ATOMIC_RELEASE);
282+ // Store pointer before publishing count. The RELEASE here pairs with
283+ // the ACQUIRE load in operator[]/at() and count().
284+ __atomic_store_n (&_libs[slot], lib, __ATOMIC_RELEASE);
285+ // Advance _count to publish the new slot. Spin until our slot is next
286+ // in line, preserving contiguous ordering when multiple adds race.
287+ while (__atomic_load_n (&_count, __ATOMIC_RELAXED) != slot) {
288+ // wait for preceding slots to publish
289+ }
290+ __atomic_store_n (&_count, slot + 1 , __ATOMIC_RELEASE);
282291 }
283292
284- // Non-blocking read; may return NULL if the slot has not been stored yet.
285293 CodeCache* at (int index) const {
286294 if (index >= MAX_NATIVE_LIBS ) {
287295 return nullptr ;
0 commit comments