# HG changeset patch # User jmasa # Date 1465420311 25200 # Wed Jun 08 14:11:51 2016 -0700 # Node ID 6c95f454802bc8d6c8a1a31f48bf96ecdc064b40 # Parent 3f4173a750ac10fa4c22ed3d0e3d8d43bab3020b 8159073: : Error handling incomplete when creating GC threads lazily Reviewed-by: ehelin, drwhite diff --git a/src/share/vm/gc/shared/workerManager.hpp b/src/share/vm/gc/shared/workerManager.hpp --- a/src/share/vm/gc/shared/workerManager.hpp +++ b/src/share/vm/gc/shared/workerManager.hpp @@ -58,10 +58,10 @@ WorkerThread* new_worker = holder->install_worker(worker_id); assert(new_worker != NULL, "Failed to allocate GangWorker"); if (new_worker == NULL || !os::create_thread(new_worker, worker_type)) { - if(initializing) { - vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, - "Cannot create worker GC thread. Out of system resources."); + if (initializing) { + vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, "Cannot create worker GC thread. Out of system resources."); } + break; } created_workers++; os::start_thread(new_worker); # HG changeset patch # User jmasa # Date 1465839953 25200 # Mon Jun 13 10:45:53 2016 -0700 # Node ID de8483a6e09f849ea78419cb52b9541eab7077ce # Parent 6c95f454802bc8d6c8a1a31f48bf96ecdc064b40 code_review1: add logging for failures diff --git a/src/share/vm/gc/shared/workerManager.hpp b/src/share/vm/gc/shared/workerManager.hpp --- a/src/share/vm/gc/shared/workerManager.hpp +++ b/src/share/vm/gc/shared/workerManager.hpp @@ -55,9 +55,17 @@ uint start = created_workers; uint end = MIN2(active_workers, total_workers); for (uint worker_id = start; worker_id < end; worker_id += 1) { - WorkerThread* new_worker = holder->install_worker(worker_id); - assert(new_worker != NULL, "Failed to allocate GangWorker"); + WorkerThread* new_worker = NULL; + if (initializing || !InjectGCWorkerCreationFailure) { + new_worker = holder->install_worker(worker_id); + } if (new_worker == NULL || !os::create_thread(new_worker, worker_type)) { + log_trace(gc, task)("WorkerManager::add_workers() : " + "creation failed due to failed allocation of native %s", + new_worker == NULL ? "memory" : "thread"); + if (new_worker != NULL) { + delete new_worker; + } if (initializing) { vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, "Cannot create worker GC thread. Out of system resources."); } @@ -67,9 +75,8 @@ os::start_thread(new_worker); } - log_trace(gc, task)("AdaptiveSizePolicy::add_workers() : " - "active_workers: %u created_workers: %u", - active_workers, created_workers); + log_trace(gc, task)("WorkerManager::add_workers() : " + "created_workers: %u", created_workers); return created_workers; } diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -1539,6 +1539,10 @@ "Dynamically choose the number of parallel threads " \ "parallel gc will use") \ \ + diagnostic(bool, InjectGCWorkerCreationFailure, false, \ + "Inject thread creation failures for " \ + "UseDynamicNumberOfGCThreads") \ + \ diagnostic(bool, ForceDynamicNumberOfGCThreads, false, \ "Force dynamic selection of the number of " \ "parallel threads parallel gc will use to aid debugging") \ # HG changeset patch # User jmasa # Date 1466710329 25200 # Thu Jun 23 12:32:09 2016 -0700 # Node ID 7fc81548a8b61d2bf0f7b1a11472cb78f463c518 # Parent de8483a6e09f849ea78419cb52b9541eab7077ce create_thread_failed: fix errors from thread creation failures diff --git a/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp b/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp --- a/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp +++ b/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp @@ -2888,7 +2888,10 @@ CMSParInitialMarkTask tsk(this, &srs, n_workers); initialize_sequential_subtasks_for_young_gen_rescan(n_workers); - if (n_workers > 1) { + // If the total workers is greater than 1, then multiple workers + // may be used at some time and the initialization has been set + // such that the single threaded path cannot be used. + if (workers->total_workers() > 1) { workers->run_task(&tsk); } else { tsk.work(0); @@ -3509,6 +3512,10 @@ Threads::number_of_non_daemon_threads()); conc_workers()->set_active_workers(num_workers); + // The number of workers available can be limited by the call + // to set_active_workers(). + num_workers = conc_workers()->active_workers(); + CompactibleFreeListSpace* cms_space = _cmsGen->cmsSpace(); CMSConcMarkingTask tsk(this, diff --git a/src/share/vm/gc/cms/parNewGeneration.cpp b/src/share/vm/gc/cms/parNewGeneration.cpp --- a/src/share/vm/gc/cms/parNewGeneration.cpp +++ b/src/share/vm/gc/cms/parNewGeneration.cpp @@ -899,6 +899,9 @@ workers->active_workers(), Threads::number_of_non_daemon_threads()); workers->set_active_workers(active_workers); + // active_workers may be limited in set_active_workers() to less than + // the input value. + active_workers = workers->active_workers(); _old_gen = gch->old_gen(); // If the next generation is too full to accommodate worst-case promotion @@ -952,7 +955,9 @@ // separate thread causes wide variance in run times. We can't help this // in the multi-threaded case, but we special-case n=1 here to get // repeatable measurements of the 1-thread overhead of the parallel code. - if (active_workers > 1) { + // Might multiple workers ever be used? If yes, initialization + // has been done such that the single threaded path should not be used. + if (workers->total_workers() > 1) { workers->run_task(&tsk); } else { tsk.work(0); diff --git a/src/share/vm/gc/g1/g1ConcurrentMark.cpp b/src/share/vm/gc/g1/g1ConcurrentMark.cpp --- a/src/share/vm/gc/g1/g1ConcurrentMark.cpp +++ b/src/share/vm/gc/g1/g1ConcurrentMark.cpp @@ -1033,11 +1033,14 @@ uint active_workers = MAX2(1U, parallel_marking_threads()); assert(active_workers > 0, "Should have been set"); + // Setting active workers is not guaranteed since fewer + // worker threads may currently exist and more may not be + // available. + _parallel_workers->set_active_workers(active_workers); // Parallel task terminator is set in "set_concurrency_and_phase()" - set_concurrency_and_phase(active_workers, true /* concurrent */); + set_concurrency_and_phase(_parallel_workers->active_workers(), true /* concurrent */); G1CMConcurrentMarkingTask markingTask(this, cmThread()); - _parallel_workers->set_active_workers(active_workers); _parallel_workers->run_task(&markingTask); print_stats(); } diff --git a/src/share/vm/gc/parallel/psScavenge.cpp b/src/share/vm/gc/parallel/psScavenge.cpp --- a/src/share/vm/gc/parallel/psScavenge.cpp +++ b/src/share/vm/gc/parallel/psScavenge.cpp @@ -391,11 +391,15 @@ ParallelTaskTerminator terminator( active_workers, (TaskQueueSetSuper*) promotion_manager->stack_array_depth()); - if (active_workers > 1) { - for (uint j = 0; j < active_workers; j++) { - q->enqueue(new StealTask(&terminator)); + // If active_workers can exceed 1, add a StrealTask. + // PSPromotionManager::drain_stacks_depth() does not fully drain its + // stacks and expects a StealTask to complete the draining if + // ParallelGCThreads is > 1. + if (gc_task_manager()->workers() > 1) { + for (uint j = 0; j < active_workers; j++) { + q->enqueue(new StealTask(&terminator)); + } } - } gc_task_manager()->execute_and_wait(q); } diff --git a/src/share/vm/gc/shared/workgroup.cpp b/src/share/vm/gc/shared/workgroup.cpp --- a/src/share/vm/gc/shared/workgroup.cpp +++ b/src/share/vm/gc/shared/workgroup.cpp @@ -271,8 +271,11 @@ "Trying to execute task %s with %u workers which is more than the amount of total workers %u.", task->name(), num_workers, total_workers()); guarantee(num_workers > 0, "Trying to execute task %s with zero workers", task->name()); - add_workers(num_workers, false); - _dispatcher->coordinator_execute_on_workers(task, num_workers); + // num_workers may have been calculated based on different criteria + // than _active_workers so use num_workers limited by the number of + // created workers. + uint active_workers = MIN2(_created_workers, num_workers); + _dispatcher->coordinator_execute_on_workers(task, active_workers); } AbstractGangWorker::AbstractGangWorker(AbstractWorkGang* gang, uint id) { diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -1539,7 +1539,7 @@ "Dynamically choose the number of parallel threads " \ "parallel gc will use") \ \ - diagnostic(bool, InjectGCWorkerCreationFailure, false, \ + diagnostic(bool, InjectGCWorkerCreationFailure, false, \ "Inject thread creation failures for " \ "UseDynamicNumberOfGCThreads") \ \ # HG changeset patch # User jmasa # Date 1466711740 25200 # Thu Jun 23 12:55:40 2016 -0700 # Node ID 169b49eab335a726f05923d520ae2e82a8aa6f12 # Parent 7fc81548a8b61d2bf0f7b1a11472cb78f463c518 refactor: switch to update_active_threads() diff --git a/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp b/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp --- a/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp +++ b/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp @@ -3510,11 +3510,7 @@ uint num_workers = AdaptiveSizePolicy::calc_active_conc_workers(conc_workers()->total_workers(), conc_workers()->active_workers(), Threads::number_of_non_daemon_threads()); - conc_workers()->set_active_workers(num_workers); - - // The number of workers available can be limited by the call - // to set_active_workers(). - num_workers = conc_workers()->active_workers(); + num_workers = conc_workers()->update_active_workers(num_workers); CompactibleFreeListSpace* cms_space = _cmsGen->cmsSpace(); diff --git a/src/share/vm/gc/cms/parNewGeneration.cpp b/src/share/vm/gc/cms/parNewGeneration.cpp --- a/src/share/vm/gc/cms/parNewGeneration.cpp +++ b/src/share/vm/gc/cms/parNewGeneration.cpp @@ -898,10 +898,7 @@ AdaptiveSizePolicy::calc_active_workers(workers->total_workers(), workers->active_workers(), Threads::number_of_non_daemon_threads()); - workers->set_active_workers(active_workers); - // active_workers may be limited in set_active_workers() to less than - // the input value. - active_workers = workers->active_workers(); + active_workers = workers->update_active_workers(active_workers); _old_gen = gch->old_gen(); // If the next generation is too full to accommodate worst-case promotion diff --git a/src/share/vm/gc/g1/g1CollectedHeap.cpp b/src/share/vm/gc/g1/g1CollectedHeap.cpp --- a/src/share/vm/gc/g1/g1CollectedHeap.cpp +++ b/src/share/vm/gc/g1/g1CollectedHeap.cpp @@ -1333,7 +1333,7 @@ AdaptiveSizePolicy::calc_active_workers(workers()->total_workers(), workers()->active_workers(), Threads::number_of_non_daemon_threads()); - workers()->set_active_workers(n_workers); + workers()->update_active_workers(n_workers); ParRebuildRSTask rebuild_rs_task(this); workers()->run_task(&rebuild_rs_task); @@ -3166,7 +3166,7 @@ uint active_workers = AdaptiveSizePolicy::calc_active_workers(workers()->total_workers(), workers()->active_workers(), Threads::number_of_non_daemon_threads()); - workers()->set_active_workers(active_workers); + workers()->update_active_workers(active_workers); TraceCollectorStats tcs(g1mm()->incremental_collection_counters()); TraceMemoryManagerStats tms(false /* fullGC */, gc_cause()); diff --git a/src/share/vm/gc/g1/g1ConcurrentMark.cpp b/src/share/vm/gc/g1/g1ConcurrentMark.cpp --- a/src/share/vm/gc/g1/g1ConcurrentMark.cpp +++ b/src/share/vm/gc/g1/g1ConcurrentMark.cpp @@ -1036,9 +1036,9 @@ // Setting active workers is not guaranteed since fewer // worker threads may currently exist and more may not be // available. - _parallel_workers->set_active_workers(active_workers); + active_workers = _parallel_workers->update_active_workers(active_workers); // Parallel task terminator is set in "set_concurrency_and_phase()" - set_concurrency_and_phase(_parallel_workers->active_workers(), true /* concurrent */); + set_concurrency_and_phase(active_workers, true /* concurrent */); G1CMConcurrentMarkingTask markingTask(this, cmThread()); _parallel_workers->run_task(&markingTask); diff --git a/src/share/vm/gc/parallel/gcTaskManager.cpp b/src/share/vm/gc/parallel/gcTaskManager.cpp --- a/src/share/vm/gc/parallel/gcTaskManager.cpp +++ b/src/share/vm/gc/parallel/gcTaskManager.cpp @@ -529,7 +529,7 @@ created_workers() - active_workers() - idle_workers(); if (more_inactive_workers < 0) { int reduced_active_workers = active_workers() + more_inactive_workers; - set_active_workers(reduced_active_workers); + update_active_workers(reduced_active_workers); more_inactive_workers = 0; } log_trace(gc, task)("JT: %d workers %d active %d idle %d more %d", diff --git a/src/share/vm/gc/parallel/gcTaskManager.hpp b/src/share/vm/gc/parallel/gcTaskManager.hpp --- a/src/share/vm/gc/parallel/gcTaskManager.hpp +++ b/src/share/vm/gc/parallel/gcTaskManager.hpp @@ -457,11 +457,12 @@ uint workers() const { return _workers; } - void set_active_workers(uint v) { + uint update_active_workers(uint v) { assert(v <= _workers, "Trying to set more workers active than there are"); _active_workers = MIN2(v, _workers); assert(v != 0, "Trying to set active workers to 0"); _active_workers = MAX2(1U, _active_workers); + return _active_workers; } // Sets the number of threads that will be used in a collection void set_active_gang(); diff --git a/src/share/vm/gc/shared/workgroup.hpp b/src/share/vm/gc/shared/workgroup.hpp --- a/src/share/vm/gc/shared/workgroup.hpp +++ b/src/share/vm/gc/shared/workgroup.hpp @@ -156,7 +156,7 @@ return _active_workers; } - void set_active_workers(uint v) { + uint update_active_workers(uint v) { assert(v <= _total_workers, "Trying to set more workers active than there are"); _active_workers = MIN2(v, _total_workers); @@ -165,6 +165,7 @@ assert(UseDynamicNumberOfGCThreads || _active_workers == _total_workers, "Unless dynamic should use total workers"); log_info(gc, task)("GC Workers: using %d out of %d", _active_workers, _total_workers); + return _active_workers; } // Add GC workers as needed. diff --git a/test/gc/stress/TestGCOld.java b/test/gc/stress/TestGCOld.java --- a/test/gc/stress/TestGCOld.java +++ b/test/gc/stress/TestGCOld.java @@ -33,6 +33,7 @@ * @run main/othervm -Xmx384M -XX:+UseConcMarkSweepGC TestGCOld 50 1 20 10 10000 * @run main/othervm -Xmx384M -XX:+UseG1GC TestGCOld 50 1 20 10 10000 * @run main/othervm -Xms64m -Xmx128m -XX:+UseG1GC -XX:+UseDynamicNumberOfGCThreads -Xlog:gc,gc+task=trace TestGCOld 50 5 20 1 5000 + * @run main/othervm -Xms64m -Xmx128m -XX:+UseG1GC -XX:+UseDynamicNumberOfGCThreads -XX:+UnlockDiagnosticVMOptions -XX:+InjectGCWorkerCreationFailure -Xlog:gc,gc+task=trace TestGCOld 50 5 20 1 5000 */ import java.text.*; # HG changeset patch # User jmasa # Date 1469037085 25200 # Wed Jul 20 10:51:25 2016 -0700 # Node ID 8789de0c1f1d2dd9b1fb61a51dbf755fbaf6ab41 # Parent 169b49eab335a726f05923d520ae2e82a8aa6f12 [mq]: code_review2 diff --git a/src/share/vm/gc/shared/workerManager.hpp b/src/share/vm/gc/shared/workerManager.hpp --- a/src/share/vm/gc/shared/workerManager.hpp +++ b/src/share/vm/gc/shared/workerManager.hpp @@ -47,11 +47,11 @@ // threads and a failure would not be optimal but should not be fatal. template static uint add_workers (WorkerType* holder, - uint active_workers, - uint total_workers, - uint created_workers, - os::ThreadType worker_type, - bool initializing) { + uint active_workers, + uint total_workers, + uint created_workers, + os::ThreadType worker_type, + bool initializing) { uint start = created_workers; uint end = MIN2(active_workers, total_workers); for (uint worker_id = start; worker_id < end; worker_id += 1) { @@ -61,8 +61,8 @@ } if (new_worker == NULL || !os::create_thread(new_worker, worker_type)) { log_trace(gc, task)("WorkerManager::add_workers() : " - "creation failed due to failed allocation of native %s", - new_worker == NULL ? "memory" : "thread"); + "creation failed due to failed allocation of native %s", + new_worker == NULL ? "memory" : "thread"); if (new_worker != NULL) { delete new_worker; } @@ -76,7 +76,7 @@ } log_trace(gc, task)("WorkerManager::add_workers() : " - "created_workers: %u", created_workers); + "created_workers: %u", created_workers); return created_workers; } diff --git a/src/share/vm/gc/shared/workgroup.cpp b/src/share/vm/gc/shared/workgroup.cpp --- a/src/share/vm/gc/shared/workgroup.cpp +++ b/src/share/vm/gc/shared/workgroup.cpp @@ -271,11 +271,13 @@ "Trying to execute task %s with %u workers which is more than the amount of total workers %u.", task->name(), num_workers, total_workers()); guarantee(num_workers > 0, "Trying to execute task %s with zero workers", task->name()); - // num_workers may have been calculated based on different criteria - // than _active_workers so use num_workers limited by the number of - // created workers. - uint active_workers = MIN2(_created_workers, num_workers); - _dispatcher->coordinator_execute_on_workers(task, active_workers); + uint old_num_workers = _active_workers; + log_debug(gc)("run_task: updating active workers for %s from %u to %u", task->name(), old_num_workers, num_workers); + update_active_workers(num_workers); + guarantee(_active_workers == num_workers, "active workers %u num_workers %u", _active_workers, num_workers); + _dispatcher->coordinator_execute_on_workers(task, num_workers); + log_debug(gc)("run_task: restoring active workers from %u to %u", num_workers, old_num_workers); + update_active_workers(old_num_workers); } AbstractGangWorker::AbstractGangWorker(AbstractWorkGang* gang, uint id) {