8229516: Thread.isInterrupted() always returns false after thread termination
Reviewed-by: dnsimon, sspitsyn, dcubed, alanb
--- a/make/hotspot/symbols/symbols-unix Sun Nov 03 14:07:43 2019 +0000
+++ b/make/hotspot/symbols/symbols-unix Sun Nov 03 18:02:29 2019 -0500
@@ -142,7 +142,6 @@
JVM_IsArrayClass
JVM_IsConstructorIx
JVM_IsInterface
-JVM_IsInterrupted
JVM_IsPrimitiveClass
JVM_IsSameClassPackage
JVM_IsSupportedJNIVersion
--- a/src/hotspot/os/windows/osThread_windows.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/os/windows/osThread_windows.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -39,28 +39,15 @@
}
}
-// We need to specialize these to interact with the _interrupt_event.
-
-volatile bool OSThread::interrupted() {
- return _interrupted != 0 &&
- (WaitForSingleObject(_interrupt_event, 0) == WAIT_OBJECT_0);
-}
+// We need to specialize this to interact with the _interrupt_event.
void OSThread::set_interrupted(bool z) {
if (z) {
- _interrupted = 1;
- // More than one thread can get here with the same value of osthread,
- // resulting in multiple notifications. We do, however, want the store
- // to interrupted() to be visible to other threads before we post
- // the interrupt event.
- OrderAccess::release();
SetEvent(_interrupt_event);
}
else {
// We should only ever clear the interrupt if we are in fact interrupted,
// and this can only be done by the current thread on itself.
- assert(_interrupted == 1, "invariant for clearing interrupt state");
- _interrupted = 0;
ResetEvent(_interrupt_event);
}
}
--- a/src/hotspot/os/windows/osThread_windows.hpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/os/windows/osThread_windows.hpp Sun Nov 03 18:02:29 2019 -0500
@@ -43,10 +43,7 @@
void set_thread_handle(HANDLE handle) { _thread_handle = handle; }
HANDLE interrupt_event() const { return _interrupt_event; }
void set_interrupt_event(HANDLE interrupt_event) { _interrupt_event = interrupt_event; }
- // These are specialized on Windows to interact with the _interrupt_event.
- // Also note that Windows does not skip these calls if we are interrupted - see
- // LibraryCallKit::inline_native_isInterrupted
- volatile bool interrupted();
+ // This is specialized on Windows to interact with the _interrupt_event.
void set_interrupted(bool z);
#ifndef PRODUCT
--- a/src/hotspot/share/aot/aotCodeHeap.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/aot/aotCodeHeap.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -450,7 +450,6 @@
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_write_barrier_post", address, JVMCIRuntime::write_barrier_post);
#endif
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_identity_hash_code", address, JVMCIRuntime::identity_hash_code);
- SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_thread_is_interrupted", address, JVMCIRuntime::thread_is_interrupted);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_exception_handler_for_pc", address, JVMCIRuntime::exception_handler_for_pc);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_test_deoptimize_call_int", address, JVMCIRuntime::test_deoptimize_call_int);
SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_throw_and_post_jvmti_exception", address, JVMCIRuntime::throw_and_post_jvmti_exception);
--- a/src/hotspot/share/classfile/javaClasses.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/classfile/javaClasses.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -56,6 +56,7 @@
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/handles.inline.hpp"
+#include "runtime/init.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/javaCalls.hpp"
@@ -1634,6 +1635,7 @@
int java_lang_Thread::_inheritedAccessControlContext_offset = 0;
int java_lang_Thread::_priority_offset = 0;
int java_lang_Thread::_eetop_offset = 0;
+int java_lang_Thread::_interrupted_offset = 0;
int java_lang_Thread::_daemon_offset = 0;
int java_lang_Thread::_stillborn_offset = 0;
int java_lang_Thread::_stackSize_offset = 0;
@@ -1649,6 +1651,7 @@
macro(_priority_offset, k, vmSymbols::priority_name(), int_signature, false); \
macro(_daemon_offset, k, vmSymbols::daemon_name(), bool_signature, false); \
macro(_eetop_offset, k, "eetop", long_signature, false); \
+ macro(_interrupted_offset, k, "interrupted", bool_signature, false); \
macro(_stillborn_offset, k, "stillborn", bool_signature, false); \
macro(_stackSize_offset, k, "stackSize", long_signature, false); \
macro(_tid_offset, k, "tid", long_signature, false); \
@@ -1677,6 +1680,21 @@
java_thread->address_field_put(_eetop_offset, (address)thread);
}
+bool java_lang_Thread::interrupted(oop java_thread) {
+#if INCLUDE_JFR
+ if (java_thread == NULL) {
+ // can happen from Jfr::on_vm_init leading to call of JavaThread::sleep
+ assert(!is_init_completed(), "should only happen during init");
+ return false;
+ }
+#endif
+ return java_thread->bool_field_volatile(_interrupted_offset);
+}
+
+void java_lang_Thread::set_interrupted(oop java_thread, bool val) {
+ java_thread->bool_field_put_volatile(_interrupted_offset, val);
+}
+
oop java_lang_Thread::name(oop java_thread) {
return java_thread->obj_field(_name_offset);
--- a/src/hotspot/share/classfile/javaClasses.hpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/classfile/javaClasses.hpp Sun Nov 03 18:02:29 2019 -0500
@@ -373,6 +373,7 @@
static int _inheritedAccessControlContext_offset;
static int _priority_offset;
static int _eetop_offset;
+ static int _interrupted_offset;
static int _daemon_offset;
static int _stillborn_offset;
static int _stackSize_offset;
@@ -391,6 +392,9 @@
static JavaThread* thread(oop java_thread);
// Set JavaThread for instance
static void set_thread(oop java_thread, JavaThread* thread);
+ // Interrupted status
+ static bool interrupted(oop java_thread);
+ static void set_interrupted(oop java_thread, bool val);
// Name
static oop name(oop java_thread);
static void set_name(oop java_thread, oop name);
--- a/src/hotspot/share/classfile/vmSymbols.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/classfile/vmSymbols.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -568,7 +568,6 @@
if (!InlineClassNatives) return true;
break;
case vmIntrinsics::_currentThread:
- case vmIntrinsics::_isInterrupted:
if (!InlineThreadNatives) return true;
break;
case vmIntrinsics::_floatToRawIntBits:
--- a/src/hotspot/share/classfile/vmSymbols.hpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/classfile/vmSymbols.hpp Sun Nov 03 18:02:29 2019 -0500
@@ -862,9 +862,6 @@
do_intrinsic(_arraycopy, java_lang_System, arraycopy_name, arraycopy_signature, F_S) \
do_name( arraycopy_name, "arraycopy") \
do_signature(arraycopy_signature, "(Ljava/lang/Object;ILjava/lang/Object;II)V") \
- do_intrinsic(_isInterrupted, java_lang_Thread, isInterrupted_name, isInterrupted_signature, F_R) \
- do_name( isInterrupted_name, "isInterrupted") \
- do_signature(isInterrupted_signature, "(Z)Z") \
do_intrinsic(_currentThread, java_lang_Thread, currentThread_name, currentThread_signature, F_S) \
do_name( currentThread_name, "currentThread") \
do_signature(currentThread_signature, "()Ljava/lang/Thread;") \
--- a/src/hotspot/share/include/jvm.h Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/include/jvm.h Sun Nov 03 18:02:29 2019 -0500
@@ -252,9 +252,6 @@
JVM_Interrupt(JNIEnv *env, jobject thread);
JNIEXPORT jboolean JNICALL
-JVM_IsInterrupted(JNIEnv *env, jobject thread, jboolean clearInterrupted);
-
-JNIEXPORT jboolean JNICALL
JVM_HoldsLock(JNIEnv *env, jclass threadClass, jobject obj);
JNIEXPORT void JNICALL
--- a/src/hotspot/share/jvmci/jvmciRuntime.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -620,21 +620,6 @@
return (jint) obj->identity_hash();
JRT_END
-JRT_ENTRY(jboolean, JVMCIRuntime::thread_is_interrupted(JavaThread* thread, oopDesc* receiver, jboolean clear_interrupted))
- Handle receiverHandle(thread, receiver);
- // 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 || (EnableThreadSMRExtraValidityChecks && !tlh.includes(receiverThread))) {
- // The other thread may exit during this process, which is ok so return false.
- return JNI_FALSE;
- } else {
- return (jint) receiverThread->is_interrupted(clear_interrupted != 0);
- }
-JRT_END
-
JRT_ENTRY(jint, JVMCIRuntime::test_deoptimize_call_int(JavaThread* thread, int value))
deopt_caller();
return (jint) value;
--- a/src/hotspot/share/jvmci/jvmciRuntime.hpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp Sun Nov 03 18:02:29 2019 -0500
@@ -309,7 +309,6 @@
static void dynamic_new_array_or_null(JavaThread* thread, oopDesc* element_mirror, jint length) { dynamic_new_array_common(thread, element_mirror, length, true); }
static void dynamic_new_instance_or_null(JavaThread* thread, oopDesc* type_mirror) { dynamic_new_instance_common(thread, type_mirror, true); }
- static jboolean thread_is_interrupted(JavaThread* thread, oopDesc* obj, jboolean clear_interrupted);
static void vm_message(jboolean vmError, jlong format, jlong v1, jlong v2, jlong v3);
static jint identity_hash_code(JavaThread* thread, oopDesc* obj);
static address exception_handler_for_pc(JavaThread* thread);
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -263,8 +263,6 @@
\
static_field(os, _polling_page, address) \
\
- volatile_nonstatic_field(OSThread, _interrupted, jint) \
- \
static_field(StubRoutines, _verify_oop_count, jint) \
\
static_field(StubRoutines, _throw_delayed_StackOverflowError_entry, address) \
@@ -641,7 +639,6 @@
declare_function(JVMCIRuntime::dynamic_new_array_or_null) \
declare_function(JVMCIRuntime::dynamic_new_instance_or_null) \
\
- declare_function(JVMCIRuntime::thread_is_interrupted) \
declare_function(JVMCIRuntime::vm_message) \
declare_function(JVMCIRuntime::identity_hash_code) \
declare_function(JVMCIRuntime::exception_handler_for_pc) \
--- a/src/hotspot/share/oops/oop.hpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/oops/oop.hpp Sun Nov 03 18:02:29 2019 -0500
@@ -173,6 +173,8 @@
jboolean bool_field(int offset) const;
void bool_field_put(int offset, jboolean contents);
+ jboolean bool_field_volatile(int offset) const;
+ void bool_field_put_volatile(int offset, jboolean contents);
jint int_field(int offset) const;
jint int_field_raw(int offset) const;
--- a/src/hotspot/share/oops/oop.inline.hpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/oops/oop.inline.hpp Sun Nov 03 18:02:29 2019 -0500
@@ -301,9 +301,10 @@
inline jchar oopDesc::char_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); }
inline void oopDesc::char_field_put(int offset, jchar value) { HeapAccess<>::store_at(as_oop(), offset, value); }
-inline jboolean oopDesc::bool_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); }
+inline jboolean oopDesc::bool_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); }
inline void oopDesc::bool_field_put(int offset, jboolean value) { HeapAccess<>::store_at(as_oop(), offset, jboolean(value & 1)); }
-
+inline jboolean oopDesc::bool_field_volatile(int offset) const { return HeapAccess<MO_SEQ_CST>::load_at(as_oop(), offset); }
+inline void oopDesc::bool_field_put_volatile(int offset, jboolean value) { HeapAccess<MO_SEQ_CST>::store_at(as_oop(), offset, jboolean(value & 1)); }
inline jshort oopDesc::short_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); }
inline void oopDesc::short_field_put(int offset, jshort value) { HeapAccess<>::store_at(as_oop(), offset, value); }
--- a/src/hotspot/share/opto/c2compiler.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/opto/c2compiler.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -582,7 +582,6 @@
case vmIntrinsics::_storeFence:
case vmIntrinsics::_fullFence:
case vmIntrinsics::_currentThread:
- case vmIntrinsics::_isInterrupted:
#ifdef JFR_HAVE_INTRINSICS
case vmIntrinsics::_counterTime:
case vmIntrinsics::_getClassId:
--- a/src/hotspot/share/opto/library_call.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/opto/library_call.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -264,7 +264,6 @@
bool inline_native_classID();
bool inline_native_getEventWriter();
#endif
- bool inline_native_isInterrupted();
bool inline_native_Class_query(vmIntrinsics::ID id);
bool inline_native_subtype_check();
bool inline_native_getLength();
@@ -752,7 +751,6 @@
case vmIntrinsics::_onSpinWait: return inline_onspinwait();
case vmIntrinsics::_currentThread: return inline_native_currentThread();
- case vmIntrinsics::_isInterrupted: return inline_native_isInterrupted();
#ifdef JFR_HAVE_INTRINSICS
case vmIntrinsics::_counterTime: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, JFR_TIME_FUNCTION), "counterTime");
@@ -3041,128 +3039,6 @@
return true;
}
-//------------------------inline_native_isInterrupted------------------
-// private native boolean java.lang.Thread.isInterrupted(boolean ClearInterrupted);
-bool LibraryCallKit::inline_native_isInterrupted() {
- // Add a fast path to t.isInterrupted(clear_int):
- // (t == Thread.current() &&
- // (!TLS._osthread._interrupted || WINDOWS_ONLY(false) NOT_WINDOWS(!clear_int)))
- // ? TLS._osthread._interrupted : /*slow path:*/ t.isInterrupted(clear_int)
- // So, in the common case that the interrupt bit is false,
- // we avoid making a call into the VM. Even if the interrupt bit
- // is true, if the clear_int argument is false, we avoid the VM call.
- // However, if the receiver is not currentThread, we must call the VM,
- // because there must be some locking done around the operation.
-
- // We only go to the fast case code if we pass two guards.
- // Paths which do not pass are accumulated in the slow_region.
-
- enum {
- no_int_result_path = 1, // t == Thread.current() && !TLS._osthread._interrupted
- no_clear_result_path = 2, // t == Thread.current() && TLS._osthread._interrupted && !clear_int
- slow_result_path = 3, // slow path: t.isInterrupted(clear_int)
- PATH_LIMIT
- };
-
- // Ensure that it's not possible to move the load of TLS._osthread._interrupted flag
- // out of the function.
- insert_mem_bar(Op_MemBarCPUOrder);
-
- RegionNode* result_rgn = new RegionNode(PATH_LIMIT);
- PhiNode* result_val = new PhiNode(result_rgn, TypeInt::BOOL);
-
- RegionNode* slow_region = new RegionNode(1);
- record_for_igvn(slow_region);
-
- // (a) Receiving thread must be the current thread.
- Node* rec_thr = argument(0);
- Node* tls_ptr = NULL;
- Node* cur_thr = generate_current_thread(tls_ptr);
-
- // Resolve oops to stable for CmpP below.
- cur_thr = access_resolve(cur_thr, 0);
- rec_thr = access_resolve(rec_thr, 0);
-
- Node* cmp_thr = _gvn.transform(new CmpPNode(cur_thr, rec_thr));
- Node* bol_thr = _gvn.transform(new BoolNode(cmp_thr, BoolTest::ne));
-
- generate_slow_guard(bol_thr, slow_region);
-
- // (b) Interrupt bit on TLS must be false.
- Node* p = basic_plus_adr(top()/*!oop*/, tls_ptr, in_bytes(JavaThread::osthread_offset()));
- Node* osthread = make_load(NULL, p, TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered);
- p = basic_plus_adr(top()/*!oop*/, osthread, in_bytes(OSThread::interrupted_offset()));
-
- // Set the control input on the field _interrupted read to prevent it floating up.
- Node* int_bit = make_load(control(), p, TypeInt::BOOL, T_INT, MemNode::unordered);
- Node* cmp_bit = _gvn.transform(new CmpINode(int_bit, intcon(0)));
- Node* bol_bit = _gvn.transform(new BoolNode(cmp_bit, BoolTest::ne));
-
- IfNode* iff_bit = create_and_map_if(control(), bol_bit, PROB_UNLIKELY_MAG(3), COUNT_UNKNOWN);
-
- // First fast path: if (!TLS._interrupted) return false;
- Node* false_bit = _gvn.transform(new IfFalseNode(iff_bit));
- result_rgn->init_req(no_int_result_path, false_bit);
- result_val->init_req(no_int_result_path, intcon(0));
-
- // drop through to next case
- set_control( _gvn.transform(new IfTrueNode(iff_bit)));
-
-#ifndef _WINDOWS
- // (c) Or, if interrupt bit is set and clear_int is false, use 2nd fast path.
- Node* clr_arg = argument(1);
- Node* cmp_arg = _gvn.transform(new CmpINode(clr_arg, intcon(0)));
- Node* bol_arg = _gvn.transform(new BoolNode(cmp_arg, BoolTest::ne));
- IfNode* iff_arg = create_and_map_if(control(), bol_arg, PROB_FAIR, COUNT_UNKNOWN);
-
- // Second fast path: ... else if (!clear_int) return true;
- Node* false_arg = _gvn.transform(new IfFalseNode(iff_arg));
- result_rgn->init_req(no_clear_result_path, false_arg);
- result_val->init_req(no_clear_result_path, intcon(1));
-
- // drop through to next case
- set_control( _gvn.transform(new IfTrueNode(iff_arg)));
-#else
- // To return true on Windows you must read the _interrupted field
- // and check the event state i.e. take the slow path.
-#endif // _WINDOWS
-
- // (d) Otherwise, go to the slow path.
- slow_region->add_req(control());
- set_control( _gvn.transform(slow_region));
-
- if (stopped()) {
- // There is no slow path.
- result_rgn->init_req(slow_result_path, top());
- result_val->init_req(slow_result_path, top());
- } else {
- // non-virtual because it is a private non-static
- CallJavaNode* slow_call = generate_method_call(vmIntrinsics::_isInterrupted);
-
- Node* slow_val = set_results_for_java_call(slow_call);
- // this->control() comes from set_results_for_java_call
-
- Node* fast_io = slow_call->in(TypeFunc::I_O);
- Node* fast_mem = slow_call->in(TypeFunc::Memory);
-
- // These two phis are pre-filled with copies of of the fast IO and Memory
- PhiNode* result_mem = PhiNode::make(result_rgn, fast_mem, Type::MEMORY, TypePtr::BOTTOM);
- PhiNode* result_io = PhiNode::make(result_rgn, fast_io, Type::ABIO);
-
- result_rgn->init_req(slow_result_path, control());
- result_io ->init_req(slow_result_path, i_o());
- result_mem->init_req(slow_result_path, reset_memory());
- result_val->init_req(slow_result_path, slow_val);
-
- set_all_memory(_gvn.transform(result_mem));
- set_i_o( _gvn.transform(result_io));
- }
-
- C->set_has_split_ifs(true); // Has chance for split-if optimization
- set_result(result_rgn, result_val);
- return true;
-}
-
//---------------------------load_mirror_from_klass----------------------------
// Given a klass oop, load its java mirror (a java.lang.Class oop).
Node* LibraryCallKit::load_mirror_from_klass(Node* klass) {
--- a/src/hotspot/share/prims/jvm.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/prims/jvm.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -3065,21 +3065,6 @@
JVM_END
-JVM_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) receiver->is_interrupted(clear_interrupted != 0);
- } else {
- return JNI_FALSE;
- }
-JVM_END
-
-
// Return true iff the current thread has locked the object passed in
JVM_ENTRY(jboolean, JVM_HoldsLock(JNIEnv* env, jclass threadClass, jobject obj))
--- a/src/hotspot/share/prims/jvmtiEnv.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -879,8 +879,7 @@
if (jts == _thread_in_native) {
state |= JVMTI_THREAD_STATE_IN_NATIVE;
}
- OSThread* osThread = java_thread->osthread();
- if (osThread != NULL && osThread->interrupted()) {
+ if (java_thread->is_interrupted(false)) {
state |= JVMTI_THREAD_STATE_INTERRUPTED;
}
}
@@ -1092,7 +1091,6 @@
// thread - NOT pre-checked
jvmtiError
JvmtiEnv::InterruptThread(jthread thread) {
- // TODO: this is very similar to JVM_Interrupt(); share code in future
JavaThread* current_thread = JavaThread::current();
JavaThread* java_thread = NULL;
ThreadsListHandle tlh(current_thread);
@@ -1100,7 +1098,11 @@
if (err != JVMTI_ERROR_NONE) {
return err;
}
-
+ // Really this should be a Java call to Thread.interrupt to ensure the same
+ // semantics, however historically this has not been done for some reason.
+ // So we continue with that (which means we don't interact with any Java-level
+ // Interruptible object) but we must set the Java-level interrupted state.
+ java_lang_Thread::set_interrupted(JNIHandles::resolve(thread), true);
java_thread->interrupt();
return JVMTI_ERROR_NONE;
--- a/src/hotspot/share/prims/jvmtiEnvBase.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -1214,8 +1214,7 @@
if (jts == _thread_in_native) {
state |= JVMTI_THREAD_STATE_IN_NATIVE;
}
- OSThread* osThread = thr->osthread();
- if (osThread != NULL && osThread->interrupted()) {
+ if (thr->is_interrupted(false)) {
state |= JVMTI_THREAD_STATE_INTERRUPTED;
}
}
--- a/src/hotspot/share/runtime/osThread.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/runtime/osThread.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -30,7 +30,6 @@
pd_initialize();
set_start_proc(start_proc);
set_start_parm(start_parm);
- _interrupted = 0;
}
OSThread::~OSThread() {
--- a/src/hotspot/share/runtime/osThread.hpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/runtime/osThread.hpp Sun Nov 03 18:02:29 2019 -0500
@@ -62,12 +62,6 @@
OSThreadStartFunc _start_proc; // Thread start routine
void* _start_parm; // Thread start routine parameter
volatile ThreadState _state; // Thread state *hint*
- volatile jint _interrupted; // Thread.isInterrupted state
-
- // Note: _interrupted must be jint, so that Java intrinsics can access it.
- // The value stored there must be either 0 or 1. It must be possible
- // for Java to emulate Thread.currentThread().isInterrupted() by performing
- // the double indirection Thread::current()->_osthread->_interrupted.
// Methods
public:
@@ -82,18 +76,14 @@
void set_start_proc(OSThreadStartFunc start_proc) { _start_proc = start_proc; }
void* start_parm() const { return _start_parm; }
void set_start_parm(void* start_parm) { _start_parm = start_parm; }
- // These are specialized on Windows.
+ // This is specialized on Windows.
#ifndef _WINDOWS
- volatile bool interrupted() const { return _interrupted != 0; }
- void set_interrupted(bool z) { _interrupted = z ? 1 : 0; }
+ void set_interrupted(bool z) { /* nothing to do */ }
#endif
// Printing
void print_on(outputStream* st) const;
void print() const;
- // For java intrinsics:
- static ByteSize interrupted_offset() { return byte_offset_of(OSThread, _interrupted); }
-
// Platform dependent stuff
#include OS_HEADER(osThread)
--- a/src/hotspot/share/runtime/thread.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/runtime/thread.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -1725,19 +1725,13 @@
void JavaThread::interrupt() {
debug_only(check_for_dangling_thread_pointer(this);)
- if (!osthread()->interrupted()) {
- osthread()->set_interrupted(true);
- // More than one thread can get here with the same value of osthread,
- // resulting in multiple notifications. We do, however, want the store
- // to interrupted() to be visible to other threads before we execute unpark().
- OrderAccess::fence();
-
- // For JavaThread::sleep. Historically we only unpark if changing to the interrupted
- // state, in contrast to the other events below. Not clear exactly why.
- _SleepEvent->unpark();
- }
-
- // For JSR166. Unpark even if interrupt status already was set.
+ // For Windows _interrupt_event
+ osthread()->set_interrupted(true);
+
+ // For Thread.sleep
+ _SleepEvent->unpark();
+
+ // For JSR166 LockSupport.park
parker()->unpark();
// For ObjectMonitor and JvmtiRawMonitor
@@ -1747,11 +1741,11 @@
bool JavaThread::is_interrupted(bool clear_interrupted) {
debug_only(check_for_dangling_thread_pointer(this);)
- bool interrupted = osthread()->interrupted();
+ bool interrupted = java_lang_Thread::interrupted(threadObj());
// NOTE that since there is no "lock" around the interrupt and
// is_interrupted operations, there is the possibility that the
- // interrupted flag (in osThread) will be "false" but that the
+ // interrupted flag will be "false" but that the
// low-level events will be in the signaled state. This is
// intentional. The effect of this is that Object.wait() and
// LockSupport.park() will appear to have a spurious wakeup, which
@@ -1761,9 +1755,12 @@
// to JavaThread::sleep, so there is no early return. It has also been
// recommended not to put the interrupted flag into the "event"
// structure because it hides the issue.
+ // Also, because there is no lock, we must only clear the interrupt
+ // state if we are going to report that we were interrupted; otherwise
+ // an interrupt that happens just after we read the field would be lost.
if (interrupted && clear_interrupted) {
+ java_lang_Thread::set_interrupted(threadObj(), false);
osthread()->set_interrupted(false);
- // consider thread->_SleepEvent->reset() ... optional optimization
}
return interrupted;
@@ -2417,6 +2414,7 @@
// Interrupt thread so it will wake up from a potential wait()/sleep()/park()
+ java_lang_Thread::set_interrupted(threadObj(), true);
this->interrupt();
}
--- a/src/hotspot/share/runtime/vmStructs.cpp Sun Nov 03 14:07:43 2019 +0000
+++ b/src/hotspot/share/runtime/vmStructs.cpp Sun Nov 03 18:02:29 2019 -0500
@@ -786,7 +786,6 @@
/* OSThread */ \
/************/ \
\
- volatile_nonstatic_field(OSThread, _interrupted, jint) \
volatile_nonstatic_field(OSThread, _state, ThreadState) \
\
/************************/ \
--- a/src/java.base/share/classes/java/lang/Thread.java Sun Nov 03 14:07:43 2019 +0000
+++ b/src/java.base/share/classes/java/lang/Thread.java Sun Nov 03 18:02:29 2019 -0500
@@ -154,6 +154,9 @@
/* Whether or not the thread is a daemon thread. */
private boolean daemon = false;
+ /* Interrupt state of the thread - read/written directly by JVM */
+ private volatile boolean interrupted;
+
/* Fields reserved for exclusive use by the JVM */
private boolean stillborn = false;
private long eetop;
@@ -971,10 +974,14 @@
*
* <p> Interrupting a thread that is not alive need not have any effect.
*
+ * @implNote In the JDK Reference Implementation, interruption of a thread
+ * that is not alive still records that the interrupt request was made and
+ * will report it via {@link #interrupted} and {@link #isInterrupted()}.
+ *
* @throws SecurityException
* if the current thread cannot modify this thread
*
- * @revised 6.0
+ * @revised 6.0, 14
* @spec JSR-51
*/
public void interrupt() {
@@ -985,14 +992,15 @@
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
- interrupt0(); // set interrupt status
+ interrupted = true;
+ interrupt0(); // inform VM of interrupt
b.interrupt(this);
return;
}
}
}
-
- // set interrupt status
+ interrupted = true;
+ // inform VM of interrupt
interrupt0();
}
@@ -1004,45 +1012,38 @@
* interrupted again, after the first call had cleared its interrupted
* status and before the second call had examined it).
*
- * <p>A thread interruption ignored because a thread was not alive
- * at the time of the interrupt will be reflected by this method
- * returning false.
- *
* @return {@code true} if the current thread has been interrupted;
* {@code false} otherwise.
* @see #isInterrupted()
- * @revised 6.0
+ * @revised 6.0, 14
*/
public static boolean interrupted() {
- return currentThread().isInterrupted(true);
+ Thread t = currentThread();
+ boolean interrupted = t.interrupted;
+ // We may have been interrupted the moment after we read the field,
+ // so only clear the field if we saw that it was set and will return
+ // true; otherwise we could lose an interrupt.
+ if (interrupted) {
+ t.interrupted = false;
+ clearInterruptEvent();
+ }
+ return interrupted;
}
/**
* Tests whether this thread has been interrupted. The <i>interrupted
* status</i> of the thread is unaffected by this method.
*
- * <p>A thread interruption ignored because a thread was not alive
- * at the time of the interrupt will be reflected by this method
- * returning false.
- *
* @return {@code true} if this thread has been interrupted;
* {@code false} otherwise.
* @see #interrupted()
- * @revised 6.0
+ * @revised 6.0, 14
*/
public boolean isInterrupted() {
- return isInterrupted(false);
+ return interrupted;
}
/**
- * Tests if some Thread has been interrupted. The interrupted state
- * is reset or not based on the value of ClearInterrupted that is
- * passed.
- */
- @HotSpotIntrinsicCandidate
- private native boolean isInterrupted(boolean ClearInterrupted);
-
- /**
* Tests if this thread is alive. A thread is alive if it has
* been started and has not yet died.
*
@@ -2080,5 +2081,6 @@
private native void suspend0();
private native void resume0();
private native void interrupt0();
+ private static native void clearInterruptEvent();
private native void setNativeName(String name);
}
--- a/src/java.base/share/native/libjava/Thread.c Sun Nov 03 14:07:43 2019 +0000
+++ b/src/java.base/share/native/libjava/Thread.c Sun Nov 03 18:02:29 2019 -0500
@@ -51,7 +51,6 @@
{"sleep", "(J)V", (void *)&JVM_Sleep},
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
- {"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted},
{"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
{"getThreads", "()[" THD, (void *)&JVM_GetAllThreads},
{"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
@@ -68,3 +67,12 @@
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
+
+JNIEXPORT void JNICALL
+Java_java_lang_Thread_clearInterruptEvent(JNIEnv *env, jclass cls)
+{
+#if defined(_WIN32)
+ // Need to reset the interrupt event used by Process.waitFor
+ ResetEvent((HANDLE) JVM_GetThreadInterruptEvent());
+#endif
+}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java Sun Nov 03 14:07:43 2019 +0000
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java Sun Nov 03 18:02:29 2019 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,6 @@
// The OSThread class holds OS-specific thread information. It is equivalent
// to the sys_thread_t structure of the classic JVM implementation.
public class OSThread extends VMObject {
- private static JIntField interruptedField;
private static Field threadIdField;
private static CIntegerField threadStateField;
@@ -56,7 +55,6 @@
private static synchronized void initialize(TypeDataBase db) {
Type type = db.lookupType("OSThread");
- interruptedField = type.getJIntField("_interrupted");
threadIdField = type.getField("_thread_id");
threadStateField = type.getCIntegerField("_state");
@@ -75,10 +73,6 @@
super(addr);
}
- public boolean interrupted() {
- return ((int)interruptedField.getValue(addr)) != 0;
- }
-
public int threadId() {
return threadIdField.getJInt(addr);
}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotMethodSubstitutionTest.java Sun Nov 03 14:07:43 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotMethodSubstitutionTest.java Sun Nov 03 18:02:29 2019 -0500
@@ -28,12 +28,13 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
-import org.graalvm.compiler.nodes.IfNode;
-import org.junit.Test;
-
import org.graalvm.compiler.api.directives.GraalDirectives;
import org.graalvm.compiler.api.replacements.MethodSubstitution;
+import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
+import org.graalvm.compiler.hotspot.HotSpotBackend;
+import org.graalvm.compiler.nodes.IfNode;
import org.graalvm.compiler.replacements.test.MethodSubstitutionTest;
+import org.junit.Test;
/**
* Tests HotSpot specific {@link MethodSubstitution}s.
@@ -133,13 +134,18 @@
@Test
public void testThreadSubstitutions() {
+ GraalHotSpotVMConfig config = ((HotSpotBackend) getBackend()).getRuntime().getVMConfig();
testGraph("currentThread");
- assertInGraph(testGraph("threadIsInterrupted", "isInterrupted", true), IfNode.class);
- assertInGraph(testGraph("threadInterrupted", "isInterrupted", true), IfNode.class);
+ if (config.osThreadInterruptedOffset != Integer.MAX_VALUE) {
+ assertInGraph(testGraph("threadIsInterrupted", "isInterrupted", true), IfNode.class);
+ assertInGraph(testGraph("threadInterrupted", "isInterrupted", true), IfNode.class);
+ }
Thread currentThread = Thread.currentThread();
test("currentThread", currentThread);
- test("threadIsInterrupted", currentThread);
+ if (config.osThreadInterruptedOffset != Integer.MAX_VALUE) {
+ test("threadIsInterrupted", currentThread);
+ }
}
@SuppressWarnings("all")
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java Sun Nov 03 14:07:43 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java Sun Nov 03 18:02:29 2019 -0500
@@ -315,7 +315,7 @@
public final int javaThreadAnchorOffset = getFieldOffset("JavaThread::_anchor", Integer.class, "JavaFrameAnchor");
public final int javaThreadShouldPostOnExceptionsFlagOffset = getFieldOffset("JavaThread::_should_post_on_exceptions_flag", Integer.class, "int", Integer.MIN_VALUE);
public final int threadObjectOffset = getFieldOffset("JavaThread::_threadObj", Integer.class, "oop");
- public final int osThreadOffset = getFieldOffset("JavaThread::_osthread", Integer.class, "OSThread*");
+ public final int osThreadOffset = getFieldOffset("JavaThread::_osthread", Integer.class, "OSThread*", Integer.MAX_VALUE);
public final int threadIsMethodHandleReturnOffset = getFieldOffset("JavaThread::_is_method_handle_return", Integer.class, "int");
public final int threadObjectResultOffset = getFieldOffset("JavaThread::_vm_result", Integer.class, "oop");
public final int jvmciCountersThreadOffset = getFieldOffset("JavaThread::_jvmci_counters", Integer.class, "jlong*");
@@ -419,7 +419,7 @@
public final int frameInterpreterFrameSenderSpOffset = getConstant("frame::interpreter_frame_sender_sp_offset", Integer.class, intRequiredOnAMD64);
public final int frameInterpreterFrameLastSpOffset = getConstant("frame::interpreter_frame_last_sp_offset", Integer.class, intRequiredOnAMD64);
- public final int osThreadInterruptedOffset = getFieldOffset("OSThread::_interrupted", Integer.class, "jint");
+ public final int osThreadInterruptedOffset = getFieldOffset("OSThread::_interrupted", Integer.class, "jint", Integer.MAX_VALUE);
public final long markWordHashShift = getConstant(markWordField("hash_shift"), Long.class);
@@ -714,7 +714,6 @@
return true;
}
- public final long threadIsInterruptedAddress = getAddress("JVMCIRuntime::thread_is_interrupted");
public final long vmMessageAddress = getAddress("JVMCIRuntime::vm_message");
public final long identityHashCodeAddress = getAddress("JVMCIRuntime::identity_hash_code");
public final long exceptionHandlerForPcAddress = getAddress("JVMCIRuntime::exception_handler_for_pc");
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java Sun Nov 03 14:07:43 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java Sun Nov 03 18:02:29 2019 -0500
@@ -441,7 +441,10 @@
}
});
- r.registerMethodSubstitution(ThreadSubstitutions.class, "isInterrupted", Receiver.class, boolean.class);
+ if (config.osThreadInterruptedOffset != Integer.MAX_VALUE) {
+ r.registerMethodSubstitution(ThreadSubstitutions.class, "isInterrupted", Receiver.class, boolean.class);
+ }
+
}
public static final String reflectionClass;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java Sun Nov 03 14:07:43 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java Sun Nov 03 18:02:29 2019 -0500
@@ -308,11 +308,13 @@
@Fold
public static int osThreadOffset(@InjectedParameter GraalHotSpotVMConfig config) {
+ assert config.osThreadOffset != Integer.MAX_VALUE;
return config.osThreadOffset;
}
@Fold
public static int osThreadInterruptedOffset(@InjectedParameter GraalHotSpotVMConfig config) {
+ assert config.osThreadInterruptedOffset != Integer.MAX_VALUE;
return config.osThreadInterruptedOffset;
}