8238761: Asynchronous handshakes

Reviewed-by: pchilanomate, dcubed, dholmes, coleenp, sspitsyn
This commit is contained in:
Robbin Ehn 2020-09-29 08:50:54 +00:00
parent 6d19fe65d1
commit 6bddeb709d
24 changed files with 975 additions and 375 deletions

@ -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

@ -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

@ -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

@ -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);