< prev index next >
src/hotspot/share/prims/jvm.cpp
Print this page
rev 47287 : Port 09.17.Thread_SMR_logging_update from JDK9 to JDK10
rev 47289 : eosterlund, stefank CR - refactor code into threadSMR.cpp and threadSMR.hpp
*** 64,73 ****
--- 64,74 ----
#include "runtime/orderAccess.inline.hpp"
#include "runtime/os.inline.hpp"
#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"
#include "services/attachListener.hpp"
#include "services/management.hpp"
*** 2753,2772 ****
} // Extern C
// 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.
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
--- 2754,2769 ----
} // Extern C
// java.lang.Thread //////////////////////////////////////////////////////////////////////////////
! // 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);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
*** 2837,2847 ****
assert(native_thread != NULL, "Starting null thread?");
if (native_thread->osthread() == NULL) {
// No one should hold a reference to the 'native_thread'.
! delete native_thread;
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
os::native_thread_creation_failed_msg());
}
--- 2834,2844 ----
assert(native_thread != NULL, "Starting null thread?");
if (native_thread->osthread() == NULL) {
// No one should hold a reference to the '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,
os::native_thread_creation_failed_msg());
}
*** 2851,2895 ****
Thread::start(native_thread);
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.
JVM_ENTRY(void, JVM_StopThread(JNIEnv* env, jobject jthread, jobject throwable))
JVMWrapper("JVM_StopThread");
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(),
"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()) {
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));
}
! }
! 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
java_lang_Thread::set_stillborn(java_thread);
}
JVM_END
--- 2848,2896 ----
Thread::start(native_thread);
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 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 = 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));
!
! 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 {
! // Use a VM_Operation to throw the exception.
! Thread::send_async_exception(java_thread, java_throwable);
}
! } 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 effect.
java_lang_Thread::set_stillborn(java_thread);
}
JVM_END
*** 2901,2916 ****
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)
!
{
MutexLockerEx ml(receiver->SR_lock(), Mutex::_no_safepoint_check_flag);
if (receiver->is_external_suspend()) {
// Don't allow nested external suspend requests. We can't return
// an error from this interface so just ignore the problem.
--- 2902,2917 ----
JVM_END
JVM_ENTRY(void, JVM_SuspendThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_SuspendThread");
! 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()) {
// Don't allow nested external suspend requests. We can't return
// an error from this interface so just ignore the problem.
*** 2938,2971 ****
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();
}
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);
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);
}
JVM_END
JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass))
JVMWrapper("JVM_Yield");
--- 2939,2991 ----
JVM_END
JVM_ENTRY(void, JVM_ResumeThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_ResumeThread");
!
! 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");
!
! 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);
!
! 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
JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass))
JVMWrapper("JVM_Yield");
*** 3032,3119 ****
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;
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 (!vfst.method()->is_native()) count++;
}
! }
! }
!
! 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);
}
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;
} else {
! return (jboolean) Thread::is_interrupted(thr, clear_interrupted != 0);
}
JVM_END
// Return true iff the current thread has locked the object passed in
--- 3052,3109 ----
JVM_ENTRY(jint, JVM_CountStackFrames(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_CountStackFrames");
! 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;
! 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.
+
return count;
JVM_END
JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_Interrupt");
! 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
JVM_QUICK_ENTRY(jboolean, JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clear_interrupted))
JVMWrapper("JVM_IsInterrupted");
! ThreadsListHandle tlh(thread);
! JavaThread* receiver = NULL;
! bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
! if (is_alive) {
! // jthread refers to a live JavaThread.
! return (jboolean) Thread::is_interrupted(receiver, clear_interrupted != 0);
} else {
! return JNI_FALSE;
}
JVM_END
// Return true iff the current thread has locked the object passed in
*** 3138,3155 ****
}
JVM_END
JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name))
JVMWrapper("JVM_SetNativeThreadName");
! ResourceMark rm(THREAD);
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()) {
// we don't set the name of an attached thread to avoid stepping
! // on other programs
const char *thread_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name));
os::set_native_thread_name(thread_name);
}
JVM_END
--- 3128,3147 ----
}
JVM_END
JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name))
JVMWrapper("JVM_SetNativeThreadName");
!
! // 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);
! 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.
! 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);
}
JVM_END
*** 3705,3714 ****
--- 3697,3708 ----
oop thread_obj = ah->obj_at(i);
instanceHandle h(THREAD, (instanceOop) thread_obj);
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());
JVM_END
< prev index next >