/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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_implementation/parallelScavenge/gcTaskManager.hpp"
#include "gc_implementation/parallelScavenge/gcTaskThread.hpp"
#include "gc_implementation/shared/adaptiveSizePolicy.hpp"
#include "memory/allocation.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/mutexLocker.hpp"
//
// GCTask
//
switch (value) {
default:
result = "unknown GCTask kind";
break;
case unknown_task:
result = "unknown task";
break;
case ordinary_task:
result = "ordinary task";
break;
case barrier_task:
result = "barrier task";
break;
case noop_task:
result = "noop task";
break;
case idle_task:
result = "idle task";
break;
}
return result;
};
initialize();
}
initialize();
}
initialize();
}
initialize();
}
}
// Nothing to do.
}
}
)
//
// GCTaskQueue
//
if (TraceGCTaskQueue) {
}
return result;
}
if (TraceGCTaskQueue) {
" returns " INTPTR_FORMAT,
result);
}
return result;
}
initialize();
if (TraceGCTaskQueue) {
" GCTaskQueue::GCTaskQueue() constructor",
this);
}
}
// Nothing to do.
}
if (TraceGCTaskQueue) {
" GCTaskQueue::destroy()"
" is_c_heap_obj: %s",
that,
}
// That instance may have been allocated as a CHeapObj,
// in which case we have to free it explicitly.
if (that->is_c_heap_obj()) {
}
}
}
set_length(0);
}
// Enqueue one task.
if (TraceGCTaskQueue) {
" GCTaskQueue::enqueue(task: "
INTPTR_FORMAT ")",
this, task);
print("before:");
}
if (is_empty()) {
} else {
}
if (TraceGCTaskQueue) {
print("after:");
}
}
// Enqueue a whole list of tasks. Empties the argument list.
if (TraceGCTaskQueue) {
" GCTaskQueue::enqueue(list: "
INTPTR_FORMAT ")",
this, list);
print("before:");
}
// Enqueuing the empty list: nothing to do.
return;
}
if (is_empty()) {
// Enqueuing to empty list: just acquire elements.
} else {
// Prepend argument list to our queue.
// empty the argument list.
}
list->initialize();
if (TraceGCTaskQueue) {
print("after:");
}
}
// Dequeue one task.
if (TraceGCTaskQueue) {
" GCTaskQueue::dequeue()", this);
print("before:");
}
if (TraceGCTaskQueue) {
print("after:");
}
return result;
}
// Dequeue one task, preferring one with affinity.
if (TraceGCTaskQueue) {
" GCTaskQueue::dequeue(%u)", this, affinity);
print("before:");
}
// Look down to the next barrier for a task with this affinity.
if (element->is_barrier_task()) {
// Don't consider barrier tasks, nor past them.
break;
}
break;
}
}
// If we didn't find anything with affinity, just take the next task.
}
if (TraceGCTaskQueue) {
print("after:");
}
return result;
}
// Dequeue from remove end.
if (remove_end() == NULL) {
} else {
}
return result;
}
// This is slightly more work, and has slightly fewer asserts
// than removing from the remove end.
} else {
}
} else {
}
return result;
}
// Count the elements in the queue and verify the length against
// that count.
void GCTaskQueue::verify_length() const {
count++;
}
}
" insert_end: " INTPTR_FORMAT
" remove_end: " INTPTR_FORMAT
" length: %d"
" %s",
count++;
}
}
)
//
// SynchronizedGCTaskQueue
//
}
// Nothing to do.
}
//
// GCTaskManager
//
_active_workers(0),
_idle_workers(0),
initialize();
}
_active_workers(0),
_idle_workers(0),
initialize();
}
if (TraceGCTaskManager) {
}
"GCTaskManager monitor", // name
// The queue for the GCTaskManager must be a CHeapObj.
{
// Set up worker threads.
// Distribute the workers among the available processors,
// unless we were told not to, or if the os doesn't want to.
if (!BindGCTaskThreadsToCPUs ||
processor_assignment[a] = sentinel_worker();
}
}
}
if (TraceGCTaskThread) {
}
}
}
set_resource_flag(w, false);
}
}
}
GCTaskManager::~GCTaskManager() {
_noop_task = NULL;
set_thread(i, NULL);
}
}
if (_resource_flag != NULL) {
}
}
delete monitor();
}
}
err_msg("all_workers_active() is incorrect: "
"active %d ParallelGCThreads %d", active_workers(),
if (TraceDynamicGCThreads) {
"all_workers_active() %d workers %d "
"active %d ParallelGCThreads %d ",
}
}
// Create IdleGCTasks for inactive workers.
// Creates tasks in a ResourceArea and assumes
// an appropriate ResourceMark.
{
int more_inactive_workers = 0;
{
// Stop any idle tasks from exiting their IdleGCTask's
// and get the count for additional IdleGCTask's under
// the GCTaskManager's monitor so that the "more_inactive_workers"
// count is correct.
_idle_inactive_task->set_should_wait(true);
// active_workers are a number being requested. idle_workers
// are the number currently idle. If all the workers are being
// requested to be active but some are already idle, reduce
// the number of active_workers to be consistent with the
// number of idle_workers. The idle_workers are stuck in
// idle tasks and will no longer be release (since a new GC
// is starting). Try later to release enough idle_workers
// to allow the desired number of active_workers.
if (more_inactive_workers < 0) {
}
if (TraceDynamicGCThreads) {
"idle %d more %d",
workers(),
idle_workers(),
}
}
}
"total workers should equal active + inactive");
add_list(q);
// GCTaskQueue* q was created in a ResourceArea so a
// destroy() call is not needed.
}
}
{
_idle_inactive_task->set_should_wait(false);
monitor()->notify_all();
// Release monitor
}
}
for(uint i=0; i<ParallelGCThreads; i++) {
GCTaskThread* t = thread(i);
t->print_task_time_stamps();
}
}
}
}
}
}
}
}
if (TraceGCTaskManager) {
}
// Notify with the lock held to avoid missed notifies.
if (TraceGCTaskManager) {
}
(void) monitor()->notify_all();
// Release monitor().
}
if (TraceGCTaskManager) {
}
// Notify with the lock held to avoid missed notifies.
if (TraceGCTaskManager) {
}
(void) monitor()->notify_all();
// Release monitor().
}
// GC workers wait in get_task() for new work to be added
// to the GCTaskManager's queue. When new work is added,
// a notify is sent to the waiting GC workers which then
// compete to get tasks. If a GC worker wakes up and there
// is no work on the queue, it is given a noop_task to execute
// and then loops to find more work.
// Grab the queue lock.
// Wait while the queue is block or
// there is nothing to do, except maybe release resources.
while (is_blocked() ||
if (TraceGCTaskManager) {
" blocked: %s"
" empty: %s"
" release: %s",
}
}
// We've reacquired the queue lock here.
// Figure out which condition caused us to exit the loop above.
if (UseGCTaskAffinity) {
} else {
}
if (result->is_barrier_task()) {
"blocker shouldn't be bogus");
}
} else {
// The queue is empty, but we were woken up.
// Just hand back a Noop task,
// in case someone wanted us to release resources, or whatever.
}
if (TraceGCTaskManager) {
}
if (!result->is_idle_task()) {
}
return result;
// Release monitor().
}
if (TraceGCTaskManager) {
}
// If we are blocked, check if the completing thread is the blocker.
if (blocking_worker() == which) {
"blocker shouldn't be bogus");
}
if (TraceGCTaskManager) {
}
// Notify client that we are done.
}
}
if (TraceGCTaskManager) {
" blocked: %s"
" empty: %s"
" release: %s",
" delivered: %u"
" completed: %u"
" barriers: %u"
" emptied: %u",
barriers(),
emptied_queue());
}
// Tell everyone that a task has completed.
(void) monitor()->notify_all();
// Release monitor().
}
_busy_workers += 1;
return _busy_workers;
}
_busy_workers -= 1;
return _busy_workers;
}
void GCTaskManager::release_all_resources() {
// If you want this to be done atomically, do it in a BarrierGCTask.
set_resource_flag(i, true);
}
}
// This can be done without a lock because each thread reads one element.
return resource_flag(which);
}
// This can be done without a lock because each thread writes one element.
set_resource_flag(which, false);
}
// "list" contains tasks that are ready to execute. Those
// tasks are added to the GCTaskManager's queue of tasks and
// then the GC workers are notified that there is new work to
// do.
//
// Typically different types of tasks can be added to the "list".
// For example in PSScavenge OldToYoungRootsTask, SerialOldToYoungRootsTask,
// ScavengeRootsTask, and StealTask tasks are all added to the list
// and then the GC workers are notified of new work. The tasks are
// handed out in the order in which they are added to the list
// (although execution is not necessarily in that order). As long
// as any tasks are running the GCTaskManager will wait for execution
// to complete. GC workers that execute a stealing task remain in
// the stealing task until all stealing tasks have completed. The load
// balancing afforded by the stealing tasks work best if the stealing
// tasks are added last to the list.
// The barrier task will be read by one of the GC
// workers once it is added to the list of tasks.
// Be sure that is globally visible before the
// GC worker reads it (which is after the task is added
// to the list of tasks below).
// We have to release the barrier tasks!
}
return _resource_flag[which];
}
}
//
// NoopGCTask
//
return result;
}
return result;
}
if (that->is_c_heap_obj()) {
}
}
}
void NoopGCTask::destruct() {
// This has to know it's superclass structure, just like the constructor.
// Nothing else to do.
}
//
// IdleGCTask
//
"Should only be used with dynamic GC thread");
return result;
}
"Should only be used with dynamic GC thread");
return result;
}
if (TraceGCTaskManager) {
" IdleGCTask:::do_it()"
" should_wait: %s",
}
if (TraceDynamicGCThreads) {
}
// Increment has to be done when the idle tasks are created.
// manager->increment_idle_workers();
while (wait_for_task->should_wait()) {
if (TraceGCTaskManager) {
" IdleGCTask::do_it()"
}
}
if (TraceDynamicGCThreads) {
}
if (TraceGCTaskManager) {
" IdleGCTask::do_it() returns"
" should_wait: %s",
}
// Release monitor().
}
if (that->is_c_heap_obj()) {
}
}
}
void IdleGCTask::destruct() {
// This has to know it's superclass structure, just like the constructor.
// Nothing else to do.
}
//
// BarrierGCTask
//
// Wait for this to be the only busy worker.
// ??? I thought of having a StackObj class
// whose constructor would grab the lock and come to the barrier,
// and whose destructor would release the lock,
// but that seems like too much mechanism for two lines of code.
// Release manager->lock().
}
// Wait for this to be the only busy worker.
if (TraceGCTaskManager) {
}
}
}
void BarrierGCTask::destruct() {
// Nothing else to do.
}
//
// ReleasingBarrierGCTask
//
// Release manager->lock().
}
void ReleasingBarrierGCTask::destruct() {
this->BarrierGCTask::destruct();
// Nothing else to do.
}
//
// NotifyingBarrierGCTask
//
}
// Release manager->lock().
}
void NotifyingBarrierGCTask::destruct() {
this->BarrierGCTask::destruct();
// Nothing else to do.
}
//
// WaitForBarrierGCTask
//
return result;
}
return result;
}
set_should_wait(true);
if (TraceGCTaskManager) {
" WaitForBarrierGCTask::WaitForBarrierGCTask()"
" monitor: " INTPTR_FORMAT,
this, monitor());
}
}
if (TraceGCTaskManager) {
" WaitForBarrierGCTask::destroy()"
" is_c_heap_obj: %s"
" monitor: " INTPTR_FORMAT,
that,
}
if (that->is_c_heap_obj()) {
}
}
}
void WaitForBarrierGCTask::destruct() {
if (TraceGCTaskManager) {
" WaitForBarrierGCTask::destruct()"
" monitor: " INTPTR_FORMAT,
this, monitor());
}
this->BarrierGCTask::destruct();
// Clean up that should be in the destructor,
// except that ResourceMarks don't call destructors.
}
}
if (TraceGCTaskManager) {
" WaitForBarrierGCTask::do_it() waiting for idle"
" monitor: " INTPTR_FORMAT,
this, monitor());
}
{
// First, wait for the barrier to arrive.
// Release manager->lock().
}
{
// Then notify the waiter.
set_should_wait(false);
// Waiter doesn't miss the notify in the wait_for method
// since it checks the flag after grabbing the monitor.
if (TraceGCTaskManager) {
" WaitForBarrierGCTask::do_it()"
}
monitor()->notify_all();
// Release monitor().
}
}
if (TraceGCTaskManager) {
" WaitForBarrierGCTask::wait_for()"
" should_wait: %s",
}
{
// Grab the lock and check again.
while (should_wait()) {
if (TraceGCTaskManager) {
" WaitForBarrierGCTask::wait_for()"
}
}
// Reset the flag in case someone reuses this task.
if (reset) {
set_should_wait(true);
}
if (TraceGCTaskManager) {
" WaitForBarrierGCTask::wait_for() returns"
" should_wait: %s",
}
// Release monitor().
}
}
// Lazy initialization: possible race.
"MonitorSupply mutex", // name
}
{
// Lazy initialization.
true);
}
} else {
"MonitorSupply monitor", // name
}
// release lock().
}
return result;
}
{
// release lock().
}
}