8387490: G1: Dynamically creating worker threads exposes memory visibility race

Reviewed-by: aboldtch, ayang
This commit is contained in:
Thomas Schatzl 2026-07-07 12:54:23 +00:00
parent fc73ca7f38
commit f264341bac
2 changed files with 18 additions and 10 deletions

View File

@ -103,7 +103,7 @@ bool WorkerThreads::allow_inject_creation_failure() const {
return false;
}
if (_created_workers == 0) {
if (_created_workers.load_relaxed() == 0) {
// Never allow creation failures of the first worker, it will cause the VM to exit
return false;
}
@ -135,18 +135,20 @@ uint WorkerThreads::set_active_workers(uint num_workers) {
"Invalid number of active workers %u (should be 1-%u)",
num_workers, _max_workers);
while (_created_workers < num_workers) {
WorkerThread* const worker = create_worker(_created_workers);
uint local_created_workers = created_workers();
while (local_created_workers < num_workers) {
WorkerThread* const worker = create_worker(local_created_workers);
if (worker == nullptr) {
log_error(gc, task)("Failed to create worker thread");
break;
}
_workers[_created_workers] = worker;
_created_workers++;
_workers[local_created_workers] = worker;
local_created_workers++;
_created_workers.release_store(local_created_workers);
}
_active_workers = MIN2(_created_workers, num_workers);
_active_workers = MIN2(local_created_workers, num_workers);
log_trace(gc, task)("%s: using %d out of %d workers", _name, _active_workers, _max_workers);
@ -154,14 +156,16 @@ uint WorkerThreads::set_active_workers(uint num_workers) {
}
void WorkerThreads::threads_do(ThreadClosure* tc) const {
for (uint i = 0; i < _created_workers; i++) {
uint local_created_workers = created_workers();
for (uint i = 0; i < local_created_workers; i++) {
tc->do_thread(_workers[i]);
}
}
template <typename Function>
void WorkerThreads::threads_do_f(Function function) const {
for (uint i = 0; i < _created_workers; i++) {
uint local_created_workers = created_workers();
for (uint i = 0; i < local_created_workers; i++) {
function(_workers[i]);
}
}

View File

@ -88,7 +88,11 @@ private:
const char* const _name;
WorkerThread** _workers;
const uint _max_workers;
uint _created_workers;
// _created_workers publishes the initialized prefix of _workers.
// Writers release-store to it after initializing an entry. Readers
// load-acquire before accessing _workers to not access uninitalized
// data.
Atomic<uint> _created_workers;
uint _active_workers;
WorkerTaskDispatcher _dispatcher;
@ -107,7 +111,7 @@ public:
bool allow_inject_creation_failure() const;
uint max_workers() const { return _max_workers; }
uint created_workers() const { return _created_workers; }
uint created_workers() const { return _created_workers.load_acquire(); }
uint active_workers() const { return _active_workers; }
uint set_active_workers(uint num_workers);