8167108: inconsistent handling of SR_lock can lead to crashes
Summary: Add Thread Safe Memory Reclamation (Thread-SMR) mechanism.
Reviewed-by: coleenp, dcubed, dholmes, eosterlund, gthornbr, kbarrett, rehn, sspitsyn, stefank
Contributed-by: daniel.daugherty@oracle.com, erik.osterlund@oracle.com, robbin.ehn@oracle.com
--- a/src/hotspot/os/linux/os_linux.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/os/linux/os_linux.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -59,6 +59,7 @@
#include "runtime/stubRoutines.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/threadCritical.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/timer.hpp"
#include "semaphore_posix.hpp"
#include "services/attachListener.hpp"
@@ -1646,7 +1647,10 @@
//
// Dynamic loader will make all stacks executable after
// this function returns, and will not do that again.
- assert(Threads::first() == NULL, "no Java threads should exist yet.");
+#ifdef ASSERT
+ ThreadsListHandle tlh;
+ assert(tlh.length() == 0, "no Java threads should exist yet.");
+#endif
} else {
warning("You have loaded library %s which might have disabled stack guard. "
"The VM will try to fix the stack guard now.\n"
@@ -1874,16 +1878,13 @@
// may have been queued at the same time.
if (!_stack_is_executable) {
- JavaThread *jt = Threads::first();
-
- while (jt) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
if (!jt->stack_guard_zone_unused() && // Stack not yet fully initialized
jt->stack_guards_enabled()) { // No pending stack overflow exceptions
if (!os::guard_memory((char *)jt->stack_end(), jt->stack_guard_zone_size())) {
warning("Attempt to reguard stack yellow zone failed.");
}
}
- jt = jt->next();
}
}
--- a/src/hotspot/os/posix/os_posix.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/os/posix/os_posix.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -478,8 +478,7 @@
// interrupt support
void os::interrupt(Thread* thread) {
- assert(Thread::current() == thread || Threads_lock->owned_by_self(),
- "possibility of dangling Thread pointer");
+ debug_only(Thread::check_for_dangling_thread_pointer(thread);)
OSThread* osthread = thread->osthread();
@@ -499,12 +498,10 @@
ParkEvent * ev = thread->_ParkEvent ;
if (ev != NULL) ev->unpark() ;
-
}
bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
- assert(Thread::current() == thread || Threads_lock->owned_by_self(),
- "possibility of dangling Thread pointer");
+ debug_only(Thread::check_for_dangling_thread_pointer(thread);)
OSThread* osthread = thread->osthread();
--- a/src/hotspot/os/windows/os_windows.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/os/windows/os_windows.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -3490,9 +3490,7 @@
void os::hint_no_preempt() {}
void os::interrupt(Thread* thread) {
- assert(!thread->is_Java_thread() || Thread::current() == thread ||
- Threads_lock->owned_by_self(),
- "possibility of dangling Thread pointer");
+ debug_only(Thread::check_for_dangling_thread_pointer(thread);)
OSThread* osthread = thread->osthread();
osthread->set_interrupted(true);
@@ -3513,8 +3511,7 @@
bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
- assert(!thread->is_Java_thread() || Thread::current() == thread || Threads_lock->owned_by_self(),
- "possibility of dangling Thread pointer");
+ debug_only(Thread::check_for_dangling_thread_pointer(thread);)
OSThread* osthread = thread->osthread();
// There is no synchronization between the setting of the interrupt
--- a/src/hotspot/share/gc/g1/dirtyCardQueue.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/gc/g1/dirtyCardQueue.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -32,6 +32,7 @@
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
// Closure used for updating remembered sets and recording references that
// point into the collection set while the mutator is running.
@@ -319,7 +320,7 @@
clear();
// Since abandon is done only at safepoints, we can safely manipulate
// these queues.
- for (JavaThread* t = Threads::first(); t; t = t->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
t->dirty_card_queue().reset();
}
shared_dirty_card_queue()->reset();
@@ -338,7 +339,7 @@
int save_max_completed_queue = _max_completed_queue;
_max_completed_queue = max_jint;
assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
- for (JavaThread* t = Threads::first(); t; t = t->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
concatenate_log(t->dirty_card_queue());
}
concatenate_log(_shared_dirty_card_queue);
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -81,6 +81,7 @@
#include "runtime/atomic.hpp"
#include "runtime/init.hpp"
#include "runtime/orderAccess.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vmThread.hpp"
#include "utilities/align.hpp"
#include "utilities/globalDefinitions.hpp"
@@ -2653,11 +2654,9 @@
size_t G1CollectedHeap::pending_card_num() {
size_t extra_cards = 0;
- JavaThread *curr = Threads::first();
- while (curr != NULL) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *curr = jtiwh.next(); ) {
DirtyCardQueue& dcq = curr->dirty_card_queue();
extra_cards += dcq.size();
- curr = curr->next();
}
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
size_t buffer_size = dcqs.buffer_size();
--- a/src/hotspot/share/gc/g1/satbMarkQueue.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/gc/g1/satbMarkQueue.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -32,6 +32,7 @@
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vmThread.hpp"
SATBMarkQueue::SATBMarkQueue(SATBMarkQueueSet* qset, bool permanent) :
@@ -214,7 +215,7 @@
log_error(gc, verify)("Expected SATB active state: %s", expected_active ? "ACTIVE" : "INACTIVE");
log_error(gc, verify)("Actual SATB active states:");
log_error(gc, verify)(" Queue set: %s", is_active() ? "ACTIVE" : "INACTIVE");
- for (JavaThread* t = Threads::first(); t; t = t->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
log_error(gc, verify)(" Thread \"%s\" queue: %s", t->name(), t->satb_mark_queue().is_active() ? "ACTIVE" : "INACTIVE");
}
log_error(gc, verify)(" Shared queue: %s", shared_satb_queue()->is_active() ? "ACTIVE" : "INACTIVE");
@@ -228,7 +229,7 @@
}
// Verify thread queue states
- for (JavaThread* t = Threads::first(); t; t = t->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
if (t->satb_mark_queue().is_active() != expected_active) {
dump_active_states(expected_active);
guarantee(false, "Thread SATB queue has an unexpected active state");
@@ -249,14 +250,14 @@
verify_active_states(expected_active);
#endif // ASSERT
_all_active = active;
- for (JavaThread* t = Threads::first(); t; t = t->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
t->satb_mark_queue().set_active(active);
}
shared_satb_queue()->set_active(active);
}
void SATBMarkQueueSet::filter_thread_buffers() {
- for(JavaThread* t = Threads::first(); t; t = t->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
t->satb_mark_queue().filter();
}
shared_satb_queue()->filter();
@@ -309,7 +310,7 @@
i += 1;
}
- for (JavaThread* t = Threads::first(); t; t = t->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
jio_snprintf(buffer, SATB_PRINTER_BUFFER_SIZE, "Thread: %s", t->name());
t->satb_mark_queue().print(buffer);
}
@@ -341,8 +342,8 @@
}
assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
// So we can safely manipulate these queues.
- for (JavaThread* t = Threads::first(); t; t = t->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
t->satb_mark_queue().reset();
}
- shared_satb_queue()->reset();
+ shared_satb_queue()->reset();
}
--- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -29,6 +29,7 @@
#include "oops/oop.inline.hpp"
#include "runtime/atomic.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "utilities/align.hpp"
MutableNUMASpace::MutableNUMASpace(size_t alignment) : MutableSpace(alignment), _must_use_large_pages(false) {
@@ -287,7 +288,7 @@
FREE_C_HEAP_ARRAY(int, lgrp_ids);
if (changed) {
- for (JavaThread *thread = Threads::first(); thread; thread = thread->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
thread->set_lgrp_id(-1);
}
}
--- a/src/hotspot/share/gc/shared/collectedHeap.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/gc/shared/collectedHeap.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -40,6 +40,7 @@
#include "oops/oop.inline.hpp"
#include "runtime/init.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "services/heapDumper.hpp"
#include "utilities/align.hpp"
@@ -540,10 +541,11 @@
const bool deferred = _defer_initial_card_mark;
// The main thread starts allocating via a TLAB even before it
// has added itself to the threads list at vm boot-up.
- assert(!use_tlab || Threads::first() != NULL,
+ JavaThreadIteratorWithHandle jtiwh;
+ assert(!use_tlab || jtiwh.length() > 0,
"Attempt to fill tlabs before main thread has been added"
" to threads list is doomed to failure!");
- for (JavaThread *thread = Threads::first(); thread; thread = thread->next()) {
+ for (; JavaThread *thread = jtiwh.next(); ) {
if (use_tlab) thread->tlab().make_parsable(retire_tlabs);
#if COMPILER2_OR_JVMCI
// The deferred store barriers must all have been flushed to the
--- a/src/hotspot/share/gc/shared/gcLocker.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/gc/shared/gcLocker.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -29,6 +29,7 @@
#include "logging/log.hpp"
#include "runtime/atomic.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
volatile jint GCLocker::_jni_lock_count = 0;
volatile bool GCLocker::_needs_gc = false;
@@ -45,14 +46,16 @@
assert(!needs_gc() || _debug_jni_lock_count == _jni_lock_count, "must agree");
int count = 0;
// Count the number of threads with critical operations in progress
- for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) {
+ JavaThreadIteratorWithHandle jtiwh;
+ for (; JavaThread *thr = jtiwh.next(); ) {
if (thr->in_critical()) {
count++;
}
}
if (_jni_lock_count != count) {
log_error(gc, verify)("critical counts don't match: %d != %d", _jni_lock_count, count);
- for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) {
+ jtiwh.rewind();
+ for (; JavaThread *thr = jtiwh.next(); ) {
if (thr->in_critical()) {
log_error(gc, verify)(INTPTR_FORMAT " in_critical %d", p2i(thr), thr->in_critical());
}
--- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -30,6 +30,7 @@
#include "memory/universe.inline.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "utilities/copy.hpp"
// Thread-Local Edens support
@@ -48,7 +49,7 @@
void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() {
global_stats()->initialize();
- for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
thread->tlab().accumulate_statistics();
thread->tlab().initialize_statistics();
}
@@ -130,7 +131,7 @@
void ThreadLocalAllocBuffer::resize_all_tlabs() {
if (ResizeTLAB) {
- for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
thread->tlab().resize();
}
}
--- a/src/hotspot/share/jvmci/jvmciRuntime.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -42,6 +42,7 @@
#include "runtime/interfaceSupport.hpp"
#include "runtime/reflection.hpp"
#include "runtime/sharedRuntime.hpp"
+#include "runtime/threadSMR.hpp"
#include "utilities/debug.hpp"
#include "utilities/defaultStream.hpp"
#include "utilities/macros.hpp"
@@ -598,12 +599,13 @@
JRT_END
JRT_ENTRY(jboolean, JVMCIRuntime::thread_is_interrupted(JavaThread* thread, oopDesc* receiver, jboolean clear_interrupted))
- // Ensure that the C++ Thread and OSThread structures aren't freed before we operate.
- // This locking requires thread_in_vm which is why this method cannot be JRT_LEAF.
Handle receiverHandle(thread, receiver);
- MutexLockerEx ml(thread->threadObj() == (void*)receiver ? NULL : Threads_lock);
+ // A nested ThreadsListHandle may require the Threads_lock which
+ // requires thread_in_vm which is why this method cannot be JRT_LEAF.
+ ThreadsListHandle tlh;
+
JavaThread* receiverThread = java_lang_Thread::thread(receiverHandle());
- if (receiverThread == NULL) {
+ if (receiverThread == NULL || (EnableThreadSMRExtraValidityChecks && !tlh.includes(receiverThread))) {
// The other thread may exit during this process, which is ok so return false.
return JNI_FALSE;
} else {
--- a/src/hotspot/share/logging/logTag.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/logging/logTag.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -121,6 +121,7 @@
LOG_TAG(safepoint) \
LOG_TAG(scavenge) \
LOG_TAG(scrub) \
+ LOG_TAG(smr) \
LOG_TAG(stacktrace) \
LOG_TAG(stackwalk) \
LOG_TAG(start) \
--- a/src/hotspot/share/opto/idealGraphPrinter.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/opto/idealGraphPrinter.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2017, 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
@@ -29,6 +29,7 @@
#include "opto/machnode.hpp"
#include "opto/parse.hpp"
#include "runtime/threadCritical.hpp"
+#include "runtime/threadSMR.hpp"
#ifndef PRODUCT
@@ -91,8 +92,7 @@
}
void IdealGraphPrinter::clean_up() {
- JavaThread *p;
- for (p = Threads::first(); p; p = p->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *p = jtiwh.next(); ) {
if (p->is_Compiler_thread()) {
CompilerThread *c = (CompilerThread *)p;
IdealGraphPrinter *printer = c->ideal_graph_printer();
--- a/src/hotspot/share/prims/jni.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jni.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -4119,7 +4119,7 @@
thread->initialize_thread_current();
if (!os::create_attached_thread(thread)) {
- delete thread;
+ thread->smr_delete();
return JNI_ERR;
}
// Enable stack overflow checks
@@ -4250,7 +4250,7 @@
// (platform-dependent) methods where we do alternate stack
// maintenance work?)
thread->exit(false, JavaThread::jni_detach);
- delete thread;
+ thread->smr_delete();
HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN(JNI_OK);
return JNI_OK;
--- a/src/hotspot/share/prims/jvm.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvm.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -66,6 +66,7 @@
#include "runtime/perfData.hpp"
#include "runtime/reflection.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vm_operations.hpp"
#include "runtime/vm_version.hpp"
@@ -2737,16 +2738,12 @@
// java.lang.Thread //////////////////////////////////////////////////////////////////////////////
-// In most of the JVM Thread support functions we need to be sure to lock the Threads_lock
-// to prevent the target thread from exiting after we have a pointer to the C++ Thread or
-// OSThread objects. The exception to this rule is when the target object is the thread
-// doing the operation, in which case we know that the thread won't exit until the
-// operation is done (all exits being voluntary). There are a few cases where it is
-// rather silly to do operations on yourself, like resuming yourself or asking whether
-// you are alive. While these can still happen, they are not subject to deadlocks if
-// the lock is held while the operation occurs (this is not the case for suspend, for
-// instance), and are very unlikely. Because IsAlive needs to be fast and its
-// implementation is local to this file, we always lock Threads_lock for that one.
+// In most of the JVM thread support functions we need to access the
+// thread through a ThreadsListHandle to prevent it from exiting and
+// being reclaimed while we try to operate on it. The exceptions to this
+// rule are when operating on the current thread, or if the monitor of
+// the target java.lang.Thread is locked at the Java level - in both
+// cases the target cannot exit.
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
@@ -2821,7 +2818,7 @@
if (native_thread->osthread() == NULL) {
// No one should hold a reference to the 'native_thread'.
- delete native_thread;
+ native_thread->smr_delete();
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
@@ -2835,41 +2832,45 @@
JVM_END
+
// JVM_Stop is implemented using a VM_Operation, so threads are forced to safepoints
// before the quasi-asynchronous exception is delivered. This is a little obtrusive,
// but is thought to be reliable and simple. In the case, where the receiver is the
-// same thread as the sender, no safepoint is needed.
+// same thread as the sender, no VM_Operation is needed.
JVM_ENTRY(void, JVM_StopThread(JNIEnv* env, jobject jthread, jobject throwable))
JVMWrapper("JVM_StopThread");
+ // A nested ThreadsListHandle will grab the Threads_lock so create
+ // tlh before we resolve throwable.
+ ThreadsListHandle tlh(thread);
oop java_throwable = JNIHandles::resolve(throwable);
if (java_throwable == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
- oop java_thread = JNIHandles::resolve_non_null(jthread);
- JavaThread* receiver = java_lang_Thread::thread(java_thread);
- Events::log_exception(JavaThread::current(),
+ oop java_thread = NULL;
+ JavaThread* receiver = NULL;
+ bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, &java_thread);
+ Events::log_exception(thread,
"JVM_StopThread thread JavaThread " INTPTR_FORMAT " as oop " INTPTR_FORMAT " [exception " INTPTR_FORMAT "]",
p2i(receiver), p2i((address)java_thread), p2i(throwable));
- // First check if thread is alive
- if (receiver != NULL) {
- // Check if exception is getting thrown at self (use oop equality, since the
- // target object might exit)
- if (java_thread == thread->threadObj()) {
+
+ if (is_alive) {
+ // jthread refers to a live JavaThread.
+ if (thread == receiver) {
+ // Exception is getting thrown at self so no VM_Operation needed.
THROW_OOP(java_throwable);
} else {
- // Enques a VM_Operation to stop all threads and then deliver the exception...
- Thread::send_async_exception(java_thread, JNIHandles::resolve(throwable));
+ // Use a VM_Operation to throw the exception.
+ Thread::send_async_exception(java_thread, java_throwable);
}
- }
- else {
+ } else {
// Either:
// - target thread has not been started before being stopped, or
// - target thread already terminated
// We could read the threadStatus to determine which case it is
// but that is overkill as it doesn't matter. We must set the
// stillborn flag for the first case, and if the thread has already
- // exited setting this flag has no affect
+ // exited setting this flag has no effect.
java_lang_Thread::set_stillborn(java_thread);
}
JVM_END
@@ -2885,12 +2886,12 @@
JVM_ENTRY(void, JVM_SuspendThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_SuspendThread");
- oop java_thread = JNIHandles::resolve_non_null(jthread);
- JavaThread* receiver = java_lang_Thread::thread(java_thread);
-
- if (receiver != NULL) {
- // thread has run and has not exited (still on threads list)
-
+
+ ThreadsListHandle tlh(thread);
+ JavaThread* receiver = NULL;
+ bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
+ if (is_alive) {
+ // jthread refers to a live JavaThread.
{
MutexLockerEx ml(receiver->SR_lock(), Mutex::_no_safepoint_check_flag);
if (receiver->is_external_suspend()) {
@@ -2922,30 +2923,49 @@
JVM_ENTRY(void, JVM_ResumeThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_ResumeThread");
- // Ensure that the C++ Thread and OSThread structures aren't freed before we operate.
- // We need to *always* get the threads lock here, since this operation cannot be allowed during
- // a safepoint. The safepoint code relies on suspending a thread to examine its state. If other
- // threads randomly resumes threads, then a thread might not be suspended when the safepoint code
- // looks at it.
- MutexLocker ml(Threads_lock);
- JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
- if (thr != NULL) {
- // the thread has run and is not in the process of exiting
- thr->java_resume();
+
+ ThreadsListHandle tlh(thread);
+ JavaThread* receiver = NULL;
+ bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
+ if (is_alive) {
+ // jthread refers to a live JavaThread.
+
+ // This is the original comment for this Threads_lock grab:
+ // We need to *always* get the threads lock here, since this operation cannot be allowed during
+ // a safepoint. The safepoint code relies on suspending a thread to examine its state. If other
+ // threads randomly resumes threads, then a thread might not be suspended when the safepoint code
+ // looks at it.
+ //
+ // The above comment dates back to when we had both internal and
+ // external suspend APIs that shared a common underlying mechanism.
+ // External suspend is now entirely cooperative and doesn't share
+ // anything with internal suspend. That said, there are some
+ // assumptions in the VM that an external resume grabs the
+ // Threads_lock. We can't drop the Threads_lock grab here until we
+ // resolve the assumptions that exist elsewhere.
+ //
+ MutexLocker ml(Threads_lock);
+ receiver->java_resume();
}
JVM_END
JVM_ENTRY(void, JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio))
JVMWrapper("JVM_SetThreadPriority");
- // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
- MutexLocker ml(Threads_lock);
- oop java_thread = JNIHandles::resolve_non_null(jthread);
+
+ ThreadsListHandle tlh(thread);
+ oop java_thread = NULL;
+ JavaThread* receiver = NULL;
+ bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, &java_thread);
java_lang_Thread::set_priority(java_thread, (ThreadPriority)prio);
- JavaThread* thr = java_lang_Thread::thread(java_thread);
- if (thr != NULL) { // Thread not yet started; priority pushed down when it is
- Thread::set_priority(thr, (ThreadPriority)prio);
+
+ if (is_alive) {
+ // jthread refers to a live JavaThread.
+ Thread::set_priority(receiver, (ThreadPriority)prio);
}
+ // Implied else: If the JavaThread hasn't started yet, then the
+ // priority set in the java.lang.Thread object above will be pushed
+ // down when it does start.
JVM_END
@@ -3016,67 +3036,39 @@
JVM_ENTRY(jint, JVM_CountStackFrames(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_CountStackFrames");
- // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
- oop java_thread = JNIHandles::resolve_non_null(jthread);
- bool throw_illegal_thread_state = false;
+ uint32_t debug_bits = 0;
+ ThreadsListHandle tlh(thread);
+ JavaThread* receiver = NULL;
+ bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
int count = 0;
-
- {
- MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
- // We need to re-resolve the java_thread, since a GC might have happened during the
- // acquire of the lock
- JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
-
- if (thr == NULL) {
- // do nothing
- } else if(! thr->is_external_suspend() || ! thr->frame_anchor()->walkable()) {
- // Check whether this java thread has been suspended already. If not, throws
- // IllegalThreadStateException. We defer to throw that exception until
- // Threads_lock is released since loading exception class has to leave VM.
- // The correct way to test a thread is actually suspended is
- // wait_for_ext_suspend_completion(), but we can't call that while holding
- // the Threads_lock. The above tests are sufficient for our purposes
- // provided the walkability of the stack is stable - which it isn't
- // 100% but close enough for most practical purposes.
- throw_illegal_thread_state = true;
+ if (is_alive) {
+ // jthread refers to a live JavaThread.
+ if (receiver->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) {
+ // Count all java activation, i.e., number of vframes.
+ for (vframeStream vfst(receiver); !vfst.at_end(); vfst.next()) {
+ // Native frames are not counted.
+ if (!vfst.method()->is_native()) count++;
+ }
} else {
- // Count all java activation, i.e., number of vframes
- for(vframeStream vfst(thr); !vfst.at_end(); vfst.next()) {
- // Native frames are not counted
- if (!vfst.method()->is_native()) count++;
- }
+ THROW_MSG_0(vmSymbols::java_lang_IllegalThreadStateException(),
+ "this thread is not suspended");
}
}
-
- if (throw_illegal_thread_state) {
- THROW_MSG_0(vmSymbols::java_lang_IllegalThreadStateException(),
- "this thread is not suspended");
- }
+ // Implied else: if JavaThread is not alive simply return a count of 0.
+
return count;
JVM_END
-// Consider: A better way to implement JVM_Interrupt() is to acquire
-// Threads_lock to resolve the jthread into a Thread pointer, fetch
-// Thread->platformevent, Thread->native_thr, Thread->parker, etc.,
-// drop Threads_lock, and the perform the unpark() and thr_kill() operations
-// outside the critical section. Threads_lock is hot so we want to minimize
-// the hold-time. A cleaner interface would be to decompose interrupt into
-// two steps. The 1st phase, performed under Threads_lock, would return
-// a closure that'd be invoked after Threads_lock was dropped.
-// This tactic is safe as PlatformEvent and Parkers are type-stable (TSM) and
-// admit spurious wakeups.
JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_Interrupt");
- // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
- oop java_thread = JNIHandles::resolve_non_null(jthread);
- MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
- // We need to re-resolve the java_thread, since a GC might have happened during the
- // acquire of the lock
- JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
- if (thr != NULL) {
- Thread::interrupt(thr);
+ ThreadsListHandle tlh(thread);
+ JavaThread* receiver = NULL;
+ bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
+ if (is_alive) {
+ // jthread refers to a live JavaThread.
+ Thread::interrupt(receiver);
}
JVM_END
@@ -3084,16 +3076,14 @@
JVM_QUICK_ENTRY(jboolean, JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clear_interrupted))
JVMWrapper("JVM_IsInterrupted");
- // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
- oop java_thread = JNIHandles::resolve_non_null(jthread);
- MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
- // We need to re-resolve the java_thread, since a GC might have happened during the
- // acquire of the lock
- JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
- if (thr == NULL) {
+ ThreadsListHandle tlh(thread);
+ JavaThread* receiver = NULL;
+ bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
+ if (is_alive) {
+ // jthread refers to a live JavaThread.
+ return (jboolean) Thread::is_interrupted(receiver, clear_interrupted != 0);
+ } else {
return JNI_FALSE;
- } else {
- return (jboolean) Thread::is_interrupted(thr, clear_interrupted != 0);
}
JVM_END
@@ -3122,14 +3112,16 @@
JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name))
JVMWrapper("JVM_SetNativeThreadName");
- ResourceMark rm(THREAD);
+
+ // We don't use a ThreadsListHandle here because the current thread
+ // must be alive.
oop java_thread = JNIHandles::resolve_non_null(jthread);
JavaThread* thr = java_lang_Thread::thread(java_thread);
- // Thread naming only supported for the current thread, doesn't work for
- // target threads.
- if (Thread::current() == thr && !thr->has_attached_via_jni()) {
+ if (thread == thr && !thr->has_attached_via_jni()) {
+ // Thread naming is only supported for the current thread and
// we don't set the name of an attached thread to avoid stepping
- // on other programs
+ // on other programs.
+ ResourceMark rm(thread);
const char *thread_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name));
os::set_native_thread_name(thread_name);
}
@@ -3671,6 +3663,8 @@
thread_handle_array->append(h);
}
+ // The JavaThread references in thread_handle_array are validated
+ // in VM_ThreadDump::doit().
Handle stacktraces = ThreadService::dump_stack_traces(thread_handle_array, num_threads, CHECK_NULL);
return (jobjectArray)JNIHandles::make_local(env, stacktraces());
--- a/src/hotspot/share/prims/jvmtiEnter.xsl Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiEnter.xsl Wed Nov 22 17:54:50 2017 -0800
@@ -45,6 +45,7 @@
# include "prims/jvmtiEnter.hpp"
# include "prims/jvmtiRawMonitor.hpp"
# include "prims/jvmtiUtil.hpp"
+# include "runtime/threadSMR.hpp"
</xsl:text>
@@ -769,47 +770,27 @@
<xsl:template match="jthread" mode="dochecksbody">
<xsl:param name="name"/>
- <xsl:text> oop thread_oop = JNIHandles::resolve_external_guard(</xsl:text>
+ <xsl:text> err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), </xsl:text>
<xsl:value-of select="$name"/>
- <xsl:text>);
- if (thread_oop == NULL) {
+ <xsl:text>, &java_thread, NULL);
+ if (err != JVMTI_ERROR_NONE) {
</xsl:text>
<xsl:apply-templates select=".." mode="traceError">
- <xsl:with-param name="err">JVMTI_ERROR_INVALID_THREAD</xsl:with-param>
- <xsl:with-param name="comment"> - jthread resolved to NULL - jthread = " PTR_FORMAT "</xsl:with-param>
+ <xsl:with-param name="err">err</xsl:with-param>
+ <xsl:with-param name="comment"> - jthread did not convert to a JavaThread - jthread = " PTR_FORMAT "</xsl:with-param>
<xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param>
</xsl:apply-templates>
<xsl:text>
}
- if (!thread_oop->is_a(SystemDictionary::Thread_klass())) {
</xsl:text>
- <xsl:apply-templates select=".." mode="traceError">
- <xsl:with-param name="err">JVMTI_ERROR_INVALID_THREAD</xsl:with-param>
- <xsl:with-param name="comment"> - oop is not a thread - jthread = " PTR_FORMAT "</xsl:with-param>
- <xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param>
- </xsl:apply-templates>
- <xsl:text>
- }
- java_thread = java_lang_Thread::thread(thread_oop);
- if (java_thread == NULL) {
-</xsl:text>
- <xsl:apply-templates select=".." mode="traceError">
- <xsl:with-param name="err">
- <xsl:text>JVMTI_ERROR_THREAD_NOT_ALIVE</xsl:text>
- </xsl:with-param>
- <xsl:with-param name="comment"> - not a Java thread - jthread = " PTR_FORMAT "</xsl:with-param>
- <xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param>
- </xsl:apply-templates>
- <xsl:text>
- }
-</xsl:text>
</xsl:template>
<xsl:template match="jthread" mode="dochecks">
<xsl:param name="name"/>
<!-- If we convert and test threads -->
<xsl:if test="count(@impl)=0 or not(contains(@impl,'noconvert'))">
- <xsl:text> JavaThread* java_thread;
+ <xsl:text> JavaThread* java_thread = NULL;
+ ThreadsListHandle tlh(this_thread);
</xsl:text>
<xsl:choose>
<xsl:when test="count(@null)=0">
--- a/src/hotspot/share/prims/jvmtiEnv.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -62,6 +62,7 @@
#include "runtime/reflectionUtils.hpp"
#include "runtime/signature.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/timerTrace.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vmThread.hpp"
@@ -162,7 +163,6 @@
*data_ptr = (state == NULL) ? NULL :
state->env_thread_state(this)->get_agent_thread_local_storage_data();
} else {
-
// jvmti_GetThreadLocalStorage is "in native" and doesn't transition
// the thread to _thread_in_vm. However, when the TLS for a thread
// other than the current thread is required we need to transition
@@ -172,17 +172,13 @@
VM_ENTRY_BASE(jvmtiError, JvmtiEnv::GetThreadLocalStorage , current_thread)
debug_only(VMNativeEntryWrapper __vew;)
- oop thread_oop = JNIHandles::resolve_external_guard(thread);
- if (thread_oop == NULL) {
- return JVMTI_ERROR_INVALID_THREAD;
+ JavaThread* java_thread = NULL;
+ ThreadsListHandle tlh(current_thread);
+ jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, NULL);
+ if (err != JVMTI_ERROR_NONE) {
+ return err;
}
- if (!thread_oop->is_a(SystemDictionary::Thread_klass())) {
- return JVMTI_ERROR_INVALID_THREAD;
- }
- JavaThread* java_thread = java_lang_Thread::thread(thread_oop);
- if (java_thread == NULL) {
- return JVMTI_ERROR_THREAD_NOT_ALIVE;
- }
+
JvmtiThreadState* state = java_thread->jvmti_thread_state();
*data_ptr = (state == NULL) ? NULL :
state->env_thread_state(this)->get_agent_thread_local_storage_data();
@@ -518,43 +514,61 @@
// event_thread - NULL is a valid value, must be checked
jvmtiError
JvmtiEnv::SetEventNotificationMode(jvmtiEventMode mode, jvmtiEvent event_type, jthread event_thread, ...) {
- JavaThread* java_thread = NULL;
- if (event_thread != NULL) {
- oop thread_oop = JNIHandles::resolve_external_guard(event_thread);
- if (thread_oop == NULL) {
- return JVMTI_ERROR_INVALID_THREAD;
+ if (event_thread == NULL) {
+ // Can be called at Agent_OnLoad() time with event_thread == NULL
+ // when Thread::current() does not work yet so we cannot create a
+ // ThreadsListHandle that is common to both thread-specific and
+ // global code paths.
+
+ // event_type must be valid
+ if (!JvmtiEventController::is_valid_event_type(event_type)) {
+ return JVMTI_ERROR_INVALID_EVENT_TYPE;
+ }
+
+ bool enabled = (mode == JVMTI_ENABLE);
+
+ // assure that needed capabilities are present
+ if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) {
+ return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
+ }
+
+ if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
+ record_class_file_load_hook_enabled();
}
- if (!thread_oop->is_a(SystemDictionary::Thread_klass())) {
- return JVMTI_ERROR_INVALID_THREAD;
+ JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled);
+ } else {
+ // We have a specified event_thread.
+
+ JavaThread* java_thread = NULL;
+ ThreadsListHandle tlh;
+ jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), event_thread, &java_thread, NULL);
+ if (err != JVMTI_ERROR_NONE) {
+ return err;
+ }
+
+ // event_type must be valid
+ if (!JvmtiEventController::is_valid_event_type(event_type)) {
+ return JVMTI_ERROR_INVALID_EVENT_TYPE;
}
- java_thread = java_lang_Thread::thread(thread_oop);
- if (java_thread == NULL) {
- return JVMTI_ERROR_THREAD_NOT_ALIVE;
+
+ // global events cannot be controlled at thread level.
+ if (JvmtiEventController::is_global_event(event_type)) {
+ return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
+
+ bool enabled = (mode == JVMTI_ENABLE);
+
+ // assure that needed capabilities are present
+ if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) {
+ return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
+ }
+
+ if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
+ record_class_file_load_hook_enabled();
+ }
+ JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled);
}
- // event_type must be valid
- if (!JvmtiEventController::is_valid_event_type(event_type)) {
- return JVMTI_ERROR_INVALID_EVENT_TYPE;
- }
-
- // global events cannot be controlled at thread level.
- if (java_thread != NULL && JvmtiEventController::is_global_event(event_type)) {
- return JVMTI_ERROR_ILLEGAL_ARGUMENT;
- }
-
- bool enabled = (mode == JVMTI_ENABLE);
-
- // assure that needed capabilities are present
- if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) {
- return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
- }
-
- if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
- record_class_file_load_hook_enabled();
- }
- JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled);
-
return JVMTI_ERROR_NONE;
} /* end SetEventNotificationMode */
@@ -817,35 +831,45 @@
// thread_state_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetThreadState(jthread thread, jint* thread_state_ptr) {
- jint state;
- oop thread_oop;
- JavaThread* thr;
+ JavaThread* current_thread = JavaThread::current();
+ JavaThread* java_thread = NULL;
+ oop thread_oop = NULL;
+ ThreadsListHandle tlh(current_thread);
if (thread == NULL) {
- thread_oop = JavaThread::current()->threadObj();
+ java_thread = current_thread;
+ thread_oop = java_thread->threadObj();
+
+ if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
+ return JVMTI_ERROR_INVALID_THREAD;
+ }
} else {
- thread_oop = JNIHandles::resolve_external_guard(thread);
- }
-
- if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
- return JVMTI_ERROR_INVALID_THREAD;
+ jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
+ if (err != JVMTI_ERROR_NONE) {
+ // We got an error code so we don't have a JavaThread *, but
+ // only return an error from here if we didn't get a valid
+ // thread_oop.
+ if (thread_oop == NULL) {
+ return err;
+ }
+ // We have a valid thread_oop so we can return some thread state.
+ }
}
// get most state bits
- state = (jint)java_lang_Thread::get_thread_status(thread_oop);
-
- // add more state bits
- thr = java_lang_Thread::thread(thread_oop);
- if (thr != NULL) {
- JavaThreadState jts = thr->thread_state();
-
- if (thr->is_being_ext_suspended()) {
+ jint state = (jint)java_lang_Thread::get_thread_status(thread_oop);
+
+ if (java_thread != NULL) {
+ // We have a JavaThread* so add more state bits.
+ JavaThreadState jts = java_thread->thread_state();
+
+ if (java_thread->is_being_ext_suspended()) {
state |= JVMTI_THREAD_STATE_SUSPENDED;
}
if (jts == _thread_in_native) {
state |= JVMTI_THREAD_STATE_IN_NATIVE;
}
- OSThread* osThread = thr->osthread();
+ OSThread* osThread = java_thread->osthread();
if (osThread != NULL && osThread->interrupted()) {
state |= JVMTI_THREAD_STATE_INTERRUPTED;
}
@@ -891,7 +915,6 @@
thread_objs[i] = Handle(tle.get_threadObj(i));
}
- // have to make global handles outside of Threads_lock
jthread *jthreads = new_jthreadArray(nthreads, thread_objs);
NULL_CHECK(jthreads, JVMTI_ERROR_OUT_OF_MEMORY);
@@ -935,19 +958,12 @@
jvmtiError
JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvmtiError* results) {
int needSafepoint = 0; // > 0 if we need a safepoint
+ ThreadsListHandle tlh;
for (int i = 0; i < request_count; i++) {
- JavaThread *java_thread = get_JavaThread(request_list[i]);
- if (java_thread == NULL) {
- results[i] = JVMTI_ERROR_INVALID_THREAD;
- continue;
- }
- // the thread has not yet run or has exited (not on threads list)
- if (java_thread->threadObj() == NULL) {
- results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE;
- continue;
- }
- if (java_lang_Thread::thread(java_thread->threadObj()) == NULL) {
- results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE;
+ JavaThread *java_thread = NULL;
+ jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), request_list[i], &java_thread, NULL);
+ if (err != JVMTI_ERROR_NONE) {
+ results[i] = err;
continue;
}
// don't allow hidden thread suspend request.
@@ -1018,10 +1034,12 @@
// results - pre-checked for NULL
jvmtiError
JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmtiError* results) {
+ ThreadsListHandle tlh;
for (int i = 0; i < request_count; i++) {
- JavaThread *java_thread = get_JavaThread(request_list[i]);
- if (java_thread == NULL) {
- results[i] = JVMTI_ERROR_INVALID_THREAD;
+ JavaThread* java_thread = NULL;
+ jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), request_list[i], &java_thread, NULL);
+ if (err != JVMTI_ERROR_NONE) {
+ results[i] = err;
continue;
}
// don't allow hidden thread resume request.
@@ -1039,7 +1057,7 @@
continue;
}
- results[i] = JVMTI_ERROR_NONE; // indicate successful suspend
+ results[i] = JVMTI_ERROR_NONE; // indicate successful resume
}
// per-thread resume results returned via results parameter
return JVMTI_ERROR_NONE;
@@ -1064,20 +1082,14 @@
// thread - NOT pre-checked
jvmtiError
JvmtiEnv::InterruptThread(jthread thread) {
- oop thread_oop = JNIHandles::resolve_external_guard(thread);
- if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass()))
- return JVMTI_ERROR_INVALID_THREAD;
-
+ // TODO: this is very similar to JVM_Interrupt(); share code in future
JavaThread* current_thread = JavaThread::current();
-
- // Todo: this is a duplicate of JVM_Interrupt; share code in future
- // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
- MutexLockerEx ml(current_thread->threadObj() == thread_oop ? NULL : Threads_lock);
- // We need to re-resolve the java_thread, since a GC might have happened during the
- // acquire of the lock
-
- JavaThread* java_thread = java_lang_Thread::thread(JNIHandles::resolve_external_guard(thread));
- NULL_CHECK(java_thread, JVMTI_ERROR_THREAD_NOT_ALIVE);
+ JavaThread* java_thread = NULL;
+ ThreadsListHandle tlh(current_thread);
+ jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, NULL);
+ if (err != JVMTI_ERROR_NONE) {
+ return err;
+ }
Thread::interrupt(java_thread);
@@ -1094,16 +1106,28 @@
HandleMark hm;
JavaThread* current_thread = JavaThread::current();
+ ThreadsListHandle tlh(current_thread);
// if thread is NULL the current thread is used
- oop thread_oop;
+ oop thread_oop = NULL;
if (thread == NULL) {
thread_oop = current_thread->threadObj();
+ if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
+ return JVMTI_ERROR_INVALID_THREAD;
+ }
} else {
- thread_oop = JNIHandles::resolve_external_guard(thread);
+ JavaThread* java_thread = NULL;
+ jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
+ if (err != JVMTI_ERROR_NONE) {
+ // We got an error code so we don't have a JavaThread *, but
+ // only return an error from here if we didn't get a valid
+ // thread_oop.
+ if (thread_oop == NULL) {
+ return err;
+ }
+ // We have a valid thread_oop so we can return some thread info.
+ }
}
- if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass()))
- return JVMTI_ERROR_INVALID_THREAD;
Handle thread_obj(current_thread, thread_oop);
Handle name;
@@ -1272,17 +1296,31 @@
// arg - NULL is a valid value, must be checked
jvmtiError
JvmtiEnv::RunAgentThread(jthread thread, jvmtiStartFunction proc, const void* arg, jint priority) {
- oop thread_oop = JNIHandles::resolve_external_guard(thread);
- if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
+ JavaThread* current_thread = JavaThread::current();
+
+ JavaThread* java_thread = NULL;
+ oop thread_oop = NULL;
+ ThreadsListHandle tlh(current_thread);
+ jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
+ if (err != JVMTI_ERROR_NONE) {
+ // We got an error code so we don't have a JavaThread *, but
+ // only return an error from here if we didn't get a valid
+ // thread_oop.
+ if (thread_oop == NULL) {
+ return err;
+ }
+ // We have a valid thread_oop.
+ }
+
+ if (java_thread != NULL) {
+ // 'thread' refers to an existing JavaThread.
return JVMTI_ERROR_INVALID_THREAD;
}
+
if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) {
return JVMTI_ERROR_INVALID_PRIORITY;
}
- //Thread-self
- JavaThread* current_thread = JavaThread::current();
-
Handle thread_hndl(current_thread, thread_oop);
{
MutexLocker mu(Threads_lock); // grab Threads_lock
@@ -1292,7 +1330,9 @@
// At this point it may be possible that no osthread was created for the
// JavaThread due to lack of memory.
if (new_thread == NULL || new_thread->osthread() == NULL) {
- if (new_thread) delete new_thread;
+ if (new_thread != NULL) {
+ new_thread->smr_delete();
+ }
return JVMTI_ERROR_OUT_OF_MEMORY;
}
@@ -1394,36 +1434,53 @@
int ngroups = 0;
int hidden_threads = 0;
- ResourceMark rm;
- HandleMark hm;
+ ResourceMark rm(current_thread);
+ HandleMark hm(current_thread);
Handle group_hdl(current_thread, group_obj);
- { MutexLocker mu(Threads_lock);
+ { // Cannot allow thread or group counts to change.
+ MutexLocker mu(Threads_lock);
nthreads = java_lang_ThreadGroup::nthreads(group_hdl());
ngroups = java_lang_ThreadGroup::ngroups(group_hdl());
if (nthreads > 0) {
+ ThreadsListHandle tlh(current_thread);
objArrayOop threads = java_lang_ThreadGroup::threads(group_hdl());
assert(nthreads <= threads->length(), "too many threads");
thread_objs = NEW_RESOURCE_ARRAY(Handle,nthreads);
for (int i=0, j=0; i<nthreads; i++) {
oop thread_obj = threads->obj_at(i);
assert(thread_obj != NULL, "thread_obj is NULL");
- JavaThread *javathread = java_lang_Thread::thread(thread_obj);
- // Filter out hidden java threads.
- if (javathread != NULL && javathread->is_hidden_from_external_view()) {
- hidden_threads++;
- continue;
+ JavaThread *java_thread = NULL;
+ jvmtiError err = JvmtiExport::cv_oop_to_JavaThread(tlh.list(), thread_obj, &java_thread);
+ if (err == JVMTI_ERROR_NONE) {
+ // Have a valid JavaThread*.
+ if (java_thread->is_hidden_from_external_view()) {
+ // Filter out hidden java threads.
+ hidden_threads++;
+ continue;
+ }
+ } else {
+ // We couldn't convert thread_obj into a JavaThread*.
+ if (err == JVMTI_ERROR_INVALID_THREAD) {
+ // The thread_obj does not refer to a java.lang.Thread object
+ // so skip it.
+ hidden_threads++;
+ continue;
+ }
+ // We have a valid thread_obj, but no JavaThread*; the caller
+ // can still have limited use for the thread_obj.
}
thread_objs[j++] = Handle(current_thread, thread_obj);
}
nthreads -= hidden_threads;
- }
+ } // ThreadsListHandle is destroyed here.
+
if (ngroups > 0) {
objArrayOop groups = java_lang_ThreadGroup::groups(group_hdl());
- assert(ngroups <= groups->length(), "too many threads");
+ assert(ngroups <= groups->length(), "too many groups");
group_objs = NEW_RESOURCE_ARRAY(Handle,ngroups);
for (int i=0; i<ngroups; i++) {
oop group_obj = groups->obj_at(i);
@@ -1556,7 +1613,7 @@
}
// Check if java_thread is fully suspended
- if (!is_thread_fully_suspended(java_thread, true /* wait for suspend completion */, &debug_bits)) {
+ if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
// Check to see if a PopFrame was already in progress
@@ -1686,8 +1743,8 @@
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
- if (!JvmtiEnv::is_thread_fully_suspended(java_thread, true, &debug_bits)) {
- return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
+ if (!java_thread->is_thread_fully_suspended(true, &debug_bits)) {
+ return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
if (TraceJVMTICalls) {
--- a/src/hotspot/share/prims/jvmtiEnvBase.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -44,6 +44,7 @@
#include "runtime/objectMonitor.inline.hpp"
#include "runtime/signature.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vframe_hp.hpp"
#include "runtime/vmThread.hpp"
@@ -487,37 +488,6 @@
}
}
-// Called from JVMTI entry points which perform stack walking. If the
-// associated JavaThread is the current thread, then wait_for_suspend
-// is not used. Otherwise, it determines if we should wait for the
-// "other" thread to complete external suspension. (NOTE: in future
-// releases the suspension mechanism should be reimplemented so this
-// is not necessary.)
-//
-bool
-JvmtiEnvBase::is_thread_fully_suspended(JavaThread* thr, bool wait_for_suspend, uint32_t *bits) {
- // "other" threads require special handling
- if (thr != JavaThread::current()) {
- if (wait_for_suspend) {
- // We are allowed to wait for the external suspend to complete
- // so give the other thread a chance to get suspended.
- if (!thr->wait_for_ext_suspend_completion(SuspendRetryCount,
- SuspendRetryDelay, bits)) {
- // didn't make it so let the caller know
- return false;
- }
- }
- // We aren't allowed to wait for the external suspend to complete
- // so if the other thread isn't externally suspended we need to
- // let the caller know.
- else if (!thr->is_ext_suspend_completed_with_lock(bits)) {
- return false;
- }
- }
-
- return true;
-}
-
// In the fullness of time, all users of the method should instead
// directly use allocate, besides being cleaner and faster, this will
@@ -560,19 +530,6 @@
return (jthreadGroup *) new_jobjectArray(length,handles);
}
-
-JavaThread *
-JvmtiEnvBase::get_JavaThread(jthread jni_thread) {
- oop t = JNIHandles::resolve_external_guard(jni_thread);
- if (t == NULL || !t->is_a(SystemDictionary::Thread_klass())) {
- return NULL;
- }
- // The following returns NULL if the thread has not yet run or is in
- // process of exiting
- return java_lang_Thread::thread(t);
-}
-
-
// return the vframe on the specified thread and depth, NULL if no such frame
vframe*
JvmtiEnvBase::vframeFor(JavaThread* java_thread, jint depth) {
@@ -670,7 +627,7 @@
uint32_t debug_bits = 0;
#endif
assert((SafepointSynchronize::is_at_safepoint() ||
- is_thread_fully_suspended(java_thread, false, &debug_bits)),
+ java_thread->is_thread_fully_suspended(false, &debug_bits)),
"at safepoint or target thread is suspended");
oop obj = NULL;
ObjectMonitor *mon = java_thread->current_waiting_monitor();
@@ -709,7 +666,7 @@
uint32_t debug_bits = 0;
#endif
assert((SafepointSynchronize::is_at_safepoint() ||
- is_thread_fully_suspended(java_thread, false, &debug_bits)),
+ java_thread->is_thread_fully_suspended(false, &debug_bits)),
"at safepoint or target thread is suspended");
if (java_thread->has_last_Java_frame()) {
@@ -831,7 +788,7 @@
uint32_t debug_bits = 0;
#endif
assert((SafepointSynchronize::is_at_safepoint() ||
- is_thread_fully_suspended(java_thread, false, &debug_bits)),
+ java_thread->is_thread_fully_suspended(false, &debug_bits)),
"at safepoint or target thread is suspended");
int count = 0;
if (java_thread->has_last_Java_frame()) {
@@ -914,7 +871,7 @@
uint32_t debug_bits = 0;
#endif
assert((SafepointSynchronize::is_at_safepoint() ||
- is_thread_fully_suspended(java_thread, false, &debug_bits)),
+ java_thread->is_thread_fully_suspended(false, &debug_bits)),
"at safepoint or target thread is suspended");
Thread* current_thread = Thread::current();
ResourceMark rm(current_thread);
@@ -976,7 +933,7 @@
// first derive the object's owner and entry_count (if any)
{
// Revoke any biases before querying the mark word
- if (SafepointSynchronize::is_at_safepoint()) {
+ if (at_safepoint) {
BiasedLocking::revoke_at_safepoint(hobj);
} else {
BiasedLocking::revoke_and_rebias(hobj, false, calling_thread);
@@ -1008,11 +965,11 @@
}
if (owner != NULL) {
+ // Use current thread since function can be called from a
+ // JavaThread or the VMThread.
+ ThreadsListHandle tlh;
// This monitor is owned so we have to find the owning JavaThread.
- // Since owning_thread_from_monitor_owner() grabs a lock, GC can
- // move our object at this point. However, our owner value is safe
- // since it is either the Lock word on a stack or a JavaThread *.
- owning_thread = Threads::owning_thread_from_monitor_owner(owner, !at_safepoint);
+ owning_thread = Threads::owning_thread_from_monitor_owner(tlh.list(), owner);
// Cannot assume (owning_thread != NULL) here because this function
// may not have been called at a safepoint and the owning_thread
// might not be suspended.
@@ -1021,7 +978,7 @@
// or it has to be suspended. Any of these conditions will prevent both
// contending and waiting threads from modifying the state of
// the monitor.
- if (!at_safepoint && !JvmtiEnv::is_thread_fully_suspended(owning_thread, true, &debug_bits)) {
+ if (!at_safepoint && !owning_thread->is_thread_fully_suspended(true, &debug_bits)) {
// Don't worry! This return of JVMTI_ERROR_THREAD_NOT_SUSPENDED
// will not make it back to the JVM/TI agent. The error code will
// get intercepted in JvmtiEnv::GetObjectMonitorUsage() which
@@ -1033,7 +990,7 @@
ret.owner = (jthread)jni_reference(calling_thread, th);
}
// implied else: no owner
- }
+ } // ThreadsListHandle is destroyed here.
if (owning_thread != NULL) { // monitor is owned
// The recursions field of a monitor does not reflect recursions
@@ -1084,13 +1041,15 @@
if (ret.waiter_count > 0) {
// we have contending and/or waiting threads
HandleMark hm;
+ // Use current thread since function can be called from a
+ // JavaThread or the VMThread.
+ ThreadsListHandle tlh;
if (nWant > 0) {
// we have contending threads
ResourceMark rm;
// get_pending_threads returns only java thread so we do not need to
- // check for non java threads.
- GrowableArray<JavaThread*>* wantList = Threads::get_pending_threads(
- nWant, (address)mon, !at_safepoint);
+ // check for non java threads.
+ GrowableArray<JavaThread*>* wantList = Threads::get_pending_threads(tlh.list(), nWant, (address)mon);
if (wantList->length() < nWant) {
// robustness: the pending list has gotten smaller
nWant = wantList->length();
@@ -1101,7 +1060,7 @@
// thread could potentially change the state of the monitor by
// entering it. The JVM/TI spec doesn't allow this.
if (owning_thread == NULL && !at_safepoint &
- !JvmtiEnv::is_thread_fully_suspended(pending_thread, true, &debug_bits)) {
+ !pending_thread->is_thread_fully_suspended(true, &debug_bits)) {
if (ret.owner != NULL) {
destroy_jni_reference(calling_thread, ret.owner);
}
@@ -1139,7 +1098,7 @@
waiter = mon->next_waiter(waiter);
}
}
- }
+ } // ThreadsListHandle is destroyed here.
// Adjust count. nWant and nWait count values may be less than original.
ret.waiter_count = nWant + nWait;
@@ -1291,14 +1250,23 @@
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
ResourceMark rm;
+ ThreadsListHandle tlh;
for (int i = 0; i < _thread_count; ++i) {
jthread jt = _thread_list[i];
- oop thread_oop = JNIHandles::resolve_external_guard(jt);
- if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
- set_result(JVMTI_ERROR_INVALID_THREAD);
- return;
+ JavaThread* java_thread = NULL;
+ oop thread_oop = NULL;
+ jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), jt, &java_thread, &thread_oop);
+ if (err != JVMTI_ERROR_NONE) {
+ // We got an error code so we don't have a JavaThread *, but
+ // only return an error from here if we didn't get a valid
+ // thread_oop.
+ if (thread_oop == NULL) {
+ set_result(err);
+ return;
+ }
+ // We have a valid thread_oop.
}
- fill_frames(jt, java_lang_Thread::thread(thread_oop), thread_oop);
+ fill_frames(jt, java_thread, thread_oop);
}
allocate_and_fill_stacks(_thread_count);
}
@@ -1309,7 +1277,7 @@
ResourceMark rm;
_final_thread_count = 0;
- for (JavaThread *jt = Threads::first(); jt != NULL; jt = jt->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
oop thread_oop = jt->threadObj();
if (thread_oop != NULL &&
!jt->is_exiting() &&
@@ -1404,9 +1372,7 @@
}
// Check if java_thread is fully suspended
- if (!is_thread_fully_suspended(java_thread,
- true /* wait for suspend completion */,
- &debug_bits)) {
+ if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
@@ -1521,3 +1487,79 @@
return JVMTI_ERROR_NONE;
}
+void
+VM_UpdateForPopTopFrame::doit() {
+ JavaThread* jt = _state->get_thread();
+ ThreadsListHandle tlh;
+ if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
+ _state->update_for_pop_top_frame();
+ } else {
+ _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+ }
+}
+
+void
+VM_SetFramePop::doit() {
+ JavaThread* jt = _state->get_thread();
+ ThreadsListHandle tlh;
+ if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
+ int frame_number = _state->count_frames() - _depth;
+ _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number);
+ } else {
+ _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+ }
+}
+
+void
+VM_GetOwnedMonitorInfo::doit() {
+ _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+ ThreadsListHandle tlh;
+ if (_java_thread != NULL && tlh.includes(_java_thread)
+ && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) {
+ _result = ((JvmtiEnvBase *)_env)->get_owned_monitors(_calling_thread, _java_thread,
+ _owned_monitors_list);
+ }
+}
+
+void
+VM_GetCurrentContendedMonitor::doit() {
+ _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+ ThreadsListHandle tlh;
+ if (_java_thread != NULL && tlh.includes(_java_thread)
+ && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) {
+ _result = ((JvmtiEnvBase *)_env)->get_current_contended_monitor(_calling_thread,_java_thread,_owned_monitor_ptr);
+ }
+}
+
+void
+VM_GetStackTrace::doit() {
+ _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+ ThreadsListHandle tlh;
+ if (_java_thread != NULL && tlh.includes(_java_thread)
+ && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) {
+ _result = ((JvmtiEnvBase *)_env)->get_stack_trace(_java_thread,
+ _start_depth, _max_count,
+ _frame_buffer, _count_ptr);
+ }
+}
+
+void
+VM_GetFrameCount::doit() {
+ _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+ JavaThread* jt = _state->get_thread();
+ ThreadsListHandle tlh;
+ if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
+ _result = ((JvmtiEnvBase*)_env)->get_frame_count(_state, _count_ptr);
+ }
+}
+
+void
+VM_GetFrameLocation::doit() {
+ _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+ ThreadsListHandle tlh;
+ if (_java_thread != NULL && tlh.includes(_java_thread)
+ && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) {
+ _result = ((JvmtiEnvBase*)_env)->get_frame_location(_java_thread, _depth,
+ _method_ptr, _location_ptr);
+ }
+}
--- a/src/hotspot/share/prims/jvmtiEnvBase.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiEnvBase.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -280,9 +280,6 @@
jthread * new_jthreadArray(int length, Handle *handles);
jthreadGroup * new_jthreadGroupArray(int length, Handle *handles);
- // convert from JNIHandle to JavaThread *
- JavaThread * get_JavaThread(jthread jni_thread);
-
// convert to a jni jclass from a non-null Klass*
jclass get_jni_class_non_null(Klass* k);
@@ -297,11 +294,6 @@
public:
// get a field descriptor for the specified class and field
static bool get_field_descriptor(Klass* k, jfieldID field, fieldDescriptor* fd);
- // test for suspend - most (all?) of these should go away
- static bool is_thread_fully_suspended(JavaThread *thread,
- bool wait_for_suspend,
- uint32_t *bits);
-
// JVMTI API helper functions which are called at safepoint or thread is suspended.
jvmtiError get_frame_count(JvmtiThreadState *state, jint *count_ptr);
@@ -360,14 +352,7 @@
}
VMOp_Type type() const { return VMOp_UpdateForPopTopFrame; }
jvmtiError result() { return _result; }
- void doit() {
- JavaThread* jt = _state->get_thread();
- if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
- _state->update_for_pop_top_frame();
- } else {
- _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
- }
- }
+ void doit();
};
// VM operation to set frame pop.
@@ -390,15 +375,7 @@
bool allow_nested_vm_operations() const { return true; }
VMOp_Type type() const { return VMOp_SetFramePop; }
jvmtiError result() { return _result; }
- void doit() {
- JavaThread* jt = _state->get_thread();
- if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
- int frame_number = _state->count_frames() - _depth;
- _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number);
- } else {
- _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
- }
- }
+ void doit();
};
@@ -422,14 +399,7 @@
_result = JVMTI_ERROR_NONE;
}
VMOp_Type type() const { return VMOp_GetOwnedMonitorInfo; }
- void doit() {
- _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
- if (Threads::includes(_java_thread) && !_java_thread->is_exiting()
- && _java_thread->threadObj() != NULL) {
- _result = ((JvmtiEnvBase *)_env)->get_owned_monitors(_calling_thread, _java_thread,
- _owned_monitors_list);
- }
- }
+ void doit();
jvmtiError result() { return _result; }
};
@@ -476,13 +446,7 @@
}
VMOp_Type type() const { return VMOp_GetCurrentContendedMonitor; }
jvmtiError result() { return _result; }
- void doit() {
- _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
- if (Threads::includes(_java_thread) && !_java_thread->is_exiting() &&
- _java_thread->threadObj() != NULL) {
- _result = ((JvmtiEnvBase *)_env)->get_current_contended_monitor(_calling_thread,_java_thread,_owned_monitor_ptr);
- }
- }
+ void doit();
};
// VM operation to get stack trace at safepoint.
@@ -509,15 +473,7 @@
}
jvmtiError result() { return _result; }
VMOp_Type type() const { return VMOp_GetStackTrace; }
- void doit() {
- _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
- if (Threads::includes(_java_thread) && !_java_thread->is_exiting()
- && _java_thread->threadObj() != NULL) {
- _result = ((JvmtiEnvBase *)_env)->get_stack_trace(_java_thread,
- _start_depth, _max_count,
- _frame_buffer, _count_ptr);
- }
- }
+ void doit();
};
// forward declaration
@@ -607,13 +563,7 @@
}
VMOp_Type type() const { return VMOp_GetFrameCount; }
jvmtiError result() { return _result; }
- void doit() {
- _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
- JavaThread* jt = _state->get_thread();
- if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
- _result = ((JvmtiEnvBase*)_env)->get_frame_count(_state, _count_ptr);
- }
- }
+ void doit();
};
// VM operation to frame location at safepoint.
@@ -637,14 +587,7 @@
}
VMOp_Type type() const { return VMOp_GetFrameLocation; }
jvmtiError result() { return _result; }
- void doit() {
- _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
- if (Threads::includes(_java_thread) && !_java_thread->is_exiting() &&
- _java_thread->threadObj() != NULL) {
- _result = ((JvmtiEnvBase*)_env)->get_frame_location(_java_thread, _depth,
- _method_ptr, _location_ptr);
- }
- }
+ void doit();
};
--- a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -35,6 +35,7 @@
#include "runtime/interfaceSupport.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/signature.hpp"
+#include "runtime/thread.inline.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vm_operations.hpp"
--- a/src/hotspot/share/prims/jvmtiEventController.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiEventController.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -33,7 +33,8 @@
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiThreadState.inline.hpp"
#include "runtime/frame.hpp"
-#include "runtime/thread.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vframe_hp.hpp"
#include "runtime/vmThread.hpp"
@@ -580,13 +581,10 @@
// filtered events and there weren't last time
if ( (any_env_thread_enabled & THREAD_FILTERED_EVENT_BITS) != 0 &&
(was_any_env_thread_enabled & THREAD_FILTERED_EVENT_BITS) == 0) {
- {
- MutexLocker mu(Threads_lock); //hold the Threads_lock for the iteration
- for (JavaThread *tp = Threads::first(); tp != NULL; tp = tp->next()) {
- // state_for_while_locked() makes tp->is_exiting() check
- JvmtiThreadState::state_for_while_locked(tp); // create the thread state if missing
- }
- }// release Threads_lock
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *tp = jtiwh.next(); ) {
+ // state_for_while_locked() makes tp->is_exiting() check
+ JvmtiThreadState::state_for_while_locked(tp); // create the thread state if missing
+ }
}
// compute and set thread-filtered events
--- a/src/hotspot/share/prims/jvmtiExport.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiExport.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -53,6 +53,7 @@
#include "runtime/objectMonitor.inline.hpp"
#include "runtime/os.inline.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vframe.hpp"
#include "services/serviceUtil.hpp"
#include "utilities/macros.hpp"
@@ -721,6 +722,108 @@
}
}
+// Convert an external thread reference to a JavaThread found on the
+// specified ThreadsList. The ThreadsListHandle in the caller "protects"
+// the returned JavaThread *.
+//
+// If thread_oop_p is not NULL, then the caller wants to use the oop
+// after this call so the oop is returned. On success, *jt_pp is set
+// to the converted JavaThread * and JVMTI_ERROR_NONE is returned.
+// On error, returns various JVMTI_ERROR_* values.
+//
+jvmtiError
+JvmtiExport::cv_external_thread_to_JavaThread(ThreadsList * t_list,
+ jthread thread,
+ JavaThread ** jt_pp,
+ oop * thread_oop_p) {
+ assert(t_list != NULL, "must have a ThreadsList");
+ assert(jt_pp != NULL, "must have a return JavaThread pointer");
+ // thread_oop_p is optional so no assert()
+
+ oop thread_oop = JNIHandles::resolve_external_guard(thread);
+ if (thread_oop == NULL) {
+ // NULL jthread, GC'ed jthread or a bad JNI handle.
+ return JVMTI_ERROR_INVALID_THREAD;
+ }
+ // Looks like an oop at this point.
+
+ if (!thread_oop->is_a(SystemDictionary::Thread_klass())) {
+ // The oop is not a java.lang.Thread.
+ return JVMTI_ERROR_INVALID_THREAD;
+ }
+ // Looks like a java.lang.Thread oop at this point.
+
+ if (thread_oop_p != NULL) {
+ // Return the oop to the caller; the caller may still want
+ // the oop even if this function returns an error.
+ *thread_oop_p = thread_oop;
+ }
+
+ JavaThread * java_thread = java_lang_Thread::thread(thread_oop);
+ if (java_thread == NULL) {
+ // The java.lang.Thread does not contain a JavaThread * so it has
+ // not yet run or it has died.
+ return JVMTI_ERROR_THREAD_NOT_ALIVE;
+ }
+ // Looks like a live JavaThread at this point.
+
+ // We do not check the EnableThreadSMRExtraValidityChecks option
+ // for this includes() call because JVM/TI's spec is tighter.
+ if (!t_list->includes(java_thread)) {
+ // Not on the JavaThreads list so it is not alive.
+ return JVMTI_ERROR_THREAD_NOT_ALIVE;
+ }
+
+ // Return a live JavaThread that is "protected" by the
+ // ThreadsListHandle in the caller.
+ *jt_pp = java_thread;
+
+ return JVMTI_ERROR_NONE;
+}
+
+// Convert an oop to a JavaThread found on the specified ThreadsList.
+// The ThreadsListHandle in the caller "protects" the returned
+// JavaThread *.
+//
+// On success, *jt_pp is set to the converted JavaThread * and
+// JVMTI_ERROR_NONE is returned. On error, returns various
+// JVMTI_ERROR_* values.
+//
+jvmtiError
+JvmtiExport::cv_oop_to_JavaThread(ThreadsList * t_list, oop thread_oop,
+ JavaThread ** jt_pp) {
+ assert(t_list != NULL, "must have a ThreadsList");
+ assert(thread_oop != NULL, "must have an oop");
+ assert(jt_pp != NULL, "must have a return JavaThread pointer");
+
+ if (!thread_oop->is_a(SystemDictionary::Thread_klass())) {
+ // The oop is not a java.lang.Thread.
+ return JVMTI_ERROR_INVALID_THREAD;
+ }
+ // Looks like a java.lang.Thread oop at this point.
+
+ JavaThread * java_thread = java_lang_Thread::thread(thread_oop);
+ if (java_thread == NULL) {
+ // The java.lang.Thread does not contain a JavaThread * so it has
+ // not yet run or it has died.
+ return JVMTI_ERROR_THREAD_NOT_ALIVE;
+ }
+ // Looks like a live JavaThread at this point.
+
+ // We do not check the EnableThreadSMRExtraValidityChecks option
+ // for this includes() call because JVM/TI's spec is tighter.
+ if (!t_list->includes(java_thread)) {
+ // Not on the JavaThreads list so it is not alive.
+ return JVMTI_ERROR_THREAD_NOT_ALIVE;
+ }
+
+ // Return a live JavaThread that is "protected" by the
+ // ThreadsListHandle in the caller.
+ *jt_pp = java_thread;
+
+ return JVMTI_ERROR_NONE;
+}
+
class JvmtiClassFileLoadHookPoster : public StackObj {
private:
Symbol* _h_name;
@@ -2685,8 +2788,7 @@
return;
}
- // Runs at safepoint. So no need to acquire Threads_lock.
- for (JavaThread *jthr = Threads::first(); jthr != NULL; jthr = jthr->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jthr = jtiwh.next(); ) {
JvmtiThreadState *state = jthr->jvmti_thread_state();
if (state != NULL) {
JvmtiVMObjectAllocEventCollector *collector;
--- a/src/hotspot/share/prims/jvmtiExport.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiExport.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -399,6 +399,14 @@
// SetNativeMethodPrefix support
static char** get_all_native_method_prefixes(int* count_ptr) NOT_JVMTI_RETURN_(NULL);
+
+ // JavaThread lifecycle support:
+ static jvmtiError cv_external_thread_to_JavaThread(ThreadsList * t_list,
+ jthread thread,
+ JavaThread ** jt_pp,
+ oop * thread_oop_p);
+ static jvmtiError cv_oop_to_JavaThread(ThreadsList * t_list, oop thread_oop,
+ JavaThread ** jt_pp);
};
// Support class used by JvmtiDynamicCodeEventCollector and others. It
--- a/src/hotspot/share/prims/jvmtiImpl.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiImpl.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -46,6 +46,7 @@
#include "runtime/serviceThread.hpp"
#include "runtime/signature.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vframe_hp.hpp"
#include "runtime/vm_operations.hpp"
@@ -878,10 +879,9 @@
void JvmtiSuspendControl::print() {
#ifndef PRODUCT
- MutexLocker mu(Threads_lock);
LogStreamHandle(Trace, jvmti) log_stream;
log_stream.print("Suspended Threads: [");
- for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
#ifdef JVMTI_TRACE
const char *name = JvmtiTrace::safe_get_thread_name(thread);
#else
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -43,6 +43,7 @@
#include "oops/oop.inline.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiRedefineClasses.hpp"
+#include "prims/jvmtiThreadState.inline.hpp"
#include "prims/resolvedMethodTable.hpp"
#include "prims/methodComparator.hpp"
#include "runtime/deoptimization.hpp"
--- a/src/hotspot/share/prims/jvmtiTagMap.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiTagMap.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -45,6 +45,8 @@
#include "runtime/mutex.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/reflectionUtils.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vmThread.hpp"
#include "runtime/vm_operations.hpp"
@@ -3174,7 +3176,7 @@
// stack to find all references and local JNI refs.
inline bool VM_HeapWalkOperation::collect_stack_roots() {
JNILocalRootsClosure blk;
- for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
oop threadObj = thread->threadObj();
if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) {
// Collect the simple root for this thread before we
--- a/src/hotspot/share/prims/jvmtiThreadState.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiThreadState.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -336,34 +336,10 @@
// already holding JvmtiThreadState_lock - retrieve or create JvmtiThreadState
// Can return NULL if JavaThread is exiting.
- inline static JvmtiThreadState *state_for_while_locked(JavaThread *thread) {
- assert(JvmtiThreadState_lock->is_locked(), "sanity check");
-
- JvmtiThreadState *state = thread->jvmti_thread_state();
- if (state == NULL) {
- if (thread->is_exiting()) {
- // don't add a JvmtiThreadState to a thread that is exiting
- return NULL;
- }
-
- state = new JvmtiThreadState(thread);
- }
- return state;
- }
-
+ static JvmtiThreadState *state_for_while_locked(JavaThread *thread);
// retrieve or create JvmtiThreadState
// Can return NULL if JavaThread is exiting.
- inline static JvmtiThreadState *state_for(JavaThread *thread) {
- JvmtiThreadState *state = thread->jvmti_thread_state();
- if (state == NULL) {
- MutexLocker mu(JvmtiThreadState_lock);
- // check again with the lock held
- state = state_for_while_locked(thread);
- } else {
- CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops());
- }
- return state;
- }
+ static JvmtiThreadState *state_for(JavaThread *thread);
// JVMTI ForceEarlyReturn support
--- a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2017, 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
@@ -68,4 +68,31 @@
_head_env_thread_state = ets;
}
+inline JvmtiThreadState* JvmtiThreadState::state_for_while_locked(JavaThread *thread) {
+ assert(JvmtiThreadState_lock->is_locked(), "sanity check");
+
+ JvmtiThreadState *state = thread->jvmti_thread_state();
+ if (state == NULL) {
+ if (thread->is_exiting()) {
+ // don't add a JvmtiThreadState to a thread that is exiting
+ return NULL;
+ }
+
+ state = new JvmtiThreadState(thread);
+ }
+ return state;
+}
+
+inline JvmtiThreadState* JvmtiThreadState::state_for(JavaThread *thread) {
+ JvmtiThreadState *state = thread->jvmti_thread_state();
+ if (state == NULL) {
+ MutexLocker mu(JvmtiThreadState_lock);
+ // check again with the lock held
+ state = state_for_while_locked(thread);
+ } else {
+ CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops());
+ }
+ return state;
+}
+
#endif // SHARE_VM_PRIMS_JVMTITHREADSTATE_INLINE_HPP
--- a/src/hotspot/share/prims/unsafe.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/unsafe.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -39,6 +39,8 @@
#include "runtime/interfaceSupport.hpp"
#include "runtime/orderAccess.inline.hpp"
#include "runtime/reflection.hpp"
+#include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vm_version.hpp"
#include "services/threadService.hpp"
#include "trace/tracing.hpp"
@@ -937,8 +939,12 @@
Parker* p = NULL;
if (jthread != NULL) {
- oop java_thread = JNIHandles::resolve_non_null(jthread);
+ ThreadsListHandle tlh;
+ JavaThread* thr = NULL;
+ oop java_thread = NULL;
+ (void) tlh.cv_internal_thread_to_JavaThread(jthread, &thr, &java_thread);
if (java_thread != NULL) {
+ // This is a valid oop.
jlong lp = java_lang_Thread::park_event(java_thread);
if (lp != 0) {
// This cast is OK even though the jlong might have been read
@@ -946,22 +952,19 @@
// always be zero anyway and the value set is always the same
p = (Parker*)addr_from_java(lp);
} else {
- // Grab lock if apparently null or using older version of library
- MutexLocker mu(Threads_lock);
- java_thread = JNIHandles::resolve_non_null(jthread);
-
- if (java_thread != NULL) {
- JavaThread* thr = java_lang_Thread::thread(java_thread);
- if (thr != NULL) {
- p = thr->parker();
- if (p != NULL) { // Bind to Java thread for next time.
- java_lang_Thread::set_park_event(java_thread, addr_to_java(p));
- }
+ // Not cached in the java.lang.Thread oop yet (could be an
+ // older version of library).
+ if (thr != NULL) {
+ // The JavaThread is alive.
+ p = thr->parker();
+ if (p != NULL) {
+ // Cache the Parker in the java.lang.Thread oop for next time.
+ java_lang_Thread::set_park_event(java_thread, addr_to_java(p));
}
}
}
}
- }
+ } // ThreadsListHandle is destroyed here.
if (p != NULL) {
HOTSPOT_THREAD_UNPARK((uintptr_t) p);
--- a/src/hotspot/share/prims/whitebox.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/prims/whitebox.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -55,6 +55,7 @@
#include "runtime/os.hpp"
#include "runtime/sweeper.hpp"
#include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vm_version.hpp"
#include "utilities/align.hpp"
#include "utilities/debug.hpp"
@@ -665,7 +666,7 @@
int result() const { return _result; }
void doit() {
- for (JavaThread* t = Threads::first(); t != NULL; t = t->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
if (t->has_last_Java_frame()) {
for (StackFrameStream fst(t, UseBiasedLocking); !fst.is_done(); fst.next()) {
frame* f = fst.current();
--- a/src/hotspot/share/runtime/biasedLocking.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/biasedLocking.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -32,6 +32,7 @@
#include "runtime/basicLock.hpp"
#include "runtime/biasedLocking.hpp"
#include "runtime/task.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vmThread.hpp"
#include "runtime/vm_operations.hpp"
@@ -214,12 +215,8 @@
if (requesting_thread == biased_thread) {
thread_is_alive = true;
} else {
- for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) {
- if (cur_thread == biased_thread) {
- thread_is_alive = true;
- break;
- }
- }
+ ThreadsListHandle tlh;
+ thread_is_alive = tlh.includes(biased_thread);
}
if (!thread_is_alive) {
if (allow_rebias) {
@@ -390,72 +387,76 @@
Klass* k_o = o->klass();
Klass* klass = k_o;
- if (bulk_rebias) {
- // Use the epoch in the klass of the object to implicitly revoke
- // all biases of objects of this data type and force them to be
- // reacquired. However, we also need to walk the stacks of all
- // threads and update the headers of lightweight locked objects
- // with biases to have the current epoch.
+ {
+ JavaThreadIteratorWithHandle jtiwh;
+
+ if (bulk_rebias) {
+ // Use the epoch in the klass of the object to implicitly revoke
+ // all biases of objects of this data type and force them to be
+ // reacquired. However, we also need to walk the stacks of all
+ // threads and update the headers of lightweight locked objects
+ // with biases to have the current epoch.
+
+ // If the prototype header doesn't have the bias pattern, don't
+ // try to update the epoch -- assume another VM operation came in
+ // and reset the header to the unbiased state, which will
+ // implicitly cause all existing biases to be revoked
+ if (klass->prototype_header()->has_bias_pattern()) {
+ int prev_epoch = klass->prototype_header()->bias_epoch();
+ klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch());
+ int cur_epoch = klass->prototype_header()->bias_epoch();
- // If the prototype header doesn't have the bias pattern, don't
- // try to update the epoch -- assume another VM operation came in
- // and reset the header to the unbiased state, which will
- // implicitly cause all existing biases to be revoked
- if (klass->prototype_header()->has_bias_pattern()) {
- int prev_epoch = klass->prototype_header()->bias_epoch();
- klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch());
- int cur_epoch = klass->prototype_header()->bias_epoch();
+ // Now walk all threads' stacks and adjust epochs of any biased
+ // and locked objects of this data type we encounter
+ for (; JavaThread *thr = jtiwh.next(); ) {
+ GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
+ for (int i = 0; i < cached_monitor_info->length(); i++) {
+ MonitorInfo* mon_info = cached_monitor_info->at(i);
+ oop owner = mon_info->owner();
+ markOop mark = owner->mark();
+ if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
+ // We might have encountered this object already in the case of recursive locking
+ assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
+ owner->set_mark(mark->set_bias_epoch(cur_epoch));
+ }
+ }
+ }
+ }
- // Now walk all threads' stacks and adjust epochs of any biased
- // and locked objects of this data type we encounter
- for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
+ // At this point we're done. All we have to do is potentially
+ // adjust the header of the given object to revoke its bias.
+ revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread, NULL);
+ } else {
+ if (log_is_enabled(Info, biasedlocking)) {
+ ResourceMark rm;
+ log_info(biasedlocking)("* Disabling biased locking for type %s", klass->external_name());
+ }
+
+ // Disable biased locking for this data type. Not only will this
+ // cause future instances to not be biased, but existing biased
+ // instances will notice that this implicitly caused their biases
+ // to be revoked.
+ klass->set_prototype_header(markOopDesc::prototype());
+
+ // Now walk all threads' stacks and forcibly revoke the biases of
+ // any locked and biased objects of this data type we encounter.
+ for (; JavaThread *thr = jtiwh.next(); ) {
GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
for (int i = 0; i < cached_monitor_info->length(); i++) {
MonitorInfo* mon_info = cached_monitor_info->at(i);
oop owner = mon_info->owner();
markOop mark = owner->mark();
if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
- // We might have encountered this object already in the case of recursive locking
- assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
- owner->set_mark(mark->set_bias_epoch(cur_epoch));
+ revoke_bias(owner, false, true, requesting_thread, NULL);
}
}
}
- }
- // At this point we're done. All we have to do is potentially
- // adjust the header of the given object to revoke its bias.
- revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread, NULL);
- } else {
- if (log_is_enabled(Info, biasedlocking)) {
- ResourceMark rm;
- log_info(biasedlocking)("* Disabling biased locking for type %s", klass->external_name());
+ // Must force the bias of the passed object to be forcibly revoked
+ // as well to ensure guarantees to callers
+ revoke_bias(o, false, true, requesting_thread, NULL);
}
-
- // Disable biased locking for this data type. Not only will this
- // cause future instances to not be biased, but existing biased
- // instances will notice that this implicitly caused their biases
- // to be revoked.
- klass->set_prototype_header(markOopDesc::prototype());
-
- // Now walk all threads' stacks and forcibly revoke the biases of
- // any locked and biased objects of this data type we encounter.
- for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
- GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
- for (int i = 0; i < cached_monitor_info->length(); i++) {
- MonitorInfo* mon_info = cached_monitor_info->at(i);
- oop owner = mon_info->owner();
- markOop mark = owner->mark();
- if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
- revoke_bias(owner, false, true, requesting_thread, NULL);
- }
- }
- }
-
- // Must force the bias of the passed object to be forcibly revoked
- // as well to ensure guarantees to callers
- revoke_bias(o, false, true, requesting_thread, NULL);
- }
+ } // ThreadsListHandle is destroyed here.
log_info(biasedlocking)("* Ending bulk revocation");
@@ -481,7 +482,7 @@
static void clean_up_cached_monitor_info() {
// Walk the thread list clearing out the cached monitors
- for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
thr->set_cached_monitor_info(NULL);
}
}
@@ -768,7 +769,7 @@
ResourceMark rm;
Thread* cur = Thread::current();
- for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
if (thread->has_last_Java_frame()) {
RegisterMap rm(thread);
for (javaVFrame* vf = thread->last_java_vframe(&rm); vf != NULL; vf = vf->java_sender()) {
--- a/src/hotspot/share/runtime/deoptimization.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/deoptimization.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -50,6 +50,7 @@
#include "runtime/signature.hpp"
#include "runtime/stubRoutines.hpp"
#include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vframeArray.hpp"
#include "runtime/vframe_hp.hpp"
@@ -1297,7 +1298,7 @@
assert(SafepointSynchronize::is_at_safepoint(), "must only be called from safepoint");
GrowableArray<Handle>* objects_to_revoke = new GrowableArray<Handle>();
- for (JavaThread* jt = Threads::first(); jt != NULL ; jt = jt->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
if (jt->has_last_Java_frame()) {
StackFrameStream sfs(jt, true);
while (!sfs.is_done()) {
--- a/src/hotspot/share/runtime/globals.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/globals.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -2484,6 +2484,12 @@
LP64_ONLY(range(-1, max_intx/MICROUNITS)) \
NOT_LP64(range(-1, max_intx)) \
\
+ diagnostic(bool, EnableThreadSMRExtraValidityChecks, true, \
+ "Enable Thread SMR extra validity checks") \
+ \
+ diagnostic(bool, EnableThreadSMRStatistics, true, \
+ "Enable Thread SMR Statistics") \
+ \
product(bool, Inline, true, \
"Enable inlining") \
\
--- a/src/hotspot/share/runtime/handshake.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/handshake.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -37,8 +37,6 @@
#include "utilities/formatBuffer.hpp"
#include "utilities/preserveException.hpp"
-#define ALL_JAVA_THREADS(X) for (JavaThread* X = Threads::first(); X; X = X->next())
-
class HandshakeOperation: public StackObj {
public:
virtual void do_handshake(JavaThread* thread) = 0;
@@ -94,8 +92,7 @@
void VM_Handshake::handle_timeout() {
LogStreamHandle(Warning, handshake) log_stream;
- MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
- ALL_JAVA_THREADS(thr) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
if (thr->has_handshake()) {
log_stream.print("Thread " PTR_FORMAT " has not cleared its handshake op", p2i(thr));
thr->print_thread_state_on(&log_stream);
@@ -117,8 +114,8 @@
TraceTime timer("Performing single-target operation (vmoperation doit)", TRACETIME_LOG(Info, handshake));
{
- MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
- if (Threads::includes(_target)) {
+ ThreadsListHandle tlh;
+ if (tlh.includes(_target)) {
set_handshake(_target);
_thread_alive = true;
}
@@ -139,9 +136,24 @@
handle_timeout();
}
+ // We need to re-think this with SMR ThreadsList.
+ // There is an assumption in the code that the Threads_lock should be
+ // locked during certain phases.
MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
- _target->handshake_process_by_vmthread();
-
+ ThreadsListHandle tlh;
+ if (tlh.includes(_target)) {
+ // Warning _target's address might be re-used.
+ // handshake_process_by_vmthread will check the semaphore for us again.
+ // Since we can't have more then one handshake in flight a reuse of
+ // _target's address should be okay since the new thread will not have
+ // an operation.
+ _target->handshake_process_by_vmthread();
+ } else {
+ // We can't warn here since the thread does cancel_handshake after
+ // it has been removed from the ThreadsList. So we should just keep
+ // looping here until while below returns false. If we have a bug,
+ // then we hang here, which is good for debugging.
+ }
} while (!poll_for_completed_thread());
}
@@ -157,15 +169,15 @@
void doit() {
TraceTime timer("Performing operation (vmoperation doit)", TRACETIME_LOG(Info, handshake));
- int number_of_threads_issued = -1;
- int number_of_threads_completed = 0;
- {
- MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
- number_of_threads_issued = Threads::number_of_threads();
+ int number_of_threads_issued = 0;
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
+ set_handshake(thr);
+ number_of_threads_issued++;
+ }
- ALL_JAVA_THREADS(thr) {
- set_handshake(thr);
- }
+ if (number_of_threads_issued < 1) {
+ log_debug(handshake)("No threads to handshake.");
+ return;
}
if (!UseMembar) {
@@ -174,6 +186,7 @@
log_debug(handshake)("Threads signaled, begin processing blocked threads by VMThtread");
const jlong start_time = os::elapsed_counter();
+ int number_of_threads_completed = 0;
do {
// Check if handshake operation has timed out
if (handshake_has_timed_out(start_time)) {
@@ -184,13 +197,19 @@
// Observing a blocked state may of course be transient but the processing is guarded
// by semaphores and we optimistically begin by working on the blocked threads
{
+ // We need to re-think this with SMR ThreadsList.
+ // There is an assumption in the code that the Threads_lock should
+ // be locked during certain phases.
MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
- ALL_JAVA_THREADS(thr) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
+ // A new thread on the ThreadsList will not have an operation,
+ // hence it is skipped in handshake_process_by_vmthread.
thr->handshake_process_by_vmthread();
}
}
while (poll_for_completed_thread()) {
+ // Includes canceled operations by exiting threads.
number_of_threads_completed++;
}
@@ -212,7 +231,7 @@
_thread_cl(cl), _target_thread(target), _all_threads(false), _thread_alive(false) {}
void doit() {
- ALL_JAVA_THREADS(t) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
if (_all_threads || t == _target_thread) {
if (t == _target_thread) {
_thread_alive = true;
@@ -298,8 +317,8 @@
assert(thread->thread_state() == _thread_in_vm, "must be in vm state");
#ifdef DEBUG
{
- MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
- assert(!Threads::includes(thread), "java thread must not be on threads list");
+ ThreadsListHandle tlh;
+ assert(!tlh.includes(_target), "java thread must not be on threads list");
}
#endif
HandshakeOperation* op = _operation;
--- a/src/hotspot/share/runtime/java.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/java.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -356,6 +356,8 @@
if (PrintNMTStatistics) {
MemTracker::final_report(tty);
}
+
+ Threads::log_smr_statistics();
}
#else // PRODUCT MODE STATISTICS
@@ -396,6 +398,8 @@
if (LogTouchedMethods && PrintTouchedMethodsAtExit) {
Method::print_touched_methods(tty);
}
+
+ Threads::log_smr_statistics();
}
#endif
--- a/src/hotspot/share/runtime/memprofiler.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/memprofiler.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -36,6 +36,7 @@
#include "runtime/os.hpp"
#include "runtime/task.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vmThread.hpp"
#ifndef PRODUCT
@@ -51,8 +52,6 @@
void MemProfilerTask::task() {
- // Get thread lock to provide mutual exclusion, and so we can iterate safely over the thread list.
- MutexLocker mu(Threads_lock);
MemProfiler::do_trace();
}
@@ -109,20 +108,21 @@
// Calculate thread local sizes
size_t handles_memory_usage = VMThread::vm_thread()->handle_area()->size_in_bytes();
size_t resource_memory_usage = VMThread::vm_thread()->resource_area()->size_in_bytes();
- JavaThread *cur = Threads::first();
- while (cur != NULL) {
- handles_memory_usage += cur->handle_area()->size_in_bytes();
- resource_memory_usage += cur->resource_area()->size_in_bytes();
- cur = cur->next();
- }
+ {
+ JavaThreadIteratorWithHandle jtiwh;
+ for (; JavaThread *cur = jtiwh.next(); ) {
+ handles_memory_usage += cur->handle_area()->size_in_bytes();
+ resource_memory_usage += cur->resource_area()->size_in_bytes();
+ }
- // Print trace line in log
- fprintf(_log_fp, "%6.1f,%5d,%5d," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",",
- os::elapsedTime(),
- Threads::number_of_threads(),
- InstanceKlass::number_of_instance_classes(),
- Universe::heap()->used() / K,
- Universe::heap()->capacity() / K);
+ // Print trace line in log
+ fprintf(_log_fp, "%6.1f,%5d,%5d," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",",
+ os::elapsedTime(),
+ jtiwh.length(),
+ InstanceKlass::number_of_instance_classes(),
+ Universe::heap()->used() / K,
+ Universe::heap()->capacity() / K);
+ }
fprintf(_log_fp, UINTX_FORMAT_W(6) ",", CodeCache::capacity() / K);
--- a/src/hotspot/share/runtime/os.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/os.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -54,6 +54,7 @@
#include "runtime/os.inline.hpp"
#include "runtime/stubRoutines.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vm_version.hpp"
#include "services/attachListener.hpp"
#include "services/mallocTracker.hpp"
@@ -197,15 +198,7 @@
}
OSReturn os::set_priority(Thread* thread, ThreadPriority p) {
-#ifdef ASSERT
- if (!(!thread->is_Java_thread() ||
- Thread::current() == thread ||
- Threads_lock->owned_by_self()
- || thread->is_Compiler_thread()
- )) {
- assert(false, "possibility of dangling Thread pointer");
- }
-#endif
+ debug_only(Thread::check_for_dangling_thread_pointer(thread);)
if (p >= MinPriority && p <= MaxPriority) {
int priority = java_to_os_priority[p];
@@ -1100,7 +1093,7 @@
}
#endif
- for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
// Check for privilege stack
if (thread->privileged_stack_top() != NULL &&
thread->privileged_stack_top()->contains(addr)) {
@@ -1126,7 +1119,6 @@
if (verbose) thread->print_on(st);
return;
}
-
}
// Check if in metaspace and print types that have vptrs (only method now)
@@ -1665,7 +1657,6 @@
}
void os::SuspendedThreadTask::run() {
- assert(Threads_lock->owned_by_self() || (_thread == VMThread::vm_thread()), "must have threads lock to call this");
internal_do_task();
_done = true;
}
--- a/src/hotspot/share/runtime/safepoint.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/safepoint.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -59,6 +59,7 @@
#include "runtime/sweeper.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/timerTrace.hpp"
#include "services/runtimeService.hpp"
#include "trace/tracing.hpp"
@@ -174,7 +175,7 @@
if (SafepointMechanism::uses_thread_local_poll()) {
// Arming the per thread poll while having _state != _not_synchronized means safepointing
log_trace(safepoint)("Setting thread local yield flag for threads");
- for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) {
// Make sure the threads start polling, it is time to yield.
SafepointMechanism::arm_local_poll(cur); // release store, global state -> local state
}
@@ -200,133 +201,137 @@
// Consider using active_processor_count() ... but that call is expensive.
int ncpus = os::processor_count() ;
+ unsigned int iterations = 0;
+ {
+ JavaThreadIteratorWithHandle jtiwh;
#ifdef ASSERT
- for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) {
- assert(cur->safepoint_state()->is_running(), "Illegal initial state");
- // Clear the visited flag to ensure that the critical counts are collected properly.
- cur->set_visited_for_critical_count(false);
- }
+ for (; JavaThread *cur = jtiwh.next(); ) {
+ assert(cur->safepoint_state()->is_running(), "Illegal initial state");
+ // Clear the visited flag to ensure that the critical counts are collected properly.
+ cur->set_visited_for_critical_count(false);
+ }
#endif // ASSERT
- if (SafepointTimeout)
- safepoint_limit_time = os::javaTimeNanos() + (jlong)SafepointTimeoutDelay * MICROUNITS;
+ if (SafepointTimeout)
+ safepoint_limit_time = os::javaTimeNanos() + (jlong)SafepointTimeoutDelay * MICROUNITS;
- // Iterate through all threads until it have been determined how to stop them all at a safepoint
- unsigned int iterations = 0;
- int steps = 0 ;
- while(still_running > 0) {
- for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) {
- assert(!cur->is_ConcurrentGC_thread(), "A concurrent GC thread is unexpectly being suspended");
- ThreadSafepointState *cur_state = cur->safepoint_state();
- if (cur_state->is_running()) {
- cur_state->examine_state_of_thread();
- if (!cur_state->is_running()) {
- still_running--;
- // consider adjusting steps downward:
- // steps = 0
- // steps -= NNN
- // steps >>= 1
- // steps = MIN(steps, 2000-100)
- // if (iterations != 0) steps -= NNN
- }
- LogTarget(Trace, safepoint) lt;
- if (lt.is_enabled()) {
- ResourceMark rm;
- LogStream ls(lt);
- cur_state->print_on(&ls);
+ // Iterate through all threads until it have been determined how to stop them all at a safepoint
+ int steps = 0 ;
+ while(still_running > 0) {
+ jtiwh.rewind();
+ for (; JavaThread *cur = jtiwh.next(); ) {
+ assert(!cur->is_ConcurrentGC_thread(), "A concurrent GC thread is unexpectly being suspended");
+ ThreadSafepointState *cur_state = cur->safepoint_state();
+ if (cur_state->is_running()) {
+ cur_state->examine_state_of_thread();
+ if (!cur_state->is_running()) {
+ still_running--;
+ // consider adjusting steps downward:
+ // steps = 0
+ // steps -= NNN
+ // steps >>= 1
+ // steps = MIN(steps, 2000-100)
+ // if (iterations != 0) steps -= NNN
+ }
+ LogTarget(Trace, safepoint) lt;
+ if (lt.is_enabled()) {
+ ResourceMark rm;
+ LogStream ls(lt);
+ cur_state->print_on(&ls);
+ }
}
}
- }
- if (iterations == 0) {
- initial_running = still_running;
- if (PrintSafepointStatistics) {
- begin_statistics(nof_threads, still_running);
- }
- }
-
- if (still_running > 0) {
- // Check for if it takes to long
- if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) {
- print_safepoint_timeout(_spinning_timeout);
+ if (iterations == 0) {
+ initial_running = still_running;
+ if (PrintSafepointStatistics) {
+ begin_statistics(nof_threads, still_running);
+ }
}
- // Spin to avoid context switching.
- // There's a tension between allowing the mutators to run (and rendezvous)
- // vs spinning. As the VM thread spins, wasting cycles, it consumes CPU that
- // a mutator might otherwise use profitably to reach a safepoint. Excessive
- // spinning by the VM thread on a saturated system can increase rendezvous latency.
- // Blocking or yielding incur their own penalties in the form of context switching
- // and the resultant loss of $ residency.
- //
- // Further complicating matters is that yield() does not work as naively expected
- // on many platforms -- yield() does not guarantee that any other ready threads
- // will run. As such we revert to naked_short_sleep() after some number of iterations.
- // nakes_short_sleep() is implemented as a short unconditional sleep.
- // Typical operating systems round a "short" sleep period up to 10 msecs, so sleeping
- // can actually increase the time it takes the VM thread to detect that a system-wide
- // stop-the-world safepoint has been reached. In a pathological scenario such as that
- // described in CR6415670 the VMthread may sleep just before the mutator(s) become safe.
- // In that case the mutators will be stalled waiting for the safepoint to complete and the
- // the VMthread will be sleeping, waiting for the mutators to rendezvous. The VMthread
- // will eventually wake up and detect that all mutators are safe, at which point
- // we'll again make progress.
- //
- // Beware too that that the VMThread typically runs at elevated priority.
- // Its default priority is higher than the default mutator priority.
- // Obviously, this complicates spinning.
- //
- // Note too that on Windows XP SwitchThreadTo() has quite different behavior than Sleep(0).
- // Sleep(0) will _not yield to lower priority threads, while SwitchThreadTo() will.
- //
- // See the comments in synchronizer.cpp for additional remarks on spinning.
- //
- // In the future we might:
- // 1. Modify the safepoint scheme to avoid potentially unbounded spinning.
- // This is tricky as the path used by a thread exiting the JVM (say on
- // on JNI call-out) simply stores into its state field. The burden
- // is placed on the VM thread, which must poll (spin).
- // 2. Find something useful to do while spinning. If the safepoint is GC-related
- // we might aggressively scan the stacks of threads that are already safe.
- // 3. Use Solaris schedctl to examine the state of the still-running mutators.
- // If all the mutators are ONPROC there's no reason to sleep or yield.
- // 4. YieldTo() any still-running mutators that are ready but OFFPROC.
- // 5. Check system saturation. If the system is not fully saturated then
- // simply spin and avoid sleep/yield.
- // 6. As still-running mutators rendezvous they could unpark the sleeping
- // VMthread. This works well for still-running mutators that become
- // safe. The VMthread must still poll for mutators that call-out.
- // 7. Drive the policy on time-since-begin instead of iterations.
- // 8. Consider making the spin duration a function of the # of CPUs:
- // Spin = (((ncpus-1) * M) + K) + F(still_running)
- // Alternately, instead of counting iterations of the outer loop
- // we could count the # of threads visited in the inner loop, above.
- // 9. On windows consider using the return value from SwitchThreadTo()
- // to drive subsequent spin/SwitchThreadTo()/Sleep(N) decisions.
-
- if (SafepointMechanism::uses_global_page_poll() && int(iterations) == DeferPollingPageLoopCount) {
- guarantee (PageArmed == 0, "invariant") ;
- PageArmed = 1 ;
- os::make_polling_page_unreadable();
- }
-
- // Instead of (ncpus > 1) consider either (still_running < (ncpus + EPSILON)) or
- // ((still_running + _waiting_to_block - TryingToBlock)) < ncpus)
- ++steps ;
- if (ncpus > 1 && steps < SafepointSpinBeforeYield) {
- SpinPause() ; // MP-Polite spin
- } else
- if (steps < DeferThrSuspendLoopCount) {
- os::naked_yield() ;
- } else {
- os::naked_short_sleep(1);
+ if (still_running > 0) {
+ // Check for if it takes to long
+ if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) {
+ print_safepoint_timeout(_spinning_timeout);
}
- iterations ++ ;
+ // Spin to avoid context switching.
+ // There's a tension between allowing the mutators to run (and rendezvous)
+ // vs spinning. As the VM thread spins, wasting cycles, it consumes CPU that
+ // a mutator might otherwise use profitably to reach a safepoint. Excessive
+ // spinning by the VM thread on a saturated system can increase rendezvous latency.
+ // Blocking or yielding incur their own penalties in the form of context switching
+ // and the resultant loss of $ residency.
+ //
+ // Further complicating matters is that yield() does not work as naively expected
+ // on many platforms -- yield() does not guarantee that any other ready threads
+ // will run. As such we revert to naked_short_sleep() after some number of iterations.
+ // nakes_short_sleep() is implemented as a short unconditional sleep.
+ // Typical operating systems round a "short" sleep period up to 10 msecs, so sleeping
+ // can actually increase the time it takes the VM thread to detect that a system-wide
+ // stop-the-world safepoint has been reached. In a pathological scenario such as that
+ // described in CR6415670 the VMthread may sleep just before the mutator(s) become safe.
+ // In that case the mutators will be stalled waiting for the safepoint to complete and the
+ // the VMthread will be sleeping, waiting for the mutators to rendezvous. The VMthread
+ // will eventually wake up and detect that all mutators are safe, at which point
+ // we'll again make progress.
+ //
+ // Beware too that that the VMThread typically runs at elevated priority.
+ // Its default priority is higher than the default mutator priority.
+ // Obviously, this complicates spinning.
+ //
+ // Note too that on Windows XP SwitchThreadTo() has quite different behavior than Sleep(0).
+ // Sleep(0) will _not yield to lower priority threads, while SwitchThreadTo() will.
+ //
+ // See the comments in synchronizer.cpp for additional remarks on spinning.
+ //
+ // In the future we might:
+ // 1. Modify the safepoint scheme to avoid potentially unbounded spinning.
+ // This is tricky as the path used by a thread exiting the JVM (say on
+ // on JNI call-out) simply stores into its state field. The burden
+ // is placed on the VM thread, which must poll (spin).
+ // 2. Find something useful to do while spinning. If the safepoint is GC-related
+ // we might aggressively scan the stacks of threads that are already safe.
+ // 3. Use Solaris schedctl to examine the state of the still-running mutators.
+ // If all the mutators are ONPROC there's no reason to sleep or yield.
+ // 4. YieldTo() any still-running mutators that are ready but OFFPROC.
+ // 5. Check system saturation. If the system is not fully saturated then
+ // simply spin and avoid sleep/yield.
+ // 6. As still-running mutators rendezvous they could unpark the sleeping
+ // VMthread. This works well for still-running mutators that become
+ // safe. The VMthread must still poll for mutators that call-out.
+ // 7. Drive the policy on time-since-begin instead of iterations.
+ // 8. Consider making the spin duration a function of the # of CPUs:
+ // Spin = (((ncpus-1) * M) + K) + F(still_running)
+ // Alternately, instead of counting iterations of the outer loop
+ // we could count the # of threads visited in the inner loop, above.
+ // 9. On windows consider using the return value from SwitchThreadTo()
+ // to drive subsequent spin/SwitchThreadTo()/Sleep(N) decisions.
+
+ if (SafepointMechanism::uses_global_page_poll() && int(iterations) == DeferPollingPageLoopCount) {
+ guarantee (PageArmed == 0, "invariant") ;
+ PageArmed = 1 ;
+ os::make_polling_page_unreadable();
+ }
+
+ // Instead of (ncpus > 1) consider either (still_running < (ncpus + EPSILON)) or
+ // ((still_running + _waiting_to_block - TryingToBlock)) < ncpus)
+ ++steps ;
+ if (ncpus > 1 && steps < SafepointSpinBeforeYield) {
+ SpinPause() ; // MP-Polite spin
+ } else
+ if (steps < DeferThrSuspendLoopCount) {
+ os::naked_yield() ;
+ } else {
+ os::naked_short_sleep(1);
+ }
+
+ iterations ++ ;
+ }
+ assert(iterations < (uint)max_jint, "We have been iterating in the safepoint loop too long");
}
- assert(iterations < (uint)max_jint, "We have been iterating in the safepoint loop too long");
- }
+ } // ThreadsListHandle destroyed here.
assert(still_running == 0, "sanity check");
if (PrintSafepointStatistics) {
@@ -341,7 +346,7 @@
sync_event.set_iterations(iterations);
sync_event.commit();
}
- } //EventSafepointStateSync
+ } // EventSafepointStateSynchronization destroyed here.
// wait until all threads are stopped
{
@@ -393,8 +398,8 @@
} // EventSafepointWaitBlocked
#ifdef ASSERT
- for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) {
- // make sure all the threads were visited
+ // Make sure all the threads were visited.
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) {
assert(cur->was_visited_for_critical_count(), "missed a thread");
}
#endif // ASSERT
@@ -452,81 +457,86 @@
end_statistics(os::javaTimeNanos());
}
+ {
+ JavaThreadIteratorWithHandle jtiwh;
#ifdef ASSERT
- // A pending_exception cannot be installed during a safepoint. The threads
- // may install an async exception after they come back from a safepoint into
- // pending_exception after they unblock. But that should happen later.
- for (JavaThread *cur = Threads::first(); cur; cur = cur->next()) {
- assert (!(cur->has_pending_exception() &&
- cur->safepoint_state()->is_at_poll_safepoint()),
- "safepoint installed a pending exception");
- }
+ // A pending_exception cannot be installed during a safepoint. The threads
+ // may install an async exception after they come back from a safepoint into
+ // pending_exception after they unblock. But that should happen later.
+ for (; JavaThread *cur = jtiwh.next(); ) {
+ assert (!(cur->has_pending_exception() &&
+ cur->safepoint_state()->is_at_poll_safepoint()),
+ "safepoint installed a pending exception");
+ }
#endif // ASSERT
- if (PageArmed) {
- assert(SafepointMechanism::uses_global_page_poll(), "sanity");
- // Make polling safepoint aware
- os::make_polling_page_readable();
- PageArmed = 0 ;
- }
-
- if (SafepointMechanism::uses_global_page_poll()) {
- // Remove safepoint check from interpreter
- Interpreter::ignore_safepoints();
- }
-
- {
- MutexLocker mu(Safepoint_lock);
-
- assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization");
+ if (PageArmed) {
+ assert(SafepointMechanism::uses_global_page_poll(), "sanity");
+ // Make polling safepoint aware
+ os::make_polling_page_readable();
+ PageArmed = 0 ;
+ }
- if (SafepointMechanism::uses_thread_local_poll()) {
- _state = _not_synchronized;
- OrderAccess::storestore(); // global state -> local state
- for (JavaThread *current = Threads::first(); current; current = current->next()) {
- ThreadSafepointState* cur_state = current->safepoint_state();
- cur_state->restart(); // TSS _running
- SafepointMechanism::disarm_local_poll(current); // release store, local state -> polling page
- }
- log_debug(safepoint)("Leaving safepoint region");
- } else {
- // Set to not synchronized, so the threads will not go into the signal_thread_blocked method
- // when they get restarted.
- _state = _not_synchronized;
- OrderAccess::fence();
-
- log_debug(safepoint)("Leaving safepoint region");
-
- // Start suspended threads
- for (JavaThread *current = Threads::first(); current; current = current->next()) {
- // A problem occurring on Solaris is when attempting to restart threads
- // the first #cpus - 1 go well, but then the VMThread is preempted when we get
- // to the next one (since it has been running the longest). We then have
- // to wait for a cpu to become available before we can continue restarting
- // threads.
- // FIXME: This causes the performance of the VM to degrade when active and with
- // large numbers of threads. Apparently this is due to the synchronous nature
- // of suspending threads.
- //
- // TODO-FIXME: the comments above are vestigial and no longer apply.
- // Furthermore, using solaris' schedctl in this particular context confers no benefit
- if (VMThreadHintNoPreempt) {
- os::hint_no_preempt();
- }
- ThreadSafepointState* cur_state = current->safepoint_state();
- assert(cur_state->type() != ThreadSafepointState::_running, "Thread not suspended at safepoint");
- cur_state->restart();
- assert(cur_state->is_running(), "safepoint state has not been reset");
- }
+ if (SafepointMechanism::uses_global_page_poll()) {
+ // Remove safepoint check from interpreter
+ Interpreter::ignore_safepoints();
}
- RuntimeService::record_safepoint_end();
+ {
+ MutexLocker mu(Safepoint_lock);
+
+ assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization");
+
+ if (SafepointMechanism::uses_thread_local_poll()) {
+ _state = _not_synchronized;
+ OrderAccess::storestore(); // global state -> local state
+ jtiwh.rewind();
+ for (; JavaThread *current = jtiwh.next(); ) {
+ ThreadSafepointState* cur_state = current->safepoint_state();
+ cur_state->restart(); // TSS _running
+ SafepointMechanism::disarm_local_poll(current); // release store, local state -> polling page
+ }
+ log_debug(safepoint)("Leaving safepoint region");
+ } else {
+ // Set to not synchronized, so the threads will not go into the signal_thread_blocked method
+ // when they get restarted.
+ _state = _not_synchronized;
+ OrderAccess::fence();
+
+ log_debug(safepoint)("Leaving safepoint region");
- // Release threads lock, so threads can be created/destroyed again. It will also starts all threads
- // blocked in signal_thread_blocked
- Threads_lock->unlock();
+ // Start suspended threads
+ jtiwh.rewind();
+ for (; JavaThread *current = jtiwh.next(); ) {
+ // A problem occurring on Solaris is when attempting to restart threads
+ // the first #cpus - 1 go well, but then the VMThread is preempted when we get
+ // to the next one (since it has been running the longest). We then have
+ // to wait for a cpu to become available before we can continue restarting
+ // threads.
+ // FIXME: This causes the performance of the VM to degrade when active and with
+ // large numbers of threads. Apparently this is due to the synchronous nature
+ // of suspending threads.
+ //
+ // TODO-FIXME: the comments above are vestigial and no longer apply.
+ // Furthermore, using solaris' schedctl in this particular context confers no benefit
+ if (VMThreadHintNoPreempt) {
+ os::hint_no_preempt();
+ }
+ ThreadSafepointState* cur_state = current->safepoint_state();
+ assert(cur_state->type() != ThreadSafepointState::_running, "Thread not suspended at safepoint");
+ cur_state->restart();
+ assert(cur_state->is_running(), "safepoint state has not been reset");
+ }
+ }
- }
+ RuntimeService::record_safepoint_end();
+
+ // Release threads lock, so threads can be created/destroyed again.
+ // It will also release all threads blocked in signal_thread_blocked.
+ Threads_lock->unlock();
+ }
+ } // ThreadsListHandle destroyed here.
+
Universe::heap()->safepoint_synchronize_end();
// record this time so VMThread can keep track how much time has elapsed
// since last safepoint.
@@ -915,12 +925,11 @@
tty->print_cr("# SafepointSynchronize::begin: Threads which did not reach the safepoint:");
ThreadSafepointState *cur_state;
ResourceMark rm;
- for (JavaThread *cur_thread = Threads::first(); cur_thread;
- cur_thread = cur_thread->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur_thread = jtiwh.next(); ) {
cur_state = cur_thread->safepoint_state();
if (cur_thread->thread_state() != _thread_blocked &&
- ((reason == _spinning_timeout && cur_state->is_running()) ||
+ ((reason == _spinning_timeout && cur_state->is_running()) ||
(reason == _blocking_timeout && !cur_state->has_called_back()))) {
tty->print("# ");
cur_thread->print();
@@ -1427,7 +1436,7 @@
tty->print_cr("State: %s", (_state == _synchronizing) ? "synchronizing" :
"synchronized");
- for (JavaThread *cur = Threads::first(); cur; cur = cur->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) {
cur->safepoint_state()->print();
}
}
--- a/src/hotspot/share/runtime/synchronizer.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/synchronizer.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -894,7 +894,7 @@
}
// FIXME: jvmti should call this
-JavaThread* ObjectSynchronizer::get_lock_owner(Handle h_obj, bool doLock) {
+JavaThread* ObjectSynchronizer::get_lock_owner(ThreadsList * t_list, Handle h_obj) {
if (UseBiasedLocking) {
if (SafepointSynchronize::is_at_safepoint()) {
BiasedLocking::revoke_at_safepoint(h_obj);
@@ -923,7 +923,7 @@
if (owner != NULL) {
// owning_thread_from_monitor_owner() may also return NULL here
- return Threads::owning_thread_from_monitor_owner(owner, doLock);
+ return Threads::owning_thread_from_monitor_owner(t_list, owner);
}
// Unlocked case, header in place
--- a/src/hotspot/share/runtime/synchronizer.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/synchronizer.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -32,6 +32,7 @@
#include "runtime/perfData.hpp"
class ObjectMonitor;
+class ThreadsList;
struct DeflateMonitorCounters {
int nInuse; // currently associated with objects
@@ -125,7 +126,7 @@
static bool current_thread_holds_lock(JavaThread* thread, Handle h_obj);
static LockOwnership query_lock_ownership(JavaThread * self, Handle h_obj);
- static JavaThread* get_lock_owner(Handle h_obj, bool doLock);
+ static JavaThread* get_lock_owner(ThreadsList * t_list, Handle h_obj);
// JNI detach support
static void release_monitors_owned_by_thread(TRAPS);
--- a/src/hotspot/share/runtime/thread.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/thread.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -71,12 +71,12 @@
#include "runtime/java.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/jniPeriodicChecker.hpp"
-#include "runtime/timerTrace.hpp"
#include "runtime/memprofiler.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/objectMonitor.hpp"
#include "runtime/orderAccess.inline.hpp"
#include "runtime/osThread.hpp"
+#include "runtime/prefetch.inline.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/safepointMechanism.inline.hpp"
#include "runtime/sharedRuntime.hpp"
@@ -86,6 +86,9 @@
#include "runtime/task.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/threadCritical.hpp"
+#include "runtime/threadSMR.inline.hpp"
+#include "runtime/timer.hpp"
+#include "runtime/timerTrace.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vframeArray.hpp"
#include "runtime/vframe_hp.hpp"
@@ -104,6 +107,7 @@
#include "utilities/events.hpp"
#include "utilities/macros.hpp"
#include "utilities/preserveException.hpp"
+#include "utilities/resourceHash.hpp"
#include "utilities/vmError.hpp"
#if INCLUDE_ALL_GCS
#include "gc/cms/concurrentMarkSweepThread.hpp"
@@ -195,13 +199,19 @@
void Thread::operator delete(void* p) {
if (UseBiasedLocking) {
- void* real_malloc_addr = ((Thread*) p)->_real_malloc_address;
- FreeHeap(real_malloc_addr);
+ FreeHeap(((Thread*) p)->_real_malloc_address);
} else {
FreeHeap(p);
}
}
+void JavaThread::smr_delete() {
+ if (_on_thread_list) {
+ Threads::smr_delete(this);
+ } else {
+ delete this;
+ }
+}
// Base class for all threads: VMThread, WatcherThread, ConcurrentMarkSweepThread,
// JavaThread
@@ -227,6 +237,9 @@
// This initial value ==> never claimed.
_oops_do_parity = 0;
+ _threads_hazard_ptr = NULL;
+ _nested_threads_hazard_ptr = NULL;
+ _nested_threads_hazard_ptr_cnt = 0;
// the handle mark links itself to last_handle_mark
new HandleMark(this);
@@ -398,9 +411,15 @@
}
#ifdef ASSERT
-// Private method to check for dangling thread pointer
-void check_for_dangling_thread_pointer(Thread *thread) {
- assert(!thread->is_Java_thread() || Thread::current() == thread || Threads_lock->owned_by_self(),
+// A JavaThread is considered "dangling" if it is not the current
+// thread, has been added the Threads list, the system is not at a
+// safepoint and the Thread is not "protected".
+//
+void Thread::check_for_dangling_thread_pointer(Thread *thread) {
+ assert(!thread->is_Java_thread() || Thread::current() == thread ||
+ !((JavaThread *) thread)->on_thread_list() ||
+ SafepointSynchronize::is_at_safepoint() ||
+ Threads::is_a_protected_JavaThread_with_lock((JavaThread *) thread),
"possibility of dangling Thread pointer");
}
#endif
@@ -732,6 +751,37 @@
return false;
}
+// Called from API entry points which perform stack walking. If the
+// associated JavaThread is the current thread, then wait_for_suspend
+// is not used. Otherwise, it determines if we should wait for the
+// "other" thread to complete external suspension. (NOTE: in future
+// releases the suspension mechanism should be reimplemented so this
+// is not necessary.)
+//
+bool
+JavaThread::is_thread_fully_suspended(bool wait_for_suspend, uint32_t *bits) {
+ if (this != JavaThread::current()) {
+ // "other" threads require special handling.
+ if (wait_for_suspend) {
+ // We are allowed to wait for the external suspend to complete
+ // so give the other thread a chance to get suspended.
+ if (!wait_for_ext_suspend_completion(SuspendRetryCount,
+ SuspendRetryDelay, bits)) {
+ // Didn't make it so let the caller know.
+ return false;
+ }
+ }
+ // We aren't allowed to wait for the external suspend to complete
+ // so if the other thread isn't externally suspended we need to
+ // let the caller know.
+ else if (!is_ext_suspend_completed_with_lock(bits)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
#ifndef PRODUCT
void JavaThread::record_jump(address target, address instr, const char* file,
int line) {
@@ -810,9 +860,33 @@
ext().print_on(st);
osthread()->print_on(st);
}
+ if (_threads_hazard_ptr != NULL) {
+ st->print("_threads_hazard_ptr=" INTPTR_FORMAT, p2i(_threads_hazard_ptr));
+ }
+ if (_nested_threads_hazard_ptr != NULL) {
+ print_nested_threads_hazard_ptrs_on(st);
+ }
+ st->print(" ");
debug_only(if (WizardMode) print_owned_locks_on(st);)
}
+void Thread::print_nested_threads_hazard_ptrs_on(outputStream* st) const {
+ assert(_nested_threads_hazard_ptr != NULL, "must be set to print");
+
+ if (EnableThreadSMRStatistics) {
+ st->print(", _nested_threads_hazard_ptr_cnt=%u", _nested_threads_hazard_ptr_cnt);
+ }
+ st->print(", _nested_threads_hazard_ptrs=");
+ for (NestedThreadsList* node = _nested_threads_hazard_ptr; node != NULL;
+ node = node->next()) {
+ if (node != _nested_threads_hazard_ptr) {
+ // First node does not need a comma-space separator.
+ st->print(", ");
+ }
+ st->print(INTPTR_FORMAT, p2i(node->t_list()));
+ }
+}
+
// Thread::print_on_error() is called by fatal error handler. Don't use
// any lock or allocate memory.
void Thread::print_on_error(outputStream* st, char* buf, int buflen) const {
@@ -834,6 +908,13 @@
if (osthread()) {
st->print(" [id=%d]", osthread()->thread_id());
}
+
+ if (_threads_hazard_ptr != NULL) {
+ st->print(" _threads_hazard_ptr=" INTPTR_FORMAT, p2i(_threads_hazard_ptr));
+ }
+ if (_nested_threads_hazard_ptr != NULL) {
+ print_nested_threads_hazard_ptrs_on(st);
+ }
}
void Thread::print_value_on(outputStream* st) const {
@@ -871,8 +952,8 @@
#ifndef PRODUCT
-// The flag: potential_vm_operation notifies if this particular safepoint state could potential
-// invoke the vm-thread (i.e., and oop allocation). In that case, we also have to make sure that
+// The flag: potential_vm_operation notifies if this particular safepoint state could potentially
+// invoke the vm-thread (e.g., an oop allocation). In that case, we also have to make sure that
// no threads which allow_vm_block's are held
void Thread::check_for_valid_safepoint_state(bool potential_vm_operation) {
// Check if current thread is allowed to block at a safepoint
@@ -1399,10 +1480,11 @@
void JavaThread::collect_counters(typeArrayOop array) {
if (JVMCICounterSize > 0) {
MutexLocker tl(Threads_lock);
+ JavaThreadIteratorWithHandle jtiwh;
for (int i = 0; i < array->length(); i++) {
array->long_at_put(i, _jvmci_old_thread_counters[i]);
}
- for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) {
+ for (; JavaThread *tp = jtiwh.next(); ) {
if (jvmci_counters_include(tp)) {
for (int i = 0; i < array->length(); i++) {
array->long_at_put(i, array->long_at(i) + tp->_jvmci_counters[i]);
@@ -1435,6 +1517,7 @@
clear_must_deopt_id();
set_monitor_chunks(NULL);
set_next(NULL);
+ _on_thread_list = false;
set_thread_state(_thread_new);
_terminated = _not_terminated;
_privileged_stack_top = NULL;
@@ -1715,12 +1798,12 @@
DTRACE_THREAD_PROBE(stop, this);
this->exit(false);
- delete this;
+ this->smr_delete();
}
static void ensure_join(JavaThread* thread) {
- // We do not need to grap the Threads_lock, since we are operating on ourself.
+ // We do not need to grab the Threads_lock, since we are operating on ourself.
Handle threadObj(thread, thread->threadObj());
assert(threadObj.not_null(), "java thread object must exist");
ObjectLocker lock(threadObj, thread);
@@ -1742,6 +1825,15 @@
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
assert(this == JavaThread::current(), "thread consistency check");
+ elapsedTimer _timer_exit_phase1;
+ elapsedTimer _timer_exit_phase2;
+ elapsedTimer _timer_exit_phase3;
+ elapsedTimer _timer_exit_phase4;
+
+ if (log_is_enabled(Debug, os, thread, timer)) {
+ _timer_exit_phase1.start();
+ }
+
HandleMark hm(this);
Handle uncaught_exception(this, this->pending_exception());
this->clear_pending_exception();
@@ -1841,12 +1933,20 @@
// before_exit() has already posted JVMTI THREAD_END events
}
+ if (log_is_enabled(Debug, os, thread, timer)) {
+ _timer_exit_phase1.stop();
+ _timer_exit_phase2.start();
+ }
// Notify waiters on thread object. This has to be done after exit() is called
// on the thread (if the thread is the last thread in a daemon ThreadGroup the
// group should have the destroyed bit set before waiters are notified).
ensure_join(this);
assert(!this->has_pending_exception(), "ensure_join should have cleared");
+ if (log_is_enabled(Debug, os, thread, timer)) {
+ _timer_exit_phase2.stop();
+ _timer_exit_phase3.start();
+ }
// 6282335 JNI DetachCurrentThread spec states that all Java monitors
// held by this thread must be released. The spec does not distinguish
// between JNI-acquired and regular Java monitors. We can only see
@@ -1914,12 +2014,26 @@
exit_type == JavaThread::normal_exit ? "exiting" : "detaching",
os::current_thread_id());
+ if (log_is_enabled(Debug, os, thread, timer)) {
+ _timer_exit_phase3.stop();
+ _timer_exit_phase4.start();
+ }
// Remove from list of active threads list, and notify VM thread if we are the last non-daemon thread
Threads::remove(this);
- // If someone set a handshake on us just as we entered exit path, we simple cancel it.
- if (ThreadLocalHandshakes) {
- cancel_handshake();
+ if (log_is_enabled(Debug, os, thread, timer)) {
+ _timer_exit_phase4.stop();
+ ResourceMark rm(this);
+ log_debug(os, thread, timer)("name='%s'"
+ ", exit-phase1=" JLONG_FORMAT
+ ", exit-phase2=" JLONG_FORMAT
+ ", exit-phase3=" JLONG_FORMAT
+ ", exit-phase4=" JLONG_FORMAT,
+ get_thread_name(),
+ _timer_exit_phase1.milliseconds(),
+ _timer_exit_phase2.milliseconds(),
+ _timer_exit_phase3.milliseconds(),
+ _timer_exit_phase4.milliseconds());
}
}
@@ -1980,7 +2094,7 @@
#endif // INCLUDE_ALL_GCS
Threads::remove(this);
- delete this;
+ this->smr_delete();
}
@@ -2235,10 +2349,9 @@
// + Target thread will not enter any new monitors
//
void JavaThread::java_suspend() {
- { MutexLocker mu(Threads_lock);
- if (!Threads::includes(this) || is_exiting() || this->threadObj() == NULL) {
- return;
- }
+ ThreadsListHandle tlh;
+ if (!tlh.includes(this) || threadObj() == NULL || is_exiting()) {
+ return;
}
{ MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag);
@@ -2327,14 +2440,8 @@
// verify the JavaThread has not yet been published in the Threads::list, and
// hence doesn't need protection from concurrent access at this stage
void JavaThread::verify_not_published() {
- if (!Threads_lock->owned_by_self()) {
- MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
- assert(!Threads::includes(this),
- "java thread shouldn't have been published yet!");
- } else {
- assert(!Threads::includes(this),
- "java thread shouldn't have been published yet!");
- }
+ ThreadsListHandle tlh;
+ assert(!tlh.includes(this), "JavaThread shouldn't have been published yet!");
}
#endif
@@ -2451,7 +2558,8 @@
// Sanity check: thread is gone, has started exiting or the thread
// was not externally suspended.
- if (!Threads::includes(this) || is_exiting() || !is_external_suspend()) {
+ ThreadsListHandle tlh;
+ if (!tlh.includes(this) || is_exiting() || !is_external_suspend()) {
return;
}
@@ -2925,6 +3033,13 @@
st->print(", stack(" PTR_FORMAT "," PTR_FORMAT ")",
p2i(stack_end()), p2i(stack_base()));
st->print("]");
+
+ if (_threads_hazard_ptr != NULL) {
+ st->print(" _threads_hazard_ptr=" INTPTR_FORMAT, p2i(_threads_hazard_ptr));
+ }
+ if (_nested_threads_hazard_ptr != NULL) {
+ print_nested_threads_hazard_ptrs_on(st);
+ }
return;
}
@@ -3318,23 +3433,136 @@
// ======= Threads ========
// The Threads class links together all active threads, and provides
-// operations over all threads. It is protected by its own Mutex
-// lock, which is also used in other contexts to protect thread
-// operations from having the thread being operated on from exiting
-// and going away unexpectedly (e.g., safepoint synchronization)
-
-JavaThread* Threads::_thread_list = NULL;
-int Threads::_number_of_threads = 0;
-int Threads::_number_of_non_daemon_threads = 0;
-int Threads::_return_code = 0;
-int Threads::_thread_claim_parity = 0;
-size_t JavaThread::_stack_size_at_create = 0;
+// operations over all threads. It is protected by the Threads_lock,
+// which is also used in other global contexts like safepointing.
+// ThreadsListHandles are used to safely perform operations on one
+// or more threads without the risk of the thread exiting during the
+// operation.
+//
+// Note: The Threads_lock is currently more widely used than we
+// would like. We are actively migrating Threads_lock uses to other
+// mechanisms in order to reduce Threads_lock contention.
+
+JavaThread* Threads::_thread_list = NULL;
+int Threads::_number_of_threads = 0;
+int Threads::_number_of_non_daemon_threads = 0;
+int Threads::_return_code = 0;
+int Threads::_thread_claim_parity = 0;
+size_t JavaThread::_stack_size_at_create = 0;
+// Safe Memory Reclamation (SMR) support:
+Monitor* Threads::_smr_delete_lock =
+ new Monitor(Monitor::special, "smr_delete_lock",
+ false /* allow_vm_block */,
+ Monitor::_safepoint_check_never);
+// The '_cnt', '_max' and '_times" fields are enabled via
+// -XX:+EnableThreadSMRStatistics:
+
+// # of parallel threads in _smr_delete_lock->wait().
+// Impl note: Hard to imagine > 64K waiting threads so this could be 16-bit,
+// but there is no nice 16-bit _FORMAT support.
+uint Threads::_smr_delete_lock_wait_cnt = 0;
+
+// Max # of parallel threads in _smr_delete_lock->wait().
+// Impl note: See _smr_delete_lock_wait_cnt note.
+uint Threads::_smr_delete_lock_wait_max = 0;
+
+// Flag to indicate when an _smr_delete_lock->notify() is needed.
+// Impl note: See _smr_delete_lock_wait_cnt note.
+volatile uint Threads::_smr_delete_notify = 0;
+
+// # of threads deleted over VM lifetime.
+// Impl note: Atomically incremented over VM lifetime so use unsigned for more
+// range. Unsigned 64-bit would be more future proof, but 64-bit atomic inc
+// isn't available everywhere (or is it?).
+volatile uint Threads::_smr_deleted_thread_cnt = 0;
+
+// Max time in millis to delete a thread.
+// Impl note: 16-bit might be too small on an overloaded machine. Use
+// unsigned since this is a time value. Set via Atomic::cmpxchg() in a
+// loop for correctness.
+volatile uint Threads::_smr_deleted_thread_time_max = 0;
+
+// Cumulative time in millis to delete threads.
+// Impl note: Atomically added to over VM lifetime so use unsigned for more
+// range. Unsigned 64-bit would be more future proof, but 64-bit atomic inc
+// isn't available everywhere (or is it?).
+volatile uint Threads::_smr_deleted_thread_times = 0;
+
+ThreadsList* volatile Threads::_smr_java_thread_list = new ThreadsList(0);
+
+// # of ThreadsLists allocated over VM lifetime.
+// Impl note: We allocate a new ThreadsList for every thread create and
+// every thread delete so we need a bigger type than the
+// _smr_deleted_thread_cnt field.
+uint64_t Threads::_smr_java_thread_list_alloc_cnt = 1;
+
+// # of ThreadsLists freed over VM lifetime.
+// Impl note: See _smr_java_thread_list_alloc_cnt note.
+uint64_t Threads::_smr_java_thread_list_free_cnt = 0;
+
+// Max size ThreadsList allocated.
+// Impl note: Max # of threads alive at one time should fit in unsigned 32-bit.
+uint Threads::_smr_java_thread_list_max = 0;
+
+// Max # of nested ThreadsLists for a thread.
+// Impl note: Hard to imagine > 64K nested ThreadsLists so this could be
+// 16-bit, but there is no nice 16-bit _FORMAT support.
+uint Threads::_smr_nested_thread_list_max = 0;
+
+// # of ThreadsListHandles deleted over VM lifetime.
+// Impl note: Atomically incremented over VM lifetime so use unsigned for
+// more range. There will be fewer ThreadsListHandles than threads so
+// unsigned 32-bit should be fine.
+volatile uint Threads::_smr_tlh_cnt = 0;
+
+// Max time in millis to delete a ThreadsListHandle.
+// Impl note: 16-bit might be too small on an overloaded machine. Use
+// unsigned since this is a time value. Set via Atomic::cmpxchg() in a
+// loop for correctness.
+volatile uint Threads::_smr_tlh_time_max = 0;
+
+// Cumulative time in millis to delete ThreadsListHandles.
+// Impl note: Atomically added to over VM lifetime so use unsigned for more
+// range. Unsigned 64-bit would be more future proof, but 64-bit atomic inc
+// isn't available everywhere (or is it?).
+volatile uint Threads::_smr_tlh_times = 0;
+
+ThreadsList* Threads::_smr_to_delete_list = NULL;
+
+// # of parallel ThreadsLists on the to-delete list.
+// Impl note: Hard to imagine > 64K ThreadsLists needing to be deleted so
+// this could be 16-bit, but there is no nice 16-bit _FORMAT support.
+uint Threads::_smr_to_delete_list_cnt = 0;
+
+// Max # of parallel ThreadsLists on the to-delete list.
+// Impl note: See _smr_to_delete_list_cnt note.
+uint Threads::_smr_to_delete_list_max = 0;
+
#ifdef ASSERT
-bool Threads::_vm_complete = false;
+bool Threads::_vm_complete = false;
#endif
+static inline void *prefetch_and_load_ptr(void **addr, intx prefetch_interval) {
+ Prefetch::read((void*)addr, prefetch_interval);
+ return *addr;
+}
+
+// Possibly the ugliest for loop the world has seen. C++ does not allow
+// multiple types in the declaration section of the for loop. In this case
+// we are only dealing with pointers and hence can cast them. It looks ugly
+// but macros are ugly and therefore it's fine to make things absurdly ugly.
+#define DO_JAVA_THREADS(LIST, X) \
+ for (JavaThread *MACRO_scan_interval = (JavaThread*)(uintptr_t)PrefetchScanIntervalInBytes, \
+ *MACRO_list = (JavaThread*)(LIST), \
+ **MACRO_end = ((JavaThread**)((ThreadsList*)MACRO_list)->threads()) + ((ThreadsList*)MACRO_list)->length(), \
+ **MACRO_current_p = (JavaThread**)((ThreadsList*)MACRO_list)->threads(), \
+ *X = (JavaThread*)prefetch_and_load_ptr((void**)MACRO_current_p, (intx)MACRO_scan_interval); \
+ MACRO_current_p != MACRO_end; \
+ MACRO_current_p++, \
+ X = (JavaThread*)prefetch_and_load_ptr((void**)MACRO_current_p, (intx)MACRO_scan_interval))
+
// All JavaThreads
-#define ALL_JAVA_THREADS(X) for (JavaThread* X = _thread_list; X; X = X->next())
+#define ALL_JAVA_THREADS(X) DO_JAVA_THREADS(get_smr_java_thread_list(), X)
// All JavaThreads + all non-JavaThreads (i.e., every thread in the system)
void Threads::threads_do(ThreadClosure* tc) {
@@ -3435,6 +3663,214 @@
vmSymbols::void_method_signature(), CHECK);
}
+// Safe Memory Reclamation (SMR) support:
+//
+
+// Acquire a stable ThreadsList.
+//
+ThreadsList *Threads::acquire_stable_list(Thread *self, bool is_ThreadsListSetter) {
+ assert(self != NULL, "sanity check");
+ // acquire_stable_list_nested_path() will grab the Threads_lock
+ // so let's make sure the ThreadsListHandle is in a safe place.
+ // ThreadsListSetter cannot make this check on this code path.
+ debug_only(if (!is_ThreadsListSetter && StrictSafepointChecks) self->check_for_valid_safepoint_state(/* potential_vm_operation */ false);)
+
+ if (self->get_threads_hazard_ptr() == NULL) {
+ // The typical case is first.
+ return acquire_stable_list_fast_path(self);
+ }
+
+ // The nested case is rare.
+ return acquire_stable_list_nested_path(self);
+}
+
+// Fast path (and lock free) way to acquire a stable ThreadsList.
+//
+ThreadsList *Threads::acquire_stable_list_fast_path(Thread *self) {
+ assert(self != NULL, "sanity check");
+ assert(self->get_threads_hazard_ptr() == NULL, "sanity check");
+ assert(self->get_nested_threads_hazard_ptr() == NULL,
+ "cannot have a nested hazard ptr with a NULL regular hazard ptr");
+
+ ThreadsList* threads;
+
+ // Stable recording of a hazard ptr for SMR. This code does not use
+ // locks so its use of the _smr_java_thread_list & _threads_hazard_ptr
+ // fields is racy relative to code that uses those fields with locks.
+ // OrderAccess and Atomic functions are used to deal with those races.
+ //
+ while (true) {
+ threads = get_smr_java_thread_list();
+
+ // Publish a tagged hazard ptr to denote that the hazard ptr is not
+ // yet verified as being stable. Due to the fence after the hazard
+ // ptr write, it will be sequentially consistent w.r.t. the
+ // sequentially consistent writes of the ThreadsList, even on
+ // non-multiple copy atomic machines where stores can be observed
+ // in different order from different observer threads.
+ ThreadsList* unverified_threads = Thread::tag_hazard_ptr(threads);
+ self->set_threads_hazard_ptr(unverified_threads);
+
+ // If _smr_java_thread_list has changed, we have lost a race with
+ // Threads::add() or Threads::remove() and have to try again.
+ if (get_smr_java_thread_list() != threads) {
+ continue;
+ }
+
+ // We try to remove the tag which will verify the hazard ptr as
+ // being stable. This exchange can race with a scanning thread
+ // which might invalidate the tagged hazard ptr to keep it from
+ // being followed to access JavaThread ptrs. If we lose the race,
+ // we simply retry. If we win the race, then the stable hazard
+ // ptr is officially published.
+ if (self->cmpxchg_threads_hazard_ptr(threads, unverified_threads) == unverified_threads) {
+ break;
+ }
+ }
+
+ // A stable hazard ptr has been published letting other threads know
+ // that the ThreadsList and the JavaThreads reachable from this list
+ // are protected and hence they should not be deleted until everyone
+ // agrees it is safe to do so.
+
+ return threads;
+}
+
+// Acquire a nested stable ThreadsList; this is rare so it uses
+// Threads_lock.
+//
+ThreadsList *Threads::acquire_stable_list_nested_path(Thread *self) {
+ assert(self != NULL, "sanity check");
+ assert(self->get_threads_hazard_ptr() != NULL,
+ "cannot have a NULL regular hazard ptr when acquiring a nested hazard ptr");
+
+ // The thread already has a hazard ptr (ThreadsList ref) so we need
+ // to create a nested ThreadsListHandle with the current ThreadsList
+ // since it might be different than our current hazard ptr. The need
+ // for a nested ThreadsListHandle is rare so we do this while holding
+ // the Threads_lock so we don't race with the scanning code; the code
+ // is so much simpler this way.
+
+ NestedThreadsList* node;
+ {
+ // Only grab the Threads_lock if we don't already own it.
+ MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock);
+ node = new NestedThreadsList(get_smr_java_thread_list());
+ // We insert at the front of the list to match up with the delete
+ // in release_stable_list().
+ node->set_next(self->get_nested_threads_hazard_ptr());
+ self->set_nested_threads_hazard_ptr(node);
+ if (EnableThreadSMRStatistics) {
+ self->inc_nested_threads_hazard_ptr_cnt();
+ if (self->nested_threads_hazard_ptr_cnt() > _smr_nested_thread_list_max) {
+ _smr_nested_thread_list_max = self->nested_threads_hazard_ptr_cnt();
+ }
+ }
+ }
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::acquire_stable_list: add NestedThreadsList node containing ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(node->t_list()));
+
+ return node->t_list();
+}
+
+// Release a stable ThreadsList.
+//
+void Threads::release_stable_list(Thread *self) {
+ assert(self != NULL, "sanity check");
+ // release_stable_list_nested_path() will grab the Threads_lock
+ // so let's make sure the ThreadsListHandle is in a safe place.
+ debug_only(if (StrictSafepointChecks) self->check_for_valid_safepoint_state(/* potential_vm_operation */ false);)
+
+ if (self->get_nested_threads_hazard_ptr() == NULL) {
+ // The typical case is first.
+ release_stable_list_fast_path(self);
+ return;
+ }
+
+ // The nested case is rare.
+ release_stable_list_nested_path(self);
+}
+
+// Fast path way to release a stable ThreadsList. The release portion
+// is lock-free, but the wake up portion is not.
+//
+void Threads::release_stable_list_fast_path(Thread *self) {
+ assert(self != NULL, "sanity check");
+ assert(self->get_threads_hazard_ptr() != NULL, "sanity check");
+ assert(self->get_nested_threads_hazard_ptr() == NULL,
+ "cannot have a nested hazard ptr when releasing a regular hazard ptr");
+
+ // After releasing the hazard ptr, other threads may go ahead and
+ // free up some memory temporarily used by a ThreadsList snapshot.
+ self->set_threads_hazard_ptr(NULL);
+
+ // We use double-check locking to reduce traffic on the system
+ // wide smr_delete_lock.
+ if (Threads::smr_delete_notify()) {
+ // An exiting thread might be waiting in smr_delete(); we need to
+ // check with smr_delete_lock to be sure.
+ release_stable_list_wake_up((char *) "regular hazard ptr");
+ }
+}
+
+// Release a nested stable ThreadsList; this is rare so it uses
+// Threads_lock.
+//
+void Threads::release_stable_list_nested_path(Thread *self) {
+ assert(self != NULL, "sanity check");
+ assert(self->get_nested_threads_hazard_ptr() != NULL, "sanity check");
+ assert(self->get_threads_hazard_ptr() != NULL,
+ "must have a regular hazard ptr to have nested hazard ptrs");
+
+ // We have a nested ThreadsListHandle so we have to release it first.
+ // The need for a nested ThreadsListHandle is rare so we do this while
+ // holding the Threads_lock so we don't race with the scanning code;
+ // the code is so much simpler this way.
+
+ NestedThreadsList *node;
+ {
+ // Only grab the Threads_lock if we don't already own it.
+ MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock);
+ // We remove from the front of the list to match up with the insert
+ // in acquire_stable_list().
+ node = self->get_nested_threads_hazard_ptr();
+ self->set_nested_threads_hazard_ptr(node->next());
+ if (EnableThreadSMRStatistics) {
+ self->dec_nested_threads_hazard_ptr_cnt();
+ }
+ }
+
+ // An exiting thread might be waiting in smr_delete(); we need to
+ // check with smr_delete_lock to be sure.
+ release_stable_list_wake_up((char *) "nested hazard ptr");
+
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::release_stable_list: delete NestedThreadsList node containing ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(node->t_list()));
+
+ delete node;
+}
+
+// Wake up portion of the release stable ThreadsList protocol;
+// uses the smr_delete_lock().
+//
+void Threads::release_stable_list_wake_up(char *log_str) {
+ assert(log_str != NULL, "sanity check");
+
+ // Note: smr_delete_lock is held in smr_delete() for the entire
+ // hazard ptr search so that we do not lose this notify() if
+ // the exiting thread has to wait. That code path also holds
+ // Threads_lock (which was grabbed before smr_delete_lock) so that
+ // threads_do() can be called. This means the system can't start a
+ // safepoint which means this thread can't take too long to get to
+ // a safepoint because of being blocked on smr_delete_lock.
+ //
+ MonitorLockerEx ml(Threads::smr_delete_lock(), Monitor::_no_safepoint_check_flag);
+ if (Threads::smr_delete_notify()) {
+ // Notify any exiting JavaThreads that are waiting in smr_delete()
+ // that we've released a ThreadsList.
+ ml.notify_all();
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::release_stable_list notified %s", os::current_thread_id(), log_str);
+ }
+}
+
void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) {
TraceTime timer("Initialize java.lang classes", TRACETIME_LOG(Info, startuptime));
@@ -3616,7 +4052,7 @@
if (!main_thread->set_as_starting_thread()) {
vm_shutdown_during_initialization(
"Failed necessary internal allocation. Out of swap space");
- delete main_thread;
+ main_thread->smr_delete();
*canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
return JNI_ENOMEM;
}
@@ -3631,7 +4067,7 @@
// Initialize global modules
jint status = init_globals();
if (status != JNI_OK) {
- delete main_thread;
+ main_thread->smr_delete();
*canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
return status;
}
@@ -4037,23 +4473,6 @@
}
}
-JavaThread* Threads::find_java_thread_from_java_tid(jlong java_tid) {
- assert(Threads_lock->owned_by_self(), "Must hold Threads_lock");
-
- JavaThread* java_thread = NULL;
- // Sequential search for now. Need to do better optimization later.
- for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) {
- oop tobj = thread->threadObj();
- if (!thread->is_exiting() &&
- tobj != NULL &&
- java_tid == java_lang_Thread::thread_id(tobj)) {
- java_thread = thread;
- break;
- }
- }
- return java_thread;
-}
-
// Last thread running calls java.lang.Shutdown.shutdown()
void JavaThread::invoke_shutdown_hooks() {
@@ -4179,6 +4598,11 @@
notify_vm_shutdown();
+ // We are after VM_Exit::set_vm_exited() so we can't call
+ // thread->smr_delete() or we will block on the Threads_lock.
+ // Deleting the shutdown thread here is safe because another
+ // JavaThread cannot have an active ThreadsListHandle for
+ // this JavaThread.
delete thread;
#if INCLUDE_JVMCI
@@ -4212,6 +4636,501 @@
return JNI_FALSE;
}
+// Hash table of pointers found by a scan. Used for collecting hazard
+// pointers (ThreadsList references). Also used for collecting JavaThreads
+// that are indirectly referenced by hazard ptrs. An instance of this
+// class only contains one type of pointer.
+//
+class ThreadScanHashtable : public CHeapObj<mtThread> {
+ private:
+ static bool ptr_equals(void * const& s1, void * const& s2) {
+ return s1 == s2;
+ }
+
+ static unsigned int ptr_hash(void * const& s1) {
+ // 2654435761 = 2^32 * Phi (golden ratio)
+ return (unsigned int)(((uint32_t)(uintptr_t)s1) * 2654435761u);
+ }
+
+ int _table_size;
+ // ResourceHashtable SIZE is specified at compile time so our
+ // dynamic _table_size is unused for now; 1031 is the first prime
+ // after 1024.
+ typedef ResourceHashtable<void *, int, &ThreadScanHashtable::ptr_hash,
+ &ThreadScanHashtable::ptr_equals, 1031,
+ ResourceObj::C_HEAP, mtThread> PtrTable;
+ PtrTable * _ptrs;
+
+ public:
+ // ResourceHashtable is passed to various functions and populated in
+ // different places so we allocate it using C_HEAP to make it immune
+ // from any ResourceMarks that happen to be in the code paths.
+ ThreadScanHashtable(int table_size) : _table_size(table_size), _ptrs(new (ResourceObj::C_HEAP, mtThread) PtrTable()) {}
+
+ ~ThreadScanHashtable() { delete _ptrs; }
+
+ bool has_entry(void *pointer) {
+ int *val_ptr = _ptrs->get(pointer);
+ return val_ptr != NULL && *val_ptr == 1;
+ }
+
+ void add_entry(void *pointer) {
+ _ptrs->put(pointer, 1);
+ }
+};
+
+// Closure to gather JavaThreads indirectly referenced by hazard ptrs
+// (ThreadsList references) into a hash table. This closure handles part 2
+// of the dance - adding all the JavaThreads referenced by the hazard
+// pointer (ThreadsList reference) to the hash table.
+//
+class AddThreadHazardPointerThreadClosure : public ThreadClosure {
+ private:
+ ThreadScanHashtable *_table;
+
+ public:
+ AddThreadHazardPointerThreadClosure(ThreadScanHashtable *table) : _table(table) {}
+
+ virtual void do_thread(Thread *thread) {
+ if (!_table->has_entry((void*)thread)) {
+ // The same JavaThread might be on more than one ThreadsList or
+ // more than one thread might be using the same ThreadsList. In
+ // either case, we only need a single entry for a JavaThread.
+ _table->add_entry((void*)thread);
+ }
+ }
+};
+
+// Closure to gather JavaThreads indirectly referenced by hazard ptrs
+// (ThreadsList references) into a hash table. This closure handles part 1
+// of the dance - hazard ptr chain walking and dispatch to another
+// closure.
+//
+class ScanHazardPtrGatherProtectedThreadsClosure : public ThreadClosure {
+ private:
+ ThreadScanHashtable *_table;
+ public:
+ ScanHazardPtrGatherProtectedThreadsClosure(ThreadScanHashtable *table) : _table(table) {}
+
+ virtual void do_thread(Thread *thread) {
+ assert_locked_or_safepoint(Threads_lock);
+
+ if (thread == NULL) return;
+
+ // This code races with Threads::acquire_stable_list() which is
+ // lock-free so we have to handle some special situations.
+ //
+ ThreadsList *current_list = NULL;
+ while (true) {
+ current_list = thread->get_threads_hazard_ptr();
+ // No hazard ptr so nothing more to do.
+ if (current_list == NULL) {
+ assert(thread->get_nested_threads_hazard_ptr() == NULL,
+ "cannot have a nested hazard ptr with a NULL regular hazard ptr");
+ return;
+ }
+
+ // If the hazard ptr is verified as stable (since it is not tagged),
+ // then it is safe to use.
+ if (!Thread::is_hazard_ptr_tagged(current_list)) break;
+
+ // The hazard ptr is tagged as not yet verified as being stable
+ // so we are racing with acquire_stable_list(). This exchange
+ // attempts to invalidate the hazard ptr. If we win the race,
+ // then we can ignore this unstable hazard ptr and the other
+ // thread will retry the attempt to publish a stable hazard ptr.
+ // If we lose the race, then we retry our attempt to look at the
+ // hazard ptr.
+ if (thread->cmpxchg_threads_hazard_ptr(NULL, current_list) == current_list) return;
+ }
+
+ // The current JavaThread has a hazard ptr (ThreadsList reference)
+ // which might be _smr_java_thread_list or it might be an older
+ // ThreadsList that has been removed but not freed. In either case,
+ // the hazard ptr is protecting all the JavaThreads on that
+ // ThreadsList.
+ AddThreadHazardPointerThreadClosure add_cl(_table);
+ current_list->threads_do(&add_cl);
+
+ // Any NestedThreadsLists are also protecting JavaThreads so
+ // gather those also; the ThreadsLists may be different.
+ for (NestedThreadsList* node = thread->get_nested_threads_hazard_ptr();
+ node != NULL; node = node->next()) {
+ node->t_list()->threads_do(&add_cl);
+ }
+ }
+};
+
+// Closure to print JavaThreads that have a hazard ptr (ThreadsList
+// reference) that contains an indirect reference to a specific JavaThread.
+//
+class ScanHazardPtrPrintMatchingThreadsClosure : public ThreadClosure {
+ private:
+ JavaThread *_thread;
+ public:
+ ScanHazardPtrPrintMatchingThreadsClosure(JavaThread *thread) : _thread(thread) {}
+
+ virtual void do_thread(Thread *thread) {
+ assert_locked_or_safepoint(Threads_lock);
+
+ if (thread == NULL) return;
+ ThreadsList *current_list = thread->get_threads_hazard_ptr();
+ if (current_list == NULL) {
+ assert(thread->get_nested_threads_hazard_ptr() == NULL,
+ "cannot have a nested hazard ptr with a NULL regular hazard ptr");
+ return;
+ }
+ // If the hazard ptr is unverified, then ignore it.
+ if (Thread::is_hazard_ptr_tagged(current_list)) return;
+
+ // The current JavaThread has a hazard ptr (ThreadsList reference)
+ // which might be _smr_java_thread_list or it might be an older
+ // ThreadsList that has been removed but not freed. In either case,
+ // the hazard ptr is protecting all the JavaThreads on that
+ // ThreadsList, but we only care about matching a specific JavaThread.
+ DO_JAVA_THREADS(current_list, p) {
+ if (p == _thread) {
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread1=" INTPTR_FORMAT " has a hazard pointer for thread2=" INTPTR_FORMAT, os::current_thread_id(), p2i(thread), p2i(_thread));
+ break;
+ }
+ }
+
+ // Any NestedThreadsLists are also protecting JavaThreads so
+ // check those also; the ThreadsLists may be different.
+ for (NestedThreadsList* node = thread->get_nested_threads_hazard_ptr();
+ node != NULL; node = node->next()) {
+ DO_JAVA_THREADS(node->t_list(), p) {
+ if (p == _thread) {
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread1=" INTPTR_FORMAT " has a nested hazard pointer for thread2=" INTPTR_FORMAT, os::current_thread_id(), p2i(thread), p2i(_thread));
+ return;
+ }
+ }
+ }
+ }
+};
+
+// Return true if the specified JavaThread is protected by a hazard
+// pointer (ThreadsList reference). Otherwise, returns false.
+//
+bool Threads::is_a_protected_JavaThread(JavaThread *thread) {
+ assert_locked_or_safepoint(Threads_lock);
+
+ // Hash table size should be first power of two higher than twice
+ // the length of the Threads list.
+ int hash_table_size = MIN2(_number_of_threads, 32) << 1;
+ hash_table_size--;
+ hash_table_size |= hash_table_size >> 1;
+ hash_table_size |= hash_table_size >> 2;
+ hash_table_size |= hash_table_size >> 4;
+ hash_table_size |= hash_table_size >> 8;
+ hash_table_size |= hash_table_size >> 16;
+ hash_table_size++;
+
+ // Gather a hash table of the JavaThreads indirectly referenced by
+ // hazard ptrs.
+ ThreadScanHashtable *scan_table = new ThreadScanHashtable(hash_table_size);
+ ScanHazardPtrGatherProtectedThreadsClosure scan_cl(scan_table);
+ Threads::threads_do(&scan_cl);
+
+ bool thread_is_protected = false;
+ if (scan_table->has_entry((void*)thread)) {
+ thread_is_protected = true;
+ }
+ delete scan_table;
+ return thread_is_protected;
+}
+
+// Safely delete a JavaThread when it is no longer in use by a
+// ThreadsListHandle.
+//
+void Threads::smr_delete(JavaThread *thread) {
+ assert(!Threads_lock->owned_by_self(), "sanity");
+
+ bool has_logged_once = false;
+ elapsedTimer timer;
+ if (EnableThreadSMRStatistics) {
+ timer.start();
+ }
+
+ while (true) {
+ {
+ // No safepoint check because this JavaThread is not on the
+ // Threads list.
+ MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
+ // Cannot use a MonitorLockerEx helper here because we have
+ // to drop the Threads_lock first if we wait.
+ Threads::smr_delete_lock()->lock_without_safepoint_check();
+ // Set the smr_delete_notify flag after we grab smr_delete_lock
+ // and before we scan hazard ptrs because we're doing
+ // double-check locking in release_stable_list().
+ Threads::set_smr_delete_notify();
+
+ if (!is_a_protected_JavaThread(thread)) {
+ // This is the common case.
+ Threads::clear_smr_delete_notify();
+ Threads::smr_delete_lock()->unlock();
+ break;
+ }
+ if (!has_logged_once) {
+ has_logged_once = true;
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread=" INTPTR_FORMAT " is not deleted.", os::current_thread_id(), p2i(thread));
+ if (log_is_enabled(Debug, os, thread)) {
+ ScanHazardPtrPrintMatchingThreadsClosure scan_cl(thread);
+ Threads::threads_do(&scan_cl);
+ }
+ }
+ } // We have to drop the Threads_lock to wait or delete the thread
+
+ if (EnableThreadSMRStatistics) {
+ _smr_delete_lock_wait_cnt++;
+ if (_smr_delete_lock_wait_cnt > _smr_delete_lock_wait_max) {
+ _smr_delete_lock_wait_max = _smr_delete_lock_wait_cnt;
+ }
+ }
+ // Wait for a release_stable_list() call before we check again. No
+ // safepoint check, no timeout, and not as suspend equivalent flag
+ // because this JavaThread is not on the Threads list.
+ Threads::smr_delete_lock()->wait(Mutex::_no_safepoint_check_flag, 0,
+ !Mutex::_as_suspend_equivalent_flag);
+ if (EnableThreadSMRStatistics) {
+ _smr_delete_lock_wait_cnt--;
+ }
+
+ Threads::clear_smr_delete_notify();
+ Threads::smr_delete_lock()->unlock();
+ // Retry the whole scenario.
+ }
+
+ if (ThreadLocalHandshakes) {
+ // The thread is about to be deleted so cancel any handshake.
+ thread->cancel_handshake();
+ }
+
+ delete thread;
+ if (EnableThreadSMRStatistics) {
+ timer.stop();
+ uint millis = (uint)timer.milliseconds();
+ Threads::inc_smr_deleted_thread_cnt();
+ Threads::add_smr_deleted_thread_times(millis);
+ Threads::update_smr_deleted_thread_time_max(millis);
+ }
+
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread=" INTPTR_FORMAT " is deleted.", os::current_thread_id(), p2i(thread));
+}
+
+bool Threads::smr_delete_notify() {
+ // Use load_acquire() in order to see any updates to _smr_delete_notify
+ // earlier than when smr_delete_lock is grabbed.
+ return (OrderAccess::load_acquire(&_smr_delete_notify) != 0);
+}
+
+// set_smr_delete_notify() and clear_smr_delete_notify() are called
+// under the protection of the smr_delete_lock, but we also use an
+// Atomic operation to ensure the memory update is seen earlier than
+// when the smr_delete_lock is dropped.
+//
+void Threads::set_smr_delete_notify() {
+ Atomic::inc(&_smr_delete_notify);
+}
+
+void Threads::clear_smr_delete_notify() {
+ Atomic::dec(&_smr_delete_notify);
+}
+
+// Closure to gather hazard ptrs (ThreadsList references) into a hash table.
+//
+class ScanHazardPtrGatherThreadsListClosure : public ThreadClosure {
+ private:
+ ThreadScanHashtable *_table;
+ public:
+ ScanHazardPtrGatherThreadsListClosure(ThreadScanHashtable *table) : _table(table) {}
+
+ virtual void do_thread(Thread* thread) {
+ assert_locked_or_safepoint(Threads_lock);
+
+ if (thread == NULL) return;
+ ThreadsList *threads = thread->get_threads_hazard_ptr();
+ if (threads == NULL) {
+ assert(thread->get_nested_threads_hazard_ptr() == NULL,
+ "cannot have a nested hazard ptr with a NULL regular hazard ptr");
+ return;
+ }
+ // In this closure we always ignore the tag that might mark this
+ // hazard ptr as not yet verified. If we happen to catch an
+ // unverified hazard ptr that is subsequently discarded (not
+ // published), then the only side effect is that we might keep a
+ // to-be-deleted ThreadsList alive a little longer.
+ threads = Thread::untag_hazard_ptr(threads);
+ if (!_table->has_entry((void*)threads)) {
+ _table->add_entry((void*)threads);
+ }
+
+ // Any NestedThreadsLists are also protecting JavaThreads so
+ // gather those also; the ThreadsLists may be different.
+ for (NestedThreadsList* node = thread->get_nested_threads_hazard_ptr();
+ node != NULL; node = node->next()) {
+ threads = node->t_list();
+ if (!_table->has_entry((void*)threads)) {
+ _table->add_entry((void*)threads);
+ }
+ }
+ }
+};
+
+// Safely free a ThreadsList after a Threads::add() or Threads::remove().
+// The specified ThreadsList may not get deleted during this call if it
+// is still in-use (referenced by a hazard ptr). Other ThreadsLists
+// in the chain may get deleted by this call if they are no longer in-use.
+void Threads::smr_free_list(ThreadsList* threads) {
+ assert_locked_or_safepoint(Threads_lock);
+
+ threads->set_next_list(_smr_to_delete_list);
+ _smr_to_delete_list = threads;
+ if (EnableThreadSMRStatistics) {
+ _smr_to_delete_list_cnt++;
+ if (_smr_to_delete_list_cnt > _smr_to_delete_list_max) {
+ _smr_to_delete_list_max = _smr_to_delete_list_cnt;
+ }
+ }
+
+ // Hash table size should be first power of two higher than twice the length of the ThreadsList
+ int hash_table_size = MIN2(_number_of_threads, 32) << 1;
+ hash_table_size--;
+ hash_table_size |= hash_table_size >> 1;
+ hash_table_size |= hash_table_size >> 2;
+ hash_table_size |= hash_table_size >> 4;
+ hash_table_size |= hash_table_size >> 8;
+ hash_table_size |= hash_table_size >> 16;
+ hash_table_size++;
+
+ // Gather a hash table of the current hazard ptrs:
+ ThreadScanHashtable *scan_table = new ThreadScanHashtable(hash_table_size);
+ ScanHazardPtrGatherThreadsListClosure scan_cl(scan_table);
+ Threads::threads_do(&scan_cl);
+
+ // Walk through the linked list of pending freeable ThreadsLists
+ // and free the ones that are not referenced from hazard ptrs.
+ ThreadsList* current = _smr_to_delete_list;
+ ThreadsList* prev = NULL;
+ ThreadsList* next = NULL;
+ bool threads_is_freed = false;
+ while (current != NULL) {
+ next = current->next_list();
+ if (!scan_table->has_entry((void*)current)) {
+ // This ThreadsList is not referenced by a hazard ptr.
+ if (prev != NULL) {
+ prev->set_next_list(next);
+ }
+ if (_smr_to_delete_list == current) {
+ _smr_to_delete_list = next;
+ }
+
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_free_list: threads=" INTPTR_FORMAT " is freed.", os::current_thread_id(), p2i(current));
+ if (current == threads) threads_is_freed = true;
+ delete current;
+ if (EnableThreadSMRStatistics) {
+ _smr_java_thread_list_free_cnt++;
+ _smr_to_delete_list_cnt--;
+ }
+ } else {
+ prev = current;
+ }
+ current = next;
+ }
+
+ if (!threads_is_freed) {
+ // Only report "is not freed" on the original call to
+ // smr_free_list() for this ThreadsList.
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_free_list: threads=" INTPTR_FORMAT " is not freed.", os::current_thread_id(), p2i(threads));
+ }
+
+ delete scan_table;
+}
+
+// Remove a JavaThread from a ThreadsList. The returned ThreadsList is a
+// new copy of the specified ThreadsList with the specified JavaThread
+// removed.
+ThreadsList *ThreadsList::remove_thread(ThreadsList* list, JavaThread* java_thread) {
+ assert(list->_length > 0, "sanity");
+
+ uint i = 0;
+ DO_JAVA_THREADS(list, current) {
+ if (current == java_thread) {
+ break;
+ }
+ i++;
+ }
+ assert(i < list->_length, "did not find JavaThread on the list");
+ const uint index = i;
+ const uint new_length = list->_length - 1;
+ const uint head_length = index;
+ const uint tail_length = (new_length >= index) ? (new_length - index) : 0;
+ ThreadsList *const new_list = new ThreadsList(new_length);
+
+ if (head_length > 0) {
+ Copy::disjoint_words((HeapWord*)list->_threads, (HeapWord*)new_list->_threads, head_length);
+ }
+ if (tail_length > 0) {
+ Copy::disjoint_words((HeapWord*)list->_threads + index + 1, (HeapWord*)new_list->_threads + index, tail_length);
+ }
+
+ return new_list;
+}
+
+// Add a JavaThread to a ThreadsList. The returned ThreadsList is a
+// new copy of the specified ThreadsList with the specified JavaThread
+// appended to the end.
+ThreadsList *ThreadsList::add_thread(ThreadsList *list, JavaThread *java_thread) {
+ const uint index = list->_length;
+ const uint new_length = index + 1;
+ const uint head_length = index;
+ ThreadsList *const new_list = new ThreadsList(new_length);
+
+ if (head_length > 0) {
+ Copy::disjoint_words((HeapWord*)list->_threads, (HeapWord*)new_list->_threads, head_length);
+ }
+ *(JavaThread**)(new_list->_threads + index) = java_thread;
+
+ return new_list;
+}
+
+int ThreadsList::find_index_of_JavaThread(JavaThread *target) {
+ if (target == NULL) {
+ return -1;
+ }
+ for (uint i = 0; i < length(); i++) {
+ if (target == thread_at(i)) {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
+JavaThread* ThreadsList::find_JavaThread_from_java_tid(jlong java_tid) const {
+ DO_JAVA_THREADS(this, thread) {
+ oop tobj = thread->threadObj();
+ // Ignore the thread if it hasn't run yet, has exited
+ // or is starting to exit.
+ if (tobj != NULL && !thread->is_exiting() &&
+ java_tid == java_lang_Thread::thread_id(tobj)) {
+ // found a match
+ return thread;
+ }
+ }
+ return NULL;
+}
+
+bool ThreadsList::includes(const JavaThread * const p) const {
+ if (p == NULL) {
+ return false;
+ }
+ DO_JAVA_THREADS(this, q) {
+ if (q == p) {
+ return true;
+ }
+ }
+ return false;
+}
void Threads::add(JavaThread* p, bool force_daemon) {
// The threads lock must be owned at this point
@@ -4222,6 +5141,11 @@
p->initialize_queues();
p->set_next(_thread_list);
_thread_list = p;
+
+ // Once a JavaThread is added to the Threads list, smr_delete() has
+ // to be used to delete it. Otherwise we can just delete it directly.
+ p->set_on_thread_list();
+
_number_of_threads++;
oop threadObj = p->threadObj();
bool daemon = true;
@@ -4234,6 +5158,20 @@
ThreadService::add_thread(p, daemon);
+ // Maintain fast thread list
+ ThreadsList *new_list = ThreadsList::add_thread(get_smr_java_thread_list(), p);
+ if (EnableThreadSMRStatistics) {
+ _smr_java_thread_list_alloc_cnt++;
+ if (new_list->length() > _smr_java_thread_list_max) {
+ _smr_java_thread_list_max = new_list->length();
+ }
+ }
+ // Initial _smr_java_thread_list will not generate a "Threads::add" mesg.
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::add: new ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(new_list));
+
+ ThreadsList *old_list = xchg_smr_java_thread_list(new_list);
+ smr_free_list(old_list);
+
// Possible GC point.
Events::log(p, "Thread added: " INTPTR_FORMAT, p2i(p));
}
@@ -4247,7 +5185,20 @@
// that we do not remove thread without safepoint code notice
{ MutexLocker ml(Threads_lock);
- assert(includes(p), "p must be present");
+ assert(get_smr_java_thread_list()->includes(p), "p must be present");
+
+ // Maintain fast thread list
+ ThreadsList *new_list = ThreadsList::remove_thread(get_smr_java_thread_list(), p);
+ if (EnableThreadSMRStatistics) {
+ _smr_java_thread_list_alloc_cnt++;
+ // This list is smaller so no need to check for a "longest" update.
+ }
+
+ // Final _smr_java_thread_list will not generate a "Threads::remove" mesg.
+ log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::remove: new ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(new_list));
+
+ ThreadsList *old_list = xchg_smr_java_thread_list(new_list);
+ smr_free_list(old_list);
JavaThread* current = _thread_list;
JavaThread* prev = NULL;
@@ -4262,6 +5213,7 @@
} else {
_thread_list = p->next();
}
+
_number_of_threads--;
oop threadObj = p->threadObj();
bool daemon = true;
@@ -4288,17 +5240,6 @@
Events::log(p, "Thread exited: " INTPTR_FORMAT, p2i(p));
}
-// Threads_lock must be held when this is called (or must be called during a safepoint)
-bool Threads::includes(JavaThread* p) {
- assert(Threads_lock->is_locked(), "sanity check");
- ALL_JAVA_THREADS(q) {
- if (q == p) {
- return true;
- }
- }
- return false;
-}
-
// Operations on the Threads list for GC. These are not explicitly locked,
// but the garbage collector must provide a safe context for them to run.
// In particular, these things should never be called when the Threads_lock
@@ -4411,47 +5352,36 @@
// Get count Java threads that are waiting to enter the specified monitor.
-GrowableArray<JavaThread*>* Threads::get_pending_threads(int count,
- address monitor,
- bool doLock) {
- assert(doLock || SafepointSynchronize::is_at_safepoint(),
- "must grab Threads_lock or be at safepoint");
+GrowableArray<JavaThread*>* Threads::get_pending_threads(ThreadsList * t_list,
+ int count,
+ address monitor) {
GrowableArray<JavaThread*>* result = new GrowableArray<JavaThread*>(count);
int i = 0;
- {
- MutexLockerEx ml(doLock ? Threads_lock : NULL);
- ALL_JAVA_THREADS(p) {
- if (!p->can_call_java()) continue;
-
- address pending = (address)p->current_pending_monitor();
- if (pending == monitor) { // found a match
- if (i < count) result->append(p); // save the first count matches
- i++;
- }
+ DO_JAVA_THREADS(t_list, p) {
+ if (!p->can_call_java()) continue;
+
+ address pending = (address)p->current_pending_monitor();
+ if (pending == monitor) { // found a match
+ if (i < count) result->append(p); // save the first count matches
+ i++;
}
}
+
return result;
}
-JavaThread *Threads::owning_thread_from_monitor_owner(address owner,
- bool doLock) {
- assert(doLock ||
- Threads_lock->owned_by_self() ||
- SafepointSynchronize::is_at_safepoint(),
- "must grab Threads_lock or be at safepoint");
-
+JavaThread *Threads::owning_thread_from_monitor_owner(ThreadsList * t_list,
+ address owner) {
// NULL owner means not locked so we can skip the search
if (owner == NULL) return NULL;
- {
- MutexLockerEx ml(doLock ? Threads_lock : NULL);
- ALL_JAVA_THREADS(p) {
- // first, see if owner is the address of a Java thread
- if (owner == (address)p) return p;
- }
- }
+ DO_JAVA_THREADS(t_list, p) {
+ // first, see if owner is the address of a Java thread
+ if (owner == (address)p) return p;
+ }
+
// Cannot assert on lack of success here since this function may be
// used by code that is trying to report useful problem information
// like deadlock detection.
@@ -4462,15 +5392,13 @@
// Lock Word in the owning Java thread's stack.
//
JavaThread* the_owner = NULL;
- {
- MutexLockerEx ml(doLock ? Threads_lock : NULL);
- ALL_JAVA_THREADS(q) {
- if (q->is_lock_owned(owner)) {
- the_owner = q;
- break;
- }
+ DO_JAVA_THREADS(t_list, q) {
+ if (q->is_lock_owned(owner)) {
+ the_owner = q;
+ break;
}
}
+
// cannot assert on lack of success here; see above comment
return the_owner;
}
@@ -4495,6 +5423,9 @@
}
#endif // INCLUDE_SERVICES
+ print_smr_info_on(st);
+ st->cr();
+
ALL_JAVA_THREADS(p) {
ResourceMark rm;
p->print_on(st);
@@ -4521,9 +5452,105 @@
wt->print_on(st);
st->cr();
}
+
st->flush();
}
+// Log Threads class SMR info.
+void Threads::log_smr_statistics() {
+ LogTarget(Info, thread, smr) log;
+ if (log.is_enabled()) {
+ LogStream out(log);
+ print_smr_info_on(&out);
+ }
+}
+
+// Print Threads class SMR info.
+void Threads::print_smr_info_on(outputStream* st) {
+ // Only grab the Threads_lock if we don't already own it
+ // and if we are not reporting an error.
+ MutexLockerEx ml((Threads_lock->owned_by_self() || VMError::is_error_reported()) ? NULL : Threads_lock);
+
+ st->print_cr("Threads class SMR info:");
+ st->print_cr("_smr_java_thread_list=" INTPTR_FORMAT ", length=%u, "
+ "elements={", p2i(_smr_java_thread_list),
+ _smr_java_thread_list->length());
+ print_smr_info_elements_on(st, _smr_java_thread_list);
+ st->print_cr("}");
+ if (_smr_to_delete_list != NULL) {
+ st->print_cr("_smr_to_delete_list=" INTPTR_FORMAT ", length=%u, "
+ "elements={", p2i(_smr_to_delete_list),
+ _smr_to_delete_list->length());
+ print_smr_info_elements_on(st, _smr_to_delete_list);
+ st->print_cr("}");
+ for (ThreadsList *t_list = _smr_to_delete_list->next_list();
+ t_list != NULL; t_list = t_list->next_list()) {
+ st->print("next-> " INTPTR_FORMAT ", length=%u, "
+ "elements={", p2i(t_list), t_list->length());
+ print_smr_info_elements_on(st, t_list);
+ st->print_cr("}");
+ }
+ }
+ if (!EnableThreadSMRStatistics) {
+ return;
+ }
+ st->print_cr("_smr_java_thread_list_alloc_cnt=" UINT64_FORMAT ","
+ "_smr_java_thread_list_free_cnt=" UINT64_FORMAT ","
+ "_smr_java_thread_list_max=%u, "
+ "_smr_nested_thread_list_max=%u",
+ _smr_java_thread_list_alloc_cnt,
+ _smr_java_thread_list_free_cnt,
+ _smr_java_thread_list_max,
+ _smr_nested_thread_list_max);
+ if (_smr_tlh_cnt > 0) {
+ st->print_cr("_smr_tlh_cnt=%u"
+ ", _smr_tlh_times=%u"
+ ", avg_smr_tlh_time=%0.2f"
+ ", _smr_tlh_time_max=%u",
+ _smr_tlh_cnt, _smr_tlh_times,
+ ((double) _smr_tlh_times / _smr_tlh_cnt),
+ _smr_tlh_time_max);
+ }
+ if (_smr_deleted_thread_cnt > 0) {
+ st->print_cr("_smr_deleted_thread_cnt=%u"
+ ", _smr_deleted_thread_times=%u"
+ ", avg_smr_deleted_thread_time=%0.2f"
+ ", _smr_deleted_thread_time_max=%u",
+ _smr_deleted_thread_cnt, _smr_deleted_thread_times,
+ ((double) _smr_deleted_thread_times / _smr_deleted_thread_cnt),
+ _smr_deleted_thread_time_max);
+ }
+ st->print_cr("_smr_delete_lock_wait_cnt=%u, _smr_delete_lock_wait_max=%u",
+ _smr_delete_lock_wait_cnt, _smr_delete_lock_wait_max);
+ st->print_cr("_smr_to_delete_list_cnt=%u, _smr_to_delete_list_max=%u",
+ _smr_to_delete_list_cnt, _smr_to_delete_list_max);
+}
+
+// Print ThreadsList elements (4 per line).
+void Threads::print_smr_info_elements_on(outputStream* st,
+ ThreadsList* t_list) {
+ uint cnt = 0;
+ JavaThreadIterator jti(t_list);
+ for (JavaThread *jt = jti.first(); jt != NULL; jt = jti.next()) {
+ st->print(INTPTR_FORMAT, p2i(jt));
+ if (cnt < t_list->length() - 1) {
+ // Separate with comma or comma-space except for the last one.
+ if (((cnt + 1) % 4) == 0) {
+ // Four INTPTR_FORMAT fit on an 80 column line so end the
+ // current line with just a comma.
+ st->print_cr(",");
+ } else {
+ // Not the last one on the current line so use comma-space:
+ st->print(", ");
+ }
+ } else {
+ // Last one so just end the current line.
+ st->cr();
+ }
+ cnt++;
+ }
+}
+
void Threads::print_on_error(Thread* this_thread, outputStream* st, Thread* current, char* buf,
int buflen, bool* found_current) {
if (this_thread != NULL) {
@@ -4560,6 +5587,9 @@
// memory (even in resource area), it might deadlock the error handler.
void Threads::print_on_error(outputStream* st, Thread* current, char* buf,
int buflen) {
+ print_smr_info_on(st);
+ st->cr();
+
bool found_current = false;
st->print_cr("Java Threads: ( => current thread )");
ALL_JAVA_THREADS(thread) {
@@ -4581,6 +5611,7 @@
st->cr();
}
st->cr();
+
st->print_cr("Threads with active compile tasks:");
print_threads_compiling(st, buf, buflen);
}
--- a/src/hotspot/share/runtime/thread.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/thread.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -57,6 +57,8 @@
#endif
class ThreadSafepointState;
+class ThreadsList;
+class NestedThreadsList;
class JvmtiThreadState;
class JvmtiGetLoadedClassesClosure;
@@ -101,6 +103,7 @@
// - WatcherThread
class Thread: public ThreadShadow {
+ friend class Threads;
friend class VMStructs;
friend class JVMCIVMStructs;
private:
@@ -118,6 +121,47 @@
protected:
// Support for forcing alignment of thread objects for biased locking
void* _real_malloc_address;
+ // JavaThread lifecycle support:
+ friend class ScanHazardPtrGatherProtectedThreadsClosure;
+ friend class ScanHazardPtrGatherThreadsListClosure;
+ friend class ScanHazardPtrPrintMatchingThreadsClosure;
+ friend class ThreadsListHandle;
+ friend class ThreadsListSetter;
+ ThreadsList* volatile _threads_hazard_ptr;
+ ThreadsList* cmpxchg_threads_hazard_ptr(ThreadsList* exchange_value, ThreadsList* compare_value);
+ ThreadsList* get_threads_hazard_ptr();
+ void set_threads_hazard_ptr(ThreadsList* new_list);
+ static bool is_hazard_ptr_tagged(ThreadsList* list) {
+ return (intptr_t(list) & intptr_t(1)) == intptr_t(1);
+ }
+ static ThreadsList* tag_hazard_ptr(ThreadsList* list) {
+ return (ThreadsList*)(intptr_t(list) | intptr_t(1));
+ }
+ static ThreadsList* untag_hazard_ptr(ThreadsList* list) {
+ return (ThreadsList*)(intptr_t(list) & ~intptr_t(1));
+ }
+ NestedThreadsList* _nested_threads_hazard_ptr;
+ NestedThreadsList* get_nested_threads_hazard_ptr() {
+ return _nested_threads_hazard_ptr;
+ }
+ void set_nested_threads_hazard_ptr(NestedThreadsList* value) {
+ assert(Threads_lock->owned_by_self(),
+ "must own Threads_lock for _nested_threads_hazard_ptr to be valid.");
+ _nested_threads_hazard_ptr = value;
+ }
+ // This field is enabled via -XX:+EnableThreadSMRStatistics:
+ uint _nested_threads_hazard_ptr_cnt;
+ void dec_nested_threads_hazard_ptr_cnt() {
+ assert(_nested_threads_hazard_ptr_cnt != 0, "mismatched {dec,inc}_nested_threads_hazard_ptr_cnt()");
+ _nested_threads_hazard_ptr_cnt--;
+ }
+ void inc_nested_threads_hazard_ptr_cnt() {
+ _nested_threads_hazard_ptr_cnt++;
+ }
+ uint nested_threads_hazard_ptr_cnt() {
+ return _nested_threads_hazard_ptr_cnt;
+ }
+
public:
void* operator new(size_t size) throw() { return allocate(size, true); }
void* operator new(size_t size, const std::nothrow_t& nothrow_constant) throw() {
@@ -359,6 +403,9 @@
static inline Thread* current_or_null_safe();
// Common thread operations
+#ifdef ASSERT
+ static void check_for_dangling_thread_pointer(Thread *thread);
+#endif
static void set_priority(Thread* thread, ThreadPriority priority);
static ThreadPriority get_priority(const Thread* const thread);
static void start(Thread* thread);
@@ -576,6 +623,7 @@
// Printing
virtual void print_on(outputStream* st) const;
+ virtual void print_nested_threads_hazard_ptrs_on(outputStream* st) const;
void print() const { print_on(tty); }
virtual void print_on_error(outputStream* st, char* buf, int buflen) const;
void print_value_on(outputStream* st) const;
@@ -798,6 +846,7 @@
friend class WhiteBox;
private:
JavaThread* _next; // The next thread in the Threads list
+ bool _on_thread_list; // Is set when this JavaThread is added to the Threads list
oop _threadObj; // The Java level thread object
#ifdef ASSERT
@@ -1125,15 +1174,23 @@
void set_safepoint_state(ThreadSafepointState *state) { _safepoint_state = state; }
bool is_at_poll_safepoint() { return _safepoint_state->is_at_poll_safepoint(); }
+ // JavaThread termination and lifecycle support:
+ void smr_delete();
+ bool on_thread_list() const { return _on_thread_list; }
+ void set_on_thread_list() { _on_thread_list = true; }
+
// thread has called JavaThread::exit() or is terminated
- bool is_exiting() { return _terminated == _thread_exiting || is_terminated(); }
+ bool is_exiting() const;
// thread is terminated (no longer on the threads list); we compare
// against the two non-terminated values so that a freed JavaThread
// will also be considered terminated.
- bool is_terminated() { return _terminated != _not_terminated && _terminated != _thread_exiting; }
- void set_terminated(TerminatedTypes t) { _terminated = t; }
+ bool check_is_terminated(TerminatedTypes l_terminated) const {
+ return l_terminated != _not_terminated && l_terminated != _thread_exiting;
+ }
+ bool is_terminated() const;
+ void set_terminated(TerminatedTypes t);
// special for Threads::remove() which is static:
- void set_terminated_value() { _terminated = _thread_terminated; }
+ void set_terminated_value();
void block_if_vm_exited();
bool doing_unsafe_access() { return _doing_unsafe_access; }
@@ -1220,6 +1277,9 @@
// via the appropriate -XX options.
bool wait_for_ext_suspend_completion(int count, int delay, uint32_t *bits);
+ // test for suspend - most (all?) of these should go away
+ bool is_thread_fully_suspended(bool wait_for_suspend, uint32_t *bits);
+
inline void set_external_suspend();
inline void clear_external_suspend();
@@ -2066,28 +2126,84 @@
class Threads: AllStatic {
friend class VMStructs;
private:
- static JavaThread* _thread_list;
- static int _number_of_threads;
- static int _number_of_non_daemon_threads;
- static int _return_code;
- static int _thread_claim_parity;
+ // Safe Memory Reclamation (SMR) support:
+ static Monitor* _smr_delete_lock;
+ // The '_cnt', '_max' and '_times" fields are enabled via
+ // -XX:+EnableThreadSMRStatistics (see thread.cpp for a
+ // description about each field):
+ static uint _smr_delete_lock_wait_cnt;
+ static uint _smr_delete_lock_wait_max;
+ static volatile uint _smr_delete_notify;
+ static volatile uint _smr_deleted_thread_cnt;
+ static volatile uint _smr_deleted_thread_time_max;
+ static volatile uint _smr_deleted_thread_times;
+ static ThreadsList* volatile _smr_java_thread_list;
+ static ThreadsList* get_smr_java_thread_list();
+ static ThreadsList* xchg_smr_java_thread_list(ThreadsList* new_list);
+ static uint64_t _smr_java_thread_list_alloc_cnt;
+ static uint64_t _smr_java_thread_list_free_cnt;
+ static uint _smr_java_thread_list_max;
+ static uint _smr_nested_thread_list_max;
+ static volatile uint _smr_tlh_cnt;
+ static volatile uint _smr_tlh_time_max;
+ static volatile uint _smr_tlh_times;
+ static ThreadsList* _smr_to_delete_list;
+ static uint _smr_to_delete_list_cnt;
+ static uint _smr_to_delete_list_max;
+
+ static JavaThread* _thread_list;
+ static int _number_of_threads;
+ static int _number_of_non_daemon_threads;
+ static int _return_code;
+ static int _thread_claim_parity;
#ifdef ASSERT
- static bool _vm_complete;
+ static bool _vm_complete;
#endif
static void initialize_java_lang_classes(JavaThread* main_thread, TRAPS);
static void initialize_jsr292_core_classes(TRAPS);
+
+ static void smr_free_list(ThreadsList* threads);
+
public:
// Thread management
// force_daemon is a concession to JNI, where we may need to add a
// thread to the thread list before allocating its thread object
static void add(JavaThread* p, bool force_daemon = false);
static void remove(JavaThread* p);
- static bool includes(JavaThread* p);
- static JavaThread* first() { return _thread_list; }
static void threads_do(ThreadClosure* tc);
static void possibly_parallel_threads_do(bool is_par, ThreadClosure* tc);
+ // SMR support:
+ static ThreadsList *acquire_stable_list(Thread *self, bool is_ThreadsListSetter);
+ static ThreadsList *acquire_stable_list_fast_path(Thread *self);
+ static ThreadsList *acquire_stable_list_nested_path(Thread *self);
+ static void release_stable_list(Thread *self);
+ static void release_stable_list_fast_path(Thread *self);
+ static void release_stable_list_nested_path(Thread *self);
+ static void release_stable_list_wake_up(char *log_str);
+ static bool is_a_protected_JavaThread(JavaThread *thread);
+ static bool is_a_protected_JavaThread_with_lock(JavaThread *thread) {
+ MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock);
+ return is_a_protected_JavaThread(thread);
+ }
+ static void smr_delete(JavaThread *thread);
+ // The coordination between Threads::release_stable_list() and
+ // Threads::smr_delete() uses the smr_delete_lock in order to
+ // reduce the traffic on the Threads_lock.
+ static Monitor* smr_delete_lock() { return _smr_delete_lock; }
+ // The smr_delete_notify flag is used for proper double-check
+ // locking in order to reduce the traffic on the smr_delete_lock.
+ static bool smr_delete_notify();
+ static void set_smr_delete_notify();
+ static void clear_smr_delete_notify();
+ static void inc_smr_deleted_thread_cnt();
+ static void update_smr_deleted_thread_time_max(uint new_value);
+ static void add_smr_deleted_thread_times(uint add_value);
+ static void inc_smr_tlh_cnt();
+ static void update_smr_tlh_time_max(uint new_value);
+ static void add_smr_tlh_times(uint add_value);
+
// Initializes the vm and creates the vm thread
static jint create_vm(JavaVMInitArgs* args, bool* canTryAgain);
static void convert_vm_init_libraries_to_agents();
@@ -2148,7 +2264,10 @@
// Verification
static void verify();
+ static void log_smr_statistics();
static void print_on(outputStream* st, bool print_stacks, bool internal_format, bool print_concurrent_locks);
+ static void print_smr_info_on(outputStream* st);
+ static void print_smr_info_elements_on(outputStream* st, ThreadsList* t_list);
static void print(bool print_stacks, bool internal_format) {
// this function is only used by debug.cpp
print_on(tty, print_stacks, internal_format, false /* no concurrent lock printed */);
@@ -2158,17 +2277,13 @@
int buflen, bool* found_current);
static void print_threads_compiling(outputStream* st, char* buf, int buflen);
- // Get Java threads that are waiting to enter a monitor. If doLock
- // is true, then Threads_lock is grabbed as needed. Otherwise, the
- // VM needs to be at a safepoint.
- static GrowableArray<JavaThread*>* get_pending_threads(int count,
- address monitor, bool doLock);
+ // Get Java threads that are waiting to enter a monitor.
+ static GrowableArray<JavaThread*>* get_pending_threads(ThreadsList * t_list,
+ int count, address monitor);
- // Get owning Java thread from the monitor's owner field. If doLock
- // is true, then Threads_lock is grabbed as needed. Otherwise, the
- // VM needs to be at a safepoint.
- static JavaThread *owning_thread_from_monitor_owner(address owner,
- bool doLock);
+ // Get owning Java thread from the monitor's owner field.
+ static JavaThread *owning_thread_from_monitor_owner(ThreadsList * t_list,
+ address owner);
// Number of threads on the active threads list
static int number_of_threads() { return _number_of_threads; }
@@ -2177,9 +2292,6 @@
// Deoptimizes all frames tied to marked nmethods
static void deoptimized_wrt_marked_nmethods();
-
- static JavaThread* find_java_thread_from_java_tid(jlong java_tid);
-
};
--- a/src/hotspot/share/runtime/thread.inline.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/thread.inline.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -25,13 +25,10 @@
#ifndef SHARE_VM_RUNTIME_THREAD_INLINE_HPP
#define SHARE_VM_RUNTIME_THREAD_INLINE_HPP
-#define SHARE_VM_RUNTIME_THREAD_INLINE_HPP_SCOPE
-
#include "runtime/atomic.hpp"
#include "runtime/os.inline.hpp"
#include "runtime/thread.hpp"
-
-#undef SHARE_VM_RUNTIME_THREAD_INLINE_HPP_SCOPE
+#include "runtime/threadSMR.hpp"
inline void Thread::set_suspend_flag(SuspendFlags f) {
assert(sizeof(jint) == sizeof(_suspend_flags), "size mismatch");
@@ -89,6 +86,18 @@
return allocated_bytes;
}
+inline ThreadsList* Thread::cmpxchg_threads_hazard_ptr(ThreadsList* exchange_value, ThreadsList* compare_value) {
+ return (ThreadsList*)Atomic::cmpxchg(exchange_value, &_threads_hazard_ptr, compare_value);
+}
+
+inline ThreadsList* Thread::get_threads_hazard_ptr() {
+ return (ThreadsList*)OrderAccess::load_acquire(&_threads_hazard_ptr);
+}
+
+inline void Thread::set_threads_hazard_ptr(ThreadsList* new_list) {
+ OrderAccess::release_store_fence(&_threads_hazard_ptr, new_list);
+}
+
inline void JavaThread::set_ext_suspended() {
set_suspend_flag (_ext_suspended);
}
@@ -176,4 +185,83 @@
return OrderAccess::load_acquire(polling_page_addr());
}
+inline bool JavaThread::is_exiting() const {
+ // Use load-acquire so that setting of _terminated by
+ // JavaThread::exit() is seen more quickly.
+ TerminatedTypes l_terminated = (TerminatedTypes)
+ OrderAccess::load_acquire((volatile jint *) &_terminated);
+ return l_terminated == _thread_exiting || check_is_terminated(l_terminated);
+}
+
+inline bool JavaThread::is_terminated() const {
+ // Use load-acquire so that setting of _terminated by
+ // JavaThread::exit() is seen more quickly.
+ TerminatedTypes l_terminated = (TerminatedTypes)
+ OrderAccess::load_acquire((volatile jint *) &_terminated);
+ return check_is_terminated(l_terminated);
+}
+
+inline void JavaThread::set_terminated(TerminatedTypes t) {
+ // use release-store so the setting of _terminated is seen more quickly
+ OrderAccess::release_store((volatile jint *) &_terminated, (jint) t);
+}
+
+// special for Threads::remove() which is static:
+inline void JavaThread::set_terminated_value() {
+ // use release-store so the setting of _terminated is seen more quickly
+ OrderAccess::release_store((volatile jint *) &_terminated, (jint) _thread_terminated);
+}
+
+inline ThreadsList* Threads::get_smr_java_thread_list() {
+ return (ThreadsList*)OrderAccess::load_acquire(&_smr_java_thread_list);
+}
+
+inline ThreadsList* Threads::xchg_smr_java_thread_list(ThreadsList* new_list) {
+ return (ThreadsList*)Atomic::xchg(new_list, &_smr_java_thread_list);
+}
+
+inline void Threads::inc_smr_deleted_thread_cnt() {
+ Atomic::inc(&_smr_deleted_thread_cnt);
+}
+
+inline void Threads::update_smr_deleted_thread_time_max(uint new_value) {
+ while (true) {
+ uint cur_value = _smr_deleted_thread_time_max;
+ if (new_value <= cur_value) {
+ // No need to update max value so we're done.
+ break;
+ }
+ if (Atomic::cmpxchg(new_value, &_smr_deleted_thread_time_max, cur_value) == cur_value) {
+ // Updated max value so we're done. Otherwise try it all again.
+ break;
+ }
+ }
+}
+
+inline void Threads::add_smr_deleted_thread_times(uint add_value) {
+ Atomic::add(add_value, &_smr_deleted_thread_times);
+}
+
+inline void Threads::inc_smr_tlh_cnt() {
+ Atomic::inc(&_smr_tlh_cnt);
+}
+
+inline void Threads::update_smr_tlh_time_max(uint new_value) {
+ while (true) {
+ uint cur_value = _smr_tlh_time_max;
+ if (new_value <= cur_value) {
+ // No need to update max value so we're done.
+ break;
+ }
+ if (Atomic::cmpxchg(new_value, &_smr_tlh_time_max, cur_value) == cur_value) {
+ // Updated max value so we're done. Otherwise try it all again.
+ break;
+ }
+ }
+}
+
+inline void Threads::add_smr_tlh_times(uint add_value) {
+ Atomic::add(add_value, &_smr_tlh_times);
+}
+
#endif // SHARE_VM_RUNTIME_THREAD_INLINE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/runtime/threadSMR.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2017, 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 "memory/allocation.inline.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
+#include "services/threadService.hpp"
+
+// 'entries + 1' so we always have at least one entry.
+ThreadsList::ThreadsList(int entries) : _length(entries), _threads(NEW_C_HEAP_ARRAY(JavaThread*, entries + 1, mtThread)), _next_list(NULL) {
+ *(JavaThread**)(_threads + entries) = NULL; // Make sure the extra entry is NULL.
+}
+
+ThreadsList::~ThreadsList() {
+ FREE_C_HEAP_ARRAY(JavaThread*, _threads);
+}
+
+ThreadsListSetter::~ThreadsListSetter() {
+ if (_target_needs_release) {
+ // The hazard ptr in the target needs to be released.
+ Threads::release_stable_list(_target);
+ }
+}
+
+void ThreadsListSetter::set() {
+ assert(_target->get_threads_hazard_ptr() == NULL, "hazard ptr should not already be set");
+ (void) Threads::acquire_stable_list(_target, /* is_ThreadsListSetter */ true);
+ _target_needs_release = true;
+}
+
+ThreadsListHandle::ThreadsListHandle(Thread *self) : _list(Threads::acquire_stable_list(self, /* is_ThreadsListSetter */ false)), _self(self) {
+ assert(self == Thread::current(), "sanity check");
+ if (EnableThreadSMRStatistics) {
+ _timer.start();
+ }
+}
+
+ThreadsListHandle::~ThreadsListHandle() {
+ Threads::release_stable_list(_self);
+ if (EnableThreadSMRStatistics) {
+ _timer.stop();
+ uint millis = (uint)_timer.milliseconds();
+ Threads::inc_smr_tlh_cnt();
+ Threads::add_smr_tlh_times(millis);
+ Threads::update_smr_tlh_time_max(millis);
+ }
+}
+
+// Convert an internal thread reference to a JavaThread found on the
+// associated ThreadsList. This ThreadsListHandle "protects" the
+// returned JavaThread *.
+//
+// If thread_oop_p is not NULL, then the caller wants to use the oop
+// after this call so the oop is returned. On success, *jt_pp is set
+// to the converted JavaThread * and true is returned. On error,
+// returns false.
+//
+bool ThreadsListHandle::cv_internal_thread_to_JavaThread(jobject jthread,
+ JavaThread ** jt_pp,
+ oop * thread_oop_p) {
+ assert(this->list() != NULL, "must have a ThreadsList");
+ assert(jt_pp != NULL, "must have a return JavaThread pointer");
+ // thread_oop_p is optional so no assert()
+
+ // The JVM_* interfaces don't allow a NULL thread parameter; JVM/TI
+ // allows a NULL thread parameter to signify "current thread" which
+ // allows us to avoid calling cv_external_thread_to_JavaThread().
+ // The JVM_* interfaces have no such leeway.
+
+ oop thread_oop = JNIHandles::resolve_non_null(jthread);
+ // Looks like an oop at this point.
+ if (thread_oop_p != NULL) {
+ // Return the oop to the caller; the caller may still want
+ // the oop even if this function returns false.
+ *thread_oop_p = thread_oop;
+ }
+
+ JavaThread *java_thread = java_lang_Thread::thread(thread_oop);
+ if (java_thread == NULL) {
+ // The java.lang.Thread does not contain a JavaThread * so it has
+ // not yet run or it has died.
+ return false;
+ }
+ // Looks like a live JavaThread at this point.
+
+ if (java_thread != JavaThread::current()) {
+ // jthread is not for the current JavaThread so have to verify
+ // the JavaThread * against the ThreadsList.
+ if (EnableThreadSMRExtraValidityChecks && !includes(java_thread)) {
+ // Not on the JavaThreads list so it is not alive.
+ return false;
+ }
+ }
+
+ // Return a live JavaThread that is "protected" by the
+ // ThreadsListHandle in the caller.
+ *jt_pp = java_thread;
+ return true;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/runtime/threadSMR.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ */
+
+#ifndef SHARE_VM_RUNTIME_THREADSMR_HPP
+#define SHARE_VM_RUNTIME_THREADSMR_HPP
+
+#include "memory/allocation.hpp"
+#include "runtime/timer.hpp"
+
+// Thread Safe Memory Reclamation (Thread-SMR) support.
+//
+// ThreadsListHandles are used to safely perform operations on one or more
+// threads without the risk of the thread or threads exiting during the
+// operation. It is no longer necessary to hold the Threads_lock to safely
+// perform an operation on a target thread.
+//
+// There are several different ways to refer to java.lang.Thread objects
+// so we have a few ways to get a protected JavaThread *:
+//
+// JNI jobject example:
+// jobject jthread = ...;
+// :
+// ThreadsListHandle tlh;
+// JavaThread* jt = NULL;
+// bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &jt, NULL);
+// if (is_alive) {
+// : // do stuff with 'jt'...
+// }
+//
+// JVM/TI jthread example:
+// jthread thread = ...;
+// :
+// JavaThread* jt = NULL;
+// ThreadsListHandle tlh;
+// jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &jt, NULL);
+// if (err != JVMTI_ERROR_NONE) {
+// return err;
+// }
+// : // do stuff with 'jt'...
+//
+// JVM/TI oop example (this one should be very rare):
+// oop thread_obj = ...;
+// :
+// JavaThread *jt = NULL;
+// ThreadsListHandle tlh;
+// jvmtiError err = JvmtiExport::cv_oop_to_JavaThread(tlh.list(), thread_obj, &jt);
+// if (err != JVMTI_ERROR_NONE) {
+// return err;
+// }
+// : // do stuff with 'jt'...
+//
+// A JavaThread * that is included in the ThreadsList that is held by
+// a ThreadsListHandle is protected as long as the ThreadsListHandle
+// remains in scope. The target JavaThread * may have logically exited,
+// but that target JavaThread * will not be deleted until it is no
+// longer protected by a ThreadsListHandle.
+
+
+// A fast list of JavaThreads.
+//
+class ThreadsList : public CHeapObj<mtThread> {
+ friend class ScanHazardPtrGatherProtectedThreadsClosure;
+ friend class Threads;
+
+ const uint _length;
+ ThreadsList* _next_list;
+ JavaThread *const *const _threads;
+
+ template <class T>
+ void threads_do_dispatch(T *cl, JavaThread *const thread) const;
+
+ ThreadsList *next_list() const { return _next_list; }
+ void set_next_list(ThreadsList *list) { _next_list = list; }
+
+public:
+ ThreadsList(int entries);
+ ~ThreadsList();
+
+ template <class T>
+ void threads_do(T *cl) const;
+
+ uint length() const { return _length; }
+
+ JavaThread *const thread_at(uint i) const { return _threads[i]; }
+
+ JavaThread *const *threads() const { return _threads; }
+
+ // Returns -1 if target is not found.
+ int find_index_of_JavaThread(JavaThread* target);
+ JavaThread* find_JavaThread_from_java_tid(jlong java_tid) const;
+ bool includes(const JavaThread * const p) const;
+
+ static ThreadsList* add_thread(ThreadsList* list, JavaThread* java_thread);
+ static ThreadsList* remove_thread(ThreadsList* list, JavaThread* java_thread);
+};
+
+// Linked list of ThreadsLists to support nested ThreadsListHandles.
+class NestedThreadsList : public CHeapObj<mtThread> {
+ ThreadsList*const _t_list;
+ NestedThreadsList* _next;
+
+public:
+ NestedThreadsList(ThreadsList* t_list) : _t_list(t_list) {
+ assert(Threads_lock->owned_by_self(),
+ "must own Threads_lock for saved t_list to be valid.");
+ }
+
+ ThreadsList* t_list() { return _t_list; }
+ NestedThreadsList* next() { return _next; }
+ void set_next(NestedThreadsList* value) { _next = value; }
+};
+
+// A helper to optionally set the hazard ptr in ourself. This helper can
+// be used by ourself or by another thread. If the hazard ptr is set(),
+// then the destructor will release it.
+//
+class ThreadsListSetter : public StackObj {
+private:
+ bool _target_needs_release; // needs release only when set()
+ Thread * _target;
+
+public:
+ ThreadsListSetter() : _target_needs_release(false), _target(Thread::current()) {
+ }
+ ~ThreadsListSetter();
+ ThreadsList* list();
+ void set();
+ bool target_needs_release() { return _target_needs_release; }
+};
+
+// This stack allocated ThreadsListHandle keeps all JavaThreads in the
+// ThreadsList from being deleted until it is safe.
+//
+class ThreadsListHandle : public StackObj {
+ ThreadsList * _list;
+ Thread *const _self;
+ elapsedTimer _timer; // Enabled via -XX:+EnableThreadSMRStatistics.
+
+public:
+ ThreadsListHandle(Thread *self = Thread::current());
+ ~ThreadsListHandle();
+
+ ThreadsList *list() const {
+ return _list;
+ }
+
+ template <class T>
+ void threads_do(T *cl) const {
+ return _list->threads_do(cl);
+ }
+
+ bool cv_internal_thread_to_JavaThread(jobject jthread, JavaThread ** jt_pp, oop * thread_oop_p);
+
+ bool includes(JavaThread* p) {
+ return _list->includes(p);
+ }
+
+ uint length() const {
+ return _list->length();
+ }
+};
+
+// This stack allocated JavaThreadIterator is used to walk the
+// specified ThreadsList using the following style:
+//
+// JavaThreadIterator jti(t_list);
+// for (JavaThread *jt = jti.first(); jt != NULL; jt = jti.next()) {
+// ...
+// }
+//
+class JavaThreadIterator : public StackObj {
+ ThreadsList * _list;
+ uint _index;
+
+public:
+ JavaThreadIterator(ThreadsList *list) : _list(list), _index(0) {
+ assert(list != NULL, "ThreadsList must not be NULL.");
+ }
+
+ JavaThread *first() {
+ _index = 0;
+ return _list->thread_at(_index);
+ }
+
+ uint length() const {
+ return _list->length();
+ }
+
+ ThreadsList *list() const {
+ return _list;
+ }
+
+ JavaThread *next() {
+ if (++_index >= length()) {
+ return NULL;
+ }
+ return _list->thread_at(_index);
+ }
+};
+
+// This stack allocated ThreadsListHandle and JavaThreadIterator combo
+// is used to walk the ThreadsList in the included ThreadsListHandle
+// using the following style:
+//
+// for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
+// ...
+// }
+//
+class JavaThreadIteratorWithHandle : public StackObj {
+ ThreadsListHandle _tlh;
+ uint _index;
+
+public:
+ JavaThreadIteratorWithHandle() : _index(0) {}
+
+ uint length() const {
+ return _tlh.length();
+ }
+
+ ThreadsList *list() const {
+ return _tlh.list();
+ }
+
+ JavaThread *next() {
+ if (_index >= length()) {
+ return NULL;
+ }
+ return _tlh.list()->thread_at(_index++);
+ }
+
+ void rewind() {
+ _index = 0;
+ }
+};
+
+#endif // SHARE_VM_RUNTIME_THREADSMR_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/runtime/threadSMR.inline.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ */
+
+#ifndef SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP
+#define SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP
+
+#include "runtime/atomic.hpp"
+#include "runtime/prefetch.inline.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
+
+// Devirtualize known thread closure types.
+template <class T>
+inline void ThreadsList::threads_do_dispatch(T *cl, JavaThread *const thread) const {
+ cl->T::do_thread(thread);
+}
+
+template <>
+inline void ThreadsList::threads_do_dispatch<ThreadClosure>(ThreadClosure *cl, JavaThread *const thread) const {
+ cl->do_thread(thread);
+}
+
+template <class T>
+inline void ThreadsList::threads_do(T *cl) const {
+ const intx scan_interval = PrefetchScanIntervalInBytes;
+ JavaThread *const *const end = _threads + _length;
+ for (JavaThread *const *current_p = _threads; current_p != end; current_p++) {
+ Prefetch::read((void*)current_p, scan_interval);
+ JavaThread *const current = *current_p;
+ threads_do_dispatch(cl, current);
+ }
+}
+
+inline ThreadsList* ThreadsListSetter::list() {
+ ThreadsList *ret = _target->get_threads_hazard_ptr();
+ assert(ret != NULL, "hazard ptr should be set");
+ assert(!Thread::is_hazard_ptr_tagged(ret), "hazard ptr should be validated");
+ return ret;
+}
+
+#endif // SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP
--- a/src/hotspot/share/runtime/vm_operations.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/vm_operations.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -38,6 +38,7 @@
#include "runtime/interfaceSupport.hpp"
#include "runtime/sweeper.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.inline.hpp"
#include "runtime/vm_operations.hpp"
#include "services/threadService.hpp"
#include "trace/tracing.hpp"
@@ -96,11 +97,12 @@
void VM_ThreadStop::doit() {
assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint");
+ ThreadsListHandle tlh;
JavaThread* target = java_lang_Thread::thread(target_thread());
// Note that this now allows multiple ThreadDeath exceptions to be
// thrown at a thread.
- if (target != NULL) {
- // the thread has run and is not already in the process of exiting
+ if (target != NULL && (!EnableThreadSMRExtraValidityChecks || tlh.includes(target))) {
+ // The target thread has run and has not exited yet.
target->send_thread_stop(throwable());
}
}
@@ -146,9 +148,10 @@
void VM_DeoptimizeAll::doit() {
DeoptimizationMarker dm;
+ JavaThreadIteratorWithHandle jtiwh;
// deoptimize all java threads in the system
if (DeoptimizeALot) {
- for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) {
+ for (; JavaThread *thread = jtiwh.next(); ) {
if (thread->has_last_Java_frame()) {
thread->deoptimize();
}
@@ -159,7 +162,7 @@
int tnum = os::random() & 0x3;
int fnum = os::random() & 0x3;
int tcount = 0;
- for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) {
+ for (; JavaThread *thread = jtiwh.next(); ) {
if (thread->has_last_Java_frame()) {
if (tcount++ == tnum) {
tcount = 0;
@@ -259,12 +262,19 @@
}
void VM_FindDeadlocks::doit() {
- _deadlocks = ThreadService::find_deadlocks_at_safepoint(_concurrent_locks);
+ // Update the hazard ptr in the originating thread to the current
+ // list of threads. This VM operation needs the current list of
+ // threads for proper deadlock detection and those are the
+ // JavaThreads we need to be protected when we return info to the
+ // originating thread.
+ _setter.set();
+
+ _deadlocks = ThreadService::find_deadlocks_at_safepoint(_setter.list(), _concurrent_locks);
if (_out != NULL) {
int num_deadlocks = 0;
for (DeadlockCycle* cycle = _deadlocks; cycle != NULL; cycle = cycle->next()) {
num_deadlocks++;
- cycle->print_on(_out);
+ cycle->print_on_with(_setter.list(), _out);
}
if (num_deadlocks == 1) {
@@ -331,6 +341,12 @@
void VM_ThreadDump::doit() {
ResourceMark rm;
+ // Set the hazard ptr in the originating thread to protect the
+ // current list of threads. This VM operation needs the current list
+ // of threads for a proper dump and those are the JavaThreads we need
+ // to be protected when we return info to the originating thread.
+ _result->set_t_list();
+
ConcurrentLocksDump concurrent_locks(true);
if (_with_locked_synchronizers) {
concurrent_locks.dump_at_safepoint();
@@ -338,7 +354,9 @@
if (_num_threads == 0) {
// Snapshot all live threads
- for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) {
+
+ for (uint i = 0; i < _result->t_list()->length(); i++) {
+ JavaThread* jt = _result->t_list()->thread_at(i);
if (jt->is_exiting() ||
jt->is_hidden_from_external_view()) {
// skip terminating threads and hidden threads
@@ -354,6 +372,7 @@
} else {
// Snapshot threads in the given _threads array
// A dummy snapshot is created if a thread doesn't exist
+
for (int i = 0; i < _num_threads; i++) {
instanceHandle th = _threads->at(i);
if (th() == NULL) {
@@ -366,6 +385,12 @@
// Dump thread stack only if the thread is alive and not exiting
// and not VM internal thread.
JavaThread* jt = java_lang_Thread::thread(th());
+ if (jt != NULL && !_result->t_list()->includes(jt)) {
+ // _threads[i] doesn't refer to a valid JavaThread; this check
+ // is primarily for JVM_DumpThreads() which doesn't have a good
+ // way to validate the _threads array.
+ jt = NULL;
+ }
if (jt == NULL || /* thread not alive */
jt->is_exiting() ||
jt->is_hidden_from_external_view()) {
@@ -384,7 +409,7 @@
}
ThreadSnapshot* VM_ThreadDump::snapshot_thread(JavaThread* java_thread, ThreadConcurrentLocks* tcl) {
- ThreadSnapshot* snapshot = new ThreadSnapshot(java_thread);
+ ThreadSnapshot* snapshot = new ThreadSnapshot(_result->t_list(), java_thread);
snapshot->dump_stack_at_safepoint(_max_depth, _with_locked_monitors);
snapshot->set_concurrent_locks(tcl);
return snapshot;
@@ -403,11 +428,12 @@
_shutdown_thread = thr_cur;
_vm_exited = true; // global flag
- for(JavaThread *thr = Threads::first(); thr != NULL; thr = thr->next())
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
if (thr!=thr_cur && thr->thread_state() == _thread_in_native) {
++num_active;
thr->set_terminated(JavaThread::_vm_exited); // per-thread flag
}
+ }
return num_active;
}
@@ -435,11 +461,13 @@
int max_wait = max_wait_compiler_thread;
int attempts = 0;
+ JavaThreadIteratorWithHandle jtiwh;
while (true) {
int num_active = 0;
int num_active_compiler_thread = 0;
- for(JavaThread *thr = Threads::first(); thr != NULL; thr = thr->next()) {
+ jtiwh.rewind();
+ for (; JavaThread *thr = jtiwh.next(); ) {
if (thr!=thr_cur && thr->thread_state() == _thread_in_native) {
num_active++;
if (thr->is_Compiler_thread()) {
--- a/src/hotspot/share/runtime/vm_operations.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/runtime/vm_operations.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -392,12 +392,14 @@
class DeadlockCycle;
class VM_FindDeadlocks: public VM_Operation {
private:
- bool _concurrent_locks;
- DeadlockCycle* _deadlocks;
- outputStream* _out;
+ bool _concurrent_locks;
+ DeadlockCycle* _deadlocks;
+ outputStream* _out;
+ ThreadsListSetter _setter; // Helper to set hazard ptr in the originating thread
+ // which protects the JavaThreads in _deadlocks.
public:
- VM_FindDeadlocks(bool concurrent_locks) : _concurrent_locks(concurrent_locks), _out(NULL), _deadlocks(NULL) {};
+ VM_FindDeadlocks(bool concurrent_locks) : _concurrent_locks(concurrent_locks), _out(NULL), _deadlocks(NULL), _setter() {};
VM_FindDeadlocks(outputStream* st) : _concurrent_locks(true), _out(st), _deadlocks(NULL) {};
~VM_FindDeadlocks();
--- a/src/hotspot/share/services/heapDumper.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/services/heapDumper.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -39,6 +39,8 @@
#include "runtime/jniHandles.hpp"
#include "runtime/os.hpp"
#include "runtime/reflectionUtils.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vmThread.hpp"
#include "runtime/vm_operations.hpp"
@@ -1895,7 +1897,7 @@
_stack_traces = NEW_C_HEAP_ARRAY(ThreadStackTrace*, Threads::number_of_threads(), mtInternal);
int frame_serial_num = 0;
- for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
oop threadObj = thread->threadObj();
if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) {
// dump thread stack trace
--- a/src/hotspot/share/services/management.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/services/management.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -42,6 +42,7 @@
#include "runtime/os.hpp"
#include "runtime/serviceThread.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "services/classLoadingService.hpp"
#include "services/diagnosticCommand.hpp"
#include "services/diagnosticFramework.hpp"
@@ -1025,11 +1026,15 @@
// First get an array of threadObj handles.
// A JavaThread may terminate before we get the stack trace.
GrowableArray<instanceHandle>* thread_handle_array = new GrowableArray<instanceHandle>(num_threads);
+
{
- MutexLockerEx ml(Threads_lock);
+ // Need this ThreadsListHandle for converting Java thread IDs into
+ // threadObj handles; dump_result->set_t_list() is called in the
+ // VM op below so we can't use it yet.
+ ThreadsListHandle tlh;
for (int i = 0; i < num_threads; i++) {
jlong tid = ids_ah->long_at(i);
- JavaThread* jt = Threads::find_java_thread_from_java_tid(tid);
+ JavaThread* jt = tlh.list()->find_JavaThread_from_java_tid(tid);
oop thread_obj = (jt != NULL ? jt->threadObj() : (oop)NULL);
instanceHandle threadObj_h(THREAD, (instanceOop) thread_obj);
thread_handle_array->append(threadObj_h);
@@ -1101,22 +1106,21 @@
ThreadDumpResult dump_result(num_threads);
if (maxDepth == 0) {
- // no stack trace dumped - do not need to stop the world
- {
- MutexLockerEx ml(Threads_lock);
- for (int i = 0; i < num_threads; i++) {
- jlong tid = ids_ah->long_at(i);
- JavaThread* jt = Threads::find_java_thread_from_java_tid(tid);
- ThreadSnapshot* ts;
- if (jt == NULL) {
- // if the thread does not exist or now it is terminated,
- // create dummy snapshot
- ts = new ThreadSnapshot();
- } else {
- ts = new ThreadSnapshot(jt);
- }
- dump_result.add_thread_snapshot(ts);
+ // No stack trace to dump so we do not need to stop the world.
+ // Since we never do the VM op here we must set the threads list.
+ dump_result.set_t_list();
+ for (int i = 0; i < num_threads; i++) {
+ jlong tid = ids_ah->long_at(i);
+ JavaThread* jt = dump_result.t_list()->find_JavaThread_from_java_tid(tid);
+ ThreadSnapshot* ts;
+ if (jt == NULL) {
+ // if the thread does not exist or now it is terminated,
+ // create dummy snapshot
+ ts = new ThreadSnapshot();
+ } else {
+ ts = new ThreadSnapshot(dump_result.t_list(), jt);
}
+ dump_result.add_thread_snapshot(ts);
}
} else {
// obtain thread dump with the specific list of threads with stack trace
@@ -1131,6 +1135,7 @@
int num_snapshots = dump_result.num_snapshots();
assert(num_snapshots == num_threads, "Must match the number of thread snapshots");
+ assert(num_snapshots == 0 || dump_result.t_list_has_been_set(), "ThreadsList must have been set if we have a snapshot");
int index = 0;
for (ThreadSnapshot* ts = dump_result.snapshots(); ts != NULL; index++, ts = ts->next()) {
// For each thread, create an java/lang/management/ThreadInfo object
@@ -1196,6 +1201,7 @@
}
int num_snapshots = dump_result.num_snapshots();
+ assert(num_snapshots == 0 || dump_result.t_list_has_been_set(), "ThreadsList must have been set if we have a snapshot");
// create the result ThreadInfo[] object
InstanceKlass* ik = Management::java_lang_management_ThreadInfo_klass(CHECK_NULL);
@@ -1319,10 +1325,10 @@
}
// Look for the JavaThread of this given tid
- MutexLockerEx ml(Threads_lock);
+ JavaThreadIteratorWithHandle jtiwh;
if (tid == 0) {
// reset contention statistics for all threads if tid == 0
- for (JavaThread* java_thread = Threads::first(); java_thread != NULL; java_thread = java_thread->next()) {
+ for (; JavaThread *java_thread = jtiwh.next(); ) {
if (type == JMM_STAT_THREAD_CONTENTION_COUNT) {
ThreadService::reset_contention_count_stat(java_thread);
} else {
@@ -1331,7 +1337,7 @@
}
} else {
// reset contention statistics for a given thread
- JavaThread* java_thread = Threads::find_java_thread_from_java_tid(tid);
+ JavaThread* java_thread = jtiwh.list()->find_JavaThread_from_java_tid(tid);
if (java_thread == NULL) {
return false;
}
@@ -1399,8 +1405,8 @@
// current thread
return os::current_thread_cpu_time();
} else {
- MutexLockerEx ml(Threads_lock);
- java_thread = Threads::find_java_thread_from_java_tid(thread_id);
+ ThreadsListHandle tlh;
+ java_thread = tlh.list()->find_JavaThread_from_java_tid(thread_id);
if (java_thread != NULL) {
return os::thread_cpu_time((Thread*) java_thread);
}
@@ -1649,6 +1655,7 @@
// Called with Threads_lock held
//
void ThreadTimesClosure::do_thread(Thread* thread) {
+ assert(Threads_lock->owned_by_self(), "Must hold Threads_lock");
assert(thread != NULL, "thread was NULL");
// exclude externally visible JavaThreads
@@ -2109,9 +2116,9 @@
"the given array of thread IDs");
}
- MutexLockerEx ml(Threads_lock);
+ ThreadsListHandle tlh;
for (int i = 0; i < num_threads; i++) {
- JavaThread* java_thread = Threads::find_java_thread_from_java_tid(ids_ah->long_at(i));
+ JavaThread* java_thread = tlh.list()->find_JavaThread_from_java_tid(ids_ah->long_at(i));
if (java_thread != NULL) {
sizeArray_h->long_at_put(i, java_thread->cooked_allocated_bytes());
}
@@ -2138,8 +2145,8 @@
// current thread
return os::current_thread_cpu_time(user_sys_cpu_time != 0);
} else {
- MutexLockerEx ml(Threads_lock);
- java_thread = Threads::find_java_thread_from_java_tid(thread_id);
+ ThreadsListHandle tlh;
+ java_thread = tlh.list()->find_JavaThread_from_java_tid(thread_id);
if (java_thread != NULL) {
return os::thread_cpu_time((Thread*) java_thread, user_sys_cpu_time != 0);
}
@@ -2180,9 +2187,9 @@
"the given array of thread IDs");
}
- MutexLockerEx ml(Threads_lock);
+ ThreadsListHandle tlh;
for (int i = 0; i < num_threads; i++) {
- JavaThread* java_thread = Threads::find_java_thread_from_java_tid(ids_ah->long_at(i));
+ JavaThread* java_thread = tlh.list()->find_JavaThread_from_java_tid(ids_ah->long_at(i));
if (java_thread != NULL) {
timeArray_h->long_at_put(i, os::thread_cpu_time((Thread*)java_thread,
user_sys_cpu_time != 0));
--- a/src/hotspot/share/services/threadService.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/services/threadService.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -34,9 +34,9 @@
#include "runtime/atomic.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/init.hpp"
-#include "runtime/thread.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.inline.hpp"
#include "runtime/vframe.hpp"
-#include "runtime/thread.inline.hpp"
#include "runtime/vmThread.hpp"
#include "runtime/vm_operations.hpp"
#include "services/threadService.hpp"
@@ -148,7 +148,7 @@
// FIXME: JVMTI should call this function
Handle ThreadService::get_current_contended_monitor(JavaThread* thread) {
assert(thread != NULL, "should be non-NULL");
- assert(Threads_lock->owned_by_self(), "must grab Threads_lock or be at safepoint");
+ debug_only(Thread::check_for_dangling_thread_pointer(thread);)
ObjectMonitor *wait_obj = thread->current_waiting_monitor();
@@ -266,6 +266,7 @@
int num_snapshots = dump_result.num_snapshots();
assert(num_snapshots == num_threads, "Must have num_threads thread snapshots");
+ assert(num_snapshots == 0 || dump_result.t_list_has_been_set(), "ThreadsList must have been set if we have a snapshot");
int i = 0;
for (ThreadSnapshot* ts = dump_result.snapshots(); ts != NULL; i++, ts = ts->next()) {
ThreadStackTrace* stacktrace = ts->get_stack_trace();
@@ -297,7 +298,9 @@
}
// Find deadlocks involving object monitors and concurrent locks if concurrent_locks is true
-DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks) {
+DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(ThreadsList * t_list, bool concurrent_locks) {
+ assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
+
// This code was modified from the original Threads::find_deadlocks code.
int globalDfn = 0, thisDfn;
ObjectMonitor* waitingToLockMonitor = NULL;
@@ -306,15 +309,16 @@
JavaThread *currentThread, *previousThread;
int num_deadlocks = 0;
- for (JavaThread* p = Threads::first(); p != NULL; p = p->next()) {
- // Initialize the depth-first-number
- p->set_depth_first_number(-1);
+ // Initialize the depth-first-number for each JavaThread.
+ JavaThreadIterator jti(t_list);
+ for (JavaThread* jt = jti.first(); jt != NULL; jt = jti.next()) {
+ jt->set_depth_first_number(-1);
}
DeadlockCycle* deadlocks = NULL;
DeadlockCycle* last = NULL;
DeadlockCycle* cycle = new DeadlockCycle();
- for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) {
+ for (JavaThread* jt = jti.first(); jt != NULL; jt = jti.next()) {
if (jt->depth_first_number() >= 0) {
// this thread was already visited
continue;
@@ -339,9 +343,8 @@
if (waitingToLockMonitor != NULL) {
address currentOwner = (address)waitingToLockMonitor->owner();
if (currentOwner != NULL) {
- currentThread = Threads::owning_thread_from_monitor_owner(
- currentOwner,
- false /* no locking needed */);
+ currentThread = Threads::owning_thread_from_monitor_owner(t_list,
+ currentOwner);
if (currentThread == NULL) {
// This function is called at a safepoint so the JavaThread
// that owns waitingToLockMonitor should be findable, but
@@ -366,6 +369,8 @@
if (concurrent_locks) {
if (waitingToLockBlocker->is_a(SystemDictionary::abstract_ownable_synchronizer_klass())) {
oop threadObj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(waitingToLockBlocker);
+ // This JavaThread (if there is one) is protected by the
+ // ThreadsListSetter in VM_FindDeadlocks::doit().
currentThread = threadObj != NULL ? java_lang_Thread::thread(threadObj) : NULL;
} else {
currentThread = NULL;
@@ -414,7 +419,7 @@
return deadlocks;
}
-ThreadDumpResult::ThreadDumpResult() : _num_threads(0), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL) {
+ThreadDumpResult::ThreadDumpResult() : _num_threads(0), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL), _setter() {
// Create a new ThreadDumpResult object and append to the list.
// If GC happens before this function returns, Method*
@@ -422,7 +427,7 @@
ThreadService::add_thread_dump(this);
}
-ThreadDumpResult::ThreadDumpResult(int num_threads) : _num_threads(num_threads), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL) {
+ThreadDumpResult::ThreadDumpResult(int num_threads) : _num_threads(num_threads), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL), _setter() {
// Create a new ThreadDumpResult object and append to the list.
// If GC happens before this function returns, oops
// will be visited.
@@ -467,6 +472,10 @@
}
}
+ThreadsList* ThreadDumpResult::t_list() {
+ return _setter.list();
+}
+
StackFrameInfo::StackFrameInfo(javaVFrame* jvf, bool with_lock_info) {
_method = jvf->method();
_bci = jvf->bci();
@@ -683,6 +692,8 @@
oop o = aos_objects->at(i);
oop owner_thread_obj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(o);
if (owner_thread_obj != NULL) {
+ // See comments in ThreadConcurrentLocks to see how this
+ // JavaThread* is protected.
JavaThread* thread = java_lang_Thread::thread(owner_thread_obj);
assert(o->is_instance(), "Must be an instanceOop");
add_lock(thread, (instanceOop) o);
@@ -764,7 +775,7 @@
memset((void*) _perf_recursion_counts, 0, sizeof(_perf_recursion_counts));
}
-ThreadSnapshot::ThreadSnapshot(JavaThread* thread) {
+ThreadSnapshot::ThreadSnapshot(ThreadsList * t_list, JavaThread* thread) {
_thread = thread;
_threadObj = thread->threadObj();
_stack_trace = NULL;
@@ -796,7 +807,7 @@
_thread_status = java_lang_Thread::RUNNABLE;
} else {
_blocker_object = obj();
- JavaThread* owner = ObjectSynchronizer::get_lock_owner(obj, false);
+ JavaThread* owner = ObjectSynchronizer::get_lock_owner(t_list, obj);
if ((owner == NULL && _thread_status == java_lang_Thread::BLOCKED_ON_MONITOR_ENTER)
|| (owner != NULL && owner->is_attaching_via_jni())) {
// ownership information of the monitor is not available
@@ -865,7 +876,7 @@
delete _threads;
}
-void DeadlockCycle::print_on(outputStream* st) const {
+void DeadlockCycle::print_on_with(ThreadsList * t_list, outputStream* st) const {
st->cr();
st->print_cr("Found one Java-level deadlock:");
st->print("=============================");
@@ -895,9 +906,8 @@
// No Java object associated - a JVMTI raw monitor
owner_desc = " (JVMTI raw monitor),\n which is held by";
}
- currentThread = Threads::owning_thread_from_monitor_owner(
- (address)waitingToLockMonitor->owner(),
- false /* no locking needed */);
+ currentThread = Threads::owning_thread_from_monitor_owner(t_list,
+ (address)waitingToLockMonitor->owner());
if (currentThread == NULL) {
// The deadlock was detected at a safepoint so the JavaThread
// that owns waitingToLockMonitor should be findable, but
@@ -915,6 +925,7 @@
"Must be an AbstractOwnableSynchronizer");
oop ownerObj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(waitingToLockBlocker);
currentThread = java_lang_Thread::thread(ownerObj);
+ assert(currentThread != NULL, "AbstractOwnableSynchronizer owning thread is unexpectedly NULL");
}
st->print("%s \"%s\"", owner_desc, currentThread->get_thread_name());
}
@@ -943,9 +954,7 @@
int init_size = ThreadService::get_live_thread_count();
_threads_array = new GrowableArray<instanceHandle>(init_size);
- MutexLockerEx ml(Threads_lock);
-
- for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) {
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
// skips JavaThreads in the process of exiting
// and also skips VM internal JavaThreads
// Threads in _thread_new or _thread_new_trans state are included.
--- a/src/hotspot/share/services/threadService.hpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/services/threadService.hpp Wed Nov 22 17:54:50 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -32,6 +32,7 @@
#include "runtime/objectMonitor.hpp"
#include "runtime/objectMonitor.inline.hpp"
#include "runtime/perfData.hpp"
+#include "runtime/thread.hpp"
#include "services/management.hpp"
#include "services/serviceUtil.hpp"
@@ -109,7 +110,7 @@
static void reset_contention_count_stat(JavaThread* thread);
static void reset_contention_time_stat(JavaThread* thread);
- static DeadlockCycle* find_deadlocks_at_safepoint(bool object_monitors_only);
+ static DeadlockCycle* find_deadlocks_at_safepoint(ThreadsList * t_list, bool object_monitors_only);
// GC support
static void oops_do(OopClosure* f);
@@ -189,6 +190,8 @@
// Thread snapshot to represent the thread state and statistics
class ThreadSnapshot : public CHeapObj<mtInternal> {
private:
+ // This JavaThread* is protected by being stored in objects that are
+ // protected by a ThreadsListSetter (ThreadDumpResult).
JavaThread* _thread;
oop _threadObj;
java_lang_Thread::ThreadStatus _thread_status;
@@ -213,7 +216,7 @@
// Dummy snapshot
ThreadSnapshot() : _thread(NULL), _threadObj(NULL), _stack_trace(NULL), _concurrent_locks(NULL), _next(NULL),
_blocker_object(NULL), _blocker_object_owner(NULL) {};
- ThreadSnapshot(JavaThread* thread);
+ ThreadSnapshot(ThreadsList * t_list, JavaThread* thread);
~ThreadSnapshot();
java_lang_Thread::ThreadStatus thread_status() { return _thread_status; }
@@ -310,6 +313,12 @@
private:
GrowableArray<instanceOop>* _owned_locks;
ThreadConcurrentLocks* _next;
+ // This JavaThread* is protected in one of two different ways
+ // depending on the usage of the ThreadConcurrentLocks object:
+ // 1) by being stored in objects that are only allocated and used at a
+ // safepoint (ConcurrentLocksDump), or 2) by being stored in objects
+ // that are protected by a ThreadsListSetter (ThreadSnapshot inside
+ // ThreadDumpResult).
JavaThread* _thread;
public:
ThreadConcurrentLocks(JavaThread* thread);
@@ -333,8 +342,12 @@
void add_lock(JavaThread* thread, instanceOop o);
public:
- ConcurrentLocksDump(bool retain_map_on_free) : _map(NULL), _last(NULL), _retain_map_on_free(retain_map_on_free) {};
- ConcurrentLocksDump() : _map(NULL), _last(NULL), _retain_map_on_free(false) {};
+ ConcurrentLocksDump(bool retain_map_on_free) : _map(NULL), _last(NULL), _retain_map_on_free(retain_map_on_free) {
+ assert(SafepointSynchronize::is_at_safepoint(), "Must be constructed at a safepoint.");
+ };
+ ConcurrentLocksDump() : _map(NULL), _last(NULL), _retain_map_on_free(false) {
+ assert(SafepointSynchronize::is_at_safepoint(), "Must be constructed at a safepoint.");
+ };
~ConcurrentLocksDump();
void dump_at_safepoint();
@@ -349,6 +362,9 @@
ThreadSnapshot* _snapshots;
ThreadSnapshot* _last;
ThreadDumpResult* _next;
+ ThreadsListSetter _setter; // Helper to set hazard ptr in the originating thread
+ // which protects the JavaThreads in _snapshots.
+
public:
ThreadDumpResult();
ThreadDumpResult(int num_threads);
@@ -360,6 +376,9 @@
int num_threads() { return _num_threads; }
int num_snapshots() { return _num_snapshots; }
ThreadSnapshot* snapshots() { return _snapshots; }
+ void set_t_list() { _setter.set(); }
+ ThreadsList* t_list();
+ bool t_list_has_been_set() { return _setter.target_needs_release(); }
void oops_do(OopClosure* f);
void metadata_do(void f(Metadata*));
};
@@ -381,7 +400,7 @@
bool is_deadlock() { return _is_deadlock; }
int num_threads() { return _threads->length(); }
GrowableArray<JavaThread*>* threads() { return _threads; }
- void print_on(outputStream* st) const;
+ void print_on_with(ThreadsList * t_list, outputStream* st) const;
};
// Utility class to get list of java threads.
--- a/src/hotspot/share/utilities/vmError.cpp Wed Nov 22 14:31:48 2017 -0500
+++ b/src/hotspot/share/utilities/vmError.cpp Wed Nov 22 17:54:50 2017 -0800
@@ -36,6 +36,7 @@
#include "runtime/init.hpp"
#include "runtime/os.hpp"
#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
#include "runtime/vmThread.hpp"
#include "runtime/vm_operations.hpp"
#include "runtime/vm_version.hpp"
@@ -1655,7 +1656,12 @@
char * const dataPtr = NULL; // bad data pointer
const void (*funcPtr)(void) = (const void(*)()) 0xF; // bad function pointer
- // Keep this in sync with test/runtime/ErrorHandling/ErrorHandler.java
+ // Keep this in sync with test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java
+ // which tests cases 1 thru 13.
+ // Case 14 is tested by test/hotspot/jtreg/runtime/ErrorHandling/SafeFetchInErrorHandlingTest.java.
+ // Case 15 is tested by test/hotspot/jtreg/runtime/ErrorHandling/SecondaryErrorTest.java.
+ // Case 16 is tested by test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java.
+ // Case 17 is tested by test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java.
switch (how) {
case 1: vmassert(str == NULL, "expected null");
case 2: vmassert(num == 1023 && *str == 'X',
@@ -1683,6 +1689,17 @@
case 13: (*funcPtr)(); break;
case 14: crash_with_segfault(); break;
case 15: crash_with_sigfpe(); break;
+ case 16: {
+ ThreadsListHandle tlh;
+ fatal("Force crash with an active ThreadsListHandle.");
+ }
+ case 17: {
+ ThreadsListHandle tlh;
+ {
+ ThreadsListHandle tlh2;
+ fatal("Force crash with a nested ThreadsListHandle.");
+ }
+ }
default: tty->print_cr("ERROR: %d: unexpected test_num value.", how);
}
--- a/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java Wed Nov 22 14:31:48 2017 -0500
+++ b/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java Wed Nov 22 17:54:50 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -23,6 +23,7 @@
/*
* @test
+ * @requires (vm.debug == true)
* @bug 6888954
* @bug 8015884
* @summary Exercise HotSpot error handling code by invoking java with
@@ -39,6 +40,7 @@
public class ErrorHandler {
public static OutputAnalyzer runTest(int testcase) throws Exception {
+ // The -XX:ErrorHandlerTest=N option requires debug bits.
return new OutputAnalyzer(
ProcessTools.createJavaProcessBuilder(
"-XX:-TransmitErrorReport", "-XX:-CreateCoredumpOnCrash", "-XX:ErrorHandlerTest=" + testcase)
@@ -46,10 +48,6 @@
}
public static void main(String[] args) throws Exception {
- // Test is only applicable for debug builds
- if (!Platform.isDebugBuild()) {
- return;
- }
// Keep this in sync with hotspot/src/share/vm/utilities/debug.cpp
int i = 1;
String[] strings = {
@@ -69,6 +67,10 @@
String[] patterns = {
"(SIGILL|SIGSEGV|EXCEPTION_ACCESS_VIOLATION).* at pc=",
"(SIGBUS|SIGSEGV|SIGILL|EXCEPTION_ACCESS_VIOLATION).* at pc="
+ // -XX:ErrorHandlerTest=14 is tested by SafeFetchInErrorHandlingTest.java
+ // -XX:ErrorHandlerTest=15 is tested by SecondaryErrorTest.java
+ // -XX:ErrorHandlerTest=16 is tested by ThreadsListHandleInErrorHandlingTest.java
+ // -XX:ErrorHandlerTest=17 is tested by NestedThreadsListHandleInErrorHandlingTest.java
};
for (String s : strings) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.util.regex.Pattern;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.ProcessTools;
+
+/*
+ * @test
+ * @requires (vm.debug == true)
+ * @bug 8167108
+ * @summary Nested ThreadsListHandle info should be in error handling output.
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics NestedThreadsListHandleInErrorHandlingTest
+ */
+
+/*
+ * This test was created using SafeFetchInErrorHandlingTest.java
+ * as a guide.
+ */
+public class NestedThreadsListHandleInErrorHandlingTest {
+ public static void main(String[] args) throws Exception {
+
+ // The -XX:ErrorHandlerTest=N option requires debug bits.
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-Xmx100M",
+ "-XX:ErrorHandlerTest=17",
+ "-XX:-CreateCoredumpOnCrash",
+ "-version");
+
+ OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
+
+ // We should have crashed with a specific fatal error:
+ output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*");
+ System.out.println("Found fatal error header.");
+ output_detail.shouldMatch("# +fatal error: Force crash with a nested ThreadsListHandle.");
+ System.out.println("Found specific fatal error.");
+
+ // Extract hs_err_pid file.
+ String hs_err_file = output_detail.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1);
+ if (hs_err_file == null) {
+ throw new RuntimeException("Did not find hs_err_pid file in output.\n");
+ }
+
+ File f = new File(hs_err_file);
+ if (!f.exists()) {
+ throw new RuntimeException("hs_err_pid file missing at "
+ + f.getAbsolutePath() + ".\n");
+ }
+
+ System.out.println("Found hs_err_pid file. Scanning...");
+
+ FileInputStream fis = new FileInputStream(f);
+ BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+ String line = null;
+
+ Pattern [] pattern = new Pattern[] {
+ // The "Current thread" line should show a hazard ptr and
+ // a nested hazard ptr:
+ Pattern.compile("Current thread .* _threads_hazard_ptr=0x[0-9A-Fa-f][0-9A-Fa-f]*, _nested_threads_hazard_ptr_cnt=1, _nested_threads_hazard_ptrs=0x.*"),
+ // We should have a section of Threads class SMR info:
+ Pattern.compile("Threads class SMR info:"),
+ // We should have one nested ThreadsListHandle:
+ Pattern.compile(".*, _smr_nested_thread_list_max=1"),
+ // The current thread (marked with '=>') in the threads list
+ // should show a hazard ptr:
+ Pattern.compile("=>.* JavaThread \"main\" .*_threads_hazard_ptr=0x[0-9A-Fa-f][0-9A-Fa-f]*, _nested_threads_hazard_ptr_cnt=1, _nested_threads_hazard_ptrs=0x.*"),
+ };
+ int currentPattern = 0;
+
+ String lastLine = null;
+ while ((line = br.readLine()) != null) {
+ if (currentPattern < pattern.length) {
+ if (pattern[currentPattern].matcher(line).matches()) {
+ System.out.println("Found: " + line + ".");
+ currentPattern++;
+ }
+ }
+ lastLine = line;
+ }
+ br.close();
+
+ if (currentPattern < pattern.length) {
+ throw new RuntimeException("hs_err_pid file incomplete (first missing pattern: " + currentPattern + ")");
+ }
+
+ if (!lastLine.equals("END.")) {
+ throw new RuntimeException("hs-err file incomplete (missing END marker.)");
+ } else {
+ System.out.println("End marker found.");
+ }
+
+ System.out.println("Done scanning hs_err_pid_file.");
+ System.out.println("PASSED.");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.util.regex.Pattern;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.ProcessTools;
+
+/*
+ * @test
+ * @requires (vm.debug == true)
+ * @bug 8167108
+ * @summary ThreadsListHandle info should be in error handling output.
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics ThreadsListHandleInErrorHandlingTest
+ */
+
+/*
+ * This test was created using SafeFetchInErrorHandlingTest.java
+ * as a guide.
+ */
+public class ThreadsListHandleInErrorHandlingTest {
+ public static void main(String[] args) throws Exception {
+
+ // The -XX:ErrorHandlerTest=N option requires debug bits.
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-Xmx100M",
+ "-XX:ErrorHandlerTest=16",
+ "-XX:-CreateCoredumpOnCrash",
+ "-version");
+
+ OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
+
+ // We should have crashed with a specific fatal error:
+ output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*");
+ System.out.println("Found fatal error header.");
+ output_detail.shouldMatch("# +fatal error: Force crash with an active ThreadsListHandle.");
+ System.out.println("Found specific fatal error.");
+
+ // Extract hs_err_pid file.
+ String hs_err_file = output_detail.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1);
+ if (hs_err_file == null) {
+ throw new RuntimeException("Did not find hs_err_pid file in output.\n");
+ }
+
+ File f = new File(hs_err_file);
+ if (!f.exists()) {
+ throw new RuntimeException("hs_err_pid file missing at "
+ + f.getAbsolutePath() + ".\n");
+ }
+
+ System.out.println("Found hs_err_pid file. Scanning...");
+
+ FileInputStream fis = new FileInputStream(f);
+ BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+ String line = null;
+
+ Pattern [] pattern = new Pattern[] {
+ // The "Current thread" line should show a hazard ptr:
+ Pattern.compile("Current thread .* _threads_hazard_ptr=0x.*"),
+ // We should have a section of Threads class SMR info:
+ Pattern.compile("Threads class SMR info:"),
+ // The current thread (marked with '=>') in the threads list
+ // should show a hazard ptr:
+ Pattern.compile("=>.* JavaThread \"main\" .*_threads_hazard_ptr=0x.*"),
+ };
+ int currentPattern = 0;
+
+ String lastLine = null;
+ while ((line = br.readLine()) != null) {
+ if (currentPattern < pattern.length) {
+ if (pattern[currentPattern].matcher(line).matches()) {
+ System.out.println("Found: " + line + ".");
+ currentPattern++;
+ }
+ }
+ lastLine = line;
+ }
+ br.close();
+
+ if (currentPattern < pattern.length) {
+ throw new RuntimeException("hs_err_pid file incomplete (first missing pattern: " + currentPattern + ")");
+ }
+
+ if (!lastLine.equals("END.")) {
+ throw new RuntimeException("hs-err file incomplete (missing END marker.)");
+ } else {
+ System.out.println("End marker found.");
+ }
+
+ System.out.println("Done scanning hs_err_pid_file.");
+ System.out.println("PASSED.");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.countStackFrames() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug CountStackFramesAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class CountStackFramesAtExit extends Thread {
+ final static int N_THREADS = 32;
+ final static int N_LATE_CALLS = 1000;
+
+ public CountDownLatch exitSyncObj = new CountDownLatch(1);
+ public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+ @Override
+ public void run() {
+ // Tell main thread we have started.
+ startSyncObj.countDown();
+ try {
+ // Wait for main thread to interrupt us so we
+ // can race to exit.
+ exitSyncObj.await();
+ } catch (InterruptedException e) {
+ // ignore because we expect one
+ }
+ }
+
+ public static void main(String[] args) {
+ CountStackFramesAtExit threads[] = new CountStackFramesAtExit[N_THREADS];
+
+ for (int i = 0; i < N_THREADS; i++ ) {
+ threads[i] = new CountStackFramesAtExit();
+ int late_count = 1;
+ threads[i].start();
+ try {
+ // Wait for the worker thread to get going.
+ threads[i].startSyncObj.await();
+
+ // This interrupt() call will break the worker out of
+ // the exitSyncObj.await() call and the countStackFrames()
+ // calls will come in during thread exit.
+ threads[i].interrupt();
+ for (; late_count <= N_LATE_CALLS; late_count++) {
+ try {
+ threads[i].countStackFrames();
+ } catch (IllegalThreadStateException itse) {
+ // ignore because we expect it
+ }
+
+ if (!threads[i].isAlive()) {
+ // Done with Thread.countStackFrames() calls since
+ // thread is not alive.
+ break;
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+
+ System.out.println("INFO: thread #" + i + ": made " + late_count +
+ " late calls to java.lang.Thread.countStackFrames()");
+ System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+ N_LATE_CALLS + " value is " +
+ ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+ "large enough to cause a Thread.countStackFrames() " +
+ "call after thread exit.");
+
+ try {
+ threads[i].join();
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+ threads[i].countStackFrames();
+ if (threads[i].isAlive()) {
+ throw new Error("Expected !Thread.isAlive() after thread #" +
+ i + " has been join()'ed");
+ }
+ }
+
+ String cmd = System.getProperty("sun.java.command");
+ if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+ // Exit with success in a non-JavaTest environment:
+ System.exit(0);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.interrupt() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug InterruptAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class InterruptAtExit extends Thread {
+ final static int N_THREADS = 32;
+ final static int N_LATE_CALLS = 1000;
+
+ public CountDownLatch exitSyncObj = new CountDownLatch(1);
+ public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+ @Override
+ public void run() {
+ // Tell main thread we have started.
+ startSyncObj.countDown();
+ try {
+ // Wait for main thread to interrupt us so we
+ // can race to exit.
+ exitSyncObj.await();
+ } catch (InterruptedException e) {
+ // ignore because we expect one
+ }
+ }
+
+ public static void main(String[] args) {
+ InterruptAtExit threads[] = new InterruptAtExit[N_THREADS];
+
+ for (int i = 0; i < N_THREADS; i++ ) {
+ threads[i] = new InterruptAtExit();
+ int late_count = 1;
+ threads[i].start();
+ try {
+ // Wait for the worker thread to get going.
+ threads[i].startSyncObj.await();
+
+ // The first interrupt() call will break the
+ // worker out of the exitSyncObj.await() call
+ // and the rest will come in during thread exit.
+ for (; late_count <= N_LATE_CALLS; late_count++) {
+ threads[i].interrupt();
+
+ if (!threads[i].isAlive()) {
+ // Done with Thread.interrupt() calls since
+ // thread is not alive.
+ break;
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+
+ System.out.println("INFO: thread #" + i + ": made " + late_count +
+ " late calls to java.lang.Thread.interrupt()");
+ System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+ N_LATE_CALLS + " value is " +
+ ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+ "large enough to cause a Thread.interrupt() " +
+ "call after thread exit.");
+
+ try {
+ threads[i].join();
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+ threads[i].interrupt();
+ if (threads[i].isAlive()) {
+ throw new Error("Expected !Thread.isAlive() after thread #" +
+ i + " has been join()'ed");
+ }
+ }
+
+ String cmd = System.getProperty("sun.java.command");
+ if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+ // Exit with success in a non-JavaTest environment:
+ System.exit(0);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.isInterrupted() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug IsInterruptedAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class IsInterruptedAtExit extends Thread {
+ final static int N_THREADS = 32;
+ final static int N_LATE_CALLS = 2000;
+
+ public CountDownLatch exitSyncObj = new CountDownLatch(1);
+ public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+ @Override
+ public void run() {
+ // Tell main thread we have started.
+ startSyncObj.countDown();
+ try {
+ // Wait for main thread to interrupt us so we
+ // can race to exit.
+ exitSyncObj.await();
+ } catch (InterruptedException e) {
+ // ignore because we expect one
+ }
+ }
+
+ public static void main(String[] args) {
+ IsInterruptedAtExit threads[] = new IsInterruptedAtExit[N_THREADS];
+
+ for (int i = 0; i < N_THREADS; i++ ) {
+ threads[i] = new IsInterruptedAtExit();
+ int late_count = 1;
+ threads[i].start();
+ try {
+ // Wait for the worker thread to get going.
+ threads[i].startSyncObj.await();
+
+ // This interrupt() call will break the worker out of
+ // the exitSyncObj.await() call and the isInterrupted()
+ // calls will come in during thread exit.
+ threads[i].interrupt();
+ for (; late_count <= N_LATE_CALLS; late_count++) {
+ threads[i].isInterrupted();
+
+ if (!threads[i].isAlive()) {
+ // Done with Thread.isInterrupted() calls since
+ // thread is not alive.
+ break;
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+
+ System.out.println("INFO: thread #" + i + ": made " + late_count +
+ " late calls to java.lang.Thread.isInterrupted()");
+ System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+ N_LATE_CALLS + " value is " +
+ ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+ "large enough to cause a Thread.isInterrupted() " +
+ "call after thread exit.");
+
+ try {
+ threads[i].join();
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+ threads[i].isInterrupted();
+ if (threads[i].isAlive()) {
+ throw new Error("Expected !Thread.isAlive() after thread #" +
+ i + " has been join()'ed");
+ }
+ }
+
+ String cmd = System.getProperty("sun.java.command");
+ if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+ // Exit with success in a non-JavaTest environment:
+ System.exit(0);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.resume() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug ResumeAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class ResumeAtExit extends Thread {
+ final static int N_THREADS = 32;
+ final static int N_LATE_CALLS = 2000;
+
+ public CountDownLatch exitSyncObj = new CountDownLatch(1);
+ public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+ @Override
+ public void run() {
+ // Tell main thread we have started.
+ startSyncObj.countDown();
+ try {
+ // Wait for main thread to interrupt us so we
+ // can race to exit.
+ exitSyncObj.await();
+ } catch (InterruptedException e) {
+ // ignore because we expect one
+ }
+ }
+
+ public static void main(String[] args) {
+ ResumeAtExit threads[] = new ResumeAtExit[N_THREADS];
+
+ for (int i = 0; i < N_THREADS; i++ ) {
+ threads[i] = new ResumeAtExit();
+ int late_count = 1;
+ threads[i].start();
+ try {
+ // Wait for the worker thread to get going.
+ threads[i].startSyncObj.await();
+
+ // This interrupt() call will break the worker out
+ // of the exitSyncObj.await() call and the resume()
+ // calls will come in during thread exit.
+ threads[i].interrupt();
+ for (; late_count <= N_LATE_CALLS; late_count++) {
+ threads[i].resume();
+
+ if (!threads[i].isAlive()) {
+ // Done with Thread.resume() calls since
+ // thread is not alive.
+ break;
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+
+ System.out.println("INFO: thread #" + i + ": made " + late_count +
+ " late calls to java.lang.Thread.resume()");
+ System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+ N_LATE_CALLS + " value is " +
+ ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+ "large enough to cause a Thread.resume() " +
+ "call after thread exit.");
+
+ try {
+ threads[i].join();
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+ threads[i].resume();
+ if (threads[i].isAlive()) {
+ throw new Error("Expected !Thread.isAlive() after thread #" +
+ i + " has been join()'ed");
+ }
+ }
+
+ String cmd = System.getProperty("sun.java.command");
+ if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+ // Exit with success in a non-JavaTest environment:
+ System.exit(0);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.setName() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug SetNameAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class SetNameAtExit extends Thread {
+ final static int N_THREADS = 32;
+ final static int N_LATE_CALLS = 1000;
+
+ public CountDownLatch exitSyncObj = new CountDownLatch(1);
+ public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+ @Override
+ public void run() {
+ // Tell main thread we have started.
+ startSyncObj.countDown();
+ try {
+ // Wait for main thread to interrupt us so we
+ // can race to exit.
+ exitSyncObj.await();
+ } catch (InterruptedException e) {
+ // ignore because we expect one
+ }
+ }
+
+ public static void main(String[] args) {
+ SetNameAtExit threads[] = new SetNameAtExit[N_THREADS];
+
+ for (int i = 0; i < N_THREADS; i++ ) {
+ threads[i] = new SetNameAtExit();
+ int late_count = 1;
+ threads[i].start();
+ try {
+ // Wait for the worker thread to get going.
+ threads[i].startSyncObj.await();
+
+ // This interrupt() call will break the worker out
+ // of the exitSyncObj.await() call and the setName()
+ // calls will come in during thread exit.
+ threads[i].interrupt();
+ for (; late_count <= N_LATE_CALLS; late_count++) {
+ threads[i].setName("T" + i + "-" + late_count);
+
+ if (!threads[i].isAlive()) {
+ // Done with Thread.setName() calls since
+ // thread is not alive.
+ break;
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+
+ System.out.println("INFO: thread #" + i + ": made " + late_count +
+ " late calls to java.lang.Thread.setName()");
+ System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+ N_LATE_CALLS + " value is " +
+ ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+ "large enough to cause a Thread.setName() " +
+ "call after thread exit.");
+
+ try {
+ threads[i].join();
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+ threads[i].setName("T" + i + "-done");
+ if (threads[i].isAlive()) {
+ throw new Error("Expected !Thread.isAlive() after thread #" +
+ i + " has been join()'ed");
+ }
+ }
+
+ String cmd = System.getProperty("sun.java.command");
+ if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+ // Exit with success in a non-JavaTest environment:
+ System.exit(0);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.setPriority() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug SetPriorityAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class SetPriorityAtExit extends Thread {
+ final static int N_THREADS = 32;
+ final static int N_LATE_CALLS = 2000;
+
+ final static int MIN = java.lang.Thread.MIN_PRIORITY;
+ final static int NORM = java.lang.Thread.NORM_PRIORITY;
+
+ public CountDownLatch exitSyncObj = new CountDownLatch(1);
+ public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+ @Override
+ public void run() {
+ // Tell main thread we have started.
+ startSyncObj.countDown();
+ try {
+ // Wait for main thread to interrupt us so we
+ // can race to exit.
+ exitSyncObj.await();
+ } catch (InterruptedException e) {
+ // ignore because we expect one
+ }
+ }
+
+ public static void main(String[] args) {
+ SetPriorityAtExit threads[] = new SetPriorityAtExit[N_THREADS];
+
+ int prio = MIN;
+ for (int i = 0; i < N_THREADS; i++ ) {
+ threads[i] = new SetPriorityAtExit();
+ int late_count = 1;
+ threads[i].start();
+ try {
+ // Wait for the worker thread to get going.
+ threads[i].startSyncObj.await();
+
+ // This interrupt() call will break the worker out of
+ // the exitSyncObj.await() call and the setPriority()
+ // calls will come in during thread exit.
+ threads[i].interrupt();
+ for (; late_count <= N_LATE_CALLS; late_count++) {
+ threads[i].setPriority(prio);
+ if (prio == MIN) {
+ prio = NORM;
+ } else {
+ prio = MIN;
+ }
+
+ if (!threads[i].isAlive()) {
+ // Done with Thread.setPriority() calls since
+ // thread is not alive.
+ break;
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+
+ System.out.println("INFO: thread #" + i + ": made " + late_count +
+ " late calls to java.lang.Thread.setPriority()");
+ System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+ N_LATE_CALLS + " value is " +
+ ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+ "large enough to cause a Thread.setPriority() " +
+ "call after thread exit.");
+
+ try {
+ threads[i].join();
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+ threads[i].setPriority(prio);
+ if (threads[i].isAlive()) {
+ throw new Error("Expected !Thread.isAlive() after thread #" +
+ i + " has been join()'ed");
+ }
+ }
+
+ String cmd = System.getProperty("sun.java.command");
+ if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+ // Exit with success in a non-JavaTest environment:
+ System.exit(0);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/Thread/StopAtExit.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.stop() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug StopAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class StopAtExit extends Thread {
+ final static int N_THREADS = 32;
+ final static int N_LATE_CALLS = 1000;
+
+ public CountDownLatch exitSyncObj = new CountDownLatch(1);
+ public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+ @Override
+ public void run() {
+ try {
+ // Tell main thread we have started.
+ startSyncObj.countDown();
+ try {
+ // Wait for main thread to interrupt us so we
+ // can race to exit.
+ exitSyncObj.await();
+ } catch (InterruptedException e) {
+ // ignore because we expect one
+ }
+ } catch (ThreadDeath td) {
+ // ignore because we're testing Thread.stop() which throws it
+ } catch (NoClassDefFoundError ncdfe) {
+ // ignore because we're testing Thread.stop() which can cause it
+ }
+ }
+
+ public static void main(String[] args) {
+ StopAtExit threads[] = new StopAtExit[N_THREADS];
+
+ for (int i = 0; i < N_THREADS; i++ ) {
+ threads[i] = new StopAtExit();
+ int late_count = 1;
+ threads[i].start();
+ try {
+ // Wait for the worker thread to get going.
+ threads[i].startSyncObj.await();
+
+ // This interrupt() call will break the worker out
+ // of the exitSyncObj.await() call and the stop()
+ // calls will come in during thread exit.
+ threads[i].interrupt();
+ for (; late_count <= N_LATE_CALLS; late_count++) {
+ threads[i].stop();
+
+ if (!threads[i].isAlive()) {
+ // Done with Thread.stop() calls since
+ // thread is not alive.
+ break;
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ } catch (NoClassDefFoundError ncdfe) {
+ // Ignore because we're testing Thread.stop() which can
+ // cause it. Yes, a NoClassDefFoundError that happens
+ // in a worker thread can subsequently be seen in the
+ // main thread.
+ }
+
+ System.out.println("INFO: thread #" + i + ": made " + late_count +
+ " late calls to java.lang.Thread.stop()");
+ System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+ N_LATE_CALLS + " value is " +
+ ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+ "large enough to cause a Thread.stop() " +
+ "call after thread exit.");
+
+ try {
+ threads[i].join();
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+ threads[i].stop();
+ if (threads[i].isAlive()) {
+ throw new Error("Expected !Thread.isAlive() after thread #" +
+ i + " has been join()'ed");
+ }
+ }
+
+ String cmd = System.getProperty("sun.java.command");
+ if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+ // Exit with success in a non-JavaTest environment:
+ System.exit(0);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.suspend() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug SuspendAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class SuspendAtExit extends Thread {
+ final static int N_THREADS = 32;
+ final static int N_LATE_CALLS = 10000;
+
+ public CountDownLatch exitSyncObj = new CountDownLatch(1);
+ public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+ @Override
+ public void run() {
+ // Tell main thread we have started.
+ startSyncObj.countDown();
+ try {
+ // Wait for main thread to interrupt us so we
+ // can race to exit.
+ exitSyncObj.await();
+ } catch (InterruptedException e) {
+ // ignore because we expect one
+ }
+ }
+
+ public static void main(String[] args) {
+ SuspendAtExit threads[] = new SuspendAtExit[N_THREADS];
+
+ for (int i = 0; i < N_THREADS; i++ ) {
+ threads[i] = new SuspendAtExit();
+ int late_count = 1;
+ threads[i].start();
+ try {
+ // Wait for the worker thread to get going.
+ threads[i].startSyncObj.await();
+
+ // This interrupt() call will break the worker out
+ // of the exitSyncObj.await() call and the suspend()
+ // calls will come in during thread exit.
+ threads[i].interrupt();
+ for (; late_count <= N_LATE_CALLS; late_count++) {
+ threads[i].suspend();
+
+ if (!threads[i].isAlive()) {
+ // Done with Thread.suspend() calls since
+ // thread is not alive.
+ break;
+ }
+ threads[i].resume();
+ }
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+
+ System.out.println("INFO: thread #" + i + ": made " + late_count +
+ " late calls to java.lang.Thread.suspend()");
+ System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+ N_LATE_CALLS + " value is " +
+ ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+ "large enough to cause a Thread.suspend() " +
+ "call after thread exit.");
+
+ try {
+ threads[i].join();
+ } catch (InterruptedException e) {
+ throw new Error("Unexpected: " + e);
+ }
+ threads[i].suspend();
+ threads[i].resume();
+ if (threads[i].isAlive()) {
+ throw new Error("Expected !Thread.isAlive() after thread #" +
+ i + " has been join()'ed");
+ }
+ }
+
+ String cmd = System.getProperty("sun.java.command");
+ if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+ // Exit with success in a non-JavaTest environment:
+ System.exit(0);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8167108
+ * @summary Checks whether jstack reports a "Threads class SMR info" section.
+ *
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * java.management
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics TestThreadDumpSMRInfo
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.JDKToolFinder;
+
+public class TestThreadDumpSMRInfo {
+ // jstack tends to be closely bound to the VM that we are running
+ // so use getTestJDKTool() instead of getCompileJDKTool() or even
+ // getJDKTool() which can fall back to "compile.jdk".
+ final static String JSTACK = JDKToolFinder.getTestJDKTool("jstack");
+ final static String PID = "" + ProcessHandle.current().pid();
+
+ // Here's a sample "Threads class SMR info" section:
+ //
+ // Threads class SMR info:
+ // _smr_java_thread_list=0x0000000000ce8da0, length=23, elements={
+ // 0x000000000043a800, 0x0000000000aee800, 0x0000000000b06800, 0x0000000000b26000,
+ // 0x0000000000b28800, 0x0000000000b2b000, 0x0000000000b2e000, 0x0000000000b30000,
+ // 0x0000000000b32800, 0x0000000000b35000, 0x0000000000b3f000, 0x0000000000b41800,
+ // 0x0000000000b44000, 0x0000000000b46800, 0x0000000000b48800, 0x0000000000b53000,
+ // 0x0000000000b55800, 0x0000000000b57800, 0x0000000000b5a000, 0x0000000000b5c800,
+ // 0x0000000000cc8800, 0x0000000000fd9800, 0x0000000000ef4800
+ // }
+ // _smr_java_thread_list_alloc_cnt=24, _smr_java_thread_list_free_cnt=23, _smr_java_thread_list_max=23, _smr_nested_thread_list_max=0
+ // _smr_delete_lock_wait_cnt=0, _smr_delete_lock_wait_max=0
+ // _smr_to_delete_list_cnt=0, _smr_to_delete_list_max=1
+
+ final static String HEADER_STR = "Threads class SMR info:";
+
+ static boolean verbose = false;
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 0) {
+ int arg_i = 0;
+ if (args[arg_i].equals("-v")) {
+ verbose = true;
+ arg_i++;
+ }
+ }
+
+ ProcessBuilder pb = new ProcessBuilder(JSTACK, PID);
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ if (verbose) {
+ System.out.println("stdout: " + output.getStdout());
+ }
+
+ output.shouldHaveExitValue(0);
+ System.out.println("INFO: jstack ran successfully.");
+
+ output.shouldContain(HEADER_STR);
+ System.out.println("INFO: Found: '" + HEADER_STR + "' in jstack output.");
+
+ System.out.println("Test PASSED.");
+ }
+
+ static void usage() {
+ System.err.println("Usage: java TestThreadDumpSMRInfo [-v]");
+ System.exit(1);
+ }
+}
--- a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkExitTest.java Wed Nov 22 14:31:48 2017 -0500
+++ b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkExitTest.java Wed Nov 22 17:54:50 2017 -0800
@@ -42,22 +42,18 @@
}
static volatile boolean exit_now = false;
- static Thread[] threads;
public static void main(String... args) throws Exception {
- int testRuns = 100;
- int testThreads = 500;
+ int testRuns = 20;
+ int testThreads = 128;
HandshakeWalkExitTest test = new HandshakeWalkExitTest();
- threads = new Thread[64];
-
Runnable hser = new Runnable(){
public void run(){
WhiteBox wb = WhiteBox.getWhiteBox();
while(!exit_now) {
wb.handshakeWalkStack(null, true);
- try { Thread.sleep(1); } catch(Exception e) {}
}
}
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkOneExitTest.java Wed Nov 22 17:54:50 2017 -0800
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2017, 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 HandshakeWalkOneExitTest
+ * @summary This test tries to stress the handshakes with new and exiting threads
+ * @library /testlibrary /test/lib
+ * @build HandshakeWalkOneExitTest
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI HandshakeWalkOneExitTest
+ */
+
+import jdk.test.lib.Asserts;
+import sun.hotspot.WhiteBox;
+
+public class HandshakeWalkOneExitTest implements Runnable {
+
+ @Override
+ public void run() {
+ }
+
+ static volatile boolean exit_now = false;
+ static Thread[] threads;
+
+ public static void main(String... args) throws Exception {
+ int testRuns = 20;
+ int testThreads = 128;
+
+ HandshakeWalkOneExitTest test = new HandshakeWalkOneExitTest();
+
+ Runnable hser = new Runnable(){
+ public void run(){
+ WhiteBox wb = WhiteBox.getWhiteBox();
+ while(!exit_now) {
+ Thread[] t = threads;
+ for (int i = 0; i<t.length ; i++) {
+ wb.handshakeWalkStack(t[i], false);
+ }
+ }
+ }
+ };
+ Thread hst = new Thread(hser);
+ for (int k = 0; k<testRuns ; k++) {
+ threads = new Thread[testThreads];
+ for (int i = 0; i<threads.length ; i++) {
+ threads[i] = new Thread(test);
+ threads[i].start();
+ }
+ if (k == 0) {
+ hst.start();
+ }
+ }
+ exit_now = true;
+ hst.join();
+ }
+}