| /* | |
| * 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/workerUtils.hpp" | |
| #include "runtime/atomic.hpp" | |
| #include "runtime/mutexLocker.hpp" | |
| // *** WorkerThreadsBarrierSync | |
| WorkerThreadsBarrierSync::WorkerThreadsBarrierSync() | |
| : _monitor(Mutex::nosafepoint, "WorkerThreadsBarrierSync_lock"), | |
| _n_workers(0), _n_completed(0), _should_reset(false), _aborted(false) { | |
| } | |
| void WorkerThreadsBarrierSync::set_n_workers(uint n_workers) { | |
| _n_workers = n_workers; | |
| _n_completed = 0; | |
| _should_reset = false; | |
| _aborted = false; | |
| } | |
| bool WorkerThreadsBarrierSync::enter() { | |
| MonitorLocker ml(monitor(), Mutex::_no_safepoint_check_flag); | |
| if (should_reset()) { | |
| // The should_reset() was set and we are the first worker to enter | |
| // the sync barrier. We will zero the n_completed() count which | |
| // effectively resets the barrier. | |
| zero_completed(); | |
| set_should_reset(false); | |
| } | |
| inc_completed(); | |
| if (n_completed() == n_workers()) { | |
| // At this point we would like to reset the barrier to be ready in | |
| // case it is used again. However, we cannot set n_completed() to | |
| // 0, even after the notify_all(), given that some other workers | |
| // might still be waiting for n_completed() to become == | |
| // n_workers(). So, if we set n_completed() to 0, those workers | |
| // will get stuck (as they will wake up, see that n_completed() != | |
| // n_workers() and go back to sleep). Instead, we raise the | |
| // should_reset() flag and the barrier will be reset the first | |
| // time a worker enters it again. | |
| set_should_reset(true); | |
| ml.notify_all(); | |
| } else { | |
| while (n_completed() != n_workers() && !aborted()) { | |
| ml.wait(); | |
| } | |
| } | |
| return !aborted(); | |
| } | |
| void WorkerThreadsBarrierSync::abort() { | |
| MutexLocker x(monitor(), Mutex::_no_safepoint_check_flag); | |
| set_aborted(); | |
| monitor()->notify_all(); | |
| } | |
| // SubTasksDone functions. | |
| SubTasksDone::SubTasksDone(uint n) : | |
| _tasks(nullptr), _n_tasks(n) { | |
| _tasks = NEW_C_HEAP_ARRAY(bool, n, mtInternal); | |
| for (uint i = 0; i < _n_tasks; i++) { | |
| _tasks[i] = false; | |
| } | |
| } | |
| #ifdef ASSERT | |
| void SubTasksDone::all_tasks_claimed_impl(uint skipped[], size_t skipped_size) { | |
| if (Atomic::cmpxchg(&_verification_done, false, true)) { | |
| // another thread has done the verification | |
| return; | |
| } | |
| // all non-skipped tasks are claimed | |
| for (uint i = 0; i < _n_tasks; ++i) { | |
| if (!_tasks[i]) { | |
| auto is_skipped = false; | |
| for (size_t j = 0; j < skipped_size; ++j) { | |
| if (i == skipped[j]) { | |
| is_skipped = true; | |
| break; | |
| } | |
| } | |
| assert(is_skipped, "%d not claimed.", i); | |
| } | |
| } | |
| // all skipped tasks are *not* claimed | |
| for (size_t i = 0; i < skipped_size; ++i) { | |
| auto task_index = skipped[i]; | |
| assert(task_index < _n_tasks, "Array in range."); | |
| assert(!_tasks[task_index], "%d is both claimed and skipped.", task_index); | |
| } | |
| } | |
| #endif | |
| bool SubTasksDone::try_claim_task(uint t) { | |
| assert(t < _n_tasks, "bad task id."); | |
| return !_tasks[t] && !Atomic::cmpxchg(&_tasks[t], false, true); | |
| } | |
| SubTasksDone::~SubTasksDone() { | |
| assert(_verification_done, "all_tasks_claimed must have been called."); | |
| FREE_C_HEAP_ARRAY(bool, _tasks); | |
| } | |
| // *** SequentialSubTasksDone | |
| bool SequentialSubTasksDone::try_claim_task(uint& t) { | |
| t = _num_claimed; | |
| if (t < _num_tasks) { | |
| t = Atomic::add(&_num_claimed, 1u) - 1; | |
| } | |
| return t < _num_tasks; | |
| } |