diff -r 76bec73a91f0 -r 4d58a35f3cfa src/hotspot/share/gc/cms/yieldingWorkgroup.cpp --- a/src/hotspot/share/gc/cms/yieldingWorkgroup.cpp Tue Nov 12 10:25:55 2019 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,399 +0,0 @@ -/* - * Copyright (c) 2005, 2019, 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 "precompiled.hpp" -#include "gc/cms/yieldingWorkgroup.hpp" -#include "gc/shared/gcId.hpp" -#include "utilities/macros.hpp" - -YieldingFlexibleGangWorker::YieldingFlexibleGangWorker(YieldingFlexibleWorkGang* gang, int id) - : AbstractGangWorker(gang, id) {} - -YieldingFlexibleWorkGang::YieldingFlexibleWorkGang( - const char* name, uint workers, bool are_GC_task_threads) : - AbstractWorkGang(name, workers, are_GC_task_threads, false), - _yielded_workers(0), - _started_workers(0), - _finished_workers(0), - _sequence_number(0), - _task(NULL) { - - // Other initialization. - _monitor = new Monitor(/* priority */ Mutex::leaf, - /* name */ "WorkGroup monitor", - /* allow_vm_block */ are_GC_task_threads, - Monitor::_safepoint_check_never); - - assert(monitor() != NULL, "Failed to allocate monitor"); -} - -AbstractGangWorker* YieldingFlexibleWorkGang::allocate_worker(uint which) { - return new YieldingFlexibleGangWorker(this, which); -} - -void YieldingFlexibleWorkGang::internal_worker_poll(YieldingWorkData* data) const { - assert(data != NULL, "worker data is null"); - data->set_task(task()); - data->set_sequence_number(sequence_number()); -} - -void YieldingFlexibleWorkGang::internal_note_start() { - assert(monitor()->owned_by_self(), "note_finish is an internal method"); - _started_workers += 1; -} - -void YieldingFlexibleWorkGang::internal_note_finish() { - assert(monitor()->owned_by_self(), "note_finish is an internal method"); - _finished_workers += 1; -} - -// Run a task; returns when the task is done, or the workers yield, -// or the task is aborted. -// A task that has been yielded can be continued via this interface -// by using the same task repeatedly as the argument to the call. -// It is expected that the YieldingFlexibleGangTask carries the appropriate -// continuation information used by workers to continue the task -// from its last yield point. Thus, a completed task will return -// immediately with no actual work having been done by the workers. -///////////////////// -// Implementatiuon notes: remove before checking XXX -/* -Each gang is working on a task at a certain time. -Some subset of workers may have yielded and some may -have finished their quota of work. Until this task has -been completed, the workers are bound to that task. -Once the task has been completed, the gang unbounds -itself from the task. - -The yielding work gang thus exports two invokation -interfaces: run_task() and continue_task(). The -first is used to initiate a new task and bind it -to the workers; the second is used to continue an -already bound task that has yielded. Upon completion -the binding is released and a new binding may be -created. - -The shape of a yielding work gang is as follows: - -Overseer invokes run_task(*task). - Lock gang monitor - Check that there is no existing binding for the gang - If so, abort with an error - Else, create a new binding of this gang to the given task - Set number of active workers (as asked) - Notify workers that work is ready to be done - [the requisite # workers would then start up - and do the task] - Wait on the monitor until either - all work is completed or the task has yielded - -- this is normally done through - yielded + completed == active - [completed workers are rest to idle state by overseer?] - return appropriate status to caller - -Overseer invokes continue_task(*task), - Lock gang monitor - Check that task is the same as current binding - If not, abort with an error - Else, set the number of active workers as requested? - Notify workers that they can continue from yield points - New workers can also start up as required - while satisfying the constraint that - active + yielded does not exceed required number - Wait (as above). - -NOTE: In the above, for simplicity in a first iteration - our gangs will be of fixed population and will not - therefore be flexible work gangs, just yielding work - gangs. Once this works well, we will in a second - iteration.refinement introduce flexibility into - the work gang. - -NOTE: we can always create a new gang per each iteration - in order to get the flexibility, but we will for now - desist that simplified route. - - */ -///////////////////// -void YieldingFlexibleWorkGang::start_task(YieldingFlexibleGangTask* new_task) { - MutexLocker ml(monitor(), Mutex::_no_safepoint_check_flag); - assert(task() == NULL, "Gang currently tied to a task"); - assert(new_task != NULL, "Null task"); - // Bind task to gang - _task = new_task; - new_task->set_gang(this); // Establish 2-way binding to support yielding - _sequence_number++; - - uint requested_size = new_task->requested_size(); - if (requested_size != 0) { - _active_workers = MIN2(requested_size, total_workers()); - } else { - _active_workers = active_workers(); - } - new_task->set_actual_size(_active_workers); - new_task->set_for_termination(_active_workers); - - assert(_started_workers == 0, "Tabula rasa non"); - assert(_finished_workers == 0, "Tabula rasa non"); - assert(_yielded_workers == 0, "Tabula rasa non"); - yielding_task()->set_status(ACTIVE); - - // Wake up all the workers, the first few will get to work, - // and the rest will go back to sleep - monitor()->notify_all(); - wait_for_gang(); -} - -void YieldingFlexibleWorkGang::wait_for_gang() { - - assert(monitor()->owned_by_self(), "Data race"); - // Wait for task to complete or yield - for (Status status = yielding_task()->status(); - status != COMPLETED && status != YIELDED && status != ABORTED; - status = yielding_task()->status()) { - assert(started_workers() <= active_workers(), "invariant"); - assert(finished_workers() <= active_workers(), "invariant"); - assert(yielded_workers() <= active_workers(), "invariant"); - monitor()->wait_without_safepoint_check(); - } - switch (yielding_task()->status()) { - case COMPLETED: - case ABORTED: { - assert(finished_workers() == active_workers(), "Inconsistent status"); - assert(yielded_workers() == 0, "Invariant"); - reset(); // for next task; gang<->task binding released - break; - } - case YIELDED: { - assert(yielded_workers() > 0, "Invariant"); - assert(yielded_workers() + finished_workers() == active_workers(), - "Inconsistent counts"); - break; - } - case ACTIVE: - case INACTIVE: - case COMPLETING: - case YIELDING: - case ABORTING: - default: - ShouldNotReachHere(); - } -} - -void YieldingFlexibleWorkGang::continue_task( - YieldingFlexibleGangTask* gang_task) { - - MutexLocker ml(monitor(), Mutex::_no_safepoint_check_flag); - assert(task() != NULL && task() == gang_task, "Incorrect usage"); - assert(_started_workers == _active_workers, "Precondition"); - assert(_yielded_workers > 0 && yielding_task()->status() == YIELDED, - "Else why are we calling continue_task()"); - // Restart the yielded gang workers - yielding_task()->set_status(ACTIVE); - monitor()->notify_all(); - wait_for_gang(); -} - -void YieldingFlexibleWorkGang::reset() { - _started_workers = 0; - _finished_workers = 0; - yielding_task()->set_gang(NULL); - _task = NULL; // unbind gang from task -} - -void YieldingFlexibleWorkGang::yield() { - assert(task() != NULL, "Inconsistency; should have task binding"); - MutexLocker ml(monitor(), Mutex::_no_safepoint_check_flag); - assert(yielded_workers() < active_workers(), "Consistency check"); - if (yielding_task()->status() == ABORTING) { - // Do not yield; we need to abort as soon as possible - // XXX NOTE: This can cause a performance pathology in the - // current implementation in Mustang, as of today, and - // pre-Mustang in that as soon as an overflow occurs, - // yields will not be honoured. The right way to proceed - // of course is to fix bug # TBF, so that abort's cause - // us to return at each potential yield point. - return; - } - if (++_yielded_workers + finished_workers() == active_workers()) { - yielding_task()->set_status(YIELDED); - monitor()->notify_all(); - } else { - yielding_task()->set_status(YIELDING); - } - - while (true) { - switch (yielding_task()->status()) { - case YIELDING: - case YIELDED: { - monitor()->wait_without_safepoint_check(); - break; // from switch - } - case ACTIVE: - case ABORTING: - case COMPLETING: { - assert(_yielded_workers > 0, "Else why am i here?"); - _yielded_workers--; - return; - } - case INACTIVE: - case ABORTED: - case COMPLETED: - default: { - ShouldNotReachHere(); - } - } - } - // Only return is from inside switch statement above - ShouldNotReachHere(); -} - -void YieldingFlexibleWorkGang::abort() { - assert(task() != NULL, "Inconsistency; should have task binding"); - MutexLocker ml(monitor(), Mutex::_no_safepoint_check_flag); - assert(yielded_workers() < active_workers(), "Consistency check"); - #ifndef PRODUCT - switch (yielding_task()->status()) { - // allowed states - case ACTIVE: - case ABORTING: - case COMPLETING: - case YIELDING: - break; - // not allowed states - case INACTIVE: - case ABORTED: - case COMPLETED: - case YIELDED: - default: - ShouldNotReachHere(); - } - #endif // !PRODUCT - Status prev_status = yielding_task()->status(); - yielding_task()->set_status(ABORTING); - if (prev_status == YIELDING) { - assert(yielded_workers() > 0, "Inconsistency"); - // At least one thread has yielded, wake it up - // so it can go back to waiting stations ASAP. - monitor()->notify_all(); - } -} - -/////////////////////////////// -// YieldingFlexibleGangTask -/////////////////////////////// -void YieldingFlexibleGangTask::yield() { - assert(gang() != NULL, "No gang to signal"); - gang()->yield(); -} - -void YieldingFlexibleGangTask::abort() { - assert(gang() != NULL, "No gang to signal"); - gang()->abort(); -} - -/////////////////////////////// -// YieldingFlexibleGangWorker -/////////////////////////////// -void YieldingFlexibleGangWorker::loop() { - int previous_sequence_number = 0; - Monitor* gang_monitor = yf_gang()->monitor(); - MutexLocker ml(gang_monitor, Mutex::_no_safepoint_check_flag); - YieldingWorkData data; - int id; - while (true) { - // Check if there is work to do. - yf_gang()->internal_worker_poll(&data); - if (data.task() != NULL && data.sequence_number() != previous_sequence_number) { - // There is work to be done. - // First check if we need to become active or if there - // are already the requisite number of workers - if (yf_gang()->started_workers() == yf_gang()->active_workers()) { - // There are already enough workers, we do not need to - // to run; fall through and wait on monitor. - } else { - // We need to pitch in and do the work. - assert(yf_gang()->started_workers() < yf_gang()->active_workers(), - "Unexpected state"); - id = yf_gang()->started_workers(); - yf_gang()->internal_note_start(); - // Now, release the gang mutex and do the work. - { - MutexUnlocker mul(gang_monitor, Mutex::_no_safepoint_check_flag); - GCIdMark gc_id_mark(data.task()->gc_id()); - data.task()->work(id); // This might include yielding - } - // Reacquire monitor and note completion of this worker - yf_gang()->internal_note_finish(); - // Update status of task based on whether all workers have - // finished or some have yielded - assert(data.task() == yf_gang()->task(), "Confused task binding"); - if (yf_gang()->finished_workers() == yf_gang()->active_workers()) { - switch (data.yf_task()->status()) { - case ABORTING: { - data.yf_task()->set_status(ABORTED); - break; - } - case ACTIVE: - case COMPLETING: { - data.yf_task()->set_status(COMPLETED); - break; - } - default: - ShouldNotReachHere(); - } - gang_monitor->notify_all(); // Notify overseer - } else { // at least one worker is still working or yielded - assert(yf_gang()->finished_workers() < yf_gang()->active_workers(), - "Counts inconsistent"); - switch (data.yf_task()->status()) { - case ACTIVE: { - // first, but not only thread to complete - data.yf_task()->set_status(COMPLETING); - break; - } - case YIELDING: { - if (yf_gang()->finished_workers() + yf_gang()->yielded_workers() - == yf_gang()->active_workers()) { - data.yf_task()->set_status(YIELDED); - gang_monitor->notify_all(); // notify overseer - } - break; - } - case ABORTING: - case COMPLETING: { - break; // nothing to do - } - default: // everything else: INACTIVE, YIELDED, ABORTED, COMPLETED - ShouldNotReachHere(); - } - } - } - } - // Remember the sequence number - previous_sequence_number = data.sequence_number(); - // Wait for more work - gang_monitor->wait_without_safepoint_check(); - } -}