--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/cms/yieldingWorkgroup.cpp Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2005, 2015, 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_sometimes);
+
+ 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) {
+ MutexLockerEx 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(Mutex::_no_safepoint_check_flag);
+ }
+ 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) {
+
+ MutexLockerEx 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");
+ MutexLockerEx 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(Mutex::_no_safepoint_check_flag);
+ 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");
+ MutexLockerEx 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();
+ MutexLockerEx 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.
+ {
+ MutexUnlockerEx 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(Mutex::_no_safepoint_check_flag);
+ }
+}