--- old/src/hotspot/os/linux/os_linux.cpp Wed Nov 15 10:23:35 2017 +++ new/src/hotspot/os/linux/os_linux.cpp Wed Nov 15 10:23:35 2017 @@ -58,6 +58,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" @@ -1602,7 +1603,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" @@ -1830,9 +1834,7 @@ // 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())) { @@ -1839,7 +1841,6 @@ warning("Attempt to reguard stack yellow zone failed."); } } - jt = jt->next(); } } --- old/src/hotspot/os/posix/os_posix.cpp Wed Nov 15 10:23:38 2017 +++ new/src/hotspot/os/posix/os_posix.cpp Wed Nov 15 10:23:37 2017 @@ -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(); --- old/src/hotspot/os/windows/os_windows.cpp Wed Nov 15 10:23:41 2017 +++ new/src/hotspot/os/windows/os_windows.cpp Wed Nov 15 10:23:40 2017 @@ -3468,9 +3468,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); @@ -3491,8 +3489,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 --- old/src/hotspot/share/gc/g1/dirtyCardQueue.cpp Wed Nov 15 10:23:43 2017 +++ new/src/hotspot/share/gc/g1/dirtyCardQueue.cpp Wed Nov 15 10:23:43 2017 @@ -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); --- old/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Wed Nov 15 10:23:46 2017 +++ new/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Wed Nov 15 10:23:45 2017 @@ -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(); --- old/src/hotspot/share/gc/g1/satbMarkQueue.cpp Wed Nov 15 10:23:48 2017 +++ new/src/hotspot/share/gc/g1/satbMarkQueue.cpp Wed Nov 15 10:23:48 2017 @@ -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,7 +250,7 @@ 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); @@ -256,7 +257,7 @@ } 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(); } --- old/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp Wed Nov 15 10:23:51 2017 +++ new/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp Wed Nov 15 10:23:50 2017 @@ -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); } } --- old/src/hotspot/share/gc/shared/collectedHeap.cpp Wed Nov 15 10:23:53 2017 +++ new/src/hotspot/share/gc/shared/collectedHeap.cpp Wed Nov 15 10:23:53 2017 @@ -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 --- old/src/hotspot/share/gc/shared/gcLocker.cpp Wed Nov 15 10:23:56 2017 +++ new/src/hotspot/share/gc/shared/gcLocker.cpp Wed Nov 15 10:23:55 2017 @@ -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,7 +46,8 @@ 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++; } @@ -52,7 +54,8 @@ } 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()); } --- old/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp Wed Nov 15 10:23:59 2017 +++ new/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp Wed Nov 15 10:23:58 2017 @@ -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(); } } --- old/src/hotspot/share/jvmci/jvmciRuntime.cpp Wed Nov 15 10:24:01 2017 +++ new/src/hotspot/share/jvmci/jvmciRuntime.cpp Wed Nov 15 10:24:00 2017 @@ -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 { --- old/src/hotspot/share/logging/logTag.hpp Wed Nov 15 10:24:03 2017 +++ new/src/hotspot/share/logging/logTag.hpp Wed Nov 15 10:24:03 2017 @@ -120,6 +120,7 @@ LOG_TAG(safepoint) \ LOG_TAG(scavenge) \ LOG_TAG(scrub) \ + LOG_TAG(smr) \ LOG_TAG(stacktrace) \ LOG_TAG(stackwalk) \ LOG_TAG(start) \ --- old/src/hotspot/share/opto/idealGraphPrinter.cpp Wed Nov 15 10:24:06 2017 +++ new/src/hotspot/share/opto/idealGraphPrinter.cpp Wed Nov 15 10:24:05 2017 @@ -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(); --- old/src/hotspot/share/prims/jni.cpp Wed Nov 15 10:24:08 2017 +++ new/src/hotspot/share/prims/jni.cpp Wed Nov 15 10:24:08 2017 @@ -4140,7 +4140,7 @@ thread->initialize_thread_current(); if (!os::create_attached_thread(thread)) { - delete thread; + thread->smr_delete(); return JNI_ERR; } // Enable stack overflow checks @@ -4271,7 +4271,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; --- old/src/hotspot/share/prims/jvm.cpp Wed Nov 15 10:24:11 2017 +++ new/src/hotspot/share/prims/jvm.cpp Wed Nov 15 10:24:10 2017 @@ -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" @@ -2754,16 +2755,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); @@ -2838,7 +2835,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, @@ -2852,34 +2849,38 @@ 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 @@ -2886,7 +2887,7 @@ // 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 @@ -2902,12 +2903,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()) { @@ -2939,16 +2940,29 @@ 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 @@ -2955,14 +2969,20 @@ 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 @@ -3033,67 +3053,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; - } 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 (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 { + 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. - if (throw_illegal_thread_state) { - THROW_MSG_0(vmSymbols::java_lang_IllegalThreadStateException(), - "this thread is not suspended"); - } 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 @@ -3101,16 +3093,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) { - return JNI_FALSE; + 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 (jboolean) Thread::is_interrupted(thr, clear_interrupted != 0); + return JNI_FALSE; } JVM_END @@ -3139,14 +3129,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); } @@ -3688,6 +3680,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()); --- old/src/hotspot/share/prims/jvmtiEnter.xsl Wed Nov 15 10:24:14 2017 +++ new/src/hotspot/share/prims/jvmtiEnter.xsl Wed Nov 15 10:24:13 2017 @@ -45,6 +45,7 @@ # include "prims/jvmtiEnter.hpp" # include "prims/jvmtiRawMonitor.hpp" # include "prims/jvmtiUtil.hpp" +# include "runtime/threadSMR.hpp" @@ -769,40 +770,19 @@ - oop thread_oop = JNIHandles::resolve_external_guard( + err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), - ); - if (thread_oop == NULL) { + , &java_thread, NULL); + if (err != JVMTI_ERROR_NONE) { - JVMTI_ERROR_INVALID_THREAD - - jthread resolved to NULL - jthread = " PTR_FORMAT " + err + - jthread did not convert to a JavaThread - jthread = " PTR_FORMAT " , p2i() } - if (!thread_oop->is_a(SystemDictionary::Thread_klass())) { - - JVMTI_ERROR_INVALID_THREAD - - oop is not a thread - jthread = " PTR_FORMAT " - , p2i() - - - } - java_thread = java_lang_Thread::thread(thread_oop); - if (java_thread == NULL) { - - - - JVMTI_ERROR_THREAD_NOT_ALIVE - - - not a Java thread - jthread = " PTR_FORMAT " - , p2i() - - - } - @@ -809,7 +789,8 @@ - JavaThread* java_thread; + JavaThread* java_thread = NULL; + ThreadsListHandle tlh(this_thread); --- old/src/hotspot/share/prims/jvmtiEnv.cpp Wed Nov 15 10:24:16 2017 +++ new/src/hotspot/share/prims/jvmtiEnv.cpp Wed Nov 15 10:24:15 2017 @@ -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,42 +514,60 @@ // 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; } - if (!thread_oop->is_a(SystemDictionary::Thread_klass())) { - return JVMTI_ERROR_INVALID_THREAD; + + 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; } - java_thread = java_lang_Thread::thread(thread_oop); - if (java_thread == NULL) { - return JVMTI_ERROR_THREAD_NOT_ALIVE; + + if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { + record_class_file_load_hook_enabled(); } - } + JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled); + } else { + // We have a specified event_thread. - // event_type must be valid - if (!JvmtiEventController::is_valid_event_type(event_type)) { - return JVMTI_ERROR_INVALID_EVENT_TYPE; - } + 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; + } - // global events cannot be controlled at thread level. - if (java_thread != NULL && JvmtiEventController::is_global_event(event_type)) { - return JVMTI_ERROR_ILLEGAL_ARGUMENT; - } + // event_type must be valid + if (!JvmtiEventController::is_valid_event_type(event_type)) { + return JVMTI_ERROR_INVALID_EVENT_TYPE; + } - bool enabled = (mode == JVMTI_ENABLE); + // global events cannot be controlled at thread level. + if (JvmtiEventController::is_global_event(event_type)) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } - // assure that needed capabilities are present - if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) { - return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; - } + bool enabled = (mode == JVMTI_ENABLE); - if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { - record_class_file_load_hook_enabled(); + // 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); } - 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); + 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. + } } - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) { - return JVMTI_ERROR_INVALID_THREAD; - } - // get most state bits - state = (jint)java_lang_Thread::get_thread_status(thread_oop); + jint 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 (java_thread != NULL) { + // We have a JavaThread* so add more state bits. + JavaThreadState jts = java_thread->thread_state(); - if (thr->is_being_ext_suspended()) { + 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,21 +958,14 @@ 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; + 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; } - // 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; - continue; - } // don't allow hidden thread suspend request. if (java_thread->is_hidden_from_external_view()) { results[i] = JVMTI_ERROR_NONE; // indicate successful suspend @@ -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,21 +1082,15 @@ // 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(); + 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; + } - // 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); - Thread::interrupt(java_thread); return JVMTI_ERROR_NONE; @@ -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,17 +1434,19 @@ 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); @@ -1411,19 +1453,34 @@ for (int i=0, j=0; iobj_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; iobj_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) { --- old/src/hotspot/share/prims/jvmtiEnvBase.cpp Wed Nov 15 10:24:19 2017 +++ new/src/hotspot/share/prims/jvmtiEnvBase.cpp Wed Nov 15 10:24:18 2017 @@ -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,38 +488,7 @@ } } -// 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 // mean much better out of memory handling @@ -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* wantList = Threads::get_pending_threads( - nWant, (address)mon, !at_safepoint); + // check for non java threads. + GrowableArray* 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); + } +} --- old/src/hotspot/share/prims/jvmtiEnvBase.hpp Wed Nov 15 10:24:21 2017 +++ new/src/hotspot/share/prims/jvmtiEnvBase.hpp Wed Nov 15 10:24:21 2017 @@ -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,12 +294,7 @@ 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); jvmtiError get_frame_location(JavaThread* java_thread, jint depth, @@ -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(); }; --- old/src/hotspot/share/prims/jvmtiEnvThreadState.cpp Wed Nov 15 10:24:24 2017 +++ new/src/hotspot/share/prims/jvmtiEnvThreadState.cpp Wed Nov 15 10:24:23 2017 @@ -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" --- old/src/hotspot/share/prims/jvmtiEventController.cpp Wed Nov 15 10:24:26 2017 +++ new/src/hotspot/share/prims/jvmtiEventController.cpp Wed Nov 15 10:24:26 2017 @@ -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 --- old/src/hotspot/share/prims/jvmtiExport.cpp Wed Nov 15 10:24:29 2017 +++ new/src/hotspot/share/prims/jvmtiExport.cpp Wed Nov 15 10:24:28 2017 @@ -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; --- old/src/hotspot/share/prims/jvmtiExport.hpp Wed Nov 15 10:24:31 2017 +++ new/src/hotspot/share/prims/jvmtiExport.hpp Wed Nov 15 10:24:31 2017 @@ -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 --- old/src/hotspot/share/prims/jvmtiImpl.cpp Wed Nov 15 10:24:34 2017 +++ new/src/hotspot/share/prims/jvmtiImpl.cpp Wed Nov 15 10:24:33 2017 @@ -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 --- old/src/hotspot/share/prims/jvmtiRedefineClasses.cpp Wed Nov 15 10:24:36 2017 +++ new/src/hotspot/share/prims/jvmtiRedefineClasses.cpp Wed Nov 15 10:24:36 2017 @@ -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" --- old/src/hotspot/share/prims/jvmtiTagMap.cpp Wed Nov 15 10:24:39 2017 +++ new/src/hotspot/share/prims/jvmtiTagMap.cpp Wed Nov 15 10:24:38 2017 @@ -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 --- old/src/hotspot/share/prims/jvmtiThreadState.hpp Wed Nov 15 10:24:42 2017 +++ new/src/hotspot/share/prims/jvmtiThreadState.hpp Wed Nov 15 10:24:41 2017 @@ -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 --- old/src/hotspot/share/prims/jvmtiThreadState.inline.hpp Wed Nov 15 10:24:44 2017 +++ new/src/hotspot/share/prims/jvmtiThreadState.inline.hpp Wed Nov 15 10:24:43 2017 @@ -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 --- old/src/hotspot/share/prims/unsafe.cpp Wed Nov 15 10:24:46 2017 +++ new/src/hotspot/share/prims/unsafe.cpp Wed Nov 15 10:24:46 2017 @@ -38,6 +38,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" @@ -1104,8 +1106,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 @@ -1113,22 +1119,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); --- old/src/hotspot/share/prims/whitebox.cpp Wed Nov 15 10:24:49 2017 +++ new/src/hotspot/share/prims/whitebox.cpp Wed Nov 15 10:24:48 2017 @@ -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" @@ -664,7 +665,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(); --- old/src/hotspot/share/runtime/biasedLocking.cpp Wed Nov 15 10:24:52 2017 +++ new/src/hotspot/share/runtime/biasedLocking.cpp Wed Nov 15 10:24:51 2017 @@ -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,7 +215,7 @@ if (requesting_thread == biased_thread) { thread_is_alive = true; } else { - for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur_thread = jtiwh.next(); ) { if (cur_thread == biased_thread) { thread_is_alive = true; break; @@ -390,25 +391,60 @@ 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 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 (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. - // 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()) { + // 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* 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)); + } + } + } + } + + // 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* 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); @@ -415,48 +451,17 @@ 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); } + } // ThreadsListHandle is destroyed here. - // 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* 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); - } - log_info(biasedlocking)("* Ending bulk revocation"); BiasedLocking::Condition status_code = BiasedLocking::BIAS_REVOKED; @@ -481,7 +486,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 +773,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()) { --- old/src/hotspot/share/runtime/deoptimization.cpp Wed Nov 15 10:24:54 2017 +++ new/src/hotspot/share/runtime/deoptimization.cpp Wed Nov 15 10:24:53 2017 @@ -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* objects_to_revoke = new GrowableArray(); - 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()) { --- old/src/hotspot/share/runtime/globals.hpp Wed Nov 15 10:24:57 2017 +++ new/src/hotspot/share/runtime/globals.hpp Wed Nov 15 10:24:56 2017 @@ -2473,6 +2473,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") \ \ --- old/src/hotspot/share/runtime/handshake.cpp Wed Nov 15 10:24:59 2017 +++ new/src/hotspot/share/runtime/handshake.cpp Wed Nov 15 10:24:59 2017 @@ -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,8 +136,22 @@ handle_timeout(); } + // We need to re-think this with SMR ThreadsList. + // There is assumption in code that Threads_lock should be lock + // during certain phases. MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); - _target->handshake_process_by_vmthread(); + ThreadsListHandle tlh; + if (tlh.includes(_target)) { + // Warning threads 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 thread address + // should be okey since the new thread will not have an operation. + _target->handshake_process_by_vmthread(); + } else { + // We can't warn here is since the thread do cancel_handshake after it have been removed + // from ThreadsList. So we should just keep looping here until while below return negative + // If we have a bug, then we deadlock here, which is good for debugging. + } } while (!poll_for_completed_thread()); } @@ -157,15 +168,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 +185,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 +196,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 assumption in code that Threads_lock should be lock + // 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 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 +230,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 +316,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; --- old/src/hotspot/share/runtime/java.cpp Wed Nov 15 10:25:02 2017 +++ new/src/hotspot/share/runtime/java.cpp Wed Nov 15 10:25:01 2017 @@ -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 --- old/src/hotspot/share/runtime/memprofiler.cpp Wed Nov 15 10:25:04 2017 +++ new/src/hotspot/share/runtime/memprofiler.cpp Wed Nov 15 10:25:03 2017 @@ -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 @@ -109,21 +110,22 @@ // 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(), + jtiwh.length(), + 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(), - Threads::number_of_threads(), - InstanceKlass::number_of_instance_classes(), - Universe::heap()->used() / K, - Universe::heap()->capacity() / K); - fprintf(_log_fp, UINTX_FORMAT_W(6) ",", CodeCache::capacity() / K); fprintf(_log_fp, UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",%6ld\n", --- old/src/hotspot/share/runtime/os.cpp Wed Nov 15 10:25:07 2017 +++ new/src/hotspot/share/runtime/os.cpp Wed Nov 15 10:25:06 2017 @@ -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; } --- old/src/hotspot/share/runtime/safepoint.cpp Wed Nov 15 10:25:09 2017 +++ new/src/hotspot/share/runtime/safepoint.cpp Wed Nov 15 10:25:08 2017 @@ -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 + // 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); + } } - 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 (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 (still_running > 0) { + // Check for if it takes to long + if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) { + print_safepoint_timeout(_spinning_timeout); + } - // 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. + // 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 (SafepointMechanism::uses_global_page_poll() && int(iterations) == DeferPollingPageLoopCount) { + guarantee (PageArmed == 0, "invariant") ; + PageArmed = 1 ; + os::make_polling_page_unreadable(); } - iterations ++ ; + // 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 (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(); - } + if (SafepointMechanism::uses_global_page_poll()) { + // Remove safepoint check from interpreter + Interpreter::ignore_safepoints(); + } - { - MutexLocker mu(Safepoint_lock); + { + MutexLocker mu(Safepoint_lock); - assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization"); + 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 - 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(); + 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"); + 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(); + // 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"); } - 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(); + RuntimeService::record_safepoint_end(); - // Release threads lock, so threads can be created/destroyed again. It will also starts all threads - // blocked in signal_thread_blocked - Threads_lock->unlock(); + // 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(); } } --- old/src/hotspot/share/runtime/synchronizer.cpp Wed Nov 15 10:25:12 2017 +++ new/src/hotspot/share/runtime/synchronizer.cpp Wed Nov 15 10:25:11 2017 @@ -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 --- old/src/hotspot/share/runtime/synchronizer.hpp Wed Nov 15 10:25:14 2017 +++ new/src/hotspot/share/runtime/synchronizer.hpp Wed Nov 15 10:25:13 2017 @@ -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); --- old/src/hotspot/share/runtime/thread.cpp Wed Nov 15 10:25:17 2017 +++ new/src/hotspot/share/runtime/thread.cpp Wed Nov 15 10:25:16 2017 @@ -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 @@ -1398,11 +1479,14 @@ void JavaThread::collect_counters(typeArrayOop array) { if (JVMCICounterSize > 0) { + // dcubed - Looks like the Threads_lock is for stable access + // to _jvmci_old_thread_counters and _jvmci_counters. 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 +1519,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 +1800,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 +1827,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,6 +1935,10 @@ // 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). @@ -1847,6 +1945,10 @@ 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,6 +2016,10 @@ 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); @@ -1921,6 +2027,21 @@ 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()); + } } #if INCLUDE_ALL_GCS @@ -1980,7 +2101,7 @@ #endif // INCLUDE_ALL_GCS Threads::remove(this); - delete this; + this->smr_delete(); } @@ -2235,10 +2356,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 +2447,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 +2565,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; } @@ -2919,6 +3034,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; } @@ -3317,18 +3439,61 @@ // 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; +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; +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: +uint Threads::_smr_delete_lock_wait_cnt = 0; +uint Threads::_smr_delete_lock_wait_max = 0; +volatile jint Threads::_smr_delete_notify = 0; +volatile jint Threads::_smr_deleted_thread_cnt = 0; +volatile jint Threads::_smr_deleted_thread_time_max = 0; +volatile jint Threads::_smr_deleted_thread_times = 0; +ThreadsList* volatile Threads::_smr_java_thread_list = new ThreadsList(0); +long Threads::_smr_java_thread_list_alloc_cnt = 1; +long Threads::_smr_java_thread_list_free_cnt = 0; +uint Threads::_smr_java_thread_list_max = 0; +uint Threads::_smr_nested_thread_list_max = 0; +volatile jint Threads::_smr_tlh_cnt = 0; +volatile jint Threads::_smr_tlh_time_max = 0; +volatile jint Threads::_smr_tlh_times = 0; +ThreadsList* Threads::_smr_to_delete_list = NULL; +uint Threads::_smr_to_delete_list_cnt = 0; +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) { @@ -3429,6 +3594,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)); @@ -3609,7 +3982,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; } @@ -3624,7 +3997,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; } @@ -4030,24 +4403,7 @@ } } -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() { HandleMark hm(this); @@ -4172,6 +4528,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 @@ -4205,7 +4566,496 @@ 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 { + private: + static bool ptr_equals(void * const& s1, void * const& s2) { + return s1 == s2; + } + static unsigned int ptr_hash(void * const& s1) { + 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 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. + } + + delete thread; + if (EnableThreadSMRStatistics) { + timer.stop(); + jint millis = (jint)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 assert_locked_or_safepoint(Threads_lock); @@ -4215,6 +5065,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; @@ -4227,6 +5082,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)); } @@ -4240,8 +5109,21 @@ // 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; @@ -4255,6 +5137,7 @@ } else { _thread_list = p->next(); } + _number_of_threads--; oop threadObj = p->threadObj(); bool daemon = true; @@ -4281,17 +5164,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 @@ -4404,47 +5276,36 @@ // Get count Java threads that are waiting to enter the specified monitor. -GrowableArray* 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* Threads::get_pending_threads(ThreadsList * t_list, + int count, + address monitor) { GrowableArray* result = new GrowableArray(count); int i = 0; - { - MutexLockerEx ml(doLock ? Threads_lock : NULL); - ALL_JAVA_THREADS(p) { - if (!p->can_call_java()) continue; + 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++; - } + 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. @@ -4455,15 +5316,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; } @@ -4488,6 +5347,9 @@ } #endif // INCLUDE_SERVICES + print_smr_info_on(st); + st->cr(); + ALL_JAVA_THREADS(p) { ResourceMark rm; p->print_on(st); @@ -4514,9 +5376,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=%ld, " + "_smr_java_thread_list_free_cnt=%ld, " + "_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=" INT32_FORMAT + ", _smr_tlh_times=" INT32_FORMAT + ", avg_smr_tlh_time=%0.2f" + ", _smr_tlh_time_max=" INT32_FORMAT, + _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=" INT32_FORMAT + ", _smr_deleted_thread_times=" INT32_FORMAT + ", avg_smr_deleted_thread_time=%0.2f" + ", _smr_deleted_thread_time_max=" INT32_FORMAT, + _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) { @@ -4553,6 +5511,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) { @@ -4574,6 +5535,7 @@ st->cr(); } st->cr(); + st->print_cr("Threads with active compile tasks:"); print_threads_compiling(st, buf, buflen); } --- old/src/hotspot/share/runtime/thread.hpp Wed Nov 15 10:25:20 2017 +++ new/src/hotspot/share/runtime/thread.hpp Wed Nov 15 10:25:19 2017 @@ -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() { 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(); + 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,17 +2126,44 @@ 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: + static uint _smr_delete_lock_wait_cnt; + static uint _smr_delete_lock_wait_max; + static volatile jint _smr_delete_notify; + static volatile jint _smr_deleted_thread_cnt; + static volatile jint _smr_deleted_thread_time_max; + static volatile jint _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 long _smr_java_thread_list_alloc_cnt; + static long _smr_java_thread_list_free_cnt; + static uint _smr_java_thread_list_max; + static uint _smr_nested_thread_list_max; + static volatile jint _smr_tlh_cnt; + static volatile jint _smr_tlh_time_max; + static volatile jint _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 @@ -2083,11 +2170,41 @@ // 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: + template + static void threads_do_smr(T *tc, Thread *self); + 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(jint new_value); + static void add_smr_deleted_thread_times(jint add_value); + static void inc_smr_tlh_cnt(); + static void update_smr_tlh_time_max(jint new_value); + static void add_smr_tlh_times(jint 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 +2265,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 +2278,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* get_pending_threads(int count, - address monitor, bool doLock); + // Get Java threads that are waiting to enter a monitor. + static GrowableArray* 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 +2293,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); - }; --- old/src/hotspot/share/runtime/thread.inline.hpp Wed Nov 15 10:25:22 2017 +++ new/src/hotspot/share/runtime/thread.inline.hpp Wed Nov 15 10:25:21 2017 @@ -25,14 +25,11 @@ #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" +#include "runtime/threadSMR.hpp" -#undef SHARE_VM_RUNTIME_THREAD_INLINE_HPP_SCOPE - inline void Thread::set_suspend_flag(SuspendFlags f) { assert(sizeof(jint) == sizeof(_suspend_flags), "size mismatch"); uint32_t flags; @@ -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); } @@ -175,4 +184,89 @@ 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() { + // 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(_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); +} + +template +inline void Threads::threads_do_smr(T *tc, Thread *self) { + ThreadsListHandle handle(self); + handle.threads_do(tc); +} + +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(jint new_value) { + while (true) { + jint 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(jint 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(jint new_value) { + while (true) { + jint 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(jint add_value) { + Atomic::add(add_value, &_smr_tlh_times); +} + #endif // SHARE_VM_RUNTIME_THREAD_INLINE_HPP --- old/src/hotspot/share/runtime/vm_operations.cpp Wed Nov 15 10:25:25 2017 +++ new/src/hotspot/share/runtime/vm_operations.cpp Wed Nov 15 10:25:24 2017 @@ -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()) { --- old/src/hotspot/share/runtime/vm_operations.hpp Wed Nov 15 10:25:27 2017 +++ new/src/hotspot/share/runtime/vm_operations.hpp Wed Nov 15 10:25:27 2017 @@ -392,17 +392,19 @@ 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(); - DeadlockCycle* result() { return _deadlocks; }; - VMOp_Type type() const { return VMOp_FindDeadlocks; } + DeadlockCycle* result() { return _deadlocks; }; + VMOp_Type type() const { return VMOp_FindDeadlocks; } void doit(); bool doit_prologue(); }; --- old/src/hotspot/share/services/heapDumper.cpp Wed Nov 15 10:25:30 2017 +++ new/src/hotspot/share/services/heapDumper.cpp Wed Nov 15 10:25:29 2017 @@ -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 --- old/src/hotspot/share/services/management.cpp Wed Nov 15 10:25:32 2017 +++ new/src/hotspot/share/services/management.cpp Wed Nov 15 10:25:32 2017 @@ -41,6 +41,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* thread_handle_array = new GrowableArray(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)); --- old/src/hotspot/share/services/threadService.cpp Wed Nov 15 10:25:35 2017 +++ new/src/hotspot/share/services/threadService.cpp Wed Nov 15 10:25:34 2017 @@ -34,9 +34,9 @@ #include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" -#include "runtime/thread.hpp" -#include "runtime/vframe.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.inline.hpp" +#include "runtime/vframe.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(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. --- old/src/hotspot/share/services/threadService.hpp Wed Nov 15 10:25:38 2017 +++ new/src/hotspot/share/services/threadService.hpp Wed Nov 15 10:25:37 2017 @@ -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 { 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* _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* 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. --- old/src/hotspot/share/utilities/vmError.cpp Wed Nov 15 10:25:40 2017 +++ new/src/hotspot/share/utilities/vmError.cpp Wed Nov 15 10:25:39 2017 @@ -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); } --- old/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java Wed Nov 15 10:25:43 2017 +++ new/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java Wed Nov 15 10:25:42 2017 @@ -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 @@ -69,6 +69,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 Wed Nov 15 10:25:46 2017 +++ new/src/hotspot/share/runtime/threadSMR.cpp Wed Nov 15 10:25:45 2017 @@ -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, mtGC)), _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(); + jint millis = (jint)_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 Wed Nov 15 10:25:48 2017 +++ new/src/hotspot/share/runtime/threadSMR.hpp Wed Nov 15 10:25:47 2017 @@ -0,0 +1,208 @@ +/* + * 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" + +// A fast list of JavaThreads. +// +class ThreadsList : public CHeapObj { + friend class ScanHazardPtrGatherProtectedThreadsClosure; + friend class Threads; + + const uint _length; + ThreadsList* _next_list; + JavaThread *const *const _threads; + + template + 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 + 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 { + 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 + 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() : _tlh(), _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 Wed Nov 15 10:25:51 2017 +++ new/src/hotspot/share/runtime/threadSMR.inline.hpp Wed Nov 15 10:25:49 2017 @@ -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 +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 *cl, JavaThread *const thread) const { + cl->do_thread(thread); +} + +template +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 --- /dev/null Wed Nov 15 10:25:53 2017 +++ new/test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java Wed Nov 15 10:25:52 2017 @@ -0,0 +1,127 @@ +/* + * 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 + * @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 { + + if (!Platform.isDebugBuild()) { + // -XX:ErrorHandlerTest=N option requires debug bits. + return; + } + + 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 Wed Nov 15 10:25:55 2017 +++ new/test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java Wed Nov 15 10:25:54 2017 @@ -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 + * @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 { + + if (!Platform.isDebugBuild()) { + // -XX:ErrorHandlerTest=N option requires debug bits. + return; + } + + 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 Wed Nov 15 10:25:58 2017 +++ new/test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java Wed Nov 15 10:25:57 2017 @@ -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 Wed Nov 15 10:26:00 2017 +++ new/test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java Wed Nov 15 10:25:59 2017 @@ -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 Wed Nov 15 10:26:03 2017 +++ new/test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java Wed Nov 15 10:26:02 2017 @@ -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 Wed Nov 15 10:26:05 2017 +++ new/test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java Wed Nov 15 10:26:04 2017 @@ -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 Wed Nov 15 10:26:08 2017 +++ new/test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java Wed Nov 15 10:26:07 2017 @@ -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 Wed Nov 15 10:26:10 2017 +++ new/test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java Wed Nov 15 10:26:09 2017 @@ -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 Wed Nov 15 10:26:13 2017 +++ new/test/hotspot/jtreg/runtime/Thread/StopAtExit.java Wed Nov 15 10:26:12 2017 @@ -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 Wed Nov 15 10:26:15 2017 +++ new/test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java Wed Nov 15 10:26:14 2017 @@ -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 Wed Nov 15 10:26:18 2017 +++ new/test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java Wed Nov 15 10:26:17 2017 @@ -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); + } +}