Skip to content

Commit 21bce4b

Browse files
jbachorikclaude
andcommitted
Pointer-first CodeCacheArray::add() eliminates NULL window
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9d2bddf commit 21bce4b

2 files changed

Lines changed: 24 additions & 21 deletions

File tree

ddprof-lib/src/main/cpp/codeCache.h

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -254,34 +254,42 @@ class CodeCache {
254254
class CodeCacheArray {
255255
private:
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

260261
public:
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;

ddprof-lib/src/main/cpp/flightRecorder.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,15 +1111,10 @@ void Recording::writeNativeLibraries(Buffer *buf) {
11111111
int native_lib_count = native_libs.count();
11121112

11131113
// Emit jdk.NativeLibrary events for newly loaded libraries.
1114-
// Stop at the first NULL slot — a concurrent add() may have incremented
1115-
// count() but not stored the pointer yet. Unrecorded entries will be
1116-
// picked up on the next flush.
1117-
int i;
1118-
for (i = _recorded_lib_count; i < native_lib_count; i++) {
1114+
// CodeCacheArray::add() stores the pointer before advancing count(),
1115+
// so all indices < native_lib_count are guaranteed non-NULL.
1116+
for (int i = _recorded_lib_count; i < native_lib_count; i++) {
11191117
CodeCache* lib = native_libs[i];
1120-
if (lib == NULL) {
1121-
break;
1122-
}
11231118

11241119
flushIfNeeded(buf, RECORDING_BUFFER_LIMIT - MAX_STRING_LENGTH);
11251120
int start = buf->skip(5);
@@ -1134,7 +1129,7 @@ void Recording::writeNativeLibraries(Buffer *buf) {
11341129
flushIfNeeded(buf);
11351130
}
11361131

1137-
_recorded_lib_count = i;
1132+
_recorded_lib_count = native_lib_count;
11381133
}
11391134

11401135
void Recording::writeCpool(Buffer *buf) {

0 commit comments

Comments
 (0)