8167108: inconsistent handling of SR_lock can lead to crashes
Add Thread Safe Memory Reclamation (Thread-SMR) mechanism. Co-authored-by: Erik Osterlund <erik.osterlund@oracle.com> Co-authored-by: Robbin Ehn <robbin.ehn@oracle.com> Reviewed-by: coleenp, dcubed, dholmes, eosterlund, gthornbr, kbarrett, rehn, sspitsyn, stefank
This commit is contained in:
parent
cd0c6d0fae
commit
0dff96ff0b
src/hotspot
os
share
gc
g1
parallel
shared
jvmci
logging
opto
prims
jni.cppjvm.cppjvmtiEnter.xsljvmtiEnv.cppjvmtiEnvBase.cppjvmtiEnvBase.hppjvmtiEnvThreadState.cppjvmtiEventController.cppjvmtiExport.cppjvmtiExport.hppjvmtiImpl.cppjvmtiRedefineClasses.cppjvmtiTagMap.cppjvmtiThreadState.hppjvmtiThreadState.inline.hppunsafe.cppwhitebox.cpp
runtime
biasedLocking.cppdeoptimization.cppglobals.hpphandshake.cppjava.cppmemprofiler.cppos.cppsafepoint.cppsynchronizer.cppsynchronizer.hppthread.cppthread.hppthread.inline.hppthreadSMR.cppthreadSMR.hppthreadSMR.inline.hppvm_operations.cppvm_operations.hpp
services
utilities
test/hotspot/jtreg/runtime
ErrorHandling
ErrorHandler.javaNestedThreadsListHandleInErrorHandlingTest.javaThreadsListHandleInErrorHandlingTest.java
Thread
CountStackFramesAtExit.javaInterruptAtExit.javaIsInterruptedAtExit.javaResumeAtExit.javaSetNameAtExit.javaSetPriorityAtExit.javaStopAtExit.javaSuspendAtExit.javaTestThreadDumpSMRInfo.java
handshake
@ -59,6 +59,7 @@
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadCritical.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/timer.hpp"
|
||||
#include "semaphore_posix.hpp"
|
||||
#include "services/attachListener.hpp"
|
||||
@ -1646,7 +1647,10 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
|
||||
//
|
||||
// Dynamic loader will make all stacks executable after
|
||||
// this function returns, and will not do that again.
|
||||
assert(Threads::first() == NULL, "no Java threads should exist yet.");
|
||||
#ifdef ASSERT
|
||||
ThreadsListHandle tlh;
|
||||
assert(tlh.length() == 0, "no Java threads should exist yet.");
|
||||
#endif
|
||||
} else {
|
||||
warning("You have loaded library %s which might have disabled stack guard. "
|
||||
"The VM will try to fix the stack guard now.\n"
|
||||
@ -1874,16 +1878,13 @@ void * os::Linux::dll_load_in_vmthread(const char *filename, char *ebuf,
|
||||
// may have been queued at the same time.
|
||||
|
||||
if (!_stack_is_executable) {
|
||||
JavaThread *jt = Threads::first();
|
||||
|
||||
while (jt) {
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
|
||||
if (!jt->stack_guard_zone_unused() && // Stack not yet fully initialized
|
||||
jt->stack_guards_enabled()) { // No pending stack overflow exceptions
|
||||
if (!os::guard_memory((char *)jt->stack_end(), jt->stack_guard_zone_size())) {
|
||||
warning("Attempt to reguard stack yellow zone failed.");
|
||||
}
|
||||
}
|
||||
jt = jt->next();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,8 +478,7 @@ int os::sleep(Thread* thread, jlong millis, bool interruptible) {
|
||||
// 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 @@ void os::interrupt(Thread* thread) {
|
||||
|
||||
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();
|
||||
|
||||
|
@ -3490,9 +3490,7 @@ OSReturn os::get_native_priority(const Thread* const thread,
|
||||
void os::hint_no_preempt() {}
|
||||
|
||||
void os::interrupt(Thread* thread) {
|
||||
assert(!thread->is_Java_thread() || Thread::current() == thread ||
|
||||
Threads_lock->owned_by_self(),
|
||||
"possibility of dangling Thread pointer");
|
||||
debug_only(Thread::check_for_dangling_thread_pointer(thread);)
|
||||
|
||||
OSThread* osthread = thread->osthread();
|
||||
osthread->set_interrupted(true);
|
||||
@ -3513,8 +3511,7 @@ void os::interrupt(Thread* thread) {
|
||||
|
||||
|
||||
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
|
||||
|
@ -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 @@ void DirtyCardQueueSet::abandon_logs() {
|
||||
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 @@ void DirtyCardQueueSet::concatenate_logs() {
|
||||
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);
|
||||
|
@ -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 @@ G1CollectedHeap::doConcurrentMark() {
|
||||
|
||||
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();
|
||||
|
@ -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 @@ void SATBMarkQueueSet::dump_active_states(bool expected_active) {
|
||||
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 @@ void SATBMarkQueueSet::verify_active_states(bool expected_active) {
|
||||
}
|
||||
|
||||
// Verify thread queue states
|
||||
for (JavaThread* t = Threads::first(); t; t = t->next()) {
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
|
||||
if (t->satb_mark_queue().is_active() != expected_active) {
|
||||
dump_active_states(expected_active);
|
||||
guarantee(false, "Thread SATB queue has an unexpected active state");
|
||||
@ -249,14 +250,14 @@ void SATBMarkQueueSet::set_active_all_threads(bool active, bool expected_active)
|
||||
verify_active_states(expected_active);
|
||||
#endif // ASSERT
|
||||
_all_active = active;
|
||||
for (JavaThread* t = Threads::first(); t; t = t->next()) {
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
|
||||
t->satb_mark_queue().set_active(active);
|
||||
}
|
||||
shared_satb_queue()->set_active(active);
|
||||
}
|
||||
|
||||
void SATBMarkQueueSet::filter_thread_buffers() {
|
||||
for(JavaThread* t = Threads::first(); t; t = t->next()) {
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
|
||||
t->satb_mark_queue().filter();
|
||||
}
|
||||
shared_satb_queue()->filter();
|
||||
@ -309,7 +310,7 @@ void SATBMarkQueueSet::print_all(const char* msg) {
|
||||
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 @@ void SATBMarkQueueSet::abandon_partial_marking() {
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
@ -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 @@ bool MutableNUMASpace::update_layout(bool force) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ void CollectedHeap::ensure_parsability(bool retire_tlabs) {
|
||||
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
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,6 +29,7 @@
|
||||
#include "logging/log.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
|
||||
volatile jint GCLocker::_jni_lock_count = 0;
|
||||
volatile bool GCLocker::_needs_gc = false;
|
||||
@ -45,14 +46,16 @@ void GCLocker::verify_critical_count() {
|
||||
assert(!needs_gc() || _debug_jni_lock_count == _jni_lock_count, "must agree");
|
||||
int count = 0;
|
||||
// Count the number of threads with critical operations in progress
|
||||
for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) {
|
||||
JavaThreadIteratorWithHandle jtiwh;
|
||||
for (; JavaThread *thr = jtiwh.next(); ) {
|
||||
if (thr->in_critical()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (_jni_lock_count != count) {
|
||||
log_error(gc, verify)("critical counts don't match: %d != %d", _jni_lock_count, count);
|
||||
for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) {
|
||||
jtiwh.rewind();
|
||||
for (; JavaThread *thr = jtiwh.next(); ) {
|
||||
if (thr->in_critical()) {
|
||||
log_error(gc, verify)(INTPTR_FORMAT " in_critical %d", p2i(thr), thr->in_critical());
|
||||
}
|
||||
|
@ -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::clear_before_allocation() {
|
||||
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::make_parsable(bool retire, bool zap) {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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_ENTRY(jint, JVMCIRuntime::identity_hash_code(JavaThread* thread, oopDesc* ob
|
||||
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 {
|
||||
|
@ -121,6 +121,7 @@
|
||||
LOG_TAG(safepoint) \
|
||||
LOG_TAG(scavenge) \
|
||||
LOG_TAG(scrub) \
|
||||
LOG_TAG(smr) \
|
||||
LOG_TAG(stacktrace) \
|
||||
LOG_TAG(stackwalk) \
|
||||
LOG_TAG(start) \
|
||||
|
@ -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 @@ IdealGraphPrinter *IdealGraphPrinter::printer() {
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -4119,7 +4119,7 @@ static jint attach_current_thread(JavaVM *vm, void **penv, void *_args, bool dae
|
||||
thread->initialize_thread_current();
|
||||
|
||||
if (!os::create_attached_thread(thread)) {
|
||||
delete thread;
|
||||
thread->smr_delete();
|
||||
return JNI_ERR;
|
||||
}
|
||||
// Enable stack overflow checks
|
||||
@ -4250,7 +4250,7 @@ jint JNICALL jni_DetachCurrentThread(JavaVM *vm) {
|
||||
// (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;
|
||||
|
@ -66,6 +66,7 @@
|
||||
#include "runtime/perfData.hpp"
|
||||
#include "runtime/reflection.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vframe.hpp"
|
||||
#include "runtime/vm_operations.hpp"
|
||||
#include "runtime/vm_version.hpp"
|
||||
@ -2737,16 +2738,12 @@ void jio_print(const char* s) {
|
||||
|
||||
// java.lang.Thread //////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// In most of the JVM Thread support functions we need to be sure to lock the Threads_lock
|
||||
// to prevent the target thread from exiting after we have a pointer to the C++ Thread or
|
||||
// OSThread objects. The exception to this rule is when the target object is the thread
|
||||
// doing the operation, in which case we know that the thread won't exit until the
|
||||
// operation is done (all exits being voluntary). There are a few cases where it is
|
||||
// rather silly to do operations on yourself, like resuming yourself or asking whether
|
||||
// you are alive. While these can still happen, they are not subject to deadlocks if
|
||||
// the lock is held while the operation occurs (this is not the case for suspend, for
|
||||
// instance), and are very unlikely. Because IsAlive needs to be fast and its
|
||||
// implementation is local to this file, we always lock Threads_lock for that one.
|
||||
// In most of the JVM thread support functions we need to access the
|
||||
// thread through a ThreadsListHandle to prevent it from exiting and
|
||||
// being reclaimed while we try to operate on it. The exceptions to this
|
||||
// rule are when operating on the current thread, or if the monitor of
|
||||
// the target java.lang.Thread is locked at the Java level - in both
|
||||
// cases the target cannot exit.
|
||||
|
||||
static void thread_entry(JavaThread* thread, TRAPS) {
|
||||
HandleMark hm(THREAD);
|
||||
@ -2821,7 +2818,7 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
|
||||
|
||||
if (native_thread->osthread() == NULL) {
|
||||
// No one should hold a reference to the 'native_thread'.
|
||||
delete native_thread;
|
||||
native_thread->smr_delete();
|
||||
if (JvmtiExport::should_post_resource_exhausted()) {
|
||||
JvmtiExport::post_resource_exhausted(
|
||||
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
|
||||
@ -2835,41 +2832,45 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
|
||||
|
||||
JVM_END
|
||||
|
||||
|
||||
// JVM_Stop is implemented using a VM_Operation, so threads are forced to safepoints
|
||||
// before the quasi-asynchronous exception is delivered. This is a little obtrusive,
|
||||
// but is thought to be reliable and simple. In the case, where the receiver is the
|
||||
// same thread as the sender, no safepoint is needed.
|
||||
// same thread as the sender, no VM_Operation is needed.
|
||||
JVM_ENTRY(void, JVM_StopThread(JNIEnv* env, jobject jthread, jobject throwable))
|
||||
JVMWrapper("JVM_StopThread");
|
||||
|
||||
// A nested ThreadsListHandle will grab the Threads_lock so create
|
||||
// tlh before we resolve throwable.
|
||||
ThreadsListHandle tlh(thread);
|
||||
oop java_throwable = JNIHandles::resolve(throwable);
|
||||
if (java_throwable == NULL) {
|
||||
THROW(vmSymbols::java_lang_NullPointerException());
|
||||
}
|
||||
oop java_thread = JNIHandles::resolve_non_null(jthread);
|
||||
JavaThread* receiver = java_lang_Thread::thread(java_thread);
|
||||
Events::log_exception(JavaThread::current(),
|
||||
oop java_thread = NULL;
|
||||
JavaThread* receiver = NULL;
|
||||
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, &java_thread);
|
||||
Events::log_exception(thread,
|
||||
"JVM_StopThread thread JavaThread " INTPTR_FORMAT " as oop " INTPTR_FORMAT " [exception " INTPTR_FORMAT "]",
|
||||
p2i(receiver), p2i((address)java_thread), p2i(throwable));
|
||||
// First check if thread is alive
|
||||
if (receiver != NULL) {
|
||||
// Check if exception is getting thrown at self (use oop equality, since the
|
||||
// target object might exit)
|
||||
if (java_thread == thread->threadObj()) {
|
||||
|
||||
if (is_alive) {
|
||||
// jthread refers to a live JavaThread.
|
||||
if (thread == receiver) {
|
||||
// Exception is getting thrown at self so no VM_Operation needed.
|
||||
THROW_OOP(java_throwable);
|
||||
} else {
|
||||
// Enques a VM_Operation to stop all threads and then deliver the exception...
|
||||
Thread::send_async_exception(java_thread, JNIHandles::resolve(throwable));
|
||||
// Use a VM_Operation to throw the exception.
|
||||
Thread::send_async_exception(java_thread, java_throwable);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Either:
|
||||
// - target thread has not been started before being stopped, or
|
||||
// - target thread already terminated
|
||||
// We could read the threadStatus to determine which case it is
|
||||
// but that is overkill as it doesn't matter. We must set the
|
||||
// stillborn flag for the first case, and if the thread has already
|
||||
// exited setting this flag has no affect
|
||||
// exited setting this flag has no effect.
|
||||
java_lang_Thread::set_stillborn(java_thread);
|
||||
}
|
||||
JVM_END
|
||||
@ -2885,12 +2886,12 @@ JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_SuspendThread(JNIEnv* env, jobject jthread))
|
||||
JVMWrapper("JVM_SuspendThread");
|
||||
oop java_thread = JNIHandles::resolve_non_null(jthread);
|
||||
JavaThread* receiver = java_lang_Thread::thread(java_thread);
|
||||
|
||||
if (receiver != NULL) {
|
||||
// thread has run and has not exited (still on threads list)
|
||||
|
||||
ThreadsListHandle tlh(thread);
|
||||
JavaThread* receiver = NULL;
|
||||
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
|
||||
if (is_alive) {
|
||||
// jthread refers to a live JavaThread.
|
||||
{
|
||||
MutexLockerEx ml(receiver->SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
if (receiver->is_external_suspend()) {
|
||||
@ -2922,30 +2923,49 @@ JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_ResumeThread(JNIEnv* env, jobject jthread))
|
||||
JVMWrapper("JVM_ResumeThread");
|
||||
// Ensure that the C++ Thread and OSThread structures aren't freed before we operate.
|
||||
// We need to *always* get the threads lock here, since this operation cannot be allowed during
|
||||
// a safepoint. The safepoint code relies on suspending a thread to examine its state. If other
|
||||
// threads randomly resumes threads, then a thread might not be suspended when the safepoint code
|
||||
// looks at it.
|
||||
MutexLocker ml(Threads_lock);
|
||||
JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
|
||||
if (thr != NULL) {
|
||||
// the thread has run and is not in the process of exiting
|
||||
thr->java_resume();
|
||||
|
||||
ThreadsListHandle tlh(thread);
|
||||
JavaThread* receiver = NULL;
|
||||
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
|
||||
if (is_alive) {
|
||||
// jthread refers to a live JavaThread.
|
||||
|
||||
// This is the original comment for this Threads_lock grab:
|
||||
// We need to *always* get the threads lock here, since this operation cannot be allowed during
|
||||
// a safepoint. The safepoint code relies on suspending a thread to examine its state. If other
|
||||
// threads randomly resumes threads, then a thread might not be suspended when the safepoint code
|
||||
// looks at it.
|
||||
//
|
||||
// The above comment dates back to when we had both internal and
|
||||
// external suspend APIs that shared a common underlying mechanism.
|
||||
// External suspend is now entirely cooperative and doesn't share
|
||||
// anything with internal suspend. That said, there are some
|
||||
// assumptions in the VM that an external resume grabs the
|
||||
// Threads_lock. We can't drop the Threads_lock grab here until we
|
||||
// resolve the assumptions that exist elsewhere.
|
||||
//
|
||||
MutexLocker ml(Threads_lock);
|
||||
receiver->java_resume();
|
||||
}
|
||||
JVM_END
|
||||
|
||||
|
||||
JVM_ENTRY(void, JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio))
|
||||
JVMWrapper("JVM_SetThreadPriority");
|
||||
// Ensure that the C++ Thread and OSThread structures aren't freed before we operate
|
||||
MutexLocker ml(Threads_lock);
|
||||
oop java_thread = JNIHandles::resolve_non_null(jthread);
|
||||
|
||||
ThreadsListHandle tlh(thread);
|
||||
oop java_thread = NULL;
|
||||
JavaThread* receiver = NULL;
|
||||
bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, &java_thread);
|
||||
java_lang_Thread::set_priority(java_thread, (ThreadPriority)prio);
|
||||
JavaThread* thr = java_lang_Thread::thread(java_thread);
|
||||
if (thr != NULL) { // Thread not yet started; priority pushed down when it is
|
||||
Thread::set_priority(thr, (ThreadPriority)prio);
|
||||
|
||||
if (is_alive) {
|
||||
// jthread refers to a live JavaThread.
|
||||
Thread::set_priority(receiver, (ThreadPriority)prio);
|
||||
}
|
||||
// Implied else: If the JavaThread hasn't started yet, then the
|
||||
// priority set in the java.lang.Thread object above will be pushed
|
||||
// down when it does start.
|
||||
JVM_END
|
||||
|
||||
|
||||
@ -3016,67 +3036,39 @@ JVM_END
|
||||
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
|
||||
|
||||
@ -3084,16 +3076,14 @@ JVM_END
|
||||
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
|
||||
|
||||
@ -3122,14 +3112,16 @@ JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name))
|
||||
JVMWrapper("JVM_SetNativeThreadName");
|
||||
ResourceMark rm(THREAD);
|
||||
|
||||
// We don't use a ThreadsListHandle here because the current thread
|
||||
// must be alive.
|
||||
oop java_thread = JNIHandles::resolve_non_null(jthread);
|
||||
JavaThread* thr = java_lang_Thread::thread(java_thread);
|
||||
// Thread naming only supported for the current thread, doesn't work for
|
||||
// target threads.
|
||||
if (Thread::current() == thr && !thr->has_attached_via_jni()) {
|
||||
if (thread == thr && !thr->has_attached_via_jni()) {
|
||||
// Thread naming is only supported for the current thread and
|
||||
// we don't set the name of an attached thread to avoid stepping
|
||||
// on other programs
|
||||
// on other programs.
|
||||
ResourceMark rm(thread);
|
||||
const char *thread_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name));
|
||||
os::set_native_thread_name(thread_name);
|
||||
}
|
||||
@ -3671,6 +3663,8 @@ JVM_ENTRY(jobjectArray, JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobject
|
||||
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());
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
||||
# include "prims/jvmtiEnter.hpp"
|
||||
# include "prims/jvmtiRawMonitor.hpp"
|
||||
# include "prims/jvmtiUtil.hpp"
|
||||
# include "runtime/threadSMR.hpp"
|
||||
|
||||
</xsl:text>
|
||||
|
||||
@ -769,47 +770,27 @@ static jvmtiError JNICALL
|
||||
|
||||
<xsl:template match="jthread" mode="dochecksbody">
|
||||
<xsl:param name="name"/>
|
||||
<xsl:text> oop thread_oop = JNIHandles::resolve_external_guard(</xsl:text>
|
||||
<xsl:text> err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), </xsl:text>
|
||||
<xsl:value-of select="$name"/>
|
||||
<xsl:text>);
|
||||
if (thread_oop == NULL) {
|
||||
<xsl:text>, &java_thread, NULL);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
</xsl:text>
|
||||
<xsl:apply-templates select=".." mode="traceError">
|
||||
<xsl:with-param name="err">JVMTI_ERROR_INVALID_THREAD</xsl:with-param>
|
||||
<xsl:with-param name="comment"> - jthread resolved to NULL - jthread = " PTR_FORMAT "</xsl:with-param>
|
||||
<xsl:with-param name="err">err</xsl:with-param>
|
||||
<xsl:with-param name="comment"> - jthread did not convert to a JavaThread - jthread = " PTR_FORMAT "</xsl:with-param>
|
||||
<xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param>
|
||||
</xsl:apply-templates>
|
||||
<xsl:text>
|
||||
}
|
||||
if (!thread_oop->is_a(SystemDictionary::Thread_klass())) {
|
||||
</xsl:text>
|
||||
<xsl:apply-templates select=".." mode="traceError">
|
||||
<xsl:with-param name="err">JVMTI_ERROR_INVALID_THREAD</xsl:with-param>
|
||||
<xsl:with-param name="comment"> - oop is not a thread - jthread = " PTR_FORMAT "</xsl:with-param>
|
||||
<xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param>
|
||||
</xsl:apply-templates>
|
||||
<xsl:text>
|
||||
}
|
||||
java_thread = java_lang_Thread::thread(thread_oop);
|
||||
if (java_thread == NULL) {
|
||||
</xsl:text>
|
||||
<xsl:apply-templates select=".." mode="traceError">
|
||||
<xsl:with-param name="err">
|
||||
<xsl:text>JVMTI_ERROR_THREAD_NOT_ALIVE</xsl:text>
|
||||
</xsl:with-param>
|
||||
<xsl:with-param name="comment"> - not a Java thread - jthread = " PTR_FORMAT "</xsl:with-param>
|
||||
<xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param>
|
||||
</xsl:apply-templates>
|
||||
<xsl:text>
|
||||
}
|
||||
</xsl:text>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="jthread" mode="dochecks">
|
||||
<xsl:param name="name"/>
|
||||
<!-- If we convert and test threads -->
|
||||
<xsl:if test="count(@impl)=0 or not(contains(@impl,'noconvert'))">
|
||||
<xsl:text> JavaThread* java_thread;
|
||||
<xsl:text> JavaThread* java_thread = NULL;
|
||||
ThreadsListHandle tlh(this_thread);
|
||||
</xsl:text>
|
||||
<xsl:choose>
|
||||
<xsl:when test="count(@null)=0">
|
||||
|
@ -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 @@ JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) {
|
||||
*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 @@ JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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 @@ JvmtiEnv::SetEventCallbacks(const jvmtiEventCallbacks* callbacks, jint size_of_c
|
||||
// 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 @@ JvmtiEnv::GetJLocationFormat(jvmtiJlocationFormat* format_ptr) {
|
||||
// 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();
|
||||
} else {
|
||||
thread_oop = JNIHandles::resolve_external_guard(thread);
|
||||
}
|
||||
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;
|
||||
if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
|
||||
return JVMTI_ERROR_INVALID_THREAD;
|
||||
}
|
||||
} else {
|
||||
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
// We got an error code so we don't have a JavaThread *, but
|
||||
// only return an error from here if we didn't get a valid
|
||||
// thread_oop.
|
||||
if (thread_oop == NULL) {
|
||||
return err;
|
||||
}
|
||||
// We have a valid thread_oop so we can return some thread state.
|
||||
}
|
||||
}
|
||||
|
||||
// get most state bits
|
||||
state = (jint)java_lang_Thread::get_thread_status(thread_oop);
|
||||
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 @@ JvmtiEnv::GetAllThreads(jint* threads_count_ptr, jthread** threads_ptr) {
|
||||
thread_objs[i] = Handle(tle.get_threadObj(i));
|
||||
}
|
||||
|
||||
// have to make global handles outside of Threads_lock
|
||||
jthread *jthreads = new_jthreadArray(nthreads, thread_objs);
|
||||
NULL_CHECK(jthreads, JVMTI_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
@ -935,19 +958,12 @@ JvmtiEnv::SuspendThread(JavaThread* java_thread) {
|
||||
jvmtiError
|
||||
JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvmtiError* results) {
|
||||
int needSafepoint = 0; // > 0 if we need a safepoint
|
||||
ThreadsListHandle tlh;
|
||||
for (int i = 0; i < request_count; i++) {
|
||||
JavaThread *java_thread = get_JavaThread(request_list[i]);
|
||||
if (java_thread == NULL) {
|
||||
results[i] = JVMTI_ERROR_INVALID_THREAD;
|
||||
continue;
|
||||
}
|
||||
// the thread has not yet run or has exited (not on threads list)
|
||||
if (java_thread->threadObj() == NULL) {
|
||||
results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
continue;
|
||||
}
|
||||
if (java_lang_Thread::thread(java_thread->threadObj()) == NULL) {
|
||||
results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
JavaThread *java_thread = NULL;
|
||||
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), request_list[i], &java_thread, NULL);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
results[i] = err;
|
||||
continue;
|
||||
}
|
||||
// don't allow hidden thread suspend request.
|
||||
@ -1018,10 +1034,12 @@ JvmtiEnv::ResumeThread(JavaThread* java_thread) {
|
||||
// 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 @@ JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmt
|
||||
continue;
|
||||
}
|
||||
|
||||
results[i] = JVMTI_ERROR_NONE; // indicate successful suspend
|
||||
results[i] = JVMTI_ERROR_NONE; // indicate successful resume
|
||||
}
|
||||
// per-thread resume results returned via results parameter
|
||||
return JVMTI_ERROR_NONE;
|
||||
@ -1064,20 +1082,14 @@ JvmtiEnv::StopThread(JavaThread* java_thread, jobject exception) {
|
||||
// thread - NOT pre-checked
|
||||
jvmtiError
|
||||
JvmtiEnv::InterruptThread(jthread thread) {
|
||||
oop thread_oop = JNIHandles::resolve_external_guard(thread);
|
||||
if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass()))
|
||||
return JVMTI_ERROR_INVALID_THREAD;
|
||||
|
||||
// TODO: this is very similar to JVM_Interrupt(); share code in future
|
||||
JavaThread* current_thread = JavaThread::current();
|
||||
|
||||
// Todo: this is a duplicate of JVM_Interrupt; share code in future
|
||||
// Ensure that the C++ Thread and OSThread structures aren't freed before we operate
|
||||
MutexLockerEx ml(current_thread->threadObj() == thread_oop ? NULL : Threads_lock);
|
||||
// We need to re-resolve the java_thread, since a GC might have happened during the
|
||||
// acquire of the lock
|
||||
|
||||
JavaThread* java_thread = java_lang_Thread::thread(JNIHandles::resolve_external_guard(thread));
|
||||
NULL_CHECK(java_thread, JVMTI_ERROR_THREAD_NOT_ALIVE);
|
||||
JavaThread* java_thread = NULL;
|
||||
ThreadsListHandle tlh(current_thread);
|
||||
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, NULL);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
return err;
|
||||
}
|
||||
|
||||
Thread::interrupt(java_thread);
|
||||
|
||||
@ -1094,16 +1106,28 @@ JvmtiEnv::GetThreadInfo(jthread thread, jvmtiThreadInfo* info_ptr) {
|
||||
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 @@ JvmtiEnv::GetCurrentContendedMonitor(JavaThread* java_thread, jobject* monitor_p
|
||||
// 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 @@ JvmtiEnv::RunAgentThread(jthread thread, jvmtiStartFunction proc, const void* ar
|
||||
// At this point it may be possible that no osthread was created for the
|
||||
// JavaThread due to lack of memory.
|
||||
if (new_thread == NULL || new_thread->osthread() == NULL) {
|
||||
if (new_thread) delete new_thread;
|
||||
if (new_thread != NULL) {
|
||||
new_thread->smr_delete();
|
||||
}
|
||||
return JVMTI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
@ -1394,36 +1434,53 @@ JvmtiEnv::GetThreadGroupChildren(jthreadGroup group, jint* thread_count_ptr, jth
|
||||
int ngroups = 0;
|
||||
int hidden_threads = 0;
|
||||
|
||||
ResourceMark rm;
|
||||
HandleMark hm;
|
||||
ResourceMark rm(current_thread);
|
||||
HandleMark hm(current_thread);
|
||||
|
||||
Handle group_hdl(current_thread, group_obj);
|
||||
|
||||
{ MutexLocker mu(Threads_lock);
|
||||
{ // Cannot allow thread or group counts to change.
|
||||
MutexLocker mu(Threads_lock);
|
||||
|
||||
nthreads = java_lang_ThreadGroup::nthreads(group_hdl());
|
||||
ngroups = java_lang_ThreadGroup::ngroups(group_hdl());
|
||||
|
||||
if (nthreads > 0) {
|
||||
ThreadsListHandle tlh(current_thread);
|
||||
objArrayOop threads = java_lang_ThreadGroup::threads(group_hdl());
|
||||
assert(nthreads <= threads->length(), "too many threads");
|
||||
thread_objs = NEW_RESOURCE_ARRAY(Handle,nthreads);
|
||||
for (int i=0, j=0; i<nthreads; i++) {
|
||||
oop thread_obj = threads->obj_at(i);
|
||||
assert(thread_obj != NULL, "thread_obj is NULL");
|
||||
JavaThread *javathread = java_lang_Thread::thread(thread_obj);
|
||||
// Filter out hidden java threads.
|
||||
if (javathread != NULL && javathread->is_hidden_from_external_view()) {
|
||||
hidden_threads++;
|
||||
continue;
|
||||
JavaThread *java_thread = NULL;
|
||||
jvmtiError err = JvmtiExport::cv_oop_to_JavaThread(tlh.list(), thread_obj, &java_thread);
|
||||
if (err == JVMTI_ERROR_NONE) {
|
||||
// Have a valid JavaThread*.
|
||||
if (java_thread->is_hidden_from_external_view()) {
|
||||
// Filter out hidden java threads.
|
||||
hidden_threads++;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// We couldn't convert thread_obj into a JavaThread*.
|
||||
if (err == JVMTI_ERROR_INVALID_THREAD) {
|
||||
// The thread_obj does not refer to a java.lang.Thread object
|
||||
// so skip it.
|
||||
hidden_threads++;
|
||||
continue;
|
||||
}
|
||||
// We have a valid thread_obj, but no JavaThread*; the caller
|
||||
// can still have limited use for the thread_obj.
|
||||
}
|
||||
thread_objs[j++] = Handle(current_thread, thread_obj);
|
||||
}
|
||||
nthreads -= hidden_threads;
|
||||
}
|
||||
} // ThreadsListHandle is destroyed here.
|
||||
|
||||
if (ngroups > 0) {
|
||||
objArrayOop groups = java_lang_ThreadGroup::groups(group_hdl());
|
||||
assert(ngroups <= groups->length(), "too many threads");
|
||||
assert(ngroups <= groups->length(), "too many groups");
|
||||
group_objs = NEW_RESOURCE_ARRAY(Handle,ngroups);
|
||||
for (int i=0; i<ngroups; i++) {
|
||||
oop group_obj = groups->obj_at(i);
|
||||
@ -1556,7 +1613,7 @@ JvmtiEnv::PopFrame(JavaThread* java_thread) {
|
||||
}
|
||||
|
||||
// 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 @@ JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) {
|
||||
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) {
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "runtime/objectMonitor.inline.hpp"
|
||||
#include "runtime/signature.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vframe.hpp"
|
||||
#include "runtime/vframe_hp.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
@ -487,37 +488,6 @@ JvmtiEnvBase::set_event_callbacks(const jvmtiEventCallbacks* callbacks,
|
||||
}
|
||||
}
|
||||
|
||||
// Called from JVMTI entry points which perform stack walking. If the
|
||||
// associated JavaThread is the current thread, then wait_for_suspend
|
||||
// is not used. Otherwise, it determines if we should wait for the
|
||||
// "other" thread to complete external suspension. (NOTE: in future
|
||||
// releases the suspension mechanism should be reimplemented so this
|
||||
// is not necessary.)
|
||||
//
|
||||
bool
|
||||
JvmtiEnvBase::is_thread_fully_suspended(JavaThread* thr, bool wait_for_suspend, uint32_t *bits) {
|
||||
// "other" threads require special handling
|
||||
if (thr != JavaThread::current()) {
|
||||
if (wait_for_suspend) {
|
||||
// We are allowed to wait for the external suspend to complete
|
||||
// so give the other thread a chance to get suspended.
|
||||
if (!thr->wait_for_ext_suspend_completion(SuspendRetryCount,
|
||||
SuspendRetryDelay, bits)) {
|
||||
// didn't make it so let the caller know
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// We aren't allowed to wait for the external suspend to complete
|
||||
// so if the other thread isn't externally suspended we need to
|
||||
// let the caller know.
|
||||
else if (!thr->is_ext_suspend_completed_with_lock(bits)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// In the fullness of time, all users of the method should instead
|
||||
// directly use allocate, besides being cleaner and faster, this will
|
||||
@ -560,19 +530,6 @@ JvmtiEnvBase::new_jthreadGroupArray(int length, Handle *handles) {
|
||||
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 @@ JvmtiEnvBase::get_current_contended_monitor(JavaThread *calling_thread, JavaThre
|
||||
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 @@ JvmtiEnvBase::get_owned_monitors(JavaThread *calling_thread, JavaThread* java_th
|
||||
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 @@ JvmtiEnvBase::get_stack_trace(JavaThread *java_thread,
|
||||
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 @@ JvmtiEnvBase::get_frame_location(JavaThread *java_thread, jint depth,
|
||||
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 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
// 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 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
}
|
||||
|
||||
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 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
// 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 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
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 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
if (ret.waiter_count > 0) {
|
||||
// we have contending and/or waiting threads
|
||||
HandleMark hm;
|
||||
// Use current thread since function can be called from a
|
||||
// JavaThread or the VMThread.
|
||||
ThreadsListHandle tlh;
|
||||
if (nWant > 0) {
|
||||
// we have contending threads
|
||||
ResourceMark rm;
|
||||
// get_pending_threads returns only java thread so we do not need to
|
||||
// check for non java threads.
|
||||
GrowableArray<JavaThread*>* wantList = Threads::get_pending_threads(
|
||||
nWant, (address)mon, !at_safepoint);
|
||||
// check for non java threads.
|
||||
GrowableArray<JavaThread*>* wantList = Threads::get_pending_threads(tlh.list(), nWant, (address)mon);
|
||||
if (wantList->length() < nWant) {
|
||||
// robustness: the pending list has gotten smaller
|
||||
nWant = wantList->length();
|
||||
@ -1101,7 +1060,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
// 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 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
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 @@ VM_GetThreadListStackTraces::doit() {
|
||||
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 @@ VM_GetAllStackTraces::doit() {
|
||||
|
||||
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 @@ JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState
|
||||
}
|
||||
|
||||
// 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 @@ JvmtiModuleClosure::get_all_modules(JvmtiEnv* env, jint* module_count_ptr, jobje
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -280,9 +280,6 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
||||
jthread * new_jthreadArray(int length, Handle *handles);
|
||||
jthreadGroup * new_jthreadGroupArray(int length, Handle *handles);
|
||||
|
||||
// convert from JNIHandle to JavaThread *
|
||||
JavaThread * get_JavaThread(jthread jni_thread);
|
||||
|
||||
// convert to a jni jclass from a non-null Klass*
|
||||
jclass get_jni_class_non_null(Klass* k);
|
||||
|
||||
@ -297,11 +294,6 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
||||
public:
|
||||
// get a field descriptor for the specified class and field
|
||||
static bool get_field_descriptor(Klass* k, jfieldID field, fieldDescriptor* fd);
|
||||
// test for suspend - most (all?) of these should go away
|
||||
static bool is_thread_fully_suspended(JavaThread *thread,
|
||||
bool wait_for_suspend,
|
||||
uint32_t *bits);
|
||||
|
||||
|
||||
// JVMTI API helper functions which are called at safepoint or thread is suspended.
|
||||
jvmtiError get_frame_count(JvmtiThreadState *state, jint *count_ptr);
|
||||
@ -360,14 +352,7 @@ public:
|
||||
}
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
_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 @@ public:
|
||||
}
|
||||
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 @@ public:
|
||||
}
|
||||
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 @@ public:
|
||||
}
|
||||
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 @@ public:
|
||||
}
|
||||
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();
|
||||
};
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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 @@ JvmtiEventControllerPrivate::recompute_enabled() {
|
||||
// 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
|
||||
|
@ -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 @@ JvmtiExport::get_all_native_method_prefixes(int* count_ptr) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 @@ void JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) {
|
||||
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;
|
||||
|
@ -399,6 +399,14 @@ class JvmtiExport : public AllStatic {
|
||||
|
||||
// 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
|
||||
|
@ -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 @@ bool JvmtiSuspendControl::resume(JavaThread *java_thread) {
|
||||
|
||||
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
|
||||
|
@ -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"
|
||||
|
@ -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 @@ inline bool VM_HeapWalkOperation::collect_stack_roots(JavaThread* java_thread,
|
||||
// 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
|
||||
|
@ -336,34 +336,10 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
|
||||
|
||||
// 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
|
||||
|
||||
|
@ -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 @@ void JvmtiThreadState::set_head_env_thread_state(JvmtiEnvThreadState* ets) {
|
||||
_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
|
||||
|
@ -39,6 +39,8 @@
|
||||
#include "runtime/interfaceSupport.hpp"
|
||||
#include "runtime/orderAccess.inline.hpp"
|
||||
#include "runtime/reflection.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vm_version.hpp"
|
||||
#include "services/threadService.hpp"
|
||||
#include "trace/tracing.hpp"
|
||||
@ -937,8 +939,12 @@ UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread))
|
||||
Parker* p = NULL;
|
||||
|
||||
if (jthread != NULL) {
|
||||
oop java_thread = JNIHandles::resolve_non_null(jthread);
|
||||
ThreadsListHandle tlh;
|
||||
JavaThread* thr = NULL;
|
||||
oop java_thread = NULL;
|
||||
(void) tlh.cv_internal_thread_to_JavaThread(jthread, &thr, &java_thread);
|
||||
if (java_thread != NULL) {
|
||||
// This is a valid oop.
|
||||
jlong lp = java_lang_Thread::park_event(java_thread);
|
||||
if (lp != 0) {
|
||||
// This cast is OK even though the jlong might have been read
|
||||
@ -946,22 +952,19 @@ UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread))
|
||||
// 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);
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "runtime/os.hpp"
|
||||
#include "runtime/sweeper.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vm_version.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
@ -665,7 +666,7 @@ class VM_WhiteBoxDeoptimizeFrames : public VM_WhiteBoxOperation {
|
||||
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();
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "runtime/basicLock.hpp"
|
||||
#include "runtime/biasedLocking.hpp"
|
||||
#include "runtime/task.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vframe.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "runtime/vm_operations.hpp"
|
||||
@ -214,12 +215,8 @@ static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_
|
||||
if (requesting_thread == biased_thread) {
|
||||
thread_is_alive = true;
|
||||
} else {
|
||||
for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) {
|
||||
if (cur_thread == biased_thread) {
|
||||
thread_is_alive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ThreadsListHandle tlh;
|
||||
thread_is_alive = tlh.includes(biased_thread);
|
||||
}
|
||||
if (!thread_is_alive) {
|
||||
if (allow_rebias) {
|
||||
@ -390,72 +387,76 @@ static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
|
||||
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<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
|
||||
for (int i = 0; i < cached_monitor_info->length(); i++) {
|
||||
MonitorInfo* mon_info = cached_monitor_info->at(i);
|
||||
oop owner = mon_info->owner();
|
||||
markOop mark = owner->mark();
|
||||
if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
|
||||
// We might have encountered this object already in the case of recursive locking
|
||||
assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
|
||||
owner->set_mark(mark->set_bias_epoch(cur_epoch));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we're done. All we have to do is potentially
|
||||
// adjust the header of the given object to revoke its bias.
|
||||
revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread, NULL);
|
||||
} else {
|
||||
if (log_is_enabled(Info, biasedlocking)) {
|
||||
ResourceMark rm;
|
||||
log_info(biasedlocking)("* Disabling biased locking for type %s", klass->external_name());
|
||||
}
|
||||
|
||||
// Disable biased locking for this data type. Not only will this
|
||||
// cause future instances to not be biased, but existing biased
|
||||
// instances will notice that this implicitly caused their biases
|
||||
// to be revoked.
|
||||
klass->set_prototype_header(markOopDesc::prototype());
|
||||
|
||||
// Now walk all threads' stacks and forcibly revoke the biases of
|
||||
// any locked and biased objects of this data type we encounter.
|
||||
for (; JavaThread *thr = jtiwh.next(); ) {
|
||||
GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
|
||||
for (int i = 0; i < cached_monitor_info->length(); i++) {
|
||||
MonitorInfo* mon_info = cached_monitor_info->at(i);
|
||||
oop owner = mon_info->owner();
|
||||
markOop mark = owner->mark();
|
||||
if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
|
||||
// We might have encountered this object already in the case of recursive locking
|
||||
assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
|
||||
owner->set_mark(mark->set_bias_epoch(cur_epoch));
|
||||
revoke_bias(owner, false, true, requesting_thread, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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 = Threads::first(); thr != NULL; thr = thr->next()) {
|
||||
GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
|
||||
for (int i = 0; i < cached_monitor_info->length(); i++) {
|
||||
MonitorInfo* mon_info = cached_monitor_info->at(i);
|
||||
oop owner = mon_info->owner();
|
||||
markOop mark = owner->mark();
|
||||
if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
|
||||
revoke_bias(owner, false, true, requesting_thread, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must force the bias of the passed object to be forcibly revoked
|
||||
// as well to ensure guarantees to callers
|
||||
revoke_bias(o, false, true, requesting_thread, NULL);
|
||||
}
|
||||
} // ThreadsListHandle is destroyed here.
|
||||
|
||||
log_info(biasedlocking)("* Ending bulk revocation");
|
||||
|
||||
@ -481,7 +482,7 @@ static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
|
||||
|
||||
static void clean_up_cached_monitor_info() {
|
||||
// Walk the thread list clearing out the cached monitors
|
||||
for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
|
||||
thr->set_cached_monitor_info(NULL);
|
||||
}
|
||||
}
|
||||
@ -768,7 +769,7 @@ void BiasedLocking::preserve_marks() {
|
||||
|
||||
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()) {
|
||||
|
@ -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 @@ void Deoptimization::revoke_biases_of_monitors(CodeBlob* cb) {
|
||||
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must only be called from safepoint");
|
||||
GrowableArray<Handle>* objects_to_revoke = new GrowableArray<Handle>();
|
||||
for (JavaThread* jt = Threads::first(); jt != NULL ; jt = jt->next()) {
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
|
||||
if (jt->has_last_Java_frame()) {
|
||||
StackFrameStream sfs(jt, true);
|
||||
while (!sfs.is_done()) {
|
||||
|
@ -2484,6 +2484,12 @@ public:
|
||||
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") \
|
||||
\
|
||||
|
@ -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 @@ bool VM_Handshake::handshake_has_timed_out(jlong start_time) {
|
||||
|
||||
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 @@ class VM_HandshakeOneThread: public VM_Handshake {
|
||||
TraceTime timer("Performing single-target operation (vmoperation doit)", TRACETIME_LOG(Info, handshake));
|
||||
|
||||
{
|
||||
MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (Threads::includes(_target)) {
|
||||
ThreadsListHandle tlh;
|
||||
if (tlh.includes(_target)) {
|
||||
set_handshake(_target);
|
||||
_thread_alive = true;
|
||||
}
|
||||
@ -139,9 +136,24 @@ class VM_HandshakeOneThread: public VM_Handshake {
|
||||
handle_timeout();
|
||||
}
|
||||
|
||||
// We need to re-think this with SMR ThreadsList.
|
||||
// There is an assumption in the code that the Threads_lock should be
|
||||
// locked during certain phases.
|
||||
MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
|
||||
_target->handshake_process_by_vmthread();
|
||||
|
||||
ThreadsListHandle tlh;
|
||||
if (tlh.includes(_target)) {
|
||||
// Warning _target's address might be re-used.
|
||||
// handshake_process_by_vmthread will check the semaphore for us again.
|
||||
// Since we can't have more then one handshake in flight a reuse of
|
||||
// _target's address should be okay since the new thread will not have
|
||||
// an operation.
|
||||
_target->handshake_process_by_vmthread();
|
||||
} else {
|
||||
// We can't warn here since the thread does cancel_handshake after
|
||||
// it has been removed from the ThreadsList. So we should just keep
|
||||
// looping here until while below returns false. If we have a bug,
|
||||
// then we hang here, which is good for debugging.
|
||||
}
|
||||
} while (!poll_for_completed_thread());
|
||||
}
|
||||
|
||||
@ -157,15 +169,15 @@ class VM_HandshakeAllThreads: public VM_Handshake {
|
||||
void doit() {
|
||||
TraceTime timer("Performing operation (vmoperation doit)", TRACETIME_LOG(Info, handshake));
|
||||
|
||||
int number_of_threads_issued = -1;
|
||||
int number_of_threads_completed = 0;
|
||||
{
|
||||
MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
|
||||
number_of_threads_issued = Threads::number_of_threads();
|
||||
int number_of_threads_issued = 0;
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
|
||||
set_handshake(thr);
|
||||
number_of_threads_issued++;
|
||||
}
|
||||
|
||||
ALL_JAVA_THREADS(thr) {
|
||||
set_handshake(thr);
|
||||
}
|
||||
if (number_of_threads_issued < 1) {
|
||||
log_debug(handshake)("No threads to handshake.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UseMembar) {
|
||||
@ -174,6 +186,7 @@ class VM_HandshakeAllThreads: public VM_Handshake {
|
||||
|
||||
log_debug(handshake)("Threads signaled, begin processing blocked threads by VMThtread");
|
||||
const jlong start_time = os::elapsed_counter();
|
||||
int number_of_threads_completed = 0;
|
||||
do {
|
||||
// Check if handshake operation has timed out
|
||||
if (handshake_has_timed_out(start_time)) {
|
||||
@ -184,13 +197,19 @@ class VM_HandshakeAllThreads: public VM_Handshake {
|
||||
// Observing a blocked state may of course be transient but the processing is guarded
|
||||
// by semaphores and we optimistically begin by working on the blocked threads
|
||||
{
|
||||
// We need to re-think this with SMR ThreadsList.
|
||||
// There is an assumption in the code that the Threads_lock should
|
||||
// be locked during certain phases.
|
||||
MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
|
||||
ALL_JAVA_THREADS(thr) {
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
|
||||
// A new thread on the ThreadsList will not have an operation,
|
||||
// hence it is skipped in handshake_process_by_vmthread.
|
||||
thr->handshake_process_by_vmthread();
|
||||
}
|
||||
}
|
||||
|
||||
while (poll_for_completed_thread()) {
|
||||
// Includes canceled operations by exiting threads.
|
||||
number_of_threads_completed++;
|
||||
}
|
||||
|
||||
@ -212,7 +231,7 @@ public:
|
||||
_thread_cl(cl), _target_thread(target), _all_threads(false), _thread_alive(false) {}
|
||||
|
||||
void doit() {
|
||||
ALL_JAVA_THREADS(t) {
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
|
||||
if (_all_threads || t == _target_thread) {
|
||||
if (t == _target_thread) {
|
||||
_thread_alive = true;
|
||||
@ -298,8 +317,8 @@ void HandshakeState::cancel_inner(JavaThread* thread) {
|
||||
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;
|
||||
|
@ -356,6 +356,8 @@ void print_statistics() {
|
||||
if (PrintNMTStatistics) {
|
||||
MemTracker::final_report(tty);
|
||||
}
|
||||
|
||||
Threads::log_smr_statistics();
|
||||
}
|
||||
|
||||
#else // PRODUCT MODE STATISTICS
|
||||
@ -396,6 +398,8 @@ void print_statistics() {
|
||||
if (LogTouchedMethods && PrintTouchedMethodsAtExit) {
|
||||
Method::print_touched_methods(tty);
|
||||
}
|
||||
|
||||
Threads::log_smr_statistics();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "runtime/os.hpp"
|
||||
#include "runtime/task.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
|
||||
#ifndef PRODUCT
|
||||
@ -51,8 +52,6 @@ class MemProfilerTask : public PeriodicTask {
|
||||
|
||||
|
||||
void MemProfilerTask::task() {
|
||||
// Get thread lock to provide mutual exclusion, and so we can iterate safely over the thread list.
|
||||
MutexLocker mu(Threads_lock);
|
||||
MemProfiler::do_trace();
|
||||
}
|
||||
|
||||
@ -109,20 +108,21 @@ void MemProfiler::do_trace() {
|
||||
// Calculate thread local sizes
|
||||
size_t handles_memory_usage = VMThread::vm_thread()->handle_area()->size_in_bytes();
|
||||
size_t resource_memory_usage = VMThread::vm_thread()->resource_area()->size_in_bytes();
|
||||
JavaThread *cur = Threads::first();
|
||||
while (cur != NULL) {
|
||||
handles_memory_usage += cur->handle_area()->size_in_bytes();
|
||||
resource_memory_usage += cur->resource_area()->size_in_bytes();
|
||||
cur = cur->next();
|
||||
}
|
||||
{
|
||||
JavaThreadIteratorWithHandle jtiwh;
|
||||
for (; JavaThread *cur = jtiwh.next(); ) {
|
||||
handles_memory_usage += cur->handle_area()->size_in_bytes();
|
||||
resource_memory_usage += cur->resource_area()->size_in_bytes();
|
||||
}
|
||||
|
||||
// Print trace line in log
|
||||
fprintf(_log_fp, "%6.1f,%5d,%5d," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",",
|
||||
os::elapsedTime(),
|
||||
Threads::number_of_threads(),
|
||||
InstanceKlass::number_of_instance_classes(),
|
||||
Universe::heap()->used() / K,
|
||||
Universe::heap()->capacity() / K);
|
||||
// Print trace line in log
|
||||
fprintf(_log_fp, "%6.1f,%5d,%5d," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",",
|
||||
os::elapsedTime(),
|
||||
jtiwh.length(),
|
||||
InstanceKlass::number_of_instance_classes(),
|
||||
Universe::heap()->used() / K,
|
||||
Universe::heap()->capacity() / K);
|
||||
}
|
||||
|
||||
fprintf(_log_fp, UINTX_FORMAT_W(6) ",", CodeCache::capacity() / K);
|
||||
|
||||
|
@ -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 @@ char* os::iso8601_time(char* buffer, size_t buffer_length, bool utc) {
|
||||
}
|
||||
|
||||
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 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) {
|
||||
}
|
||||
#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 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) {
|
||||
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::initialize_initial_active_processor_count() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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 @@ void SafepointSynchronize::begin() {
|
||||
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 @@ void SafepointSynchronize::begin() {
|
||||
|
||||
// Consider using active_processor_count() ... but that call is expensive.
|
||||
int ncpus = os::processor_count() ;
|
||||
unsigned int iterations = 0;
|
||||
|
||||
{
|
||||
JavaThreadIteratorWithHandle jtiwh;
|
||||
#ifdef ASSERT
|
||||
for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) {
|
||||
assert(cur->safepoint_state()->is_running(), "Illegal initial state");
|
||||
// Clear the visited flag to ensure that the critical counts are collected properly.
|
||||
cur->set_visited_for_critical_count(false);
|
||||
}
|
||||
for (; JavaThread *cur = jtiwh.next(); ) {
|
||||
assert(cur->safepoint_state()->is_running(), "Illegal initial state");
|
||||
// Clear the visited flag to ensure that the critical counts are collected properly.
|
||||
cur->set_visited_for_critical_count(false);
|
||||
}
|
||||
#endif // ASSERT
|
||||
|
||||
if (SafepointTimeout)
|
||||
safepoint_limit_time = os::javaTimeNanos() + (jlong)SafepointTimeoutDelay * MICROUNITS;
|
||||
if (SafepointTimeout)
|
||||
safepoint_limit_time = os::javaTimeNanos() + (jlong)SafepointTimeoutDelay * MICROUNITS;
|
||||
|
||||
// Iterate through all threads until it have been determined how to stop them all at a safepoint
|
||||
unsigned int iterations = 0;
|
||||
int steps = 0 ;
|
||||
while(still_running > 0) {
|
||||
for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) {
|
||||
assert(!cur->is_ConcurrentGC_thread(), "A concurrent GC thread is unexpectly being suspended");
|
||||
ThreadSafepointState *cur_state = cur->safepoint_state();
|
||||
if (cur_state->is_running()) {
|
||||
cur_state->examine_state_of_thread();
|
||||
if (!cur_state->is_running()) {
|
||||
still_running--;
|
||||
// consider adjusting steps downward:
|
||||
// steps = 0
|
||||
// steps -= NNN
|
||||
// steps >>= 1
|
||||
// steps = MIN(steps, 2000-100)
|
||||
// if (iterations != 0) steps -= NNN
|
||||
}
|
||||
LogTarget(Trace, safepoint) lt;
|
||||
if (lt.is_enabled()) {
|
||||
ResourceMark rm;
|
||||
LogStream ls(lt);
|
||||
cur_state->print_on(&ls);
|
||||
// Iterate through all threads until it have been determined how to stop them all at a safepoint
|
||||
int steps = 0 ;
|
||||
while(still_running > 0) {
|
||||
jtiwh.rewind();
|
||||
for (; JavaThread *cur = jtiwh.next(); ) {
|
||||
assert(!cur->is_ConcurrentGC_thread(), "A concurrent GC thread is unexpectly being suspended");
|
||||
ThreadSafepointState *cur_state = cur->safepoint_state();
|
||||
if (cur_state->is_running()) {
|
||||
cur_state->examine_state_of_thread();
|
||||
if (!cur_state->is_running()) {
|
||||
still_running--;
|
||||
// consider adjusting steps downward:
|
||||
// steps = 0
|
||||
// steps -= NNN
|
||||
// steps >>= 1
|
||||
// steps = MIN(steps, 2000-100)
|
||||
// if (iterations != 0) steps -= NNN
|
||||
}
|
||||
LogTarget(Trace, safepoint) lt;
|
||||
if (lt.is_enabled()) {
|
||||
ResourceMark rm;
|
||||
LogStream ls(lt);
|
||||
cur_state->print_on(&ls);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iterations == 0) {
|
||||
initial_running = still_running;
|
||||
if (PrintSafepointStatistics) {
|
||||
begin_statistics(nof_threads, still_running);
|
||||
}
|
||||
}
|
||||
|
||||
if (still_running > 0) {
|
||||
// Check for if it takes to long
|
||||
if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) {
|
||||
print_safepoint_timeout(_spinning_timeout);
|
||||
if (iterations == 0) {
|
||||
initial_running = still_running;
|
||||
if (PrintSafepointStatistics) {
|
||||
begin_statistics(nof_threads, still_running);
|
||||
}
|
||||
}
|
||||
|
||||
// Spin to avoid context switching.
|
||||
// There's a tension between allowing the mutators to run (and rendezvous)
|
||||
// vs spinning. As the VM thread spins, wasting cycles, it consumes CPU that
|
||||
// a mutator might otherwise use profitably to reach a safepoint. Excessive
|
||||
// spinning by the VM thread on a saturated system can increase rendezvous latency.
|
||||
// Blocking or yielding incur their own penalties in the form of context switching
|
||||
// and the resultant loss of $ residency.
|
||||
//
|
||||
// Further complicating matters is that yield() does not work as naively expected
|
||||
// on many platforms -- yield() does not guarantee that any other ready threads
|
||||
// will run. As such we revert to naked_short_sleep() after some number of iterations.
|
||||
// nakes_short_sleep() is implemented as a short unconditional sleep.
|
||||
// Typical operating systems round a "short" sleep period up to 10 msecs, so sleeping
|
||||
// can actually increase the time it takes the VM thread to detect that a system-wide
|
||||
// stop-the-world safepoint has been reached. In a pathological scenario such as that
|
||||
// described in CR6415670 the VMthread may sleep just before the mutator(s) become safe.
|
||||
// In that case the mutators will be stalled waiting for the safepoint to complete and the
|
||||
// the VMthread will be sleeping, waiting for the mutators to rendezvous. The VMthread
|
||||
// will eventually wake up and detect that all mutators are safe, at which point
|
||||
// we'll again make progress.
|
||||
//
|
||||
// Beware too that that the VMThread typically runs at elevated priority.
|
||||
// Its default priority is higher than the default mutator priority.
|
||||
// Obviously, this complicates spinning.
|
||||
//
|
||||
// Note too that on Windows XP SwitchThreadTo() has quite different behavior than Sleep(0).
|
||||
// Sleep(0) will _not yield to lower priority threads, while SwitchThreadTo() will.
|
||||
//
|
||||
// See the comments in synchronizer.cpp for additional remarks on spinning.
|
||||
//
|
||||
// In the future we might:
|
||||
// 1. Modify the safepoint scheme to avoid potentially unbounded spinning.
|
||||
// This is tricky as the path used by a thread exiting the JVM (say on
|
||||
// on JNI call-out) simply stores into its state field. The burden
|
||||
// is placed on the VM thread, which must poll (spin).
|
||||
// 2. Find something useful to do while spinning. If the safepoint is GC-related
|
||||
// we might aggressively scan the stacks of threads that are already safe.
|
||||
// 3. Use Solaris schedctl to examine the state of the still-running mutators.
|
||||
// If all the mutators are ONPROC there's no reason to sleep or yield.
|
||||
// 4. YieldTo() any still-running mutators that are ready but OFFPROC.
|
||||
// 5. Check system saturation. If the system is not fully saturated then
|
||||
// simply spin and avoid sleep/yield.
|
||||
// 6. As still-running mutators rendezvous they could unpark the sleeping
|
||||
// VMthread. This works well for still-running mutators that become
|
||||
// safe. The VMthread must still poll for mutators that call-out.
|
||||
// 7. Drive the policy on time-since-begin instead of iterations.
|
||||
// 8. Consider making the spin duration a function of the # of CPUs:
|
||||
// Spin = (((ncpus-1) * M) + K) + F(still_running)
|
||||
// Alternately, instead of counting iterations of the outer loop
|
||||
// we could count the # of threads visited in the inner loop, above.
|
||||
// 9. On windows consider using the return value from SwitchThreadTo()
|
||||
// to drive subsequent spin/SwitchThreadTo()/Sleep(N) decisions.
|
||||
|
||||
if (SafepointMechanism::uses_global_page_poll() && int(iterations) == DeferPollingPageLoopCount) {
|
||||
guarantee (PageArmed == 0, "invariant") ;
|
||||
PageArmed = 1 ;
|
||||
os::make_polling_page_unreadable();
|
||||
}
|
||||
|
||||
// Instead of (ncpus > 1) consider either (still_running < (ncpus + EPSILON)) or
|
||||
// ((still_running + _waiting_to_block - TryingToBlock)) < ncpus)
|
||||
++steps ;
|
||||
if (ncpus > 1 && steps < SafepointSpinBeforeYield) {
|
||||
SpinPause() ; // MP-Polite spin
|
||||
} else
|
||||
if (steps < DeferThrSuspendLoopCount) {
|
||||
os::naked_yield() ;
|
||||
} else {
|
||||
os::naked_short_sleep(1);
|
||||
if (still_running > 0) {
|
||||
// Check for if it takes to long
|
||||
if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) {
|
||||
print_safepoint_timeout(_spinning_timeout);
|
||||
}
|
||||
|
||||
iterations ++ ;
|
||||
// Spin to avoid context switching.
|
||||
// There's a tension between allowing the mutators to run (and rendezvous)
|
||||
// vs spinning. As the VM thread spins, wasting cycles, it consumes CPU that
|
||||
// a mutator might otherwise use profitably to reach a safepoint. Excessive
|
||||
// spinning by the VM thread on a saturated system can increase rendezvous latency.
|
||||
// Blocking or yielding incur their own penalties in the form of context switching
|
||||
// and the resultant loss of $ residency.
|
||||
//
|
||||
// Further complicating matters is that yield() does not work as naively expected
|
||||
// on many platforms -- yield() does not guarantee that any other ready threads
|
||||
// will run. As such we revert to naked_short_sleep() after some number of iterations.
|
||||
// nakes_short_sleep() is implemented as a short unconditional sleep.
|
||||
// Typical operating systems round a "short" sleep period up to 10 msecs, so sleeping
|
||||
// can actually increase the time it takes the VM thread to detect that a system-wide
|
||||
// stop-the-world safepoint has been reached. In a pathological scenario such as that
|
||||
// described in CR6415670 the VMthread may sleep just before the mutator(s) become safe.
|
||||
// In that case the mutators will be stalled waiting for the safepoint to complete and the
|
||||
// the VMthread will be sleeping, waiting for the mutators to rendezvous. The VMthread
|
||||
// will eventually wake up and detect that all mutators are safe, at which point
|
||||
// we'll again make progress.
|
||||
//
|
||||
// Beware too that that the VMThread typically runs at elevated priority.
|
||||
// Its default priority is higher than the default mutator priority.
|
||||
// Obviously, this complicates spinning.
|
||||
//
|
||||
// Note too that on Windows XP SwitchThreadTo() has quite different behavior than Sleep(0).
|
||||
// Sleep(0) will _not yield to lower priority threads, while SwitchThreadTo() will.
|
||||
//
|
||||
// See the comments in synchronizer.cpp for additional remarks on spinning.
|
||||
//
|
||||
// In the future we might:
|
||||
// 1. Modify the safepoint scheme to avoid potentially unbounded spinning.
|
||||
// This is tricky as the path used by a thread exiting the JVM (say on
|
||||
// on JNI call-out) simply stores into its state field. The burden
|
||||
// is placed on the VM thread, which must poll (spin).
|
||||
// 2. Find something useful to do while spinning. If the safepoint is GC-related
|
||||
// we might aggressively scan the stacks of threads that are already safe.
|
||||
// 3. Use Solaris schedctl to examine the state of the still-running mutators.
|
||||
// If all the mutators are ONPROC there's no reason to sleep or yield.
|
||||
// 4. YieldTo() any still-running mutators that are ready but OFFPROC.
|
||||
// 5. Check system saturation. If the system is not fully saturated then
|
||||
// simply spin and avoid sleep/yield.
|
||||
// 6. As still-running mutators rendezvous they could unpark the sleeping
|
||||
// VMthread. This works well for still-running mutators that become
|
||||
// safe. The VMthread must still poll for mutators that call-out.
|
||||
// 7. Drive the policy on time-since-begin instead of iterations.
|
||||
// 8. Consider making the spin duration a function of the # of CPUs:
|
||||
// Spin = (((ncpus-1) * M) + K) + F(still_running)
|
||||
// Alternately, instead of counting iterations of the outer loop
|
||||
// we could count the # of threads visited in the inner loop, above.
|
||||
// 9. On windows consider using the return value from SwitchThreadTo()
|
||||
// to drive subsequent spin/SwitchThreadTo()/Sleep(N) decisions.
|
||||
|
||||
if (SafepointMechanism::uses_global_page_poll() && int(iterations) == DeferPollingPageLoopCount) {
|
||||
guarantee (PageArmed == 0, "invariant") ;
|
||||
PageArmed = 1 ;
|
||||
os::make_polling_page_unreadable();
|
||||
}
|
||||
|
||||
// Instead of (ncpus > 1) consider either (still_running < (ncpus + EPSILON)) or
|
||||
// ((still_running + _waiting_to_block - TryingToBlock)) < ncpus)
|
||||
++steps ;
|
||||
if (ncpus > 1 && steps < SafepointSpinBeforeYield) {
|
||||
SpinPause() ; // MP-Polite spin
|
||||
} else
|
||||
if (steps < DeferThrSuspendLoopCount) {
|
||||
os::naked_yield() ;
|
||||
} else {
|
||||
os::naked_short_sleep(1);
|
||||
}
|
||||
|
||||
iterations ++ ;
|
||||
}
|
||||
assert(iterations < (uint)max_jint, "We have been iterating in the safepoint loop too long");
|
||||
}
|
||||
assert(iterations < (uint)max_jint, "We have been iterating in the safepoint loop too long");
|
||||
}
|
||||
} // ThreadsListHandle destroyed here.
|
||||
assert(still_running == 0, "sanity check");
|
||||
|
||||
if (PrintSafepointStatistics) {
|
||||
@ -341,7 +346,7 @@ void SafepointSynchronize::begin() {
|
||||
sync_event.set_iterations(iterations);
|
||||
sync_event.commit();
|
||||
}
|
||||
} //EventSafepointStateSync
|
||||
} // EventSafepointStateSynchronization destroyed here.
|
||||
|
||||
// wait until all threads are stopped
|
||||
{
|
||||
@ -393,8 +398,8 @@ void SafepointSynchronize::begin() {
|
||||
} // 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 @@ void SafepointSynchronize::end() {
|
||||
end_statistics(os::javaTimeNanos());
|
||||
}
|
||||
|
||||
{
|
||||
JavaThreadIteratorWithHandle jtiwh;
|
||||
#ifdef ASSERT
|
||||
// A pending_exception cannot be installed during a safepoint. The threads
|
||||
// may install an async exception after they come back from a safepoint into
|
||||
// pending_exception after they unblock. But that should happen later.
|
||||
for (JavaThread *cur = Threads::first(); cur; cur = cur->next()) {
|
||||
assert (!(cur->has_pending_exception() &&
|
||||
cur->safepoint_state()->is_at_poll_safepoint()),
|
||||
"safepoint installed a pending exception");
|
||||
}
|
||||
// A pending_exception cannot be installed during a safepoint. The threads
|
||||
// may install an async exception after they come back from a safepoint into
|
||||
// pending_exception after they unblock. But that should happen later.
|
||||
for (; JavaThread *cur = jtiwh.next(); ) {
|
||||
assert (!(cur->has_pending_exception() &&
|
||||
cur->safepoint_state()->is_at_poll_safepoint()),
|
||||
"safepoint installed a pending exception");
|
||||
}
|
||||
#endif // ASSERT
|
||||
|
||||
if (PageArmed) {
|
||||
assert(SafepointMechanism::uses_global_page_poll(), "sanity");
|
||||
// Make polling safepoint aware
|
||||
os::make_polling_page_readable();
|
||||
PageArmed = 0 ;
|
||||
}
|
||||
|
||||
if (SafepointMechanism::uses_global_page_poll()) {
|
||||
// Remove safepoint check from interpreter
|
||||
Interpreter::ignore_safepoints();
|
||||
}
|
||||
|
||||
{
|
||||
MutexLocker mu(Safepoint_lock);
|
||||
|
||||
assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization");
|
||||
|
||||
if (SafepointMechanism::uses_thread_local_poll()) {
|
||||
_state = _not_synchronized;
|
||||
OrderAccess::storestore(); // global state -> local state
|
||||
for (JavaThread *current = Threads::first(); current; current = current->next()) {
|
||||
ThreadSafepointState* cur_state = current->safepoint_state();
|
||||
cur_state->restart(); // TSS _running
|
||||
SafepointMechanism::disarm_local_poll(current); // release store, local state -> polling page
|
||||
}
|
||||
log_debug(safepoint)("Leaving safepoint region");
|
||||
} else {
|
||||
// Set to not synchronized, so the threads will not go into the signal_thread_blocked method
|
||||
// when they get restarted.
|
||||
_state = _not_synchronized;
|
||||
OrderAccess::fence();
|
||||
|
||||
log_debug(safepoint)("Leaving safepoint region");
|
||||
|
||||
// Start suspended threads
|
||||
for (JavaThread *current = Threads::first(); current; current = current->next()) {
|
||||
// A problem occurring on Solaris is when attempting to restart threads
|
||||
// the first #cpus - 1 go well, but then the VMThread is preempted when we get
|
||||
// to the next one (since it has been running the longest). We then have
|
||||
// to wait for a cpu to become available before we can continue restarting
|
||||
// threads.
|
||||
// FIXME: This causes the performance of the VM to degrade when active and with
|
||||
// large numbers of threads. Apparently this is due to the synchronous nature
|
||||
// of suspending threads.
|
||||
//
|
||||
// TODO-FIXME: the comments above are vestigial and no longer apply.
|
||||
// Furthermore, using solaris' schedctl in this particular context confers no benefit
|
||||
if (VMThreadHintNoPreempt) {
|
||||
os::hint_no_preempt();
|
||||
}
|
||||
ThreadSafepointState* cur_state = current->safepoint_state();
|
||||
assert(cur_state->type() != ThreadSafepointState::_running, "Thread not suspended at safepoint");
|
||||
cur_state->restart();
|
||||
assert(cur_state->is_running(), "safepoint state has not been reset");
|
||||
}
|
||||
if (PageArmed) {
|
||||
assert(SafepointMechanism::uses_global_page_poll(), "sanity");
|
||||
// Make polling safepoint aware
|
||||
os::make_polling_page_readable();
|
||||
PageArmed = 0 ;
|
||||
}
|
||||
|
||||
RuntimeService::record_safepoint_end();
|
||||
if (SafepointMechanism::uses_global_page_poll()) {
|
||||
// Remove safepoint check from interpreter
|
||||
Interpreter::ignore_safepoints();
|
||||
}
|
||||
|
||||
// Release threads lock, so threads can be created/destroyed again. It will also starts all threads
|
||||
// blocked in signal_thread_blocked
|
||||
Threads_lock->unlock();
|
||||
{
|
||||
MutexLocker mu(Safepoint_lock);
|
||||
|
||||
assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization");
|
||||
|
||||
if (SafepointMechanism::uses_thread_local_poll()) {
|
||||
_state = _not_synchronized;
|
||||
OrderAccess::storestore(); // global state -> local state
|
||||
jtiwh.rewind();
|
||||
for (; JavaThread *current = jtiwh.next(); ) {
|
||||
ThreadSafepointState* cur_state = current->safepoint_state();
|
||||
cur_state->restart(); // TSS _running
|
||||
SafepointMechanism::disarm_local_poll(current); // release store, local state -> polling page
|
||||
}
|
||||
log_debug(safepoint)("Leaving safepoint region");
|
||||
} else {
|
||||
// Set to not synchronized, so the threads will not go into the signal_thread_blocked method
|
||||
// when they get restarted.
|
||||
_state = _not_synchronized;
|
||||
OrderAccess::fence();
|
||||
|
||||
log_debug(safepoint)("Leaving safepoint region");
|
||||
|
||||
// Start suspended threads
|
||||
jtiwh.rewind();
|
||||
for (; JavaThread *current = jtiwh.next(); ) {
|
||||
// A problem occurring on Solaris is when attempting to restart threads
|
||||
// the first #cpus - 1 go well, but then the VMThread is preempted when we get
|
||||
// to the next one (since it has been running the longest). We then have
|
||||
// to wait for a cpu to become available before we can continue restarting
|
||||
// threads.
|
||||
// FIXME: This causes the performance of the VM to degrade when active and with
|
||||
// large numbers of threads. Apparently this is due to the synchronous nature
|
||||
// of suspending threads.
|
||||
//
|
||||
// TODO-FIXME: the comments above are vestigial and no longer apply.
|
||||
// Furthermore, using solaris' schedctl in this particular context confers no benefit
|
||||
if (VMThreadHintNoPreempt) {
|
||||
os::hint_no_preempt();
|
||||
}
|
||||
ThreadSafepointState* cur_state = current->safepoint_state();
|
||||
assert(cur_state->type() != ThreadSafepointState::_running, "Thread not suspended at safepoint");
|
||||
cur_state->restart();
|
||||
assert(cur_state->is_running(), "safepoint state has not been reset");
|
||||
}
|
||||
}
|
||||
|
||||
RuntimeService::record_safepoint_end();
|
||||
|
||||
// Release threads lock, so threads can be created/destroyed again.
|
||||
// It will also release all threads blocked in signal_thread_blocked.
|
||||
Threads_lock->unlock();
|
||||
}
|
||||
} // ThreadsListHandle destroyed here.
|
||||
|
||||
}
|
||||
Universe::heap()->safepoint_synchronize_end();
|
||||
// record this time so VMThread can keep track how much time has elapsed
|
||||
// since last safepoint.
|
||||
@ -915,12 +925,11 @@ void SafepointSynchronize::print_safepoint_timeout(SafepointTimeoutReason reason
|
||||
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 @@ void SafepointSynchronize::print_state() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -894,7 +894,7 @@ ObjectSynchronizer::LockOwnership ObjectSynchronizer::query_lock_ownership
|
||||
}
|
||||
|
||||
// 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 @@ JavaThread* ObjectSynchronizer::get_lock_owner(Handle h_obj, bool doLock) {
|
||||
|
||||
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
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "runtime/perfData.hpp"
|
||||
|
||||
class ObjectMonitor;
|
||||
class ThreadsList;
|
||||
|
||||
struct DeflateMonitorCounters {
|
||||
int nInuse; // currently associated with objects
|
||||
@ -125,7 +126,7 @@ class ObjectSynchronizer : AllStatic {
|
||||
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);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -57,6 +57,8 @@
|
||||
#endif
|
||||
|
||||
class ThreadSafepointState;
|
||||
class ThreadsList;
|
||||
class NestedThreadsList;
|
||||
|
||||
class JvmtiThreadState;
|
||||
class JvmtiGetLoadedClassesClosure;
|
||||
@ -101,6 +103,7 @@ class WorkerThread;
|
||||
// - WatcherThread
|
||||
|
||||
class Thread: public ThreadShadow {
|
||||
friend class Threads;
|
||||
friend class VMStructs;
|
||||
friend class JVMCIVMStructs;
|
||||
private:
|
||||
@ -118,6 +121,47 @@ class Thread: public ThreadShadow {
|
||||
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 @@ class Thread: public ThreadShadow {
|
||||
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 @@ protected:
|
||||
|
||||
// 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 @@ class JavaThread: public Thread {
|
||||
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 @@ class JavaThread: public Thread {
|
||||
void set_safepoint_state(ThreadSafepointState *state) { _safepoint_state = state; }
|
||||
bool is_at_poll_safepoint() { return _safepoint_state->is_at_poll_safepoint(); }
|
||||
|
||||
// JavaThread termination and lifecycle support:
|
||||
void smr_delete();
|
||||
bool on_thread_list() const { return _on_thread_list; }
|
||||
void set_on_thread_list() { _on_thread_list = true; }
|
||||
|
||||
// thread has called JavaThread::exit() or is terminated
|
||||
bool is_exiting() { return _terminated == _thread_exiting || is_terminated(); }
|
||||
bool is_exiting() const;
|
||||
// thread is terminated (no longer on the threads list); we compare
|
||||
// against the two non-terminated values so that a freed JavaThread
|
||||
// will also be considered terminated.
|
||||
bool is_terminated() { return _terminated != _not_terminated && _terminated != _thread_exiting; }
|
||||
void set_terminated(TerminatedTypes t) { _terminated = t; }
|
||||
bool check_is_terminated(TerminatedTypes l_terminated) const {
|
||||
return l_terminated != _not_terminated && l_terminated != _thread_exiting;
|
||||
}
|
||||
bool is_terminated() const;
|
||||
void set_terminated(TerminatedTypes t);
|
||||
// special for Threads::remove() which is static:
|
||||
void set_terminated_value() { _terminated = _thread_terminated; }
|
||||
void set_terminated_value();
|
||||
void block_if_vm_exited();
|
||||
|
||||
bool doing_unsafe_access() { return _doing_unsafe_access; }
|
||||
@ -1220,6 +1277,9 @@ class JavaThread: public Thread {
|
||||
// via the appropriate -XX options.
|
||||
bool wait_for_ext_suspend_completion(int count, int delay, uint32_t *bits);
|
||||
|
||||
// test for suspend - most (all?) of these should go away
|
||||
bool is_thread_fully_suspended(bool wait_for_suspend, uint32_t *bits);
|
||||
|
||||
inline void set_external_suspend();
|
||||
inline void clear_external_suspend();
|
||||
|
||||
@ -2066,28 +2126,84 @@ inline CompilerThread* CompilerThread::current() {
|
||||
class Threads: AllStatic {
|
||||
friend class VMStructs;
|
||||
private:
|
||||
static JavaThread* _thread_list;
|
||||
static int _number_of_threads;
|
||||
static int _number_of_non_daemon_threads;
|
||||
static int _return_code;
|
||||
static int _thread_claim_parity;
|
||||
// Safe Memory Reclamation (SMR) support:
|
||||
static Monitor* _smr_delete_lock;
|
||||
// The '_cnt', '_max' and '_times" fields are enabled via
|
||||
// -XX:+EnableThreadSMRStatistics (see thread.cpp for a
|
||||
// description about each field):
|
||||
static uint _smr_delete_lock_wait_cnt;
|
||||
static uint _smr_delete_lock_wait_max;
|
||||
static volatile uint _smr_delete_notify;
|
||||
static volatile uint _smr_deleted_thread_cnt;
|
||||
static volatile uint _smr_deleted_thread_time_max;
|
||||
static volatile uint _smr_deleted_thread_times;
|
||||
static ThreadsList* volatile _smr_java_thread_list;
|
||||
static ThreadsList* get_smr_java_thread_list();
|
||||
static ThreadsList* xchg_smr_java_thread_list(ThreadsList* new_list);
|
||||
static uint64_t _smr_java_thread_list_alloc_cnt;
|
||||
static uint64_t _smr_java_thread_list_free_cnt;
|
||||
static uint _smr_java_thread_list_max;
|
||||
static uint _smr_nested_thread_list_max;
|
||||
static volatile uint _smr_tlh_cnt;
|
||||
static volatile uint _smr_tlh_time_max;
|
||||
static volatile uint _smr_tlh_times;
|
||||
static ThreadsList* _smr_to_delete_list;
|
||||
static uint _smr_to_delete_list_cnt;
|
||||
static uint _smr_to_delete_list_max;
|
||||
|
||||
static JavaThread* _thread_list;
|
||||
static int _number_of_threads;
|
||||
static int _number_of_non_daemon_threads;
|
||||
static int _return_code;
|
||||
static int _thread_claim_parity;
|
||||
#ifdef ASSERT
|
||||
static bool _vm_complete;
|
||||
static bool _vm_complete;
|
||||
#endif
|
||||
|
||||
static void initialize_java_lang_classes(JavaThread* main_thread, TRAPS);
|
||||
static void initialize_jsr292_core_classes(TRAPS);
|
||||
|
||||
static void smr_free_list(ThreadsList* threads);
|
||||
|
||||
public:
|
||||
// Thread management
|
||||
// force_daemon is a concession to JNI, where we may need to add a
|
||||
// thread to the thread list before allocating its thread object
|
||||
static void add(JavaThread* p, bool force_daemon = false);
|
||||
static void remove(JavaThread* p);
|
||||
static bool includes(JavaThread* p);
|
||||
static JavaThread* first() { return _thread_list; }
|
||||
static void threads_do(ThreadClosure* tc);
|
||||
static void possibly_parallel_threads_do(bool is_par, ThreadClosure* tc);
|
||||
|
||||
// SMR support:
|
||||
static ThreadsList *acquire_stable_list(Thread *self, bool is_ThreadsListSetter);
|
||||
static ThreadsList *acquire_stable_list_fast_path(Thread *self);
|
||||
static ThreadsList *acquire_stable_list_nested_path(Thread *self);
|
||||
static void release_stable_list(Thread *self);
|
||||
static void release_stable_list_fast_path(Thread *self);
|
||||
static void release_stable_list_nested_path(Thread *self);
|
||||
static void release_stable_list_wake_up(char *log_str);
|
||||
static bool is_a_protected_JavaThread(JavaThread *thread);
|
||||
static bool is_a_protected_JavaThread_with_lock(JavaThread *thread) {
|
||||
MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock);
|
||||
return is_a_protected_JavaThread(thread);
|
||||
}
|
||||
static void smr_delete(JavaThread *thread);
|
||||
// The coordination between Threads::release_stable_list() and
|
||||
// Threads::smr_delete() uses the smr_delete_lock in order to
|
||||
// reduce the traffic on the Threads_lock.
|
||||
static Monitor* smr_delete_lock() { return _smr_delete_lock; }
|
||||
// The smr_delete_notify flag is used for proper double-check
|
||||
// locking in order to reduce the traffic on the smr_delete_lock.
|
||||
static bool smr_delete_notify();
|
||||
static void set_smr_delete_notify();
|
||||
static void clear_smr_delete_notify();
|
||||
static void inc_smr_deleted_thread_cnt();
|
||||
static void update_smr_deleted_thread_time_max(uint new_value);
|
||||
static void add_smr_deleted_thread_times(uint add_value);
|
||||
static void inc_smr_tlh_cnt();
|
||||
static void update_smr_tlh_time_max(uint new_value);
|
||||
static void add_smr_tlh_times(uint add_value);
|
||||
|
||||
// Initializes the vm and creates the vm thread
|
||||
static jint create_vm(JavaVMInitArgs* args, bool* canTryAgain);
|
||||
static void convert_vm_init_libraries_to_agents();
|
||||
@ -2148,7 +2264,10 @@ class Threads: AllStatic {
|
||||
|
||||
// Verification
|
||||
static void verify();
|
||||
static void log_smr_statistics();
|
||||
static void print_on(outputStream* st, bool print_stacks, bool internal_format, bool print_concurrent_locks);
|
||||
static void print_smr_info_on(outputStream* st);
|
||||
static void print_smr_info_elements_on(outputStream* st, ThreadsList* t_list);
|
||||
static void print(bool print_stacks, bool internal_format) {
|
||||
// this function is only used by debug.cpp
|
||||
print_on(tty, print_stacks, internal_format, false /* no concurrent lock printed */);
|
||||
@ -2158,17 +2277,13 @@ class Threads: AllStatic {
|
||||
int buflen, bool* found_current);
|
||||
static void print_threads_compiling(outputStream* st, char* buf, int buflen);
|
||||
|
||||
// Get Java threads that are waiting to enter a monitor. If doLock
|
||||
// is true, then Threads_lock is grabbed as needed. Otherwise, the
|
||||
// VM needs to be at a safepoint.
|
||||
static GrowableArray<JavaThread*>* get_pending_threads(int count,
|
||||
address monitor, bool doLock);
|
||||
// Get Java threads that are waiting to enter a monitor.
|
||||
static GrowableArray<JavaThread*>* get_pending_threads(ThreadsList * t_list,
|
||||
int count, address monitor);
|
||||
|
||||
// Get owning Java thread from the monitor's owner field. If doLock
|
||||
// is true, then Threads_lock is grabbed as needed. Otherwise, the
|
||||
// VM needs to be at a safepoint.
|
||||
static JavaThread *owning_thread_from_monitor_owner(address owner,
|
||||
bool doLock);
|
||||
// Get owning Java thread from the monitor's owner field.
|
||||
static JavaThread *owning_thread_from_monitor_owner(ThreadsList * t_list,
|
||||
address owner);
|
||||
|
||||
// Number of threads on the active threads list
|
||||
static int number_of_threads() { return _number_of_threads; }
|
||||
@ -2177,9 +2292,6 @@ class Threads: AllStatic {
|
||||
|
||||
// Deoptimizes all frames tied to marked nmethods
|
||||
static void deoptimized_wrt_marked_nmethods();
|
||||
|
||||
static JavaThread* find_java_thread_from_java_tid(jlong java_tid);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -25,13 +25,10 @@
|
||||
#ifndef SHARE_VM_RUNTIME_THREAD_INLINE_HPP
|
||||
#define SHARE_VM_RUNTIME_THREAD_INLINE_HPP
|
||||
|
||||
#define SHARE_VM_RUNTIME_THREAD_INLINE_HPP_SCOPE
|
||||
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/os.inline.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
|
||||
#undef SHARE_VM_RUNTIME_THREAD_INLINE_HPP_SCOPE
|
||||
#include "runtime/threadSMR.hpp"
|
||||
|
||||
inline void Thread::set_suspend_flag(SuspendFlags f) {
|
||||
assert(sizeof(jint) == sizeof(_suspend_flags), "size mismatch");
|
||||
@ -89,6 +86,18 @@ inline jlong Thread::cooked_allocated_bytes() {
|
||||
return allocated_bytes;
|
||||
}
|
||||
|
||||
inline ThreadsList* Thread::cmpxchg_threads_hazard_ptr(ThreadsList* exchange_value, ThreadsList* compare_value) {
|
||||
return (ThreadsList*)Atomic::cmpxchg(exchange_value, &_threads_hazard_ptr, compare_value);
|
||||
}
|
||||
|
||||
inline ThreadsList* Thread::get_threads_hazard_ptr() {
|
||||
return (ThreadsList*)OrderAccess::load_acquire(&_threads_hazard_ptr);
|
||||
}
|
||||
|
||||
inline void Thread::set_threads_hazard_ptr(ThreadsList* new_list) {
|
||||
OrderAccess::release_store_fence(&_threads_hazard_ptr, new_list);
|
||||
}
|
||||
|
||||
inline void JavaThread::set_ext_suspended() {
|
||||
set_suspend_flag (_ext_suspended);
|
||||
}
|
||||
@ -176,4 +185,83 @@ inline volatile void* JavaThread::get_polling_page() {
|
||||
return OrderAccess::load_acquire(polling_page_addr());
|
||||
}
|
||||
|
||||
inline bool JavaThread::is_exiting() const {
|
||||
// Use load-acquire so that setting of _terminated by
|
||||
// JavaThread::exit() is seen more quickly.
|
||||
TerminatedTypes l_terminated = (TerminatedTypes)
|
||||
OrderAccess::load_acquire((volatile jint *) &_terminated);
|
||||
return l_terminated == _thread_exiting || check_is_terminated(l_terminated);
|
||||
}
|
||||
|
||||
inline bool JavaThread::is_terminated() const {
|
||||
// Use load-acquire so that setting of _terminated by
|
||||
// JavaThread::exit() is seen more quickly.
|
||||
TerminatedTypes l_terminated = (TerminatedTypes)
|
||||
OrderAccess::load_acquire((volatile jint *) &_terminated);
|
||||
return check_is_terminated(l_terminated);
|
||||
}
|
||||
|
||||
inline void JavaThread::set_terminated(TerminatedTypes t) {
|
||||
// use release-store so the setting of _terminated is seen more quickly
|
||||
OrderAccess::release_store((volatile jint *) &_terminated, (jint) t);
|
||||
}
|
||||
|
||||
// special for Threads::remove() which is static:
|
||||
inline void JavaThread::set_terminated_value() {
|
||||
// use release-store so the setting of _terminated is seen more quickly
|
||||
OrderAccess::release_store((volatile jint *) &_terminated, (jint) _thread_terminated);
|
||||
}
|
||||
|
||||
inline ThreadsList* Threads::get_smr_java_thread_list() {
|
||||
return (ThreadsList*)OrderAccess::load_acquire(&_smr_java_thread_list);
|
||||
}
|
||||
|
||||
inline ThreadsList* Threads::xchg_smr_java_thread_list(ThreadsList* new_list) {
|
||||
return (ThreadsList*)Atomic::xchg(new_list, &_smr_java_thread_list);
|
||||
}
|
||||
|
||||
inline void Threads::inc_smr_deleted_thread_cnt() {
|
||||
Atomic::inc(&_smr_deleted_thread_cnt);
|
||||
}
|
||||
|
||||
inline void Threads::update_smr_deleted_thread_time_max(uint new_value) {
|
||||
while (true) {
|
||||
uint cur_value = _smr_deleted_thread_time_max;
|
||||
if (new_value <= cur_value) {
|
||||
// No need to update max value so we're done.
|
||||
break;
|
||||
}
|
||||
if (Atomic::cmpxchg(new_value, &_smr_deleted_thread_time_max, cur_value) == cur_value) {
|
||||
// Updated max value so we're done. Otherwise try it all again.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Threads::add_smr_deleted_thread_times(uint add_value) {
|
||||
Atomic::add(add_value, &_smr_deleted_thread_times);
|
||||
}
|
||||
|
||||
inline void Threads::inc_smr_tlh_cnt() {
|
||||
Atomic::inc(&_smr_tlh_cnt);
|
||||
}
|
||||
|
||||
inline void Threads::update_smr_tlh_time_max(uint new_value) {
|
||||
while (true) {
|
||||
uint cur_value = _smr_tlh_time_max;
|
||||
if (new_value <= cur_value) {
|
||||
// No need to update max value so we're done.
|
||||
break;
|
||||
}
|
||||
if (Atomic::cmpxchg(new_value, &_smr_tlh_time_max, cur_value) == cur_value) {
|
||||
// Updated max value so we're done. Otherwise try it all again.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Threads::add_smr_tlh_times(uint add_value) {
|
||||
Atomic::add(add_value, &_smr_tlh_times);
|
||||
}
|
||||
|
||||
#endif // SHARE_VM_RUNTIME_THREAD_INLINE_HPP
|
||||
|
121
src/hotspot/share/runtime/threadSMR.cpp
Normal file
121
src/hotspot/share/runtime/threadSMR.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "services/threadService.hpp"
|
||||
|
||||
// 'entries + 1' so we always have at least one entry.
|
||||
ThreadsList::ThreadsList(int entries) : _length(entries), _threads(NEW_C_HEAP_ARRAY(JavaThread*, entries + 1, mtThread)), _next_list(NULL) {
|
||||
*(JavaThread**)(_threads + entries) = NULL; // Make sure the extra entry is NULL.
|
||||
}
|
||||
|
||||
ThreadsList::~ThreadsList() {
|
||||
FREE_C_HEAP_ARRAY(JavaThread*, _threads);
|
||||
}
|
||||
|
||||
ThreadsListSetter::~ThreadsListSetter() {
|
||||
if (_target_needs_release) {
|
||||
// The hazard ptr in the target needs to be released.
|
||||
Threads::release_stable_list(_target);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadsListSetter::set() {
|
||||
assert(_target->get_threads_hazard_ptr() == NULL, "hazard ptr should not already be set");
|
||||
(void) Threads::acquire_stable_list(_target, /* is_ThreadsListSetter */ true);
|
||||
_target_needs_release = true;
|
||||
}
|
||||
|
||||
ThreadsListHandle::ThreadsListHandle(Thread *self) : _list(Threads::acquire_stable_list(self, /* is_ThreadsListSetter */ false)), _self(self) {
|
||||
assert(self == Thread::current(), "sanity check");
|
||||
if (EnableThreadSMRStatistics) {
|
||||
_timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
ThreadsListHandle::~ThreadsListHandle() {
|
||||
Threads::release_stable_list(_self);
|
||||
if (EnableThreadSMRStatistics) {
|
||||
_timer.stop();
|
||||
uint millis = (uint)_timer.milliseconds();
|
||||
Threads::inc_smr_tlh_cnt();
|
||||
Threads::add_smr_tlh_times(millis);
|
||||
Threads::update_smr_tlh_time_max(millis);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert an internal thread reference to a JavaThread found on the
|
||||
// associated ThreadsList. This ThreadsListHandle "protects" the
|
||||
// returned JavaThread *.
|
||||
//
|
||||
// If thread_oop_p is not NULL, then the caller wants to use the oop
|
||||
// after this call so the oop is returned. On success, *jt_pp is set
|
||||
// to the converted JavaThread * and true is returned. On error,
|
||||
// returns false.
|
||||
//
|
||||
bool ThreadsListHandle::cv_internal_thread_to_JavaThread(jobject jthread,
|
||||
JavaThread ** jt_pp,
|
||||
oop * thread_oop_p) {
|
||||
assert(this->list() != NULL, "must have a ThreadsList");
|
||||
assert(jt_pp != NULL, "must have a return JavaThread pointer");
|
||||
// thread_oop_p is optional so no assert()
|
||||
|
||||
// The JVM_* interfaces don't allow a NULL thread parameter; JVM/TI
|
||||
// allows a NULL thread parameter to signify "current thread" which
|
||||
// allows us to avoid calling cv_external_thread_to_JavaThread().
|
||||
// The JVM_* interfaces have no such leeway.
|
||||
|
||||
oop thread_oop = JNIHandles::resolve_non_null(jthread);
|
||||
// Looks like an oop at this point.
|
||||
if (thread_oop_p != NULL) {
|
||||
// Return the oop to the caller; the caller may still want
|
||||
// the oop even if this function returns false.
|
||||
*thread_oop_p = thread_oop;
|
||||
}
|
||||
|
||||
JavaThread *java_thread = java_lang_Thread::thread(thread_oop);
|
||||
if (java_thread == NULL) {
|
||||
// The java.lang.Thread does not contain a JavaThread * so it has
|
||||
// not yet run or it has died.
|
||||
return false;
|
||||
}
|
||||
// Looks like a live JavaThread at this point.
|
||||
|
||||
if (java_thread != JavaThread::current()) {
|
||||
// jthread is not for the current JavaThread so have to verify
|
||||
// the JavaThread * against the ThreadsList.
|
||||
if (EnableThreadSMRExtraValidityChecks && !includes(java_thread)) {
|
||||
// Not on the JavaThreads list so it is not alive.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return a live JavaThread that is "protected" by the
|
||||
// ThreadsListHandle in the caller.
|
||||
*jt_pp = java_thread;
|
||||
return true;
|
||||
}
|
257
src/hotspot/share/runtime/threadSMR.hpp
Normal file
257
src/hotspot/share/runtime/threadSMR.hpp
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_VM_RUNTIME_THREADSMR_HPP
|
||||
#define SHARE_VM_RUNTIME_THREADSMR_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "runtime/timer.hpp"
|
||||
|
||||
// Thread Safe Memory Reclamation (Thread-SMR) support.
|
||||
//
|
||||
// ThreadsListHandles are used to safely perform operations on one or more
|
||||
// threads without the risk of the thread or threads exiting during the
|
||||
// operation. It is no longer necessary to hold the Threads_lock to safely
|
||||
// perform an operation on a target thread.
|
||||
//
|
||||
// There are several different ways to refer to java.lang.Thread objects
|
||||
// so we have a few ways to get a protected JavaThread *:
|
||||
//
|
||||
// JNI jobject example:
|
||||
// jobject jthread = ...;
|
||||
// :
|
||||
// ThreadsListHandle tlh;
|
||||
// JavaThread* jt = NULL;
|
||||
// bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &jt, NULL);
|
||||
// if (is_alive) {
|
||||
// : // do stuff with 'jt'...
|
||||
// }
|
||||
//
|
||||
// JVM/TI jthread example:
|
||||
// jthread thread = ...;
|
||||
// :
|
||||
// JavaThread* jt = NULL;
|
||||
// ThreadsListHandle tlh;
|
||||
// jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &jt, NULL);
|
||||
// if (err != JVMTI_ERROR_NONE) {
|
||||
// return err;
|
||||
// }
|
||||
// : // do stuff with 'jt'...
|
||||
//
|
||||
// JVM/TI oop example (this one should be very rare):
|
||||
// oop thread_obj = ...;
|
||||
// :
|
||||
// JavaThread *jt = NULL;
|
||||
// ThreadsListHandle tlh;
|
||||
// jvmtiError err = JvmtiExport::cv_oop_to_JavaThread(tlh.list(), thread_obj, &jt);
|
||||
// if (err != JVMTI_ERROR_NONE) {
|
||||
// return err;
|
||||
// }
|
||||
// : // do stuff with 'jt'...
|
||||
//
|
||||
// A JavaThread * that is included in the ThreadsList that is held by
|
||||
// a ThreadsListHandle is protected as long as the ThreadsListHandle
|
||||
// remains in scope. The target JavaThread * may have logically exited,
|
||||
// but that target JavaThread * will not be deleted until it is no
|
||||
// longer protected by a ThreadsListHandle.
|
||||
|
||||
|
||||
// A fast list of JavaThreads.
|
||||
//
|
||||
class ThreadsList : public CHeapObj<mtThread> {
|
||||
friend class ScanHazardPtrGatherProtectedThreadsClosure;
|
||||
friend class Threads;
|
||||
|
||||
const uint _length;
|
||||
ThreadsList* _next_list;
|
||||
JavaThread *const *const _threads;
|
||||
|
||||
template <class T>
|
||||
void threads_do_dispatch(T *cl, JavaThread *const thread) const;
|
||||
|
||||
ThreadsList *next_list() const { return _next_list; }
|
||||
void set_next_list(ThreadsList *list) { _next_list = list; }
|
||||
|
||||
public:
|
||||
ThreadsList(int entries);
|
||||
~ThreadsList();
|
||||
|
||||
template <class T>
|
||||
void threads_do(T *cl) const;
|
||||
|
||||
uint length() const { return _length; }
|
||||
|
||||
JavaThread *const thread_at(uint i) const { return _threads[i]; }
|
||||
|
||||
JavaThread *const *threads() const { return _threads; }
|
||||
|
||||
// Returns -1 if target is not found.
|
||||
int find_index_of_JavaThread(JavaThread* target);
|
||||
JavaThread* find_JavaThread_from_java_tid(jlong java_tid) const;
|
||||
bool includes(const JavaThread * const p) const;
|
||||
|
||||
static ThreadsList* add_thread(ThreadsList* list, JavaThread* java_thread);
|
||||
static ThreadsList* remove_thread(ThreadsList* list, JavaThread* java_thread);
|
||||
};
|
||||
|
||||
// Linked list of ThreadsLists to support nested ThreadsListHandles.
|
||||
class NestedThreadsList : public CHeapObj<mtThread> {
|
||||
ThreadsList*const _t_list;
|
||||
NestedThreadsList* _next;
|
||||
|
||||
public:
|
||||
NestedThreadsList(ThreadsList* t_list) : _t_list(t_list) {
|
||||
assert(Threads_lock->owned_by_self(),
|
||||
"must own Threads_lock for saved t_list to be valid.");
|
||||
}
|
||||
|
||||
ThreadsList* t_list() { return _t_list; }
|
||||
NestedThreadsList* next() { return _next; }
|
||||
void set_next(NestedThreadsList* value) { _next = value; }
|
||||
};
|
||||
|
||||
// A helper to optionally set the hazard ptr in ourself. This helper can
|
||||
// be used by ourself or by another thread. If the hazard ptr is set(),
|
||||
// then the destructor will release it.
|
||||
//
|
||||
class ThreadsListSetter : public StackObj {
|
||||
private:
|
||||
bool _target_needs_release; // needs release only when set()
|
||||
Thread * _target;
|
||||
|
||||
public:
|
||||
ThreadsListSetter() : _target_needs_release(false), _target(Thread::current()) {
|
||||
}
|
||||
~ThreadsListSetter();
|
||||
ThreadsList* list();
|
||||
void set();
|
||||
bool target_needs_release() { return _target_needs_release; }
|
||||
};
|
||||
|
||||
// This stack allocated ThreadsListHandle keeps all JavaThreads in the
|
||||
// ThreadsList from being deleted until it is safe.
|
||||
//
|
||||
class ThreadsListHandle : public StackObj {
|
||||
ThreadsList * _list;
|
||||
Thread *const _self;
|
||||
elapsedTimer _timer; // Enabled via -XX:+EnableThreadSMRStatistics.
|
||||
|
||||
public:
|
||||
ThreadsListHandle(Thread *self = Thread::current());
|
||||
~ThreadsListHandle();
|
||||
|
||||
ThreadsList *list() const {
|
||||
return _list;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void threads_do(T *cl) const {
|
||||
return _list->threads_do(cl);
|
||||
}
|
||||
|
||||
bool cv_internal_thread_to_JavaThread(jobject jthread, JavaThread ** jt_pp, oop * thread_oop_p);
|
||||
|
||||
bool includes(JavaThread* p) {
|
||||
return _list->includes(p);
|
||||
}
|
||||
|
||||
uint length() const {
|
||||
return _list->length();
|
||||
}
|
||||
};
|
||||
|
||||
// This stack allocated JavaThreadIterator is used to walk the
|
||||
// specified ThreadsList using the following style:
|
||||
//
|
||||
// JavaThreadIterator jti(t_list);
|
||||
// for (JavaThread *jt = jti.first(); jt != NULL; jt = jti.next()) {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
class JavaThreadIterator : public StackObj {
|
||||
ThreadsList * _list;
|
||||
uint _index;
|
||||
|
||||
public:
|
||||
JavaThreadIterator(ThreadsList *list) : _list(list), _index(0) {
|
||||
assert(list != NULL, "ThreadsList must not be NULL.");
|
||||
}
|
||||
|
||||
JavaThread *first() {
|
||||
_index = 0;
|
||||
return _list->thread_at(_index);
|
||||
}
|
||||
|
||||
uint length() const {
|
||||
return _list->length();
|
||||
}
|
||||
|
||||
ThreadsList *list() const {
|
||||
return _list;
|
||||
}
|
||||
|
||||
JavaThread *next() {
|
||||
if (++_index >= length()) {
|
||||
return NULL;
|
||||
}
|
||||
return _list->thread_at(_index);
|
||||
}
|
||||
};
|
||||
|
||||
// This stack allocated ThreadsListHandle and JavaThreadIterator combo
|
||||
// is used to walk the ThreadsList in the included ThreadsListHandle
|
||||
// using the following style:
|
||||
//
|
||||
// for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
class JavaThreadIteratorWithHandle : public StackObj {
|
||||
ThreadsListHandle _tlh;
|
||||
uint _index;
|
||||
|
||||
public:
|
||||
JavaThreadIteratorWithHandle() : _index(0) {}
|
||||
|
||||
uint length() const {
|
||||
return _tlh.length();
|
||||
}
|
||||
|
||||
ThreadsList *list() const {
|
||||
return _tlh.list();
|
||||
}
|
||||
|
||||
JavaThread *next() {
|
||||
if (_index >= length()) {
|
||||
return NULL;
|
||||
}
|
||||
return _tlh.list()->thread_at(_index++);
|
||||
}
|
||||
|
||||
void rewind() {
|
||||
_index = 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_RUNTIME_THREADSMR_HPP
|
62
src/hotspot/share/runtime/threadSMR.inline.hpp
Normal file
62
src/hotspot/share/runtime/threadSMR.inline.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP
|
||||
#define SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP
|
||||
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/prefetch.inline.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
|
||||
// Devirtualize known thread closure types.
|
||||
template <class T>
|
||||
inline void ThreadsList::threads_do_dispatch(T *cl, JavaThread *const thread) const {
|
||||
cl->T::do_thread(thread);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ThreadsList::threads_do_dispatch<ThreadClosure>(ThreadClosure *cl, JavaThread *const thread) const {
|
||||
cl->do_thread(thread);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void ThreadsList::threads_do(T *cl) const {
|
||||
const intx scan_interval = PrefetchScanIntervalInBytes;
|
||||
JavaThread *const *const end = _threads + _length;
|
||||
for (JavaThread *const *current_p = _threads; current_p != end; current_p++) {
|
||||
Prefetch::read((void*)current_p, scan_interval);
|
||||
JavaThread *const current = *current_p;
|
||||
threads_do_dispatch(cl, current);
|
||||
}
|
||||
}
|
||||
|
||||
inline ThreadsList* ThreadsListSetter::list() {
|
||||
ThreadsList *ret = _target->get_threads_hazard_ptr();
|
||||
assert(ret != NULL, "hazard ptr should be set");
|
||||
assert(!Thread::is_hazard_ptr_tagged(ret), "hazard ptr should be validated");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP
|
@ -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_Operation::print_on_error(outputStream* st) const {
|
||||
|
||||
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_DeoptimizeFrame::doit() {
|
||||
|
||||
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 @@ void VM_DeoptimizeAll::doit() {
|
||||
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 @@ bool VM_FindDeadlocks::doit_prologue() {
|
||||
}
|
||||
|
||||
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_epilogue() {
|
||||
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 @@ void VM_ThreadDump::doit() {
|
||||
|
||||
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 @@ void VM_ThreadDump::doit() {
|
||||
} 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 @@ void VM_ThreadDump::doit() {
|
||||
// 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 @@ void VM_ThreadDump::doit() {
|
||||
}
|
||||
|
||||
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 @@ int VM_Exit::set_vm_exited() {
|
||||
|
||||
_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 VM_Exit::wait_for_threads_in_native_to_block() {
|
||||
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()) {
|
||||
|
@ -392,12 +392,14 @@ class VM_PrintMetadata : public VM_Operation {
|
||||
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();
|
||||
|
||||
|
@ -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 @@ void VM_HeapDumper::dump_stack_traces() {
|
||||
|
||||
_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
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "runtime/os.hpp"
|
||||
#include "runtime/serviceThread.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "services/classLoadingService.hpp"
|
||||
#include "services/diagnosticCommand.hpp"
|
||||
#include "services/diagnosticFramework.hpp"
|
||||
@ -1025,11 +1026,15 @@ static void do_thread_dump(ThreadDumpResult* dump_result,
|
||||
// First get an array of threadObj handles.
|
||||
// A JavaThread may terminate before we get the stack trace.
|
||||
GrowableArray<instanceHandle>* thread_handle_array = new GrowableArray<instanceHandle>(num_threads);
|
||||
|
||||
{
|
||||
MutexLockerEx ml(Threads_lock);
|
||||
// Need this ThreadsListHandle for converting Java thread IDs into
|
||||
// threadObj handles; dump_result->set_t_list() is called in the
|
||||
// VM op below so we can't use it yet.
|
||||
ThreadsListHandle tlh;
|
||||
for (int i = 0; i < num_threads; i++) {
|
||||
jlong tid = ids_ah->long_at(i);
|
||||
JavaThread* jt = Threads::find_java_thread_from_java_tid(tid);
|
||||
JavaThread* jt = tlh.list()->find_JavaThread_from_java_tid(tid);
|
||||
oop thread_obj = (jt != NULL ? jt->threadObj() : (oop)NULL);
|
||||
instanceHandle threadObj_h(THREAD, (instanceOop) thread_obj);
|
||||
thread_handle_array->append(threadObj_h);
|
||||
@ -1101,22 +1106,21 @@ JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jo
|
||||
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 @@ JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jo
|
||||
|
||||
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 @@ JVM_ENTRY(jobjectArray, jmm_DumpThreads(JNIEnv *env, jlongArray thread_ids, jboo
|
||||
}
|
||||
|
||||
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 @@ JVM_ENTRY(jboolean, jmm_ResetStatistic(JNIEnv *env, jvalue obj, jmmStatisticType
|
||||
}
|
||||
|
||||
// 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 @@ JVM_ENTRY(jboolean, jmm_ResetStatistic(JNIEnv *env, jvalue obj, jmmStatisticType
|
||||
}
|
||||
} 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 @@ JVM_ENTRY(jlong, jmm_GetThreadCpuTime(JNIEnv *env, jlong thread_id))
|
||||
// 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 @@ ThreadTimesClosure::ThreadTimesClosure(objArrayHandle names,
|
||||
// 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 @@ JVM_ENTRY(void, jmm_GetThreadAllocatedMemory(JNIEnv *env, jlongArray ids,
|
||||
"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 @@ JVM_ENTRY(jlong, jmm_GetThreadCpuTimeWithKind(JNIEnv *env, jlong thread_id, jboo
|
||||
// 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 @@ JVM_ENTRY(void, jmm_GetThreadCpuTimesWithKind(JNIEnv *env, jlongArray ids,
|
||||
"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));
|
||||
|
@ -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 @@ void ThreadService::current_thread_exiting(JavaThread* jt) {
|
||||
// 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 @@ Handle ThreadService::dump_stack_traces(GrowableArray<instanceHandle>* threads,
|
||||
|
||||
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 @@ void ThreadService::reset_contention_time_stat(JavaThread* thread) {
|
||||
}
|
||||
|
||||
// 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 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks)
|
||||
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 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks)
|
||||
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 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks)
|
||||
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 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks)
|
||||
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 @@ ThreadDumpResult::ThreadDumpResult() : _num_threads(0), _num_snapshots(0), _snap
|
||||
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 @@ void ThreadDumpResult::metadata_do(void f(Metadata*)) {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ void ConcurrentLocksDump::build_map(GrowableArray<oop>* aos_objects) {
|
||||
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 @@ ThreadStatistics::ThreadStatistics() {
|
||||
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 @@ ThreadSnapshot::ThreadSnapshot(JavaThread* thread) {
|
||||
_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 @@ DeadlockCycle::~DeadlockCycle() {
|
||||
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 @@ void DeadlockCycle::print_on(outputStream* st) const {
|
||||
// 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 @@ void DeadlockCycle::print_on(outputStream* st) const {
|
||||
"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 @@ ThreadsListEnumerator::ThreadsListEnumerator(Thread* cur_thread,
|
||||
int init_size = ThreadService::get_live_thread_count();
|
||||
_threads_array = new GrowableArray<instanceHandle>(init_size);
|
||||
|
||||
MutexLockerEx ml(Threads_lock);
|
||||
|
||||
for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) {
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
|
||||
// skips JavaThreads in the process of exiting
|
||||
// and also skips VM internal JavaThreads
|
||||
// Threads in _thread_new or _thread_new_trans state are included.
|
||||
|
@ -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 @@ public:
|
||||
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 @@ public:
|
||||
// Thread snapshot to represent the thread state and statistics
|
||||
class ThreadSnapshot : public CHeapObj<mtInternal> {
|
||||
private:
|
||||
// This JavaThread* is protected by being stored in objects that are
|
||||
// protected by a ThreadsListSetter (ThreadDumpResult).
|
||||
JavaThread* _thread;
|
||||
oop _threadObj;
|
||||
java_lang_Thread::ThreadStatus _thread_status;
|
||||
@ -213,7 +216,7 @@ public:
|
||||
// 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 @@ class ThreadConcurrentLocks : public CHeapObj<mtInternal> {
|
||||
private:
|
||||
GrowableArray<instanceOop>* _owned_locks;
|
||||
ThreadConcurrentLocks* _next;
|
||||
// This JavaThread* is protected in one of two different ways
|
||||
// depending on the usage of the ThreadConcurrentLocks object:
|
||||
// 1) by being stored in objects that are only allocated and used at a
|
||||
// safepoint (ConcurrentLocksDump), or 2) by being stored in objects
|
||||
// that are protected by a ThreadsListSetter (ThreadSnapshot inside
|
||||
// ThreadDumpResult).
|
||||
JavaThread* _thread;
|
||||
public:
|
||||
ThreadConcurrentLocks(JavaThread* thread);
|
||||
@ -333,8 +342,12 @@ class ConcurrentLocksDump : public StackObj {
|
||||
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 @@ class ThreadDumpResult : public StackObj {
|
||||
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 @@ class ThreadDumpResult : public StackObj {
|
||||
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 @@ class DeadlockCycle : public CHeapObj<mtInternal> {
|
||||
bool is_deadlock() { return _is_deadlock; }
|
||||
int num_threads() { return _threads->length(); }
|
||||
GrowableArray<JavaThread*>* threads() { return _threads; }
|
||||
void print_on(outputStream* st) const;
|
||||
void print_on_with(ThreadsList * t_list, outputStream* st) const;
|
||||
};
|
||||
|
||||
// Utility class to get list of java threads.
|
||||
|
@ -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 @@ void VMError::controlled_crash(int how) {
|
||||
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 @@ void VMError::controlled_crash(int how) {
|
||||
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);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,6 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @requires (vm.debug == true)
|
||||
* @bug 6888954
|
||||
* @bug 8015884
|
||||
* @summary Exercise HotSpot error handling code by invoking java with
|
||||
@ -39,6 +40,7 @@ import jdk.test.lib.process.OutputAnalyzer;
|
||||
public class ErrorHandler {
|
||||
|
||||
public static OutputAnalyzer runTest(int testcase) throws Exception {
|
||||
// The -XX:ErrorHandlerTest=N option requires debug bits.
|
||||
return new OutputAnalyzer(
|
||||
ProcessTools.createJavaProcessBuilder(
|
||||
"-XX:-TransmitErrorReport", "-XX:-CreateCoredumpOnCrash", "-XX:ErrorHandlerTest=" + testcase)
|
||||
@ -46,10 +48,6 @@ public class ErrorHandler {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Test is only applicable for debug builds
|
||||
if (!Platform.isDebugBuild()) {
|
||||
return;
|
||||
}
|
||||
// Keep this in sync with hotspot/src/share/vm/utilities/debug.cpp
|
||||
int i = 1;
|
||||
String[] strings = {
|
||||
@ -69,6 +67,10 @@ public class ErrorHandler {
|
||||
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) {
|
||||
|
124
test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java
Normal file
124
test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.Platform;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @requires (vm.debug == true)
|
||||
* @bug 8167108
|
||||
* @summary Nested ThreadsListHandle info should be in error handling output.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics NestedThreadsListHandleInErrorHandlingTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* This test was created using SafeFetchInErrorHandlingTest.java
|
||||
* as a guide.
|
||||
*/
|
||||
public class NestedThreadsListHandleInErrorHandlingTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
// The -XX:ErrorHandlerTest=N option requires debug bits.
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-Xmx100M",
|
||||
"-XX:ErrorHandlerTest=17",
|
||||
"-XX:-CreateCoredumpOnCrash",
|
||||
"-version");
|
||||
|
||||
OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
|
||||
|
||||
// We should have crashed with a specific fatal error:
|
||||
output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*");
|
||||
System.out.println("Found fatal error header.");
|
||||
output_detail.shouldMatch("# +fatal error: Force crash with a nested ThreadsListHandle.");
|
||||
System.out.println("Found specific fatal error.");
|
||||
|
||||
// Extract hs_err_pid file.
|
||||
String hs_err_file = output_detail.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1);
|
||||
if (hs_err_file == null) {
|
||||
throw new RuntimeException("Did not find hs_err_pid file in output.\n");
|
||||
}
|
||||
|
||||
File f = new File(hs_err_file);
|
||||
if (!f.exists()) {
|
||||
throw new RuntimeException("hs_err_pid file missing at "
|
||||
+ f.getAbsolutePath() + ".\n");
|
||||
}
|
||||
|
||||
System.out.println("Found hs_err_pid file. Scanning...");
|
||||
|
||||
FileInputStream fis = new FileInputStream(f);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
|
||||
String line = null;
|
||||
|
||||
Pattern [] pattern = new Pattern[] {
|
||||
// The "Current thread" line should show a hazard ptr and
|
||||
// a nested hazard ptr:
|
||||
Pattern.compile("Current thread .* _threads_hazard_ptr=0x[0-9A-Fa-f][0-9A-Fa-f]*, _nested_threads_hazard_ptr_cnt=1, _nested_threads_hazard_ptrs=0x.*"),
|
||||
// We should have a section of Threads class SMR info:
|
||||
Pattern.compile("Threads class SMR info:"),
|
||||
// We should have one nested ThreadsListHandle:
|
||||
Pattern.compile(".*, _smr_nested_thread_list_max=1"),
|
||||
// The current thread (marked with '=>') in the threads list
|
||||
// should show a hazard ptr:
|
||||
Pattern.compile("=>.* JavaThread \"main\" .*_threads_hazard_ptr=0x[0-9A-Fa-f][0-9A-Fa-f]*, _nested_threads_hazard_ptr_cnt=1, _nested_threads_hazard_ptrs=0x.*"),
|
||||
};
|
||||
int currentPattern = 0;
|
||||
|
||||
String lastLine = null;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (currentPattern < pattern.length) {
|
||||
if (pattern[currentPattern].matcher(line).matches()) {
|
||||
System.out.println("Found: " + line + ".");
|
||||
currentPattern++;
|
||||
}
|
||||
}
|
||||
lastLine = line;
|
||||
}
|
||||
br.close();
|
||||
|
||||
if (currentPattern < pattern.length) {
|
||||
throw new RuntimeException("hs_err_pid file incomplete (first missing pattern: " + currentPattern + ")");
|
||||
}
|
||||
|
||||
if (!lastLine.equals("END.")) {
|
||||
throw new RuntimeException("hs-err file incomplete (missing END marker.)");
|
||||
} else {
|
||||
System.out.println("End marker found.");
|
||||
}
|
||||
|
||||
System.out.println("Done scanning hs_err_pid_file.");
|
||||
System.out.println("PASSED.");
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.Platform;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @requires (vm.debug == true)
|
||||
* @bug 8167108
|
||||
* @summary ThreadsListHandle info should be in error handling output.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics ThreadsListHandleInErrorHandlingTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* This test was created using SafeFetchInErrorHandlingTest.java
|
||||
* as a guide.
|
||||
*/
|
||||
public class ThreadsListHandleInErrorHandlingTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
// The -XX:ErrorHandlerTest=N option requires debug bits.
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-Xmx100M",
|
||||
"-XX:ErrorHandlerTest=16",
|
||||
"-XX:-CreateCoredumpOnCrash",
|
||||
"-version");
|
||||
|
||||
OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
|
||||
|
||||
// We should have crashed with a specific fatal error:
|
||||
output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*");
|
||||
System.out.println("Found fatal error header.");
|
||||
output_detail.shouldMatch("# +fatal error: Force crash with an active ThreadsListHandle.");
|
||||
System.out.println("Found specific fatal error.");
|
||||
|
||||
// Extract hs_err_pid file.
|
||||
String hs_err_file = output_detail.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1);
|
||||
if (hs_err_file == null) {
|
||||
throw new RuntimeException("Did not find hs_err_pid file in output.\n");
|
||||
}
|
||||
|
||||
File f = new File(hs_err_file);
|
||||
if (!f.exists()) {
|
||||
throw new RuntimeException("hs_err_pid file missing at "
|
||||
+ f.getAbsolutePath() + ".\n");
|
||||
}
|
||||
|
||||
System.out.println("Found hs_err_pid file. Scanning...");
|
||||
|
||||
FileInputStream fis = new FileInputStream(f);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
|
||||
String line = null;
|
||||
|
||||
Pattern [] pattern = new Pattern[] {
|
||||
// The "Current thread" line should show a hazard ptr:
|
||||
Pattern.compile("Current thread .* _threads_hazard_ptr=0x.*"),
|
||||
// We should have a section of Threads class SMR info:
|
||||
Pattern.compile("Threads class SMR info:"),
|
||||
// The current thread (marked with '=>') in the threads list
|
||||
// should show a hazard ptr:
|
||||
Pattern.compile("=>.* JavaThread \"main\" .*_threads_hazard_ptr=0x.*"),
|
||||
};
|
||||
int currentPattern = 0;
|
||||
|
||||
String lastLine = null;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (currentPattern < pattern.length) {
|
||||
if (pattern[currentPattern].matcher(line).matches()) {
|
||||
System.out.println("Found: " + line + ".");
|
||||
currentPattern++;
|
||||
}
|
||||
}
|
||||
lastLine = line;
|
||||
}
|
||||
br.close();
|
||||
|
||||
if (currentPattern < pattern.length) {
|
||||
throw new RuntimeException("hs_err_pid file incomplete (first missing pattern: " + currentPattern + ")");
|
||||
}
|
||||
|
||||
if (!lastLine.equals("END.")) {
|
||||
throw new RuntimeException("hs-err file incomplete (missing END marker.)");
|
||||
} else {
|
||||
System.out.println("End marker found.");
|
||||
}
|
||||
|
||||
System.out.println("Done scanning hs_err_pid_file.");
|
||||
System.out.println("PASSED.");
|
||||
}
|
||||
}
|
111
test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java
Normal file
111
test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
106
test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java
Normal file
106
test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
107
test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java
Normal file
107
test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
107
test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java
Normal file
107
test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
107
test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java
Normal file
107
test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
116
test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java
Normal file
116
test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
119
test/hotspot/jtreg/runtime/Thread/StopAtExit.java
Normal file
119
test/hotspot/jtreg/runtime/Thread/StopAtExit.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
109
test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java
Normal file
109
test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
93
test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java
Normal file
93
test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -42,22 +42,18 @@ public class HandshakeWalkExitTest implements Runnable {
|
||||
}
|
||||
|
||||
static volatile boolean exit_now = false;
|
||||
static Thread[] threads;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
int testRuns = 100;
|
||||
int testThreads = 500;
|
||||
int testRuns = 20;
|
||||
int testThreads = 128;
|
||||
|
||||
HandshakeWalkExitTest test = new HandshakeWalkExitTest();
|
||||
|
||||
threads = new Thread[64];
|
||||
|
||||
Runnable hser = new Runnable(){
|
||||
public void run(){
|
||||
WhiteBox wb = WhiteBox.getWhiteBox();
|
||||
while(!exit_now) {
|
||||
wb.handshakeWalkStack(null, true);
|
||||
try { Thread.sleep(1); } catch(Exception e) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test HandshakeWalkOneExitTest
|
||||
* @summary This test tries to stress the handshakes with new and exiting threads
|
||||
* @library /testlibrary /test/lib
|
||||
* @build HandshakeWalkOneExitTest
|
||||
* @run main ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI HandshakeWalkOneExitTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import sun.hotspot.WhiteBox;
|
||||
|
||||
public class HandshakeWalkOneExitTest implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
}
|
||||
|
||||
static volatile boolean exit_now = false;
|
||||
static Thread[] threads;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
int testRuns = 20;
|
||||
int testThreads = 128;
|
||||
|
||||
HandshakeWalkOneExitTest test = new HandshakeWalkOneExitTest();
|
||||
|
||||
Runnable hser = new Runnable(){
|
||||
public void run(){
|
||||
WhiteBox wb = WhiteBox.getWhiteBox();
|
||||
while(!exit_now) {
|
||||
Thread[] t = threads;
|
||||
for (int i = 0; i<t.length ; i++) {
|
||||
wb.handshakeWalkStack(t[i], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Thread hst = new Thread(hser);
|
||||
for (int k = 0; k<testRuns ; k++) {
|
||||
threads = new Thread[testThreads];
|
||||
for (int i = 0; i<threads.length ; i++) {
|
||||
threads[i] = new Thread(test);
|
||||
threads[i].start();
|
||||
}
|
||||
if (k == 0) {
|
||||
hst.start();
|
||||
}
|
||||
}
|
||||
exit_now = true;
|
||||
hst.join();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user