8238761: Asynchronous handshakes
Reviewed-by: pchilanomate, dcubed, dholmes, coleenp, sspitsyn
This commit is contained in:
parent
6d19fe65d1
commit
6bddeb709d
src/hotspot/share
memory
prims
jvmtiEnv.cppjvmtiEnvBase.cppjvmtiEnvBase.hppjvmtiEnvThreadState.cppjvmtiEventController.cppjvmtiThreadState.cppwhitebox.cpp
runtime
biasedLocking.cpphandshake.cpphandshake.hppinterfaceSupport.inline.hppmutexLocker.cppsafepointMechanism.cppsafepointMechanism.hppthread.cppthread.hpp
utilities
test
hotspot
gtest/utilities
jtreg/runtime/handshake
lib/sun/hotspot
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2020, 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
|
||||
@ -44,7 +44,7 @@ class Thread;
|
||||
class Closure : public StackObj { };
|
||||
|
||||
// Thread iterator
|
||||
class ThreadClosure: public Closure {
|
||||
class ThreadClosure {
|
||||
public:
|
||||
virtual void do_thread(Thread* thread) = 0;
|
||||
};
|
||||
|
@ -1213,8 +1213,8 @@ JvmtiEnv::GetOwnedMonitorInfo(JavaThread* java_thread, jint* owned_monitor_count
|
||||
} else {
|
||||
// get owned monitors info with handshake
|
||||
GetOwnedMonitorInfoClosure op(calling_thread, this, owned_monitors_list);
|
||||
bool executed = Handshake::execute_direct(&op, java_thread);
|
||||
err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
Handshake::execute(&op, java_thread);
|
||||
err = op.result();
|
||||
}
|
||||
jint owned_monitor_count = owned_monitors_list->length();
|
||||
if (err == JVMTI_ERROR_NONE) {
|
||||
@ -1258,8 +1258,8 @@ JvmtiEnv::GetOwnedMonitorStackDepthInfo(JavaThread* java_thread, jint* monitor_i
|
||||
} else {
|
||||
// get owned monitors info with handshake
|
||||
GetOwnedMonitorInfoClosure op(calling_thread, this, owned_monitors_list);
|
||||
bool executed = Handshake::execute_direct(&op, java_thread);
|
||||
err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
Handshake::execute(&op, java_thread);
|
||||
err = op.result();
|
||||
}
|
||||
|
||||
jint owned_monitor_count = owned_monitors_list->length();
|
||||
@ -1302,8 +1302,8 @@ JvmtiEnv::GetCurrentContendedMonitor(JavaThread* java_thread, jobject* monitor_p
|
||||
} else {
|
||||
// get contended monitor information with handshake
|
||||
GetCurrentContendedMonitorClosure op(calling_thread, this, monitor_ptr);
|
||||
bool executed = Handshake::execute_direct(&op, java_thread);
|
||||
err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
Handshake::execute(&op, java_thread);
|
||||
err = op.result();
|
||||
}
|
||||
return err;
|
||||
} /* end GetCurrentContendedMonitor */
|
||||
@ -1540,8 +1540,8 @@ JvmtiEnv::GetStackTrace(JavaThread* java_thread, jint start_depth, jint max_fram
|
||||
} else {
|
||||
// Get stack trace with handshake.
|
||||
GetStackTraceClosure op(this, start_depth, max_frame_count, frame_buffer, count_ptr);
|
||||
bool executed = Handshake::execute_direct(&op, java_thread);
|
||||
err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
Handshake::execute(&op, java_thread);
|
||||
err = op.result();
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -1585,8 +1585,8 @@ JvmtiEnv::GetThreadListStackTraces(jint thread_count, const jthread* thread_list
|
||||
}
|
||||
|
||||
GetSingleStackTraceClosure op(this, current_thread, *thread_list, max_frame_count);
|
||||
bool executed = Handshake::execute_direct(&op, java_thread);
|
||||
err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
Handshake::execute(&op, java_thread);
|
||||
err = op.result();
|
||||
if (err == JVMTI_ERROR_NONE) {
|
||||
*stack_info_ptr = op.stack_info();
|
||||
}
|
||||
@ -1623,8 +1623,8 @@ JvmtiEnv::GetFrameCount(JavaThread* java_thread, jint* count_ptr) {
|
||||
} else {
|
||||
// get java stack frame count with handshake.
|
||||
GetFrameCountClosure op(this, state, count_ptr);
|
||||
bool executed = Handshake::execute_direct(&op, java_thread);
|
||||
err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
Handshake::execute(&op, java_thread);
|
||||
err = op.result();
|
||||
}
|
||||
return err;
|
||||
} /* end GetFrameCount */
|
||||
@ -1721,10 +1721,9 @@ JvmtiEnv::PopFrame(JavaThread* java_thread) {
|
||||
state->update_for_pop_top_frame();
|
||||
} else {
|
||||
UpdateForPopTopFrameClosure op(state);
|
||||
bool executed = Handshake::execute_direct(&op, java_thread);
|
||||
jvmtiError err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
return err;
|
||||
Handshake::execute(&op, java_thread);
|
||||
if (op.result() != JVMTI_ERROR_NONE) {
|
||||
return op.result();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1756,8 +1755,8 @@ JvmtiEnv::GetFrameLocation(JavaThread* java_thread, jint depth, jmethodID* metho
|
||||
} else {
|
||||
// JVMTI get java stack frame location via direct handshake.
|
||||
GetFrameLocationClosure op(this, depth, method_ptr, location_ptr);
|
||||
bool executed = Handshake::execute_direct(&op, java_thread);
|
||||
err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
Handshake::execute(&op, java_thread);
|
||||
err = op.result();
|
||||
}
|
||||
return err;
|
||||
} /* end GetFrameLocation */
|
||||
@ -1805,8 +1804,8 @@ JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) {
|
||||
state->env_thread_state(this)->set_frame_pop(frame_number);
|
||||
} else {
|
||||
SetFramePopClosure op(this, state, depth);
|
||||
bool executed = Handshake::execute_direct(&op, java_thread);
|
||||
err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
Handshake::execute(&op, java_thread);
|
||||
err = op.result();
|
||||
}
|
||||
return err;
|
||||
} /* end NotifyFramePop */
|
||||
|
@ -649,10 +649,9 @@ JvmtiEnvBase::count_locked_objects(JavaThread *java_thread, Handle hobj) {
|
||||
|
||||
jvmtiError
|
||||
JvmtiEnvBase::get_current_contended_monitor(JavaThread *calling_thread, JavaThread *java_thread, jobject *monitor_ptr) {
|
||||
JavaThread *current_jt = JavaThread::current();
|
||||
assert(current_jt == java_thread ||
|
||||
current_jt == java_thread->active_handshaker(),
|
||||
"call by myself or at direct handshake");
|
||||
Thread *current_thread = Thread::current();
|
||||
assert(java_thread->is_handshake_safe_for(current_thread),
|
||||
"call by myself or at handshake");
|
||||
oop obj = NULL;
|
||||
// The ObjectMonitor* can't be async deflated since we are either
|
||||
// at a safepoint or the calling thread is operating on itself so
|
||||
@ -676,8 +675,8 @@ JvmtiEnvBase::get_current_contended_monitor(JavaThread *calling_thread, JavaThre
|
||||
if (obj == NULL) {
|
||||
*monitor_ptr = NULL;
|
||||
} else {
|
||||
HandleMark hm(current_jt);
|
||||
Handle hobj(current_jt, obj);
|
||||
HandleMark hm(current_thread);
|
||||
Handle hobj(current_thread, obj);
|
||||
*monitor_ptr = jni_reference(calling_thread, hobj);
|
||||
}
|
||||
return JVMTI_ERROR_NONE;
|
||||
@ -687,15 +686,19 @@ JvmtiEnvBase::get_current_contended_monitor(JavaThread *calling_thread, JavaThre
|
||||
jvmtiError
|
||||
JvmtiEnvBase::get_owned_monitors(JavaThread *calling_thread, JavaThread* java_thread,
|
||||
GrowableArray<jvmtiMonitorStackDepthInfo*> *owned_monitors_list) {
|
||||
// Note:
|
||||
// calling_thread is the thread that requested the list of monitors for java_thread.
|
||||
// java_thread is the thread owning the monitors.
|
||||
// current_thread is the thread executing this code, can be a non-JavaThread (e.g. VM Thread).
|
||||
// And they all may be different threads.
|
||||
jvmtiError err = JVMTI_ERROR_NONE;
|
||||
JavaThread *current_jt = JavaThread::current();
|
||||
assert(current_jt == java_thread ||
|
||||
current_jt == java_thread->active_handshaker(),
|
||||
"call by myself or at direct handshake");
|
||||
Thread *current_thread = Thread::current();
|
||||
assert(java_thread->is_handshake_safe_for(current_thread),
|
||||
"call by myself or at handshake");
|
||||
|
||||
if (java_thread->has_last_Java_frame()) {
|
||||
ResourceMark rm(current_jt);
|
||||
HandleMark hm(current_jt);
|
||||
ResourceMark rm(current_thread);
|
||||
HandleMark hm(current_thread);
|
||||
RegisterMap reg_map(java_thread);
|
||||
|
||||
int depth = 0;
|
||||
@ -819,9 +822,8 @@ JvmtiEnvBase::get_stack_trace(JavaThread *java_thread,
|
||||
uint32_t debug_bits = 0;
|
||||
#endif
|
||||
Thread *current_thread = Thread::current();
|
||||
assert(current_thread == java_thread ||
|
||||
SafepointSynchronize::is_at_safepoint() ||
|
||||
current_thread == java_thread->active_handshaker(),
|
||||
assert(SafepointSynchronize::is_at_safepoint() ||
|
||||
java_thread->is_handshake_safe_for(current_thread),
|
||||
"call by myself / at safepoint / at handshake");
|
||||
int count = 0;
|
||||
if (java_thread->has_last_Java_frame()) {
|
||||
@ -903,9 +905,8 @@ JvmtiEnvBase::get_frame_location(JavaThread *java_thread, jint depth,
|
||||
uint32_t debug_bits = 0;
|
||||
#endif
|
||||
Thread* current_thread = Thread::current();
|
||||
assert(current_thread == java_thread ||
|
||||
current_thread == java_thread->active_handshaker(),
|
||||
"call by myself or at direct handshake");
|
||||
assert(java_thread->is_handshake_safe_for(current_thread),
|
||||
"call by myself or at handshake");
|
||||
ResourceMark rm(current_thread);
|
||||
|
||||
vframe *vf = vframeFor(java_thread, depth);
|
||||
@ -1159,9 +1160,8 @@ void
|
||||
MultipleStackTracesCollector::fill_frames(jthread jt, JavaThread *thr, oop thread_oop) {
|
||||
#ifdef ASSERT
|
||||
Thread *current_thread = Thread::current();
|
||||
assert(current_thread == thr ||
|
||||
SafepointSynchronize::is_at_safepoint() ||
|
||||
current_thread == thr->active_handshaker(),
|
||||
assert(SafepointSynchronize::is_at_safepoint() ||
|
||||
thr->is_handshake_safe_for(current_thread),
|
||||
"call by myself / at safepoint / at handshake");
|
||||
#endif
|
||||
|
||||
@ -1305,7 +1305,7 @@ VM_GetAllStackTraces::doit() {
|
||||
// HandleMark must be defined in the caller only.
|
||||
// It is to keep a ret_ob_h handle alive after return to the caller.
|
||||
jvmtiError
|
||||
JvmtiEnvBase::check_top_frame(JavaThread* current_thread, JavaThread* java_thread,
|
||||
JvmtiEnvBase::check_top_frame(Thread* current_thread, JavaThread* java_thread,
|
||||
jvalue value, TosState tos, Handle* ret_ob_h) {
|
||||
ResourceMark rm(current_thread);
|
||||
|
||||
@ -1368,7 +1368,7 @@ JvmtiEnvBase::check_top_frame(JavaThread* current_thread, JavaThread* java_threa
|
||||
|
||||
jvmtiError
|
||||
JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState tos) {
|
||||
JavaThread* current_thread = JavaThread::current();
|
||||
Thread* current_thread = Thread::current();
|
||||
HandleMark hm(current_thread);
|
||||
uint32_t debug_bits = 0;
|
||||
|
||||
|
@ -306,7 +306,7 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
||||
jobject *monitor_ptr);
|
||||
jvmtiError get_owned_monitors(JavaThread *calling_thread, JavaThread* java_thread,
|
||||
GrowableArray<jvmtiMonitorStackDepthInfo*> *owned_monitors_list);
|
||||
jvmtiError check_top_frame(JavaThread* current_thread, JavaThread* java_thread,
|
||||
jvmtiError check_top_frame(Thread* current_thread, JavaThread* java_thread,
|
||||
jvalue value, TosState tos, Handle* ret_ob_h);
|
||||
jvmtiError force_early_return(JavaThread* java_thread, jvalue value, TosState tos);
|
||||
};
|
||||
@ -336,58 +336,58 @@ class JvmtiEnvIterator : public StackObj {
|
||||
JvmtiEnv* next(JvmtiEnvBase* env) { return env->next_environment(); }
|
||||
};
|
||||
|
||||
class JvmtiHandshakeClosure : public HandshakeClosure {
|
||||
protected:
|
||||
jvmtiError _result;
|
||||
public:
|
||||
JvmtiHandshakeClosure(const char* name)
|
||||
: HandshakeClosure(name),
|
||||
_result(JVMTI_ERROR_THREAD_NOT_ALIVE) {}
|
||||
jvmtiError result() { return _result; }
|
||||
};
|
||||
|
||||
// HandshakeClosure to update for pop top frame.
|
||||
class UpdateForPopTopFrameClosure : public HandshakeClosure {
|
||||
class UpdateForPopTopFrameClosure : public JvmtiHandshakeClosure {
|
||||
private:
|
||||
JvmtiThreadState* _state;
|
||||
jvmtiError _result;
|
||||
|
||||
public:
|
||||
UpdateForPopTopFrameClosure(JvmtiThreadState* state)
|
||||
: HandshakeClosure("UpdateForPopTopFrame"),
|
||||
_state(state),
|
||||
_result(JVMTI_ERROR_THREAD_NOT_ALIVE) {}
|
||||
jvmtiError result() { return _result; }
|
||||
: JvmtiHandshakeClosure("UpdateForPopTopFrame"),
|
||||
_state(state) {}
|
||||
void do_thread(Thread *target);
|
||||
};
|
||||
|
||||
// HandshakeClosure to set frame pop.
|
||||
class SetFramePopClosure : public HandshakeClosure {
|
||||
class SetFramePopClosure : public JvmtiHandshakeClosure {
|
||||
private:
|
||||
JvmtiEnv *_env;
|
||||
JvmtiThreadState* _state;
|
||||
jint _depth;
|
||||
jvmtiError _result;
|
||||
|
||||
public:
|
||||
SetFramePopClosure(JvmtiEnv *env, JvmtiThreadState* state, jint depth)
|
||||
: HandshakeClosure("SetFramePop"),
|
||||
: JvmtiHandshakeClosure("SetFramePop"),
|
||||
_env(env),
|
||||
_state(state),
|
||||
_depth(depth),
|
||||
_result(JVMTI_ERROR_THREAD_NOT_ALIVE) {}
|
||||
jvmtiError result() { return _result; }
|
||||
_depth(depth) {}
|
||||
void do_thread(Thread *target);
|
||||
};
|
||||
|
||||
|
||||
// HandshakeClosure to get monitor information with stack depth.
|
||||
class GetOwnedMonitorInfoClosure : public HandshakeClosure {
|
||||
class GetOwnedMonitorInfoClosure : public JvmtiHandshakeClosure {
|
||||
private:
|
||||
JavaThread* _calling_thread;
|
||||
JvmtiEnv *_env;
|
||||
jvmtiError _result;
|
||||
GrowableArray<jvmtiMonitorStackDepthInfo*> *_owned_monitors_list;
|
||||
|
||||
public:
|
||||
GetOwnedMonitorInfoClosure(JavaThread* calling_thread, JvmtiEnv* env,
|
||||
GrowableArray<jvmtiMonitorStackDepthInfo*>* owned_monitor_list)
|
||||
: HandshakeClosure("GetOwnedMonitorInfo"),
|
||||
: JvmtiHandshakeClosure("GetOwnedMonitorInfo"),
|
||||
_calling_thread(calling_thread),
|
||||
_env(env),
|
||||
_result(JVMTI_ERROR_THREAD_NOT_ALIVE),
|
||||
_owned_monitors_list(owned_monitor_list) {}
|
||||
jvmtiError result() { return _result; }
|
||||
void do_thread(Thread *target);
|
||||
};
|
||||
|
||||
@ -417,46 +417,39 @@ public:
|
||||
};
|
||||
|
||||
// HandshakeClosure to get current contended monitor.
|
||||
class GetCurrentContendedMonitorClosure : public HandshakeClosure {
|
||||
class GetCurrentContendedMonitorClosure : public JvmtiHandshakeClosure {
|
||||
private:
|
||||
JavaThread *_calling_thread;
|
||||
JvmtiEnv *_env;
|
||||
jobject *_owned_monitor_ptr;
|
||||
jvmtiError _result;
|
||||
|
||||
public:
|
||||
GetCurrentContendedMonitorClosure(JavaThread* calling_thread, JvmtiEnv *env, jobject *mon_ptr)
|
||||
: HandshakeClosure("GetCurrentContendedMonitor"),
|
||||
: JvmtiHandshakeClosure("GetCurrentContendedMonitor"),
|
||||
_calling_thread(calling_thread),
|
||||
_env(env),
|
||||
_owned_monitor_ptr(mon_ptr),
|
||||
_result(JVMTI_ERROR_THREAD_NOT_ALIVE) {}
|
||||
jvmtiError result() { return _result; }
|
||||
_owned_monitor_ptr(mon_ptr) {}
|
||||
void do_thread(Thread *target);
|
||||
};
|
||||
|
||||
// HandshakeClosure to get stack trace.
|
||||
class GetStackTraceClosure : public HandshakeClosure {
|
||||
class GetStackTraceClosure : public JvmtiHandshakeClosure {
|
||||
private:
|
||||
JvmtiEnv *_env;
|
||||
jint _start_depth;
|
||||
jint _max_count;
|
||||
jvmtiFrameInfo *_frame_buffer;
|
||||
jint *_count_ptr;
|
||||
jvmtiError _result;
|
||||
|
||||
public:
|
||||
GetStackTraceClosure(JvmtiEnv *env, jint start_depth, jint max_count,
|
||||
jvmtiFrameInfo* frame_buffer, jint* count_ptr)
|
||||
: HandshakeClosure("GetStackTrace"),
|
||||
: JvmtiHandshakeClosure("GetStackTrace"),
|
||||
_env(env),
|
||||
_start_depth(start_depth),
|
||||
_max_count(max_count),
|
||||
_frame_buffer(frame_buffer),
|
||||
_count_ptr(count_ptr),
|
||||
_result(JVMTI_ERROR_THREAD_NOT_ALIVE) {
|
||||
}
|
||||
jvmtiError result() { return _result; }
|
||||
_count_ptr(count_ptr) {}
|
||||
void do_thread(Thread *target);
|
||||
};
|
||||
|
||||
@ -556,45 +549,37 @@ public:
|
||||
};
|
||||
|
||||
// HandshakeClosure to count stack frames.
|
||||
class GetFrameCountClosure : public HandshakeClosure {
|
||||
class GetFrameCountClosure : public JvmtiHandshakeClosure {
|
||||
private:
|
||||
JvmtiEnv *_env;
|
||||
JvmtiThreadState *_state;
|
||||
jint *_count_ptr;
|
||||
jvmtiError _result;
|
||||
|
||||
public:
|
||||
GetFrameCountClosure(JvmtiEnv *env, JvmtiThreadState *state, jint *count_ptr)
|
||||
: HandshakeClosure("GetFrameCount"),
|
||||
: JvmtiHandshakeClosure("GetFrameCount"),
|
||||
_env(env),
|
||||
_state(state),
|
||||
_count_ptr(count_ptr),
|
||||
_result(JVMTI_ERROR_THREAD_NOT_ALIVE) {
|
||||
}
|
||||
jvmtiError result() { return _result; }
|
||||
_count_ptr(count_ptr) {}
|
||||
void do_thread(Thread *target);
|
||||
};
|
||||
|
||||
// HandshakeClosure to get frame location.
|
||||
class GetFrameLocationClosure : public HandshakeClosure {
|
||||
class GetFrameLocationClosure : public JvmtiHandshakeClosure {
|
||||
private:
|
||||
JvmtiEnv *_env;
|
||||
jint _depth;
|
||||
jmethodID* _method_ptr;
|
||||
jlocation* _location_ptr;
|
||||
jvmtiError _result;
|
||||
|
||||
public:
|
||||
GetFrameLocationClosure(JvmtiEnv *env, jint depth,
|
||||
jmethodID* method_ptr, jlocation* location_ptr)
|
||||
: HandshakeClosure("GetFrameLocation"),
|
||||
: JvmtiHandshakeClosure("GetFrameLocation"),
|
||||
_env(env),
|
||||
_depth(depth),
|
||||
_method_ptr(method_ptr),
|
||||
_location_ptr(location_ptr),
|
||||
_result(JVMTI_ERROR_THREAD_NOT_ALIVE) {
|
||||
}
|
||||
jvmtiError result() { return _result; }
|
||||
_location_ptr(location_ptr) {}
|
||||
void do_thread(Thread *target);
|
||||
};
|
||||
|
||||
|
@ -194,7 +194,7 @@ JvmtiFramePops* JvmtiEnvThreadState::get_frame_pops() {
|
||||
#ifdef ASSERT
|
||||
Thread *current = Thread::current();
|
||||
#endif
|
||||
assert(get_thread() == current || current == get_thread()->active_handshaker(),
|
||||
assert(get_thread()->is_handshake_safe_for(current),
|
||||
"frame pop data only accessible from same thread or direct handshake");
|
||||
if (_frame_pops == NULL) {
|
||||
_frame_pops = new JvmtiFramePops();
|
||||
@ -212,7 +212,7 @@ void JvmtiEnvThreadState::set_frame_pop(int frame_number) {
|
||||
#ifdef ASSERT
|
||||
Thread *current = Thread::current();
|
||||
#endif
|
||||
assert(get_thread() == current || current == get_thread()->active_handshaker(),
|
||||
assert(get_thread()->is_handshake_safe_for(current),
|
||||
"frame pop data only accessible from same thread or direct handshake");
|
||||
JvmtiFramePop fpop(frame_number);
|
||||
JvmtiEventController::set_frame_pop(this, fpop);
|
||||
@ -223,7 +223,7 @@ void JvmtiEnvThreadState::clear_frame_pop(int frame_number) {
|
||||
#ifdef ASSERT
|
||||
Thread *current = Thread::current();
|
||||
#endif
|
||||
assert(get_thread() == current || current == get_thread()->active_handshaker(),
|
||||
assert(get_thread()->is_handshake_safe_for(current),
|
||||
"frame pop data only accessible from same thread or direct handshake");
|
||||
JvmtiFramePop fpop(frame_number);
|
||||
JvmtiEventController::clear_frame_pop(this, fpop);
|
||||
@ -234,7 +234,7 @@ bool JvmtiEnvThreadState::is_frame_pop(int cur_frame_number) {
|
||||
#ifdef ASSERT
|
||||
Thread *current = Thread::current();
|
||||
#endif
|
||||
assert(get_thread() == current || current == get_thread()->active_handshaker(),
|
||||
assert(get_thread()->is_handshake_safe_for(current),
|
||||
"frame pop data only accessible from same thread or direct handshake");
|
||||
if (!get_thread()->is_interp_only_mode() || _frame_pops == NULL) {
|
||||
return false;
|
||||
@ -248,12 +248,13 @@ class GetCurrentLocationClosure : public HandshakeClosure {
|
||||
private:
|
||||
jmethodID _method_id;
|
||||
int _bci;
|
||||
|
||||
bool _completed;
|
||||
public:
|
||||
GetCurrentLocationClosure()
|
||||
: HandshakeClosure("GetCurrentLocation"),
|
||||
_method_id(NULL),
|
||||
_bci(0) {}
|
||||
_bci(0),
|
||||
_completed(false) {}
|
||||
void do_thread(Thread *target) {
|
||||
JavaThread *jt = target->as_Java_thread();
|
||||
ResourceMark rmark; // jt != Thread::current()
|
||||
@ -272,11 +273,15 @@ class GetCurrentLocationClosure : public HandshakeClosure {
|
||||
_method_id = (jmethodID)NULL;
|
||||
_bci = 0;
|
||||
}
|
||||
_completed = true;
|
||||
}
|
||||
void get_current_location(jmethodID *method_id, int *bci) {
|
||||
*method_id = _method_id;
|
||||
*bci = _bci;
|
||||
}
|
||||
bool completed() {
|
||||
return _completed;
|
||||
}
|
||||
};
|
||||
|
||||
void JvmtiEnvThreadState::reset_current_location(jvmtiEvent event_type, bool enabled) {
|
||||
@ -314,11 +319,11 @@ void JvmtiEnvThreadState::reset_current_location(jvmtiEvent event_type, bool ena
|
||||
// so get current location with direct handshake.
|
||||
GetCurrentLocationClosure op;
|
||||
Thread *current = Thread::current();
|
||||
if (current == _thread || _thread->active_handshaker() == current) {
|
||||
if (_thread->is_handshake_safe_for(current)) {
|
||||
op.do_thread(_thread);
|
||||
} else {
|
||||
bool executed = Handshake::execute_direct(&op, _thread);
|
||||
guarantee(executed, "Direct handshake failed. Target thread is not alive?");
|
||||
Handshake::execute(&op, _thread);
|
||||
guarantee(op.completed(), "Handshake failed. Target thread is not alive?");
|
||||
}
|
||||
op.get_current_location(&method_id, &bci);
|
||||
set_current_location(method_id, bci);
|
||||
|
@ -194,9 +194,10 @@ JvmtiEnvEventEnable::~JvmtiEnvEventEnable() {
|
||||
//
|
||||
|
||||
class EnterInterpOnlyModeClosure : public HandshakeClosure {
|
||||
|
||||
public:
|
||||
EnterInterpOnlyModeClosure() : HandshakeClosure("EnterInterpOnlyMode") { }
|
||||
private:
|
||||
bool _completed;
|
||||
public:
|
||||
EnterInterpOnlyModeClosure() : HandshakeClosure("EnterInterpOnlyMode"), _completed(false) { }
|
||||
void do_thread(Thread* th) {
|
||||
JavaThread* jt = th->as_Java_thread();
|
||||
JvmtiThreadState* state = jt->jvmti_thread_state();
|
||||
@ -220,6 +221,10 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
_completed = true;
|
||||
}
|
||||
bool completed() {
|
||||
return _completed = true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -333,11 +338,11 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state
|
||||
EnterInterpOnlyModeClosure hs;
|
||||
JavaThread *target = state->get_thread();
|
||||
Thread *current = Thread::current();
|
||||
if (target == current || target->active_handshaker() == current) {
|
||||
if (target->is_handshake_safe_for(current)) {
|
||||
hs.do_thread(target);
|
||||
} else {
|
||||
bool executed = Handshake::execute_direct(&hs, target);
|
||||
guarantee(executed, "Direct handshake failed. Target thread is not alive?");
|
||||
Handshake::execute(&hs, target);
|
||||
guarantee(hs.completed(), "Handshake failed: Target thread is not alive?");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,9 +224,8 @@ int JvmtiThreadState::count_frames() {
|
||||
#ifdef ASSERT
|
||||
Thread *current_thread = Thread::current();
|
||||
#endif
|
||||
assert(current_thread == get_thread() ||
|
||||
SafepointSynchronize::is_at_safepoint() ||
|
||||
current_thread == get_thread()->active_handshaker(),
|
||||
assert(SafepointSynchronize::is_at_safepoint() ||
|
||||
get_thread()->is_handshake_safe_for(current_thread),
|
||||
"call by myself / at safepoint / at handshake");
|
||||
|
||||
if (!get_thread()->has_last_Java_frame()) return 0; // no Java frames
|
||||
@ -246,8 +245,7 @@ int JvmtiThreadState::count_frames() {
|
||||
|
||||
void JvmtiThreadState::invalidate_cur_stack_depth() {
|
||||
assert(SafepointSynchronize::is_at_safepoint() ||
|
||||
JavaThread::current() == get_thread() ||
|
||||
Thread::current() == get_thread()->active_handshaker(),
|
||||
get_thread()->is_handshake_safe_for(Thread::current()),
|
||||
"bad synchronization with owner thread");
|
||||
|
||||
_cur_stack_depth = UNKNOWN_STACK_DEPTH;
|
||||
@ -277,8 +275,8 @@ void JvmtiThreadState::decr_cur_stack_depth() {
|
||||
}
|
||||
|
||||
int JvmtiThreadState::cur_stack_depth() {
|
||||
JavaThread *current = JavaThread::current();
|
||||
guarantee(current == get_thread() || current == get_thread()->active_handshaker(),
|
||||
Thread *current = Thread::current();
|
||||
guarantee(get_thread()->is_handshake_safe_for(current),
|
||||
"must be current thread or direct handshake");
|
||||
|
||||
if (!is_interp_only_mode() || _cur_stack_depth == UNKNOWN_STACK_DEPTH) {
|
||||
|
@ -2026,6 +2026,32 @@ WB_ENTRY(jint, WB_HandshakeWalkStack(JNIEnv* env, jobject wb, jobject thread_han
|
||||
return tsc.num_threads_completed();
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(void, WB_AsyncHandshakeWalkStack(JNIEnv* env, jobject wb, jobject thread_handle))
|
||||
class TraceSelfClosure : public AsyncHandshakeClosure {
|
||||
JavaThread* _self;
|
||||
void do_thread(Thread* th) {
|
||||
assert(th->is_Java_thread(), "sanity");
|
||||
// AsynchHandshake handshakes are only executed by target.
|
||||
assert(_self == th, "Must be");
|
||||
assert(Thread::current() == th, "Must be");
|
||||
JavaThread* jt = th->as_Java_thread();
|
||||
ResourceMark rm;
|
||||
jt->print_on(tty);
|
||||
jt->print_stack_on(tty);
|
||||
tty->cr();
|
||||
}
|
||||
|
||||
public:
|
||||
TraceSelfClosure(JavaThread* self_target) : AsyncHandshakeClosure("WB_TraceSelf"), _self(self_target) {}
|
||||
};
|
||||
oop thread_oop = JNIHandles::resolve(thread_handle);
|
||||
if (thread_oop != NULL) {
|
||||
JavaThread* target = java_lang_Thread::thread(thread_oop);
|
||||
TraceSelfClosure* tsc = new TraceSelfClosure(target);
|
||||
Handshake::execute(tsc, target);
|
||||
}
|
||||
WB_END
|
||||
|
||||
//Some convenience methods to deal with objects from java
|
||||
int WhiteBox::offset_for_field(const char* field_name, oop object,
|
||||
Symbol* signature_symbol) {
|
||||
@ -2487,6 +2513,7 @@ static JNINativeMethod methods[] = {
|
||||
|
||||
{CC"clearInlineCaches0", CC"(Z)V", (void*)&WB_ClearInlineCaches },
|
||||
{CC"handshakeWalkStack", CC"(Ljava/lang/Thread;Z)I", (void*)&WB_HandshakeWalkStack },
|
||||
{CC"asyncHandshakeWalkStack", CC"(Ljava/lang/Thread;)V", (void*)&WB_AsyncHandshakeWalkStack },
|
||||
{CC"checkThreadObjOfTerminatingThread", CC"(Ljava/lang/Thread;)V", (void*)&WB_CheckThreadObjOfTerminatingThread },
|
||||
{CC"addCompilerDirective", CC"(Ljava/lang/String;)I",
|
||||
(void*)&WB_AddCompilerDirective },
|
||||
|
@ -509,6 +509,7 @@ protected:
|
||||
JavaThread* _biased_locker;
|
||||
BiasedLocking::Condition _status_code;
|
||||
traceid _biased_locker_id;
|
||||
bool _executed;
|
||||
|
||||
public:
|
||||
RevokeOneBias(Handle obj, JavaThread* requesting_thread, JavaThread* biased_locker)
|
||||
@ -517,10 +518,14 @@ public:
|
||||
, _requesting_thread(requesting_thread)
|
||||
, _biased_locker(biased_locker)
|
||||
, _status_code(BiasedLocking::NOT_BIASED)
|
||||
, _biased_locker_id(0) {}
|
||||
, _biased_locker_id(0)
|
||||
, _executed(false) {}
|
||||
|
||||
bool executed() { return _executed; }
|
||||
|
||||
void do_thread(Thread* target) {
|
||||
assert(target == _biased_locker, "Wrong thread");
|
||||
_executed = true;
|
||||
|
||||
oop o = _obj();
|
||||
markWord mark = o->mark();
|
||||
@ -622,11 +627,11 @@ BiasedLocking::Condition BiasedLocking::single_revoke_with_handshake(Handle obj,
|
||||
p2i(biaser), p2i(obj()));
|
||||
|
||||
RevokeOneBias revoke(obj, requester, biaser);
|
||||
bool executed = Handshake::execute_direct(&revoke, biaser);
|
||||
Handshake::execute(&revoke, biaser);
|
||||
if (revoke.status_code() == NOT_REVOKED) {
|
||||
return NOT_REVOKED;
|
||||
}
|
||||
if (executed) {
|
||||
if (revoke.executed()) {
|
||||
log_info(biasedlocking, handshake)("Handshake revocation for object " INTPTR_FORMAT " succeeded. Bias was %srevoked",
|
||||
p2i(obj()), (revoke.status_code() == BIAS_REVOKED ? "" : "already "));
|
||||
if (event.should_commit() && revoke.status_code() == BIAS_REVOKED) {
|
||||
@ -668,7 +673,7 @@ BiasedLocking::Condition BiasedLocking::single_revoke_with_handshake(Handle obj,
|
||||
void BiasedLocking::walk_stack_and_revoke(oop obj, JavaThread* biased_locker) {
|
||||
Thread* cur = Thread::current();
|
||||
assert(!SafepointSynchronize::is_at_safepoint(), "this should always be executed outside safepoints");
|
||||
assert(cur == biased_locker || cur == biased_locker->active_handshaker(), "wrong thread");
|
||||
assert(biased_locker->is_handshake_safe_for(cur), "wrong thread");
|
||||
|
||||
markWord mark = obj->mark();
|
||||
assert(mark.biased_locker() == biased_locker &&
|
||||
|
@ -30,37 +30,53 @@
|
||||
#include "runtime/handshake.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/osThread.hpp"
|
||||
#include "runtime/semaphore.inline.hpp"
|
||||
#include "runtime/task.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "utilities/formatBuffer.hpp"
|
||||
#include "utilities/filterQueue.inline.hpp"
|
||||
#include "utilities/preserveException.hpp"
|
||||
|
||||
class HandshakeOperation : public CHeapObj<mtThread> {
|
||||
friend class HandshakeState;
|
||||
protected:
|
||||
HandshakeClosure* _handshake_cl;
|
||||
// Keeps track of emitted and completed handshake operations.
|
||||
// Once it reaches zero all handshake operations have been performed.
|
||||
int32_t _pending_threads;
|
||||
JavaThread* _target;
|
||||
|
||||
class HandshakeOperation: public StackObj {
|
||||
HandshakeClosure* _handshake_cl;
|
||||
int32_t _pending_threads;
|
||||
bool _executed;
|
||||
bool _is_direct;
|
||||
public:
|
||||
HandshakeOperation(HandshakeClosure* cl, bool is_direct = false) :
|
||||
// Must use AsyncHandshakeOperation when using AsyncHandshakeClosure.
|
||||
HandshakeOperation(AsyncHandshakeClosure* cl, JavaThread* target) :
|
||||
_handshake_cl(cl),
|
||||
_pending_threads(1),
|
||||
_executed(false),
|
||||
_is_direct(is_direct) {}
|
||||
_target(target) {}
|
||||
|
||||
public:
|
||||
HandshakeOperation(HandshakeClosure* cl, JavaThread* target) :
|
||||
_handshake_cl(cl),
|
||||
_pending_threads(1),
|
||||
_target(target) {}
|
||||
virtual ~HandshakeOperation() {}
|
||||
void do_handshake(JavaThread* thread);
|
||||
bool is_completed() {
|
||||
int32_t val = Atomic::load(&_pending_threads);
|
||||
assert(val >= 0, "_pending_threads=%d cannot be negative", val);
|
||||
return val == 0;
|
||||
}
|
||||
void add_target_count(int count) { Atomic::add(&_pending_threads, count, memory_order_relaxed); }
|
||||
bool executed() const { return _executed; }
|
||||
const char* name() { return _handshake_cl->name(); }
|
||||
void add_target_count(int count) { Atomic::add(&_pending_threads, count); }
|
||||
const char* name() { return _handshake_cl->name(); }
|
||||
bool is_async() { return _handshake_cl->is_async(); }
|
||||
};
|
||||
|
||||
bool is_direct() { return _is_direct; }
|
||||
class AsyncHandshakeOperation : public HandshakeOperation {
|
||||
private:
|
||||
jlong _start_time_ns;
|
||||
public:
|
||||
AsyncHandshakeOperation(AsyncHandshakeClosure* cl, JavaThread* target, jlong start_ns)
|
||||
: HandshakeOperation(cl, target), _start_time_ns(start_ns) {}
|
||||
virtual ~AsyncHandshakeOperation() { delete _handshake_cl; }
|
||||
jlong start_time() const { return _start_time_ns; }
|
||||
};
|
||||
|
||||
// Performing handshakes requires a custom yielding strategy because without it
|
||||
@ -79,7 +95,6 @@ class HandshakeSpinYield : public StackObj {
|
||||
int _result_count[2][HandshakeState::_number_states];
|
||||
int _prev_result_pos;
|
||||
|
||||
int prev_result_pos() { return _prev_result_pos & 0x1; }
|
||||
int current_result_pos() { return (_prev_result_pos + 1) & 0x1; }
|
||||
|
||||
void wait_raw(jlong now) {
|
||||
@ -176,8 +191,8 @@ bool VM_Handshake::handshake_has_timed_out(jlong start_time) {
|
||||
|
||||
void VM_Handshake::handle_timeout() {
|
||||
LogStreamHandle(Warning, handshake) log_stream;
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
|
||||
if (thr->has_handshake()) {
|
||||
for (JavaThreadIteratorWithHandle jtiwh; JavaThread* thr = jtiwh.next(); ) {
|
||||
if (thr->handshake_state()->has_operation()) {
|
||||
log_stream.print("Thread " PTR_FORMAT " has not cleared its handshake op", p2i(thr));
|
||||
thr->print_thread_state_on(&log_stream);
|
||||
}
|
||||
@ -186,78 +201,34 @@ void VM_Handshake::handle_timeout() {
|
||||
fatal("Handshake operation timed out");
|
||||
}
|
||||
|
||||
static void log_handshake_info(jlong start_time_ns, const char* name, int targets, int vmt_executed, const char* extra = NULL) {
|
||||
if (start_time_ns != 0) {
|
||||
static void log_handshake_info(jlong start_time_ns, const char* name, int targets, int emitted_handshakes_executed, const char* extra = NULL) {
|
||||
if (log_is_enabled(Info, handshake)) {
|
||||
jlong completion_time = os::javaTimeNanos() - start_time_ns;
|
||||
log_info(handshake)("Handshake \"%s\", Targeted threads: %d, Executed by targeted threads: %d, Total completion time: " JLONG_FORMAT " ns%s%s",
|
||||
log_info(handshake)("Handshake \"%s\", Targeted threads: %d, Executed by requesting thread: %d, Total completion time: " JLONG_FORMAT " ns%s%s",
|
||||
name, targets,
|
||||
targets - vmt_executed,
|
||||
emitted_handshakes_executed,
|
||||
completion_time,
|
||||
extra != NULL ? ", " : "",
|
||||
extra != NULL ? extra : "");
|
||||
}
|
||||
}
|
||||
|
||||
class VM_HandshakeOneThread: public VM_Handshake {
|
||||
JavaThread* _target;
|
||||
public:
|
||||
VM_HandshakeOneThread(HandshakeOperation* op, JavaThread* target) :
|
||||
VM_Handshake(op), _target(target) {}
|
||||
|
||||
void doit() {
|
||||
jlong start_time_ns = os::javaTimeNanos();
|
||||
|
||||
ThreadsListHandle tlh;
|
||||
if (tlh.includes(_target)) {
|
||||
_target->set_handshake_operation(_op);
|
||||
} else {
|
||||
log_handshake_info(start_time_ns, _op->name(), 0, 0, "(thread dead)");
|
||||
return;
|
||||
}
|
||||
|
||||
log_trace(handshake)("JavaThread " INTPTR_FORMAT " signaled, begin attempt to process by VMThtread", p2i(_target));
|
||||
HandshakeState::ProcessResult pr = HandshakeState::_no_operation;
|
||||
HandshakeSpinYield hsy(start_time_ns);
|
||||
do {
|
||||
if (handshake_has_timed_out(start_time_ns)) {
|
||||
handle_timeout();
|
||||
}
|
||||
pr = _target->handshake_try_process(_op);
|
||||
hsy.add_result(pr);
|
||||
hsy.process();
|
||||
} while (!_op->is_completed());
|
||||
|
||||
// This pairs up with the release store in do_handshake(). It prevents future
|
||||
// loads from floating above the load of _pending_threads in is_completed()
|
||||
// and thus prevents reading stale data modified in the handshake closure
|
||||
// by the Handshakee.
|
||||
OrderAccess::acquire();
|
||||
|
||||
log_handshake_info(start_time_ns, _op->name(), 1, (pr == HandshakeState::_success) ? 1 : 0);
|
||||
}
|
||||
|
||||
VMOp_Type type() const { return VMOp_HandshakeOneThread; }
|
||||
|
||||
bool executed() const { return _op->executed(); }
|
||||
};
|
||||
|
||||
class VM_HandshakeAllThreads: public VM_Handshake {
|
||||
public:
|
||||
VM_HandshakeAllThreads(HandshakeOperation* op) : VM_Handshake(op) {}
|
||||
|
||||
void doit() {
|
||||
jlong start_time_ns = os::javaTimeNanos();
|
||||
int handshake_executed_by_vm_thread = 0;
|
||||
|
||||
JavaThreadIteratorWithHandle jtiwh;
|
||||
int number_of_threads_issued = 0;
|
||||
for (JavaThread *thr = jtiwh.next(); thr != NULL; thr = jtiwh.next()) {
|
||||
thr->set_handshake_operation(_op);
|
||||
for (JavaThread* thr = jtiwh.next(); thr != NULL; thr = jtiwh.next()) {
|
||||
thr->handshake_state()->add_operation(_op);
|
||||
number_of_threads_issued++;
|
||||
}
|
||||
|
||||
if (number_of_threads_issued < 1) {
|
||||
log_handshake_info(start_time_ns, _op->name(), 0, 0);
|
||||
log_handshake_info(start_time_ns, _op->name(), 0, 0, "no threads alive");
|
||||
return;
|
||||
}
|
||||
// _op was created with a count == 1 so don't double count.
|
||||
@ -265,6 +236,9 @@ class VM_HandshakeAllThreads: public VM_Handshake {
|
||||
|
||||
log_trace(handshake)("Threads signaled, begin processing blocked threads by VMThread");
|
||||
HandshakeSpinYield hsy(start_time_ns);
|
||||
// Keeps count on how many of own emitted handshakes
|
||||
// this thread execute.
|
||||
int emitted_handshakes_executed = 0;
|
||||
do {
|
||||
// Check if handshake operation has timed out
|
||||
if (handshake_has_timed_out(start_time_ns)) {
|
||||
@ -273,16 +247,16 @@ class VM_HandshakeAllThreads: public VM_Handshake {
|
||||
|
||||
// Have VM thread perform the handshake operation for blocked threads.
|
||||
// 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
|
||||
// by mutexes and we optimistically begin by working on the blocked threads
|
||||
jtiwh.rewind();
|
||||
for (JavaThread *thr = jtiwh.next(); thr != NULL; thr = jtiwh.next()) {
|
||||
for (JavaThread* thr = jtiwh.next(); thr != NULL; thr = jtiwh.next()) {
|
||||
// A new thread on the ThreadsList will not have an operation,
|
||||
// hence it is skipped in handshake_try_process.
|
||||
HandshakeState::ProcessResult pr = thr->handshake_try_process(_op);
|
||||
if (pr == HandshakeState::_success) {
|
||||
handshake_executed_by_vm_thread++;
|
||||
}
|
||||
HandshakeState::ProcessResult pr = thr->handshake_state()->try_process(_op);
|
||||
hsy.add_result(pr);
|
||||
if (pr == HandshakeState::_succeeded) {
|
||||
emitted_handshakes_executed++;
|
||||
}
|
||||
}
|
||||
hsy.process();
|
||||
} while (!_op->is_completed());
|
||||
@ -293,7 +267,7 @@ class VM_HandshakeAllThreads: public VM_Handshake {
|
||||
// by the Handshakee.
|
||||
OrderAccess::acquire();
|
||||
|
||||
log_handshake_info(start_time_ns, _op->name(), number_of_threads_issued, handshake_executed_by_vm_thread);
|
||||
log_handshake_info(start_time_ns, _op->name(), number_of_threads_issued, emitted_handshakes_executed);
|
||||
}
|
||||
|
||||
VMOp_Type type() const { return VMOp_HandshakeAllThreads; }
|
||||
@ -307,8 +281,8 @@ void HandshakeOperation::do_handshake(JavaThread* thread) {
|
||||
|
||||
// Only actually execute the operation for non terminated threads.
|
||||
if (!thread->is_terminated()) {
|
||||
NoSafepointVerifier nsv;
|
||||
_handshake_cl->do_thread(thread);
|
||||
_executed = true;
|
||||
}
|
||||
|
||||
if (start_time_ns != 0) {
|
||||
@ -327,37 +301,40 @@ void HandshakeOperation::do_handshake(JavaThread* thread) {
|
||||
// It is no longer safe to refer to 'this' as the VMThread/Handshaker may have destroyed this operation
|
||||
}
|
||||
|
||||
void Handshake::execute(HandshakeClosure* thread_cl) {
|
||||
HandshakeOperation cto(thread_cl);
|
||||
void Handshake::execute(HandshakeClosure* hs_cl) {
|
||||
HandshakeOperation cto(hs_cl, NULL);
|
||||
VM_HandshakeAllThreads handshake(&cto);
|
||||
VMThread::execute(&handshake);
|
||||
}
|
||||
|
||||
bool Handshake::execute(HandshakeClosure* thread_cl, JavaThread* target) {
|
||||
HandshakeOperation cto(thread_cl);
|
||||
VM_HandshakeOneThread handshake(&cto, target);
|
||||
VMThread::execute(&handshake);
|
||||
return handshake.executed();
|
||||
}
|
||||
|
||||
bool Handshake::execute_direct(HandshakeClosure* thread_cl, JavaThread* target) {
|
||||
void Handshake::execute(HandshakeClosure* hs_cl, JavaThread* target) {
|
||||
JavaThread* self = JavaThread::current();
|
||||
HandshakeOperation op(thread_cl, /*is_direct*/ true);
|
||||
HandshakeOperation op(hs_cl, target);
|
||||
|
||||
jlong start_time_ns = os::javaTimeNanos();
|
||||
|
||||
ThreadsListHandle tlh;
|
||||
if (tlh.includes(target)) {
|
||||
target->set_handshake_operation(&op);
|
||||
target->handshake_state()->add_operation(&op);
|
||||
} else {
|
||||
log_handshake_info(start_time_ns, op.name(), 0, 0, "(thread dead)");
|
||||
return false;
|
||||
char buf[128];
|
||||
jio_snprintf(buf, sizeof(buf), "(thread= " INTPTR_FORMAT " dead)", p2i(target));
|
||||
log_handshake_info(start_time_ns, op.name(), 0, 0, buf);
|
||||
return;
|
||||
}
|
||||
|
||||
HandshakeState::ProcessResult pr = HandshakeState::_no_operation;
|
||||
// Keeps count on how many of own emitted handshakes
|
||||
// this thread execute.
|
||||
int emitted_handshakes_executed = 0;
|
||||
HandshakeSpinYield hsy(start_time_ns);
|
||||
while (!op.is_completed()) {
|
||||
HandshakeState::ProcessResult pr = target->handshake_try_process(&op);
|
||||
HandshakeState::ProcessResult pr = target->handshake_state()->try_process(&op);
|
||||
if (pr == HandshakeState::_succeeded) {
|
||||
emitted_handshakes_executed++;
|
||||
}
|
||||
if (op.is_completed()) {
|
||||
break;
|
||||
}
|
||||
hsy.add_result(pr);
|
||||
// Check for pending handshakes to avoid possible deadlocks where our
|
||||
// target is trying to handshake us.
|
||||
@ -373,39 +350,65 @@ bool Handshake::execute_direct(HandshakeClosure* thread_cl, JavaThread* target)
|
||||
// by the Handshakee.
|
||||
OrderAccess::acquire();
|
||||
|
||||
log_handshake_info(start_time_ns, op.name(), 1, (pr == HandshakeState::_success) ? 1 : 0);
|
||||
|
||||
return op.executed();
|
||||
log_handshake_info(start_time_ns, op.name(), 1, emitted_handshakes_executed);
|
||||
}
|
||||
|
||||
HandshakeState::HandshakeState() :
|
||||
_operation(NULL),
|
||||
_operation_direct(NULL),
|
||||
_handshake_turn_sem(1),
|
||||
_processing_sem(1),
|
||||
_thread_in_process_handshake(false),
|
||||
_active_handshaker(NULL)
|
||||
void Handshake::execute(AsyncHandshakeClosure* hs_cl, JavaThread* target) {
|
||||
jlong start_time_ns = os::javaTimeNanos();
|
||||
AsyncHandshakeOperation* op = new AsyncHandshakeOperation(hs_cl, target, start_time_ns);
|
||||
|
||||
ThreadsListHandle tlh;
|
||||
if (tlh.includes(target)) {
|
||||
target->handshake_state()->add_operation(op);
|
||||
} else {
|
||||
log_handshake_info(start_time_ns, op->name(), 0, 0, "(thread dead)");
|
||||
delete op;
|
||||
}
|
||||
}
|
||||
|
||||
HandshakeState::HandshakeState(JavaThread* target) :
|
||||
_handshakee(target),
|
||||
_queue(),
|
||||
_lock(Monitor::leaf, "HandshakeState", Mutex::_allow_vm_block_flag, Monitor::_safepoint_check_never),
|
||||
_active_handshaker()
|
||||
{
|
||||
}
|
||||
|
||||
void HandshakeState::set_operation(HandshakeOperation* op) {
|
||||
if (!op->is_direct()) {
|
||||
assert(Thread::current()->is_VM_thread(), "should be the VMThread");
|
||||
_operation = op;
|
||||
} else {
|
||||
// Serialize direct handshakes so that only one proceeds at a time for a given target
|
||||
_handshake_turn_sem.wait_with_safepoint_check(JavaThread::current());
|
||||
_operation_direct = op;
|
||||
}
|
||||
void HandshakeState::add_operation(HandshakeOperation* op) {
|
||||
// Adds are done lock free and so is arming.
|
||||
// Calling this method with lock held is considered an error.
|
||||
assert(!_lock.owned_by_self(), "Lock should not be held");
|
||||
_queue.push(op);
|
||||
SafepointMechanism::arm_local_poll_release(_handshakee);
|
||||
}
|
||||
|
||||
void HandshakeState::clear_handshake(bool is_direct) {
|
||||
if (!is_direct) {
|
||||
_operation = NULL;
|
||||
} else {
|
||||
_operation_direct = NULL;
|
||||
_handshake_turn_sem.signal();
|
||||
HandshakeOperation* HandshakeState::pop_for_self() {
|
||||
assert(_handshakee == Thread::current(), "Must be called by self");
|
||||
assert(_lock.owned_by_self(), "Lock must be held");
|
||||
return _queue.pop();
|
||||
};
|
||||
|
||||
static bool non_self_queue_filter(HandshakeOperation* op) {
|
||||
return !op->is_async();
|
||||
}
|
||||
|
||||
bool HandshakeState::have_non_self_executable_operation() {
|
||||
assert(_handshakee != Thread::current(), "Must not be called by self");
|
||||
assert(_lock.owned_by_self(), "Lock must be held");
|
||||
return _queue.contains(non_self_queue_filter);
|
||||
}
|
||||
|
||||
HandshakeOperation* HandshakeState::pop() {
|
||||
assert(_handshakee != Thread::current(), "Must not be called by self");
|
||||
assert(_lock.owned_by_self(), "Lock must be held");
|
||||
return _queue.pop(non_self_queue_filter);
|
||||
};
|
||||
|
||||
void HandshakeState::process_by_self() {
|
||||
ThreadInVMForHandshake tivm(_handshakee);
|
||||
{
|
||||
NoSafepointVerifier nsv;
|
||||
process_self_inner();
|
||||
}
|
||||
}
|
||||
|
||||
@ -414,31 +417,24 @@ void HandshakeState::process_self_inner() {
|
||||
assert(!_handshakee->is_terminated(), "should not be a terminated thread");
|
||||
assert(_handshakee->thread_state() != _thread_blocked, "should not be in a blocked state");
|
||||
assert(_handshakee->thread_state() != _thread_in_native, "should not be in native");
|
||||
JavaThread* self = _handshakee;
|
||||
|
||||
do {
|
||||
ThreadInVMForHandshake tivm(self);
|
||||
if (!_processing_sem.trywait()) {
|
||||
_processing_sem.wait_with_safepoint_check(self);
|
||||
}
|
||||
if (has_operation()) {
|
||||
HandleMark hm(self);
|
||||
CautiouslyPreserveExceptionMark pem(self);
|
||||
HandshakeOperation * op = _operation;
|
||||
if (op != NULL) {
|
||||
// Disarm before executing the operation
|
||||
clear_handshake(/*is_direct*/ false);
|
||||
op->do_handshake(self);
|
||||
}
|
||||
op = _operation_direct;
|
||||
if (op != NULL) {
|
||||
// Disarm before executing the operation
|
||||
clear_handshake(/*is_direct*/ true);
|
||||
op->do_handshake(self);
|
||||
while (should_process()) {
|
||||
HandleMark hm(_handshakee);
|
||||
CautiouslyPreserveExceptionMark pem(_handshakee);
|
||||
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
|
||||
HandshakeOperation* op = pop_for_self();
|
||||
if (op != NULL) {
|
||||
assert(op->_target == NULL || op->_target == Thread::current(), "Wrong thread");
|
||||
bool async = op->is_async();
|
||||
log_trace(handshake)("Proc handshake %s " INTPTR_FORMAT " on " INTPTR_FORMAT " by self",
|
||||
async ? "asynchronous" : "synchronous", p2i(op), p2i(_handshakee));
|
||||
op->do_handshake(_handshakee);
|
||||
if (async) {
|
||||
log_handshake_info(((AsyncHandshakeOperation*)op)->start_time(), op->name(), 1, 0, "asynchronous");
|
||||
delete op;
|
||||
}
|
||||
}
|
||||
_processing_sem.signal();
|
||||
} while (has_operation());
|
||||
}
|
||||
}
|
||||
|
||||
bool HandshakeState::can_process_handshake() {
|
||||
@ -465,60 +461,78 @@ bool HandshakeState::possibly_can_process_handshake() {
|
||||
}
|
||||
}
|
||||
|
||||
bool HandshakeState::claim_handshake(bool is_direct) {
|
||||
if (!_processing_sem.trywait()) {
|
||||
bool HandshakeState::claim_handshake() {
|
||||
if (!_lock.try_lock()) {
|
||||
return false;
|
||||
}
|
||||
if (has_specific_operation(is_direct)){
|
||||
return true;
|
||||
// Operations are added lock free and then the poll is armed.
|
||||
// If all handshake operations for the handshakee are finished and someone
|
||||
// just adds an operation we may see it here. But if the handshakee is not
|
||||
// armed yet it is not safe to proceed.
|
||||
if (have_non_self_executable_operation()) {
|
||||
if (SafepointMechanism::local_poll_armed(_handshakee)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_processing_sem.signal();
|
||||
_lock.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
HandshakeState::ProcessResult HandshakeState::try_process(HandshakeOperation* op) {
|
||||
bool is_direct = op->is_direct();
|
||||
|
||||
if (!has_specific_operation(is_direct)){
|
||||
HandshakeState::ProcessResult HandshakeState::try_process(HandshakeOperation* match_op) {
|
||||
if (!has_operation()) {
|
||||
// JT has already cleared its handshake
|
||||
return _no_operation;
|
||||
return HandshakeState::_no_operation;
|
||||
}
|
||||
|
||||
if (!possibly_can_process_handshake()) {
|
||||
// JT is observed in an unsafe state, it must notice the handshake itself
|
||||
return _not_safe;
|
||||
return HandshakeState::_not_safe;
|
||||
}
|
||||
|
||||
// Claim the semaphore if there still an operation to be executed.
|
||||
if (!claim_handshake(is_direct)) {
|
||||
return _state_busy;
|
||||
// Claim the mutex if there still an operation to be executed.
|
||||
if (!claim_handshake()) {
|
||||
return HandshakeState::_claim_failed;
|
||||
}
|
||||
|
||||
// Check if the handshake operation is the same as the one we meant to execute. The
|
||||
// handshake could have been already processed by the handshakee and a new handshake
|
||||
// by another JavaThread might be in progress.
|
||||
if (is_direct && op != _operation_direct) {
|
||||
_processing_sem.signal();
|
||||
return _no_operation;
|
||||
}
|
||||
|
||||
// If we own the semaphore at this point and while owning the semaphore
|
||||
// If we own the mutex at this point and while owning the mutex we
|
||||
// can observe a safe state the thread cannot possibly continue without
|
||||
// getting caught by the semaphore.
|
||||
ProcessResult pr = _not_safe;
|
||||
if (can_process_handshake()) {
|
||||
guarantee(!_processing_sem.trywait(), "we should already own the semaphore");
|
||||
log_trace(handshake)("Processing handshake by %s", Thread::current()->is_VM_thread() ? "VMThread" : "Handshaker");
|
||||
_active_handshaker = Thread::current();
|
||||
op->do_handshake(_handshakee);
|
||||
_active_handshaker = NULL;
|
||||
// Disarm after we have executed the operation.
|
||||
clear_handshake(is_direct);
|
||||
pr = _success;
|
||||
// getting caught by the mutex.
|
||||
if (!can_process_handshake()) {
|
||||
_lock.unlock();
|
||||
return HandshakeState::_not_safe;
|
||||
}
|
||||
|
||||
// Release the thread
|
||||
_processing_sem.signal();
|
||||
Thread* current_thread = Thread::current();
|
||||
|
||||
return pr;
|
||||
HandshakeState::ProcessResult pr_ret = HandshakeState::_processed;
|
||||
int executed = 0;
|
||||
|
||||
do {
|
||||
HandshakeOperation* op = pop();
|
||||
if (op != NULL) {
|
||||
assert(SafepointMechanism::local_poll_armed(_handshakee), "Must be");
|
||||
assert(op->_target == NULL || _handshakee == op->_target, "Wrong thread");
|
||||
log_trace(handshake)("Processing handshake " INTPTR_FORMAT " by %s(%s)", p2i(op),
|
||||
op == match_op ? "handshaker" : "cooperative",
|
||||
current_thread->is_VM_thread() ? "VM Thread" : "JavaThread");
|
||||
|
||||
if (op == match_op) {
|
||||
pr_ret = HandshakeState::_succeeded;
|
||||
}
|
||||
|
||||
_active_handshaker = current_thread;
|
||||
op->do_handshake(_handshakee);
|
||||
_active_handshaker = NULL;
|
||||
|
||||
executed++;
|
||||
}
|
||||
} while (have_non_self_executable_operation());
|
||||
|
||||
_lock.unlock();
|
||||
|
||||
log_trace(handshake)("%s(" INTPTR_FORMAT ") executed %d ops for JavaThread: " INTPTR_FORMAT " %s target op: " INTPTR_FORMAT,
|
||||
current_thread->is_VM_thread() ? "VM Thread" : "JavaThread",
|
||||
p2i(current_thread), executed, p2i(_handshakee),
|
||||
pr_ret == HandshakeState::_succeeded ? "including" : "excluding", p2i(match_op));
|
||||
return pr_ret;
|
||||
}
|
||||
|
@ -27,86 +27,99 @@
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/iterator.hpp"
|
||||
#include "runtime/semaphore.hpp"
|
||||
#include "utilities/autoRestore.hpp"
|
||||
#include "runtime/flags/flagSetting.hpp"
|
||||
#include "runtime/mutex.hpp"
|
||||
#include "utilities/filterQueue.hpp"
|
||||
|
||||
class HandshakeOperation;
|
||||
class JavaThread;
|
||||
|
||||
// A handshake closure is a callback that is executed for each JavaThread
|
||||
// while that thread is in a safepoint safe state. The callback is executed
|
||||
// either by the target JavaThread itself or by the VMThread while keeping
|
||||
// the target thread in a blocked state. A handshake can be performed with a
|
||||
// single JavaThread as well. In that case, the callback is executed either
|
||||
// by the target JavaThread itself or, depending on whether the operation is
|
||||
// a direct handshake or not, by the JavaThread that requested the handshake
|
||||
// or the VMThread respectively.
|
||||
class HandshakeClosure : public ThreadClosure {
|
||||
// A handshake closure is a callback that is executed for a JavaThread
|
||||
// while it is in a safepoint/handshake-safe state. Depending on the
|
||||
// nature of the closure, the callback may be executed by the initiating
|
||||
// thread, the target thread, or the VMThread. If the callback is not executed
|
||||
// by the target thread it will remain in a blocked state until the callback completes.
|
||||
class HandshakeClosure : public ThreadClosure, public CHeapObj<mtThread> {
|
||||
const char* const _name;
|
||||
public:
|
||||
HandshakeClosure(const char* name) : _name(name) {}
|
||||
const char* name() const {
|
||||
return _name;
|
||||
}
|
||||
virtual ~HandshakeClosure() {}
|
||||
const char* name() const { return _name; }
|
||||
virtual bool is_async() { return false; }
|
||||
virtual void do_thread(Thread* thread) = 0;
|
||||
};
|
||||
|
||||
class AsyncHandshakeClosure : public HandshakeClosure {
|
||||
public:
|
||||
AsyncHandshakeClosure(const char* name) : HandshakeClosure(name) {}
|
||||
virtual ~AsyncHandshakeClosure() {}
|
||||
virtual bool is_async() { return true; }
|
||||
};
|
||||
|
||||
class Handshake : public AllStatic {
|
||||
public:
|
||||
// Execution of handshake operation
|
||||
static void execute(HandshakeClosure* hs_cl);
|
||||
static bool execute(HandshakeClosure* hs_cl, JavaThread* target);
|
||||
static bool execute_direct(HandshakeClosure* hs_cl, JavaThread* target);
|
||||
static void execute(HandshakeClosure* hs_cl);
|
||||
static void execute(HandshakeClosure* hs_cl, JavaThread* target);
|
||||
static void execute(AsyncHandshakeClosure* hs_cl, JavaThread* target);
|
||||
};
|
||||
|
||||
// The HandshakeState keeps track of an ongoing handshake for this JavaThread.
|
||||
// VMThread/Handshaker and JavaThread are serialized with semaphore _processing_sem
|
||||
// making sure the operation is only done by either VMThread/Handshaker on behalf
|
||||
// of the JavaThread or by the target JavaThread itself.
|
||||
// VMThread/Handshaker and JavaThread are serialized with _lock making sure the
|
||||
// operation is only done by either VMThread/Handshaker on behalf of the
|
||||
// JavaThread or by the target JavaThread itself.
|
||||
class HandshakeState {
|
||||
// This a back reference to the JavaThread,
|
||||
// the target for all operation in the queue.
|
||||
JavaThread* _handshakee;
|
||||
HandshakeOperation* volatile _operation;
|
||||
HandshakeOperation* volatile _operation_direct;
|
||||
// The queue containing handshake operations to be performed on _handshakee.
|
||||
FilterQueue<HandshakeOperation*> _queue;
|
||||
// Provides mutual exclusion to this state and queue.
|
||||
Mutex _lock;
|
||||
// Set to the thread executing the handshake operation.
|
||||
Thread* _active_handshaker;
|
||||
|
||||
Semaphore _handshake_turn_sem; // Used to serialize direct handshakes for this JavaThread.
|
||||
Semaphore _processing_sem;
|
||||
bool _thread_in_process_handshake;
|
||||
|
||||
bool claim_handshake(bool is_direct);
|
||||
bool claim_handshake();
|
||||
bool possibly_can_process_handshake();
|
||||
bool can_process_handshake();
|
||||
void clear_handshake(bool is_direct);
|
||||
|
||||
void process_self_inner();
|
||||
|
||||
public:
|
||||
HandshakeState();
|
||||
bool have_non_self_executable_operation();
|
||||
HandshakeOperation* pop_for_self();
|
||||
HandshakeOperation* pop();
|
||||
|
||||
void set_handshakee(JavaThread* thread) { _handshakee = thread; }
|
||||
public:
|
||||
HandshakeState(JavaThread* thread);
|
||||
|
||||
void set_operation(HandshakeOperation* op);
|
||||
bool has_operation() const { return _operation != NULL || _operation_direct != NULL; }
|
||||
bool has_specific_operation(bool is_direct) const {
|
||||
return is_direct ? _operation_direct != NULL : _operation != NULL;
|
||||
void add_operation(HandshakeOperation* op);
|
||||
|
||||
bool has_operation() {
|
||||
return !_queue.is_empty();
|
||||
}
|
||||
|
||||
void process_by_self() {
|
||||
if (!_thread_in_process_handshake) {
|
||||
AutoModifyRestore<bool> temporarily(_thread_in_process_handshake, true);
|
||||
process_self_inner();
|
||||
}
|
||||
// Both _queue and _lock must be checked. If a thread has seen this _handshakee
|
||||
// as safe it will execute all possible handshake operations in a loop while
|
||||
// holding _lock. We use lock free addition to the queue, which means it is
|
||||
// possible for the queue to be seen as empty by _handshakee but as non-empty
|
||||
// by the thread executing in the loop. To avoid the _handshakee continuing
|
||||
// while handshake operations are being executed, the _handshakee
|
||||
// must take slow path, process_by_self(), if _lock is held.
|
||||
bool should_process() {
|
||||
return !_queue.is_empty() || _lock.is_locked();
|
||||
}
|
||||
|
||||
void process_by_self();
|
||||
|
||||
enum ProcessResult {
|
||||
_no_operation = 0,
|
||||
_not_safe,
|
||||
_state_busy,
|
||||
_success,
|
||||
_claim_failed,
|
||||
_processed,
|
||||
_succeeded,
|
||||
_number_states
|
||||
};
|
||||
ProcessResult try_process(HandshakeOperation* op);
|
||||
ProcessResult try_process(HandshakeOperation* match_op);
|
||||
|
||||
Thread* _active_handshaker;
|
||||
Thread* active_handshaker() const { return _active_handshaker; }
|
||||
};
|
||||
|
||||
|
@ -132,10 +132,6 @@ class ThreadInVMForHandshake : public ThreadStateTransition {
|
||||
void transition_back() {
|
||||
// This can be invoked from transition states and must return to the original state properly
|
||||
assert(_thread->thread_state() == _thread_in_vm, "should only call when leaving VM after handshake");
|
||||
// Change to transition state and ensure it is seen by the VM thread.
|
||||
_thread->set_thread_state_fence(_thread_in_vm_trans);
|
||||
|
||||
SafepointMechanism::process_if_requested(_thread);
|
||||
|
||||
_thread->set_thread_state(_original_state);
|
||||
|
||||
@ -156,6 +152,9 @@ class ThreadInVMForHandshake : public ThreadStateTransition {
|
||||
}
|
||||
|
||||
thread->set_thread_state(_thread_in_vm);
|
||||
|
||||
// Threads shouldn't block if they are in the middle of printing, but...
|
||||
ttyLocker::break_tty_lock_for_safepoint(os::current_thread_id());
|
||||
}
|
||||
|
||||
~ThreadInVMForHandshake() {
|
||||
|
@ -193,7 +193,7 @@ void assert_lock_strong(const Mutex* lock) {
|
||||
}
|
||||
|
||||
void assert_locked_or_safepoint_or_handshake(const Mutex* lock, const JavaThread* thread) {
|
||||
if (Thread::current() == thread->active_handshaker()) return;
|
||||
if (thread->is_handshake_safe_for(Thread::current())) return;
|
||||
assert_locked_or_safepoint(lock);
|
||||
}
|
||||
#endif
|
||||
|
@ -78,8 +78,8 @@ void SafepointMechanism::process(JavaThread *thread) {
|
||||
OrderAccess::loadload();
|
||||
SafepointSynchronize::block(thread);
|
||||
}
|
||||
if (thread->has_handshake()) {
|
||||
thread->handshake_process_by_self();
|
||||
if (thread->handshake_state()->should_process()) {
|
||||
thread->handshake_state()->process_by_self(); // Recursive
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ void SafepointMechanism::process_if_requested_slow(JavaThread *thread) {
|
||||
disarm_local_poll_release(thread);
|
||||
// We might have disarmed next safepoint/handshake
|
||||
OrderAccess::storeload();
|
||||
if (global_poll() || thread->has_handshake()) {
|
||||
if (global_poll() || thread->handshake_state()->has_operation()) {
|
||||
arm_local_poll(thread);
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ class SafepointMechanism : public AllStatic {
|
||||
static void* poll_armed_value() { return _poll_armed_value; }
|
||||
static void* poll_disarmed_value() { return _poll_disarmed_value; }
|
||||
|
||||
static inline bool local_poll_armed(JavaThread* thread);
|
||||
|
||||
static inline void disarm_local_poll(JavaThread* thread);
|
||||
static inline void disarm_local_poll_release(JavaThread* thread);
|
||||
@ -58,6 +57,7 @@ class SafepointMechanism : public AllStatic {
|
||||
// between the armed and disarmed value by masking out this bit.
|
||||
const static intptr_t _poll_bit = 8;
|
||||
public:
|
||||
static inline bool local_poll_armed(JavaThread* thread);
|
||||
static intptr_t poll_bit() { return _poll_bit; }
|
||||
|
||||
static address get_polling_page() { return _polling_page; }
|
||||
|
@ -467,12 +467,11 @@ Thread::~Thread() {
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
// A JavaThread is considered "dangling" if it is not the current
|
||||
// thread, has been added the Threads list, the system is not at a
|
||||
// safepoint and the Thread is not "protected".
|
||||
//
|
||||
// A JavaThread is considered dangling if it not handshake-safe with respect to
|
||||
// the current thread, it is not on a ThreadsList, or not at safepoint.
|
||||
void Thread::check_for_dangling_thread_pointer(Thread *thread) {
|
||||
assert(!thread->is_Java_thread() || Thread::current() == thread ||
|
||||
assert(!thread->is_Java_thread() ||
|
||||
thread->as_Java_thread()->is_handshake_safe_for(Thread::current()) ||
|
||||
!thread->as_Java_thread()->on_thread_list() ||
|
||||
SafepointSynchronize::is_at_safepoint() ||
|
||||
ThreadsSMRSupport::is_a_protected_JavaThread_with_lock(thread->as_Java_thread()),
|
||||
@ -837,7 +836,7 @@ bool JavaThread::wait_for_ext_suspend_completion(int retries, int delay,
|
||||
//
|
||||
bool
|
||||
JavaThread::is_thread_fully_suspended(bool wait_for_suspend, uint32_t *bits) {
|
||||
if (this != JavaThread::current()) {
|
||||
if (this != Thread::current()) {
|
||||
// "other" threads require special handling.
|
||||
if (wait_for_suspend) {
|
||||
// We are allowed to wait for the external suspend to complete
|
||||
@ -1715,7 +1714,6 @@ void JavaThread::initialize() {
|
||||
_SleepEvent = ParkEvent::Allocate(this);
|
||||
// Setup safepoint state info for this thread
|
||||
ThreadSafepointState::create(this);
|
||||
_handshake.set_handshakee(this);
|
||||
|
||||
debug_only(_java_call_counter = 0);
|
||||
|
||||
@ -1733,7 +1731,7 @@ void JavaThread::initialize() {
|
||||
}
|
||||
|
||||
JavaThread::JavaThread(bool is_attaching_via_jni) :
|
||||
Thread() {
|
||||
Thread(), _handshake(this) {
|
||||
initialize();
|
||||
if (is_attaching_via_jni) {
|
||||
_jni_attach_state = _attaching_via_jni;
|
||||
@ -1848,7 +1846,7 @@ static void compiler_thread_entry(JavaThread* thread, TRAPS);
|
||||
static void sweeper_thread_entry(JavaThread* thread, TRAPS);
|
||||
|
||||
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
|
||||
Thread() {
|
||||
Thread(), _handshake(this) {
|
||||
initialize();
|
||||
_jni_attach_state = _not_attaching_via_jni;
|
||||
set_entry_point(entry_point);
|
||||
@ -2406,7 +2404,8 @@ void JavaThread::handle_special_runtime_exit_condition(bool check_asyncs) {
|
||||
|
||||
void JavaThread::send_thread_stop(oop java_throwable) {
|
||||
ResourceMark rm;
|
||||
assert(Thread::current()->is_VM_thread() || Thread::current() == this, "should be in the vm thread");
|
||||
assert(is_handshake_safe_for(Thread::current()),
|
||||
"should be self or handshakee");
|
||||
|
||||
// Do not throw asynchronous exceptions against the compiler thread
|
||||
// (the compiler thread should not be a Java thread -- fix in 1.4.2)
|
||||
|
@ -1354,24 +1354,12 @@ class JavaThread: public Thread {
|
||||
// Support for thread handshake operations
|
||||
HandshakeState _handshake;
|
||||
public:
|
||||
void set_handshake_operation(HandshakeOperation* op) {
|
||||
_handshake.set_operation(op);
|
||||
}
|
||||
HandshakeState* handshake_state() { return &_handshake; }
|
||||
|
||||
bool has_handshake() const {
|
||||
return _handshake.has_operation();
|
||||
}
|
||||
|
||||
void handshake_process_by_self() {
|
||||
_handshake.process_by_self();
|
||||
}
|
||||
|
||||
HandshakeState::ProcessResult handshake_try_process(HandshakeOperation* op) {
|
||||
return _handshake.try_process(op);
|
||||
}
|
||||
|
||||
Thread* active_handshaker() const {
|
||||
return _handshake.active_handshaker();
|
||||
// A JavaThread can always safely operate on it self and other threads
|
||||
// can do it safely if they are the active handshaker.
|
||||
bool is_handshake_safe_for(Thread* th) const {
|
||||
return _handshake.active_handshaker() == th || this == th;
|
||||
}
|
||||
|
||||
// Suspend/resume support for JavaThread
|
||||
|
86
src/hotspot/share/utilities/filterQueue.hpp
Normal file
86
src/hotspot/share/utilities/filterQueue.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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_UTILITIES_FILTERQUEUE_HPP
|
||||
#define SHARE_UTILITIES_FILTERQUEUE_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
|
||||
// The FilterQueue is FIFO with the ability to skip over queued items.
|
||||
// The skipping is controlled by using a filter when popping.
|
||||
// It also supports lock free pushes, while popping (including contains())
|
||||
// needs to be externally serialized.
|
||||
template <class E>
|
||||
class FilterQueue {
|
||||
private:
|
||||
class Node : public CHeapObj<mtInternal> {
|
||||
public:
|
||||
Node(const E& e): _next(NULL), _data(e) { }
|
||||
Node* _next;
|
||||
E _data;
|
||||
};
|
||||
|
||||
Node* _first;
|
||||
Node* load_first() {
|
||||
return Atomic::load_acquire(&_first);
|
||||
}
|
||||
|
||||
static bool match_all(E d) { return true; }
|
||||
|
||||
public:
|
||||
FilterQueue() : _first(NULL) { }
|
||||
|
||||
bool is_empty() {
|
||||
return load_first() == NULL;
|
||||
}
|
||||
|
||||
// Adds an item to the queue in a MT safe way, re-entrant.
|
||||
void push(E data);
|
||||
|
||||
// Applies the match_func to the items in the queue until match_func returns
|
||||
// true and then returns true, or there is no more items and then returns
|
||||
// false. Items pushed after execution starts will not have match_func
|
||||
// applied. The method is not re-entrant and must be executed mutually
|
||||
// exclusive to other contains and pops calls.
|
||||
template <typename MATCH_FUNC>
|
||||
bool contains(MATCH_FUNC& match_func);
|
||||
|
||||
// Same as pop(MATCH_FUNC& match_func) but matches everything, thus returning
|
||||
// the first inserted item.
|
||||
E pop() {
|
||||
return pop(match_all);
|
||||
}
|
||||
|
||||
// Applies the match_func to each item in the queue and returns the first
|
||||
// inserted item for which match_func returns true. Returns false if there are
|
||||
// no matches or the queue is empty. Any pushed item before execution is
|
||||
// complete may or may not have match_func applied. The method is not
|
||||
// re-entrant and must be executed mutual exclusive to other contains and pops
|
||||
// calls.
|
||||
template <typename MATCH_FUNC>
|
||||
E pop(MATCH_FUNC& match_func);
|
||||
};
|
||||
|
||||
#endif
|
114
src/hotspot/share/utilities/filterQueue.inline.hpp
Normal file
114
src/hotspot/share/utilities/filterQueue.inline.hpp
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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_UTILITIES_FILTERQUEUE_INLINE_HPP
|
||||
#define SHARE_UTILITIES_FILTERQUEUE_INLINE_HPP
|
||||
|
||||
#include "utilities/filterQueue.hpp"
|
||||
#include "utilities/spinYield.hpp"
|
||||
|
||||
template <class E>
|
||||
void FilterQueue<E>::push(E data) {
|
||||
Node* head;
|
||||
Node* insnode = new Node(data);
|
||||
SpinYield yield(SpinYield::default_spin_limit * 10); // Very unlikely with multiple failed CAS.
|
||||
while (true){
|
||||
head = load_first();
|
||||
insnode->_next = head;
|
||||
if (Atomic::cmpxchg(&_first, head, insnode) == head) {
|
||||
break;
|
||||
}
|
||||
yield.wait();
|
||||
}
|
||||
}
|
||||
|
||||
// MT-Unsafe, external serialization needed.
|
||||
template <class E>
|
||||
template <typename MATCH_FUNC>
|
||||
bool FilterQueue<E>::contains(MATCH_FUNC& match_func) {
|
||||
Node* cur = load_first();
|
||||
if (cur == NULL) {
|
||||
return false;
|
||||
}
|
||||
do {
|
||||
if (match_func(cur->_data)) {
|
||||
return true;
|
||||
}
|
||||
cur = cur->_next;
|
||||
} while (cur != NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
// MT-Unsafe, external serialization needed.
|
||||
template <class E>
|
||||
template <typename MATCH_FUNC>
|
||||
E FilterQueue<E>::pop(MATCH_FUNC& match_func) {
|
||||
Node* first = load_first();
|
||||
Node* cur = first;
|
||||
Node* prev = NULL;
|
||||
Node* match = NULL;
|
||||
Node* match_prev = NULL;
|
||||
|
||||
if (cur == NULL) {
|
||||
return (E)NULL;
|
||||
}
|
||||
SpinYield yield(SpinYield::default_spin_limit * 10); // Very unlikely with multiple failed CAS.
|
||||
do {
|
||||
do {
|
||||
if (match_func(cur->_data)) {
|
||||
match = cur;
|
||||
match_prev = prev;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->_next;
|
||||
} while (cur != NULL);
|
||||
|
||||
if (match == NULL) {
|
||||
return (E)NULL;
|
||||
}
|
||||
|
||||
if (match_prev == NULL) {
|
||||
// Working on first
|
||||
if (Atomic::cmpxchg(&_first, match, match->_next) == match) {
|
||||
E ret = match->_data;
|
||||
delete match;
|
||||
return ret;
|
||||
}
|
||||
yield.wait();
|
||||
// Failed, we need to restart to know the Node prior to the match.
|
||||
first = load_first();
|
||||
cur = first;
|
||||
prev = NULL;
|
||||
match = NULL;
|
||||
match_prev = NULL;
|
||||
} else {
|
||||
match_prev->_next = match->_next;
|
||||
E ret = match->_data;
|
||||
delete match;
|
||||
return ret;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
#endif // SHARE_UTILITIES_FILTERQUEUE_INLINE_HPP
|
186
test/hotspot/gtest/utilities/test_filterQueue.cpp
Normal file
186
test/hotspot/gtest/utilities/test_filterQueue.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 "runtime/mutex.hpp"
|
||||
#include "utilities/filterQueue.inline.hpp"
|
||||
#include "threadHelper.inline.hpp"
|
||||
#include "unittest.hpp"
|
||||
|
||||
// EXPECT_EQ(cht_get_copy(cht, thr, stl2), val2) << "Get did not find value.";
|
||||
|
||||
static bool match_all(uintptr_t val) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool match_1(uintptr_t val) {
|
||||
return 1 == val;
|
||||
}
|
||||
|
||||
static bool match_2(uintptr_t val) {
|
||||
return 2 == val;
|
||||
}
|
||||
|
||||
static bool match_3(uintptr_t val) {
|
||||
return 3 == val;
|
||||
}
|
||||
|
||||
static bool match_4(uintptr_t val) {
|
||||
return 4 == val;
|
||||
}
|
||||
|
||||
static bool match_even(uintptr_t val) {
|
||||
return (val & 0x1) == 0x0;
|
||||
}
|
||||
|
||||
static void is_empty(FilterQueue<uintptr_t>& queue) {
|
||||
EXPECT_EQ(queue.is_empty(), true) << "Must be empty.";
|
||||
EXPECT_EQ(queue.contains(match_1), false) << "Must be empty.";
|
||||
EXPECT_EQ(queue.contains(match_all), false) << "Must be empty.";
|
||||
EXPECT_EQ(queue.pop(match_all), (uintptr_t)0) << "Must be empty.";
|
||||
}
|
||||
|
||||
TEST_VM(FilterQueue, one) {
|
||||
FilterQueue<uintptr_t> queue;
|
||||
is_empty(queue);
|
||||
queue.push(1);
|
||||
EXPECT_EQ(queue.is_empty(), false) << "Must be not empty.";
|
||||
EXPECT_EQ(queue.contains(match_1), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_all), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_even), false) << "Must not contain a value.";
|
||||
EXPECT_EQ(queue.pop(match_all), (uintptr_t)1) << "Must not be empty.";
|
||||
is_empty(queue);
|
||||
}
|
||||
|
||||
TEST_VM(FilterQueue, two) {
|
||||
FilterQueue<uintptr_t> queue;
|
||||
|
||||
queue.push(1);
|
||||
queue.push(2);
|
||||
|
||||
EXPECT_EQ(queue.is_empty(), false) << "Must be not empty.";
|
||||
EXPECT_EQ(queue.contains(match_1), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_2), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_all), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_even), true) << "Must contain a value.";
|
||||
|
||||
EXPECT_EQ(queue.pop(match_all), (uintptr_t)1) << "Must not be empty.";
|
||||
|
||||
EXPECT_EQ(queue.is_empty(), false) << "Must be not empty.";
|
||||
EXPECT_EQ(queue.contains(match_1), false) << "Must not contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_2), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_all), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_even), true) << "Must contain a value.";
|
||||
|
||||
queue.push(3);
|
||||
|
||||
EXPECT_EQ(queue.pop(match_even), (uintptr_t)2) << "Must not be empty.";
|
||||
|
||||
queue.push(2);
|
||||
|
||||
EXPECT_EQ(queue.pop(match_even), (uintptr_t)2) << "Must not be empty.";
|
||||
|
||||
EXPECT_EQ(queue.is_empty(), false) << "Must be not empty.";
|
||||
EXPECT_EQ(queue.contains(match_3), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_2), false) << "Must not contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_all), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_even), false) << "Must not contain a value.";
|
||||
|
||||
EXPECT_EQ(queue.pop(match_even), (uintptr_t)0) << "Must be empty.";
|
||||
EXPECT_EQ(queue.pop(match_all), (uintptr_t)3) << "Must not be empty.";
|
||||
|
||||
is_empty(queue);
|
||||
}
|
||||
|
||||
TEST_VM(FilterQueue, three) {
|
||||
FilterQueue<uintptr_t> queue;
|
||||
|
||||
queue.push(1);
|
||||
queue.push(2);
|
||||
queue.push(3);
|
||||
|
||||
EXPECT_EQ(queue.is_empty(), false) << "Must be not empty.";
|
||||
EXPECT_EQ(queue.contains(match_1), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_2), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_3), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_4), false) << "Must not contain a value.";
|
||||
|
||||
EXPECT_EQ(queue.contains(match_all), true) << "Must contain a value.";
|
||||
EXPECT_EQ(queue.contains(match_even), true) << "Must contain a value.";
|
||||
|
||||
EXPECT_EQ(queue.pop(match_even), (uintptr_t)2) << "Must not be empty.";
|
||||
EXPECT_EQ(queue.pop(match_even), (uintptr_t)0) << "Must be empty.";
|
||||
EXPECT_EQ(queue.pop(match_all), (uintptr_t)1) << "Must not be empty.";
|
||||
EXPECT_EQ(queue.pop(match_all), (uintptr_t)3) << "Must not be empty.";
|
||||
|
||||
is_empty(queue);
|
||||
}
|
||||
|
||||
class FilterQueueTestThread : public JavaTestThread {
|
||||
FilterQueue<uintptr_t>* _fq;
|
||||
Mutex* _lock;
|
||||
uintptr_t _val;
|
||||
uintptr_t _pop;
|
||||
public:
|
||||
FilterQueueTestThread(Semaphore* post, FilterQueue<uintptr_t>* fq, Mutex* lock, uintptr_t val, uintptr_t pop)
|
||||
: JavaTestThread(post), _fq(fq), _lock(lock), _val(val), _pop(pop) {
|
||||
}
|
||||
virtual void main_run() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
_fq->push(_val);
|
||||
}
|
||||
{
|
||||
do {
|
||||
MutexLocker ml(_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (_fq->contains(*this) != 0) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
for (int j = 0; j < 10; j++) {
|
||||
MutexLocker ml(_lock, Mutex::_no_safepoint_check_flag);
|
||||
while (_fq->pop(*this) == 0) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool operator()(uintptr_t val) {
|
||||
return val == _pop;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_VM(FilterQueue, stress) {
|
||||
FilterQueue<uintptr_t> queue;
|
||||
Mutex lock(Mutex::leaf, "Test Lock", true, Mutex::_safepoint_check_never);
|
||||
static const int nthreads = 4;
|
||||
Semaphore post;
|
||||
FilterQueueTestThread* threads[nthreads] = {};
|
||||
for (int i = 0; i < nthreads; ++i) {
|
||||
threads[i] = new FilterQueueTestThread(&post, &queue, &lock, i + 1, i + 2 > nthreads ? 1 : i + 2);
|
||||
threads[i]->doit();
|
||||
}
|
||||
for (uint i = 0; i < nthreads; ++i) {
|
||||
post.wait();
|
||||
}
|
||||
EXPECT_EQ(queue.is_empty(), true) << "Must be empty.";
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 AsyncHandshakeWalkStackTest
|
||||
* @library /testlibrary /test/lib
|
||||
* @build AsyncHandshakeWalkStackTest
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI AsyncHandshakeWalkStackTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import sun.hotspot.WhiteBox;
|
||||
|
||||
public class AsyncHandshakeWalkStackTest {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
int iterations = 3;
|
||||
if (args.length > 0) {
|
||||
iterations = Integer.parseInt(args[0]);
|
||||
}
|
||||
test(iterations);
|
||||
}
|
||||
|
||||
private static void test(int iterations) throws Exception {
|
||||
Thread loop_thread = new Thread(() -> run_loop(create_list()));
|
||||
Thread alloc_thread = new Thread(() -> run_alloc());
|
||||
Thread wait_thread = new Thread(() -> run_wait(new Object() {}));
|
||||
loop_thread.start();
|
||||
alloc_thread.start();
|
||||
wait_thread.start();
|
||||
|
||||
WhiteBox wb = WhiteBox.getWhiteBox();
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
System.out.println("Iteration " + i);
|
||||
System.out.flush();
|
||||
Thread.sleep(200);
|
||||
wb.asyncHandshakeWalkStack(loop_thread);
|
||||
Thread.sleep(200);
|
||||
wb.asyncHandshakeWalkStack(alloc_thread);
|
||||
Thread.sleep(200);
|
||||
wb.asyncHandshakeWalkStack(wait_thread);
|
||||
Thread.sleep(200);
|
||||
wb.asyncHandshakeWalkStack(Thread.currentThread());
|
||||
}
|
||||
}
|
||||
|
||||
static class List {
|
||||
List next;
|
||||
|
||||
List(List next) {
|
||||
this.next = next;
|
||||
}
|
||||
}
|
||||
|
||||
public static List create_list() {
|
||||
List head = new List(null);
|
||||
List elem = new List(head);
|
||||
List elem2 = new List(elem);
|
||||
List elem3 = new List(elem2);
|
||||
List elem4 = new List(elem3);
|
||||
head.next = elem4;
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
public static void run_loop(List loop) {
|
||||
while (loop.next != null) {
|
||||
loop = loop.next;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] array;
|
||||
|
||||
public static void run_alloc() {
|
||||
while (true) {
|
||||
// Write to public static to ensure the byte array escapes.
|
||||
array = new byte[4096];
|
||||
}
|
||||
}
|
||||
|
||||
public static void run_wait(Object lock) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ import java.io.*;
|
||||
|
||||
public class HandshakeDirectTest implements Runnable {
|
||||
static final int WORKING_THREADS = 32;
|
||||
static final int DIRECT_HANDSHAKES_MARK = 50000;
|
||||
static final int DIRECT_HANDSHAKES_MARK = 500000;
|
||||
static Thread[] workingThreads = new Thread[WORKING_THREADS];
|
||||
static Semaphore[] handshakeSem = new Semaphore[WORKING_THREADS];
|
||||
static Object[] locks = new Object[WORKING_THREADS];
|
||||
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 MixedHandshakeWalkStackTest
|
||||
* @library /testlibrary /test/lib
|
||||
* @build MixedHandshakeWalkStackTest
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI MixedHandshakeWalkStackTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import sun.hotspot.WhiteBox;
|
||||
|
||||
public class MixedHandshakeWalkStackTest {
|
||||
public static Thread testThreads[];
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
testThreads = new Thread[Runtime.getRuntime().availableProcessors()];
|
||||
for (int i = 0; i < testThreads.length; i++) {
|
||||
testThreads[i] = new Thread(() -> handshake());
|
||||
}
|
||||
|
||||
for (Thread t : testThreads) {
|
||||
t.start();
|
||||
}
|
||||
|
||||
handshake();
|
||||
|
||||
for (Thread t : testThreads) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
public static void handshake() {
|
||||
WhiteBox wb = WhiteBox.getWhiteBox();
|
||||
java.util.concurrent.ThreadLocalRandom rand = java.util.concurrent.ThreadLocalRandom.current();
|
||||
long end = System.currentTimeMillis() + 20000;
|
||||
while (end > System.currentTimeMillis()) {
|
||||
wb.asyncHandshakeWalkStack(testThreads[rand.nextInt(testThreads.length)]);
|
||||
wb.handshakeWalkStack(testThreads[rand.nextInt(testThreads.length)], false);
|
||||
wb.handshakeWalkStack(testThreads[rand.nextInt(testThreads.length)], true);
|
||||
}
|
||||
}
|
||||
}
|
@ -587,6 +587,7 @@ public class WhiteBox {
|
||||
|
||||
// Handshakes
|
||||
public native int handshakeWalkStack(Thread t, boolean all_threads);
|
||||
public native void asyncHandshakeWalkStack(Thread t);
|
||||
|
||||
// Returns true on linux if library has the noexecstack flag set.
|
||||
public native boolean checkLibSpecifiesNoexecstack(String libfilename);
|
||||
|
Loading…
x
Reference in New Issue
Block a user