| /* |
| * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| #include "gc/shared/gc_globals.hpp" |
| #include "gc/shared/workerThread.hpp" |
| #include "logging/log.hpp" |
| #include "memory/iterator.hpp" |
| #include "runtime/atomic.hpp" |
| #include "runtime/init.hpp" |
| #include "runtime/java.hpp" |
| #include "runtime/os.hpp" |
| #include "runtime/safepoint.hpp" |
| |
| WorkerTaskDispatcher::WorkerTaskDispatcher() : |
| _task(nullptr), |
| _started(0), |
| _not_finished(0), |
| _start_semaphore(), |
| _end_semaphore() {} |
| |
| void WorkerTaskDispatcher::coordinator_distribute_task(WorkerTask* task, uint num_workers) { |
| // No workers are allowed to read the state variables until they have been signaled. |
| _task = task; |
| _not_finished = num_workers; |
| |
| // Dispatch 'num_workers' number of tasks. |
| _start_semaphore.signal(num_workers); |
| |
| // Wait for the last worker to signal the coordinator. |
| _end_semaphore.wait(); |
| |
| // No workers are allowed to read the state variables after the coordinator has been signaled. |
| assert(_not_finished == 0, "%d not finished workers?", _not_finished); |
| _task = nullptr; |
| _started = 0; |
| } |
| |
| void WorkerTaskDispatcher::worker_run_task() { |
| // Wait for the coordinator to dispatch a task. |
| _start_semaphore.wait(); |
| |
| // Get and set worker id. |
| const uint worker_id = Atomic::fetch_then_add(&_started, 1u); |
| WorkerThread::set_worker_id(worker_id); |
| |
| // Run task. |
| GCIdMark gc_id_mark(_task->gc_id()); |
| _task->work(worker_id); |
| |
| // Mark that the worker is done with the task. |
| // The worker is not allowed to read the state variables after this line. |
| const uint not_finished = Atomic::sub(&_not_finished, 1u); |
| |
| // The last worker signals to the coordinator that all work is completed. |
| if (not_finished == 0) { |
| _end_semaphore.signal(); |
| } |
| } |
| |
| WorkerThreads::WorkerThreads(const char* name, uint max_workers) : |
| _name(name), |
| _workers(NEW_C_HEAP_ARRAY(WorkerThread*, max_workers, mtInternal)), |
| _max_workers(max_workers), |
| _created_workers(0), |
| _active_workers(0), |
| _dispatcher() {} |
| |
| void WorkerThreads::initialize_workers() { |
| const uint initial_active_workers = UseDynamicNumberOfGCThreads ? 1 : _max_workers; |
| if (set_active_workers(initial_active_workers) != initial_active_workers) { |
| vm_exit_during_initialization(); |
| } |
| } |
| |
| WorkerThread* WorkerThreads::create_worker(uint name_suffix) { |
| if (is_init_completed() && InjectGCWorkerCreationFailure) { |
| return nullptr; |
| } |
| |
| WorkerThread* const worker = new WorkerThread(_name, name_suffix, &_dispatcher); |
| |
| if (!os::create_thread(worker, os::gc_thread)) { |
| delete worker; |
| return nullptr; |
| } |
| |
| on_create_worker(worker); |
| |
| os::start_thread(worker); |
| |
| return worker; |
| } |
| |
| uint WorkerThreads::set_active_workers(uint num_workers) { |
| assert(num_workers > 0 && num_workers <= _max_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); |
| if (worker == nullptr) { |
| log_error(gc, task)("Failed to create worker thread"); |
| break; |
| } |
| |
| _workers[_created_workers] = worker; |
| _created_workers++; |
| } |
| |
| _active_workers = MIN2(_created_workers, num_workers); |
| |
| log_trace(gc, task)("%s: using %d out of %d workers", _name, _active_workers, _max_workers); |
| |
| return _active_workers; |
| } |
| |
| void WorkerThreads::threads_do(ThreadClosure* tc) const { |
| for (uint i = 0; i < _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++) { |
| function(_workers[i]); |
| } |
| } |
| |
| void WorkerThreads::set_indirect_states() { |
| #ifdef ASSERT |
| const bool is_suspendible = Thread::current()->is_suspendible_thread(); |
| const bool is_safepointed = Thread::current()->is_VM_thread() && SafepointSynchronize::is_at_safepoint(); |
| |
| threads_do_f([&](Thread* thread) { |
| assert(!thread->is_indirectly_suspendible_thread(), "Unexpected"); |
| assert(!thread->is_indirectly_safepoint_thread(), "Unexpected"); |
| if (is_suspendible) { |
| thread->set_indirectly_suspendible_thread(); |
| } |
| if (is_safepointed) { |
| thread->set_indirectly_safepoint_thread(); |
| } |
| }); |
| #endif |
| } |
| |
| void WorkerThreads::clear_indirect_states() { |
| #ifdef ASSERT |
| threads_do_f([&](Thread* thread) { |
| thread->clear_indirectly_suspendible_thread(); |
| thread->clear_indirectly_safepoint_thread(); |
| }); |
| #endif |
| } |
| |
| void WorkerThreads::run_task(WorkerTask* task) { |
| set_indirect_states(); |
| _dispatcher.coordinator_distribute_task(task, _active_workers); |
| clear_indirect_states(); |
| } |
| |
| void WorkerThreads::run_task(WorkerTask* task, uint num_workers) { |
| WithActiveWorkers with_active_workers(this, num_workers); |
| run_task(task); |
| } |
| |
| THREAD_LOCAL uint WorkerThread::_worker_id = UINT_MAX; |
| |
| WorkerThread::WorkerThread(const char* name_prefix, uint name_suffix, WorkerTaskDispatcher* dispatcher) : |
| _dispatcher(dispatcher) { |
| set_name("%s#%u", name_prefix, name_suffix); |
| } |
| |
| void WorkerThread::run() { |
| os::set_priority(this, NearMaxPriority); |
| |
| while (true) { |
| _dispatcher->worker_run_task(); |
| } |
| } |