0N/A/*
2685N/A * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
1472N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1472N/A * or visit www.oracle.com if you need additional information or have any
1472N/A * questions.
0N/A *
0N/A */
0N/A
1879N/A#include "precompiled.hpp"
1879N/A#ifndef SERIALGC
1879N/A#include "utilities/yieldingWorkgroup.hpp"
1879N/A#endif
0N/A
0N/A// Forward declaration of classes declared here.
0N/A
0N/Aclass GangWorker;
0N/Aclass WorkData;
0N/A
0N/AYieldingFlexibleWorkGang::YieldingFlexibleWorkGang(
3008N/A const char* name, uint workers, bool are_GC_task_threads) :
1753N/A FlexibleWorkGang(name, workers, are_GC_task_threads, false),
1753N/A _yielded_workers(0) {}
0N/A
3008N/AGangWorker* YieldingFlexibleWorkGang::allocate_worker(uint which) {
1753N/A YieldingFlexibleGangWorker* new_member =
1753N/A new YieldingFlexibleGangWorker(this, which);
1753N/A return (YieldingFlexibleGangWorker*) new_member;
0N/A}
0N/A
0N/A// Run a task; returns when the task is done, or the workers yield,
0N/A// or the task is aborted, or the work gang is terminated via stop().
0N/A// A task that has been yielded can be continued via this interface
0N/A// by using the same task repeatedly as the argument to the call.
0N/A// It is expected that the YieldingFlexibleGangTask carries the appropriate
0N/A// continuation information used by workers to continue the task
0N/A// from its last yield point. Thus, a completed task will return
0N/A// immediately with no actual work having been done by the workers.
0N/A/////////////////////
0N/A// Implementatiuon notes: remove before checking XXX
0N/A/*
0N/AEach gang is working on a task at a certain time.
0N/ASome subset of workers may have yielded and some may
0N/Ahave finished their quota of work. Until this task has
0N/Abeen completed, the workers are bound to that task.
0N/AOnce the task has been completed, the gang unbounds
0N/Aitself from the task.
0N/A
0N/AThe yielding work gang thus exports two invokation
0N/Ainterfaces: run_task() and continue_task(). The
0N/Afirst is used to initiate a new task and bind it
0N/Ato the workers; the second is used to continue an
0N/Aalready bound task that has yielded. Upon completion
0N/Athe binding is released and a new binding may be
0N/Acreated.
0N/A
0N/AThe shape of a yielding work gang is as follows:
0N/A
0N/AOverseer invokes run_task(*task).
0N/A Lock gang monitor
0N/A Check that there is no existing binding for the gang
0N/A If so, abort with an error
0N/A Else, create a new binding of this gang to the given task
0N/A Set number of active workers (as asked)
0N/A Notify workers that work is ready to be done
0N/A [the requisite # workers would then start up
0N/A and do the task]
0N/A Wait on the monitor until either
0N/A all work is completed or the task has yielded
0N/A -- this is normally done through
0N/A yielded + completed == active
0N/A [completed workers are rest to idle state by overseer?]
0N/A return appropriate status to caller
0N/A
0N/AOverseer invokes continue_task(*task),
0N/A Lock gang monitor
0N/A Check that task is the same as current binding
0N/A If not, abort with an error
0N/A Else, set the number of active workers as requested?
0N/A Notify workers that they can continue from yield points
0N/A New workers can also start up as required
0N/A while satisfying the constraint that
0N/A active + yielded does not exceed required number
0N/A Wait (as above).
0N/A
0N/ANOTE: In the above, for simplicity in a first iteration
0N/A our gangs will be of fixed population and will not
0N/A therefore be flexible work gangs, just yielding work
0N/A gangs. Once this works well, we will in a second
0N/A iteration.refinement introduce flexibility into
0N/A the work gang.
0N/A
0N/ANOTE: we can always create a new gang per each iteration
0N/A in order to get the flexibility, but we will for now
0N/A desist that simplified route.
0N/A
0N/A */
0N/A/////////////////////
0N/Avoid YieldingFlexibleWorkGang::start_task(YieldingFlexibleGangTask* new_task) {
0N/A MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag);
0N/A assert(task() == NULL, "Gang currently tied to a task");
0N/A assert(new_task != NULL, "Null task");
0N/A // Bind task to gang
0N/A _task = new_task;
0N/A new_task->set_gang(this); // Establish 2-way binding to support yielding
0N/A _sequence_number++;
0N/A
3008N/A uint requested_size = new_task->requested_size();
0N/A assert(requested_size >= 0, "Should be non-negative");
0N/A if (requested_size != 0) {
0N/A _active_workers = MIN2(requested_size, total_workers());
0N/A } else {
2941N/A _active_workers = active_workers();
0N/A }
0N/A new_task->set_actual_size(_active_workers);
1753N/A new_task->set_for_termination(_active_workers);
0N/A
0N/A assert(_started_workers == 0, "Tabula rasa non");
0N/A assert(_finished_workers == 0, "Tabula rasa non");
0N/A assert(_yielded_workers == 0, "Tabula rasa non");
0N/A yielding_task()->set_status(ACTIVE);
0N/A
0N/A // Wake up all the workers, the first few will get to work,
0N/A // and the rest will go back to sleep
0N/A monitor()->notify_all();
0N/A wait_for_gang();
0N/A}
0N/A
0N/Avoid YieldingFlexibleWorkGang::wait_for_gang() {
0N/A
0N/A assert(monitor()->owned_by_self(), "Data race");
0N/A // Wait for task to complete or yield
0N/A for (Status status = yielding_task()->status();
0N/A status != COMPLETED && status != YIELDED && status != ABORTED;
0N/A status = yielding_task()->status()) {
2941N/A assert(started_workers() <= active_workers(), "invariant");
2941N/A assert(finished_workers() <= active_workers(), "invariant");
2941N/A assert(yielded_workers() <= active_workers(), "invariant");
0N/A monitor()->wait(Mutex::_no_safepoint_check_flag);
0N/A }
0N/A switch (yielding_task()->status()) {
0N/A case COMPLETED:
0N/A case ABORTED: {
2941N/A assert(finished_workers() == active_workers(), "Inconsistent status");
0N/A assert(yielded_workers() == 0, "Invariant");
0N/A reset(); // for next task; gang<->task binding released
0N/A break;
0N/A }
0N/A case YIELDED: {
0N/A assert(yielded_workers() > 0, "Invariant");
2941N/A assert(yielded_workers() + finished_workers() == active_workers(),
0N/A "Inconsistent counts");
0N/A break;
0N/A }
0N/A case ACTIVE:
0N/A case INACTIVE:
0N/A case COMPLETING:
0N/A case YIELDING:
0N/A case ABORTING:
0N/A default:
0N/A ShouldNotReachHere();
0N/A }
0N/A}
0N/A
0N/Avoid YieldingFlexibleWorkGang::continue_task(
0N/A YieldingFlexibleGangTask* gang_task) {
0N/A
0N/A MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag);
0N/A assert(task() != NULL && task() == gang_task, "Incorrect usage");
0N/A assert(_started_workers == _active_workers, "Precondition");
0N/A assert(_yielded_workers > 0 && yielding_task()->status() == YIELDED,
0N/A "Else why are we calling continue_task()");
0N/A // Restart the yielded gang workers
0N/A yielding_task()->set_status(ACTIVE);
0N/A monitor()->notify_all();
0N/A wait_for_gang();
0N/A}
0N/A
0N/Avoid YieldingFlexibleWorkGang::reset() {
0N/A _started_workers = 0;
0N/A _finished_workers = 0;
0N/A yielding_task()->set_gang(NULL);
0N/A _task = NULL; // unbind gang from task
0N/A}
0N/A
0N/Avoid YieldingFlexibleWorkGang::yield() {
0N/A assert(task() != NULL, "Inconsistency; should have task binding");
0N/A MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag);
2941N/A assert(yielded_workers() < active_workers(), "Consistency check");
0N/A if (yielding_task()->status() == ABORTING) {
0N/A // Do not yield; we need to abort as soon as possible
0N/A // XXX NOTE: This can cause a performance pathology in the
0N/A // current implementation in Mustang, as of today, and
0N/A // pre-Mustang in that as soon as an overflow occurs,
0N/A // yields will not be honoured. The right way to proceed
0N/A // of course is to fix bug # TBF, so that abort's cause
0N/A // us to return at each potential yield point.
0N/A return;
0N/A }
2941N/A if (++_yielded_workers + finished_workers() == active_workers()) {
0N/A yielding_task()->set_status(YIELDED);
0N/A monitor()->notify_all();
0N/A } else {
0N/A yielding_task()->set_status(YIELDING);
0N/A }
0N/A
0N/A while (true) {
0N/A switch (yielding_task()->status()) {
0N/A case YIELDING:
0N/A case YIELDED: {
0N/A monitor()->wait(Mutex::_no_safepoint_check_flag);
0N/A break; // from switch
0N/A }
0N/A case ACTIVE:
0N/A case ABORTING:
0N/A case COMPLETING: {
0N/A assert(_yielded_workers > 0, "Else why am i here?");
0N/A _yielded_workers--;
0N/A return;
0N/A }
0N/A case INACTIVE:
0N/A case ABORTED:
0N/A case COMPLETED:
0N/A default: {
0N/A ShouldNotReachHere();
0N/A }
0N/A }
0N/A }
0N/A // Only return is from inside switch statement above
0N/A ShouldNotReachHere();
0N/A}
0N/A
0N/Avoid YieldingFlexibleWorkGang::abort() {
0N/A assert(task() != NULL, "Inconsistency; should have task binding");
0N/A MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag);
0N/A assert(yielded_workers() < active_workers(), "Consistency check");
0N/A #ifndef PRODUCT
0N/A switch (yielding_task()->status()) {
0N/A // allowed states
0N/A case ACTIVE:
0N/A case ABORTING:
0N/A case COMPLETING:
0N/A case YIELDING:
0N/A break;
0N/A // not allowed states
0N/A case INACTIVE:
0N/A case ABORTED:
0N/A case COMPLETED:
0N/A case YIELDED:
0N/A default:
0N/A ShouldNotReachHere();
0N/A }
0N/A #endif // !PRODUCT
0N/A Status prev_status = yielding_task()->status();
0N/A yielding_task()->set_status(ABORTING);
0N/A if (prev_status == YIELDING) {
0N/A assert(yielded_workers() > 0, "Inconsistency");
0N/A // At least one thread has yielded, wake it up
0N/A // so it can go back to waiting stations ASAP.
0N/A monitor()->notify_all();
0N/A }
0N/A}
0N/A
0N/A///////////////////////////////
0N/A// YieldingFlexibleGangTask
0N/A///////////////////////////////
0N/Avoid YieldingFlexibleGangTask::yield() {
0N/A assert(gang() != NULL, "No gang to signal");
0N/A gang()->yield();
0N/A}
0N/A
0N/Avoid YieldingFlexibleGangTask::abort() {
0N/A assert(gang() != NULL, "No gang to signal");
0N/A gang()->abort();
0N/A}
0N/A
0N/A///////////////////////////////
0N/A// YieldingFlexibleGangWorker
0N/A///////////////////////////////
0N/Avoid YieldingFlexibleGangWorker::loop() {
0N/A int previous_sequence_number = 0;
0N/A Monitor* gang_monitor = gang()->monitor();
0N/A MutexLockerEx ml(gang_monitor, Mutex::_no_safepoint_check_flag);
0N/A WorkData data;
0N/A int id;
0N/A while (true) {
0N/A // Check if there is work to do or if we have been asked
0N/A // to terminate
0N/A gang()->internal_worker_poll(&data);
0N/A if (data.terminate()) {
0N/A // We have been asked to terminate.
0N/A assert(gang()->task() == NULL, "No task binding");
0N/A // set_status(TERMINATED);
0N/A return;
0N/A } else if (data.task() != NULL &&
0N/A data.sequence_number() != previous_sequence_number) {
0N/A // There is work to be done.
0N/A // First check if we need to become active or if there
0N/A // are already the requisite number of workers
0N/A if (gang()->started_workers() == yf_gang()->active_workers()) {
0N/A // There are already enough workers, we do not need to
0N/A // to run; fall through and wait on monitor.
0N/A } else {
0N/A // We need to pitch in and do the work.
0N/A assert(gang()->started_workers() < yf_gang()->active_workers(),
0N/A "Unexpected state");
0N/A id = gang()->started_workers();
0N/A gang()->internal_note_start();
0N/A // Now, release the gang mutex and do the work.
0N/A {
0N/A MutexUnlockerEx mul(gang_monitor, Mutex::_no_safepoint_check_flag);
0N/A data.task()->work(id); // This might include yielding
0N/A }
0N/A // Reacquire monitor and note completion of this worker
0N/A gang()->internal_note_finish();
0N/A // Update status of task based on whether all workers have
0N/A // finished or some have yielded
0N/A assert(data.task() == gang()->task(), "Confused task binding");
0N/A if (gang()->finished_workers() == yf_gang()->active_workers()) {
0N/A switch (data.yf_task()->status()) {
0N/A case ABORTING: {
0N/A data.yf_task()->set_status(ABORTED);
0N/A break;
0N/A }
0N/A case ACTIVE:
0N/A case COMPLETING: {
0N/A data.yf_task()->set_status(COMPLETED);
0N/A break;
0N/A }
0N/A default:
0N/A ShouldNotReachHere();
0N/A }
0N/A gang_monitor->notify_all(); // Notify overseer
0N/A } else { // at least one worker is still working or yielded
0N/A assert(gang()->finished_workers() < yf_gang()->active_workers(),
0N/A "Counts inconsistent");
0N/A switch (data.yf_task()->status()) {
0N/A case ACTIVE: {
0N/A // first, but not only thread to complete
0N/A data.yf_task()->set_status(COMPLETING);
0N/A break;
0N/A }
0N/A case YIELDING: {
0N/A if (gang()->finished_workers() + yf_gang()->yielded_workers()
0N/A == yf_gang()->active_workers()) {
0N/A data.yf_task()->set_status(YIELDED);
0N/A gang_monitor->notify_all(); // notify overseer
0N/A }
0N/A break;
0N/A }
0N/A case ABORTING:
0N/A case COMPLETING: {
0N/A break; // nothing to do
0N/A }
0N/A default: // everything else: INACTIVE, YIELDED, ABORTED, COMPLETED
0N/A ShouldNotReachHere();
0N/A }
0N/A }
0N/A }
0N/A }
0N/A // Remember the sequence number
0N/A previous_sequence_number = data.sequence_number();
0N/A // Wait for more work
0N/A gang_monitor->wait(Mutex::_no_safepoint_check_flag);
0N/A }
0N/A}