# HG changeset patch # User rehn # Date 1553244938 -3600 # Fri Mar 22 09:55:38 2019 +0100 # Node ID 984a79bdd84428004cc06fc6282e26b62d50fdc3 # Parent fce15a3c1922d58b9641704822bbe353d8b4b7c7 imported patch 8220774-handshakealot-v4 diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -371,6 +371,10 @@ "Generate a lot of safepoints. This works with " \ "GuaranteedSafepointInterval") \ \ + diagnostic(bool, HandshakeALot, false, \ + "Generate a lot of handshakes. This works with " \ + "GuaranteedSafepointInterval") \ + \ product_pd(bool, BackgroundCompilation, \ "A thread requesting compilation is not blocked during " \ "compilation") \ diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -2948,9 +2948,21 @@ } } +#ifdef ASSERT +void JavaThread::verify_states_for_handshake() { + // This checks that the thread has a correct frame state during a handshake. + assert((!has_last_Java_frame() && java_call_counter() == 0) || + (has_last_Java_frame() && java_call_counter() > 0), + "unexpected frame info: has_last_frame=%d, java_call_counter=%d", + has_last_Java_frame(), java_call_counter()); +} +#endif + void JavaThread::nmethods_do(CodeBlobClosure* cf) { assert((!has_last_Java_frame() && java_call_counter() == 0) || - (has_last_Java_frame() && java_call_counter() > 0), "wrong java_sp info!"); + (has_last_Java_frame() && java_call_counter() > 0), + "unexpected frame info: has_last_frame=%d, java_call_counter=%d", + has_last_Java_frame(), java_call_counter()); if (has_last_Java_frame()) { // Traverse the execution stack diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -1868,6 +1868,9 @@ // RedefineClasses Support void metadata_do(MetadataClosure* f); + // Debug method asserting thread states are correct during a handshake operation. + DEBUG_ONLY(void verify_states_for_handshake();) + // Misc. operations char* name() const { return (char*)get_thread_name(); } void print_on(outputStream* st, bool print_extended_info) const; diff --git a/src/hotspot/share/runtime/vmThread.cpp b/src/hotspot/share/runtime/vmThread.cpp --- a/src/hotspot/share/runtime/vmThread.cpp +++ b/src/hotspot/share/runtime/vmThread.cpp @@ -434,22 +434,35 @@ static VM_None safepointALot_op("SafepointALot"); static VM_Cleanup cleanup_op; -VM_Operation* VMThread::no_op_safepoint(bool check_time) { +class HandshakeALotTC : public ThreadClosure { + public: + virtual void do_thread(Thread* thread) { +#ifdef ASSERT + assert(thread->is_Java_thread(), "must be"); + JavaThread* jt = (JavaThread*)thread; + jt->verify_states_for_handshake(); +#endif + } +}; + +VM_Operation* VMThread::no_op_safepoint() { + // Check for handshakes first since we may need to return a VMop. + if (HandshakeALot) { + HandshakeALotTC haltc; + Handshake::execute(&haltc); + } + // Check for a cleanup before SafepointALot to keep stats correct. + long interval_ms = SafepointTracing::time_since_last_safepoint_ms(); + bool max_time_exceeded = GuaranteedSafepointInterval != 0 && + (interval_ms >= GuaranteedSafepointInterval); + if (max_time_exceeded && SafepointSynchronize::is_cleanup_needed()) { + return &cleanup_op; + } if (SafepointALot) { return &safepointALot_op; } - if (!SafepointSynchronize::is_cleanup_needed()) { - return NULL; - } - if (check_time) { - long interval_ms = SafepointTracing::time_since_last_safepoint_ms(); - bool max_time_exceeded = GuaranteedSafepointInterval != 0 && - (interval_ms > GuaranteedSafepointInterval); - if (!max_time_exceeded) { - return NULL; - } - } - return &cleanup_op; + // Nothing to be done. + return NULL; } void VMThread::loop() { @@ -491,19 +504,22 @@ exit(-1); } - if (timedout && (_cur_vm_operation = VMThread::no_op_safepoint(false)) != NULL) { - MutexUnlockerEx mul(VMOperationQueue_lock, - Mutex::_no_safepoint_check_flag); - // Force a safepoint since we have not had one for at least - // 'GuaranteedSafepointInterval' milliseconds. This will run all - // the clean-up processing that needs to be done regularly at a - // safepoint - SafepointSynchronize::begin(); - #ifdef ASSERT - if (GCALotAtAllSafepoints) InterfaceSupport::check_gc_alot(); - #endif - SafepointSynchronize::end(); - _cur_vm_operation = NULL; + { + // Have to unlock VMOperationQueue_lock just in case no_op_safepoint() + // has to do a handshake. + MutexUnlockerEx mul(VMOperationQueue_lock, Mutex::_no_safepoint_check_flag); + if (timedout && (_cur_vm_operation = VMThread::no_op_safepoint()) != NULL) { + // Force a safepoint since we have not had one for at least + // 'GuaranteedSafepointInterval' milliseconds and we need to clean + // something. This will run all the clean-up processing that needs + // to be done at a safepoint. + SafepointSynchronize::begin(); + #ifdef ASSERT + if (GCALotAtAllSafepoints) InterfaceSupport::check_gc_alot(); + #endif + SafepointSynchronize::end(); + _cur_vm_operation = NULL; + } } _cur_vm_operation = _vm_queue->remove_next(); @@ -615,10 +631,9 @@ VMOperationRequest_lock->notify_all(); } - // - // We want to make sure that we get to a safepoint regularly. - // - if ((_cur_vm_operation = VMThread::no_op_safepoint(false)) != NULL) { + // We want to make sure that we get to a safepoint regularly + // even when executing VMops that don't require safepoints. + if ((_cur_vm_operation = VMThread::no_op_safepoint()) != NULL) { HandleMark hm(VMThread::vm_thread()); SafepointSynchronize::begin(); SafepointSynchronize::end(); diff --git a/src/hotspot/share/runtime/vmThread.hpp b/src/hotspot/share/runtime/vmThread.hpp --- a/src/hotspot/share/runtime/vmThread.hpp +++ b/src/hotspot/share/runtime/vmThread.hpp @@ -123,7 +123,7 @@ static VMOperationTimeoutTask* _timeout_task; - static VM_Operation* no_op_safepoint(bool check_time); + static VM_Operation* no_op_safepoint(); void evaluate_operation(VM_Operation* op); # HG changeset patch # User rehn # Date 1553265716 -3600 # Fri Mar 22 15:41:56 2019 +0100 # Node ID 665a598a39498abd6b440720924bccde210dee14 # Parent 984a79bdd84428004cc06fc6282e26b62d50fdc3 [mq]: 8220774-handshakealot-v5 diff --git a/src/hotspot/share/runtime/vmThread.cpp b/src/hotspot/share/runtime/vmThread.cpp --- a/src/hotspot/share/runtime/vmThread.cpp +++ b/src/hotspot/share/runtime/vmThread.cpp @@ -504,18 +504,18 @@ exit(-1); } - { + if (timedout) { // Have to unlock VMOperationQueue_lock just in case no_op_safepoint() // has to do a handshake. MutexUnlockerEx mul(VMOperationQueue_lock, Mutex::_no_safepoint_check_flag); - if (timedout && (_cur_vm_operation = VMThread::no_op_safepoint()) != NULL) { + if ((_cur_vm_operation = VMThread::no_op_safepoint()) != NULL) { // Force a safepoint since we have not had one for at least // 'GuaranteedSafepointInterval' milliseconds and we need to clean // something. This will run all the clean-up processing that needs // to be done at a safepoint. SafepointSynchronize::begin(); #ifdef ASSERT - if (GCALotAtAllSafepoints) InterfaceSupport::check_gc_alot(); + if (GCALotAtAllSafepoints) InterfaceSupport::check_gc_alot(); #endif SafepointSynchronize::end(); _cur_vm_operation = NULL; diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -80,7 +80,6 @@ # :hotspot_runtime -runtime/handshake/HandshakeWalkSuspendExitTest.java 8214174 generic-all runtime/NMT/CheckForProperDetailStackTrace.java 8218458 generic-all runtime/SharedArchiveFile/SASymbolTableTest.java 8193639 solaris-all runtime/containers/docker/TestCPUSets.java 8220672 generic-all diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeSuspendExitTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeSuspendExitTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/handshake/HandshakeSuspendExitTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 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. + * + */ + +/* + * @test HandshakeSuspendExitTest + * @summary This test tries to stress the handshakes with new and exiting threads while suspending them. + * @library /testlibrary /test/lib + * @build HandshakeSuspendExitTest + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:GuaranteedSafepointInterval=1 -XX:+HandshakeALot HandshakeSuspendExitTest + */ + +public class HandshakeSuspendExitTest implements Runnable { + + static Thread[] _suspend_threads = new Thread[16]; + static volatile boolean _exit_now = false; + static java.util.concurrent.Semaphore _sem = new java.util.concurrent.Semaphore(0); + + @Override + public void run() { + _sem.release(); + while (!_exit_now) { + int i = 0; + for (Thread thr : _suspend_threads) { + if (i++ > _suspend_threads.length -2) { + // Leave last 2 threads running. + break; + } + if (Thread.currentThread() != thr) { + thr.suspend(); + thr.resume(); + } + } + } + _sem.release(); + } + + public static void main(String... args) throws Exception { + HandshakeSuspendExitTest test = new HandshakeSuspendExitTest(); + // Fire-up suspend thread. + for (int i = 0; i < _suspend_threads.length; i++) { + _suspend_threads[i] = new Thread(test); + } + for (int i = 0; i < _suspend_threads.length; i++) { + _suspend_threads[i].start(); + } + // Wait for all suspend thread starting to loop. + for (Thread thr : _suspend_threads) { + _sem.acquire(); + } + + // Fire-up exiting threads. + Thread[] exit_threads = new Thread[128]; + for (int i = 0; i < exit_threads.length; i++) { + exit_threads[i] = new Thread(new Runnable() { public void run() {} }); + exit_threads[i].start(); + } + + // Try to suspend them. + for (Thread thr : exit_threads) { + thr.suspend(); + } + for (Thread thr : exit_threads) { + thr.resume(); + } + + // Start exit and join. + _exit_now = true; + int waiting = _suspend_threads.length; + do { + for (Thread thr : _suspend_threads) { + thr.resume(); + } + while (_sem.tryAcquire()) { + --waiting; + } + } while (waiting > 0); + for (Thread thr : _suspend_threads) { + thr.join(); + } + for (Thread thr : exit_threads) { + thr.join(); + } + } +} diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java --- a/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java +++ b/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java @@ -62,6 +62,7 @@ true, "-Djava.library.path=" + lib, "-XX:+SafepointALot", + "-XX:+HandshakeALot", "-XX:GuaranteedSafepointInterval=20", "-Xlog:ergo*", "-XX:ParallelGCThreads=1", diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkSuspendExitTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkSuspendExitTest.java deleted file mode 100644 --- a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkSuspendExitTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2018, 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. - * - */ - -/* - * @test HandshakeWalkSuspendExitTest - * @summary This test tries to stress the handshakes with new and exiting threads while suspending them. - * @library /testlibrary /test/lib - * @build HandshakeWalkSuspendExitTest - * @run driver ClassFileInstaller sun.hotspot.WhiteBox - * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI HandshakeWalkSuspendExitTest - */ - -import jdk.test.lib.Asserts; -import sun.hotspot.WhiteBox; - -public class HandshakeWalkSuspendExitTest implements Runnable { - - static final int _test_threads = 8; - static final int _test_exit_threads = 128; - static Thread[] _threads = new Thread[_test_threads]; - static volatile boolean exit_now = false; - static java.util.concurrent.Semaphore _sem = new java.util.concurrent.Semaphore(0); - - @Override - public void run() { - WhiteBox wb = WhiteBox.getWhiteBox(); - while (!exit_now) { - _sem.release(); - // We only suspend threads on even index and not ourself. - // Otherwise we can accidentially suspend all threads. - for (int i = 0; i < _threads.length; i += 2) { - wb.handshakeWalkStack(null /* ignored */, true /* stackwalk all threads */); - if (Thread.currentThread() != _threads[i]) { - _threads[i].suspend(); - _threads[i].resume(); - } - } - for (int i = 0; i < _threads.length; i += 2) { - wb.handshakeWalkStack(_threads[i] /* thread to stackwalk */, false /* stackwalk one thread */); - if (Thread.currentThread() != _threads[i]) { - _threads[i].suspend(); - _threads[i].resume(); - } - } - } - } - - public static void main(String... args) throws Exception { - HandshakeWalkSuspendExitTest test = new HandshakeWalkSuspendExitTest(); - - for (int i = 0; i < _threads.length; i++) { - _threads[i] = new Thread(test); - _threads[i].start(); - } - for (int i = 0; i < _test_threads; i++) { - _sem.acquire(); - } - Thread[] exit_threads = new Thread[_test_exit_threads]; - for (int i = 0; i < _test_exit_threads; i++) { - exit_threads[i] = new Thread(new Runnable() { public void run() {} }); - exit_threads[i].start(); - } - exit_now = true; - for (int i = 0; i < _threads.length; i++) { - _threads[i].join(); - } - for (int i = 0; i < exit_threads.length; i++) { - exit_threads[i].join(); - } - } -}