-
Notifications
You must be signed in to change notification settings - Fork 11
Harden CodeCacheArray and profiler against signal-handler races #413
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
856cec7
9f624f5
ebaced0
6acf59e
a927fc0
c23065f
2cd9e86
9d2bddf
21bce4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -264,24 +264,28 @@ class CodeCacheArray { | |
|
|
||
| CodeCache *operator[](int index) const { return __atomic_load_n(&_libs[index], __ATOMIC_ACQUIRE); } | ||
|
|
||
| // Returns a count that may include indices whose pointer has not yet been | ||
| // stored by a concurrent add(). Callers using operator[] must handle NULL. | ||
| int count() const { return __atomic_load_n(&_count, __ATOMIC_RELAXED); } | ||
|
|
||
| // Two-phase add: _count is CAS-incremented first, then the pointer is stored. | ||
| // This creates a window where operator[]/at() may return NULL for valid indices. | ||
| void add(CodeCache *lib) { | ||
| int index = __atomic_fetch_add(&_count, 1, __ATOMIC_RELAXED); | ||
| if (index < MAX_NATIVE_LIBS) { | ||
| __atomic_fetch_add(&_used_memory, lib->memoryUsage(), __ATOMIC_RELAXED); | ||
| __atomic_store_n(&_libs[index], lib, __ATOMIC_RELEASE); | ||
| } | ||
| int old = __atomic_load_n(&_count, __ATOMIC_RELAXED); | ||
| do { | ||
| if (old >= MAX_NATIVE_LIBS) return; | ||
| } while (!__atomic_compare_exchange_n(&_count, &old, old + 1, | ||
| true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)); | ||
| __atomic_fetch_add(&_used_memory, lib->memoryUsage(), __ATOMIC_RELAXED); | ||
| __atomic_store_n(&_libs[old], lib, __ATOMIC_RELEASE); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done — added |
||
| } | ||
|
|
||
| // Non-blocking read; may return NULL if the slot has not been stored yet. | ||
| CodeCache* at(int index) const { | ||
| if (index >= MAX_NATIVE_LIBS) { | ||
| return nullptr; | ||
| return nullptr; | ||
| } | ||
| CodeCache* lib = nullptr; | ||
| while ((lib = __atomic_load_n(&_libs[index], __ATOMIC_ACQUIRE)) == nullptr); | ||
|
|
||
| return lib; | ||
| return __atomic_load_n(&_libs[index], __ATOMIC_ACQUIRE); | ||
| } | ||
|
|
||
| size_t memoryUsage() const { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,16 +54,22 @@ const void *Libraries::resolveSymbol(const char *name) { | |
| int native_lib_count = _native_libs.count(); | ||
| if (len > 0 && name[len - 1] == '*') { | ||
| for (int i = 0; i < native_lib_count; i++) { | ||
| const void *address = _native_libs[i]->findSymbolByPrefix(name, len - 1); | ||
| if (address != NULL) { | ||
| return address; | ||
| CodeCache *lib = _native_libs[i]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This goes through |
||
| if (lib != NULL) { | ||
| const void *address = lib->findSymbolByPrefix(name, len - 1); | ||
| if (address != NULL) { | ||
| return address; | ||
| } | ||
| } | ||
| } | ||
| } else { | ||
| for (int i = 0; i < native_lib_count; i++) { | ||
| const void *address = _native_libs[i]->findSymbol(name); | ||
| if (address != NULL) { | ||
| return address; | ||
| CodeCache *lib = _native_libs[i]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same as above.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same — |
||
| if (lib != NULL) { | ||
| const void *address = lib->findSymbol(name); | ||
| if (address != NULL) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checking for NULL here is unnecessary (but also harmless).
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, harmless. Keeping for visual consistency with the NULL-guard pattern at every |
||
| return address; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -79,11 +85,14 @@ CodeCache *Libraries::findLibraryByName(const char *lib_name) { | |
| const size_t lib_name_len = strlen(lib_name); | ||
| const int native_lib_count = _native_libs.count(); | ||
| for (int i = 0; i < native_lib_count; i++) { | ||
| const char *s = _native_libs[i]->name(); | ||
| if (s != NULL) { | ||
| const char *p = strrchr(s, '/'); | ||
| if (p != NULL && strncmp(p + 1, lib_name, lib_name_len) == 0) { | ||
| return _native_libs[i]; | ||
| CodeCache *lib = _native_libs[i]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same as above.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if, instead of doing a lot of naked accesses all over the place, maybe this should use an accessor instead, which does the right thing (e.g. use acquire)?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if (lib != NULL) { | ||
| const char *s = lib->name(); | ||
| if (s != NULL) { | ||
| const char *p = strrchr(s, '/'); | ||
| if (p != NULL && strncmp(p + 1, lib_name, lib_name_len) == 0) { | ||
| return lib; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -93,8 +102,9 @@ CodeCache *Libraries::findLibraryByName(const char *lib_name) { | |
| CodeCache *Libraries::findLibraryByAddress(const void *address) { | ||
| const int native_lib_count = _native_libs.count(); | ||
| for (int i = 0; i < native_lib_count; i++) { | ||
| if (_native_libs[i]->contains(address)) { | ||
| return _native_libs[i]; | ||
| CodeCache *lib = _native_libs[i]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same as above - actually, it may be clear to just call
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Already |
||
| if (lib != NULL && lib->contains(address)) { | ||
| return lib; | ||
| } | ||
| } | ||
| return NULL; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,7 +24,7 @@ class Libraries { | |
| // Note: Parameter is uint32_t to match lib_index packing (17 bits = max 131K libraries) | ||
| CodeCache *getLibraryByIndex(uint32_t index) const { | ||
| if (index < _native_libs.count()) { | ||
| return _native_libs[index]; | ||
| return _native_libs[index]; // may be NULL during concurrent add() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Already |
||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it is a problem that this can return NULL in a short window, you could also make the implementation more resilient:
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented-
add()now reserves a slot via CAS on_reserved, stores the pointer with RELEASE, then spins until it can advance_countto preserve contiguous ordering. This eliminates the NULL window entirely: all indices <count()are guaranteed non-NULL. SimplifiedwriteNativeLibrariesaccordingly; the break-on-NULL and partial cursor advancement are no longer needed.