--- old/src/hotspot/share/runtime/biasedLocking.cpp 2020-01-13 15:15:50.906592322 +0000 +++ new/src/hotspot/share/runtime/biasedLocking.cpp 2020-01-13 15:15:50.077571952 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -622,7 +622,7 @@ p2i(biaser), p2i(obj())); RevokeOneBias revoke(obj, requester, biaser); - bool executed = Handshake::execute(&revoke, biaser); + bool executed = Handshake::execute(&revoke, biaser, true); if (revoke.status_code() == NOT_REVOKED) { return NOT_REVOKED; } @@ -668,7 +668,7 @@ void BiasedLocking::walk_stack_and_revoke(oop obj, JavaThread* biased_locker) { assert(!SafepointSynchronize::is_at_safepoint() || !SafepointMechanism::uses_thread_local_poll(), "if SafepointMechanism::uses_thread_local_poll() is enabled this should always be executed outside safepoints"); - assert(Thread::current() == biased_locker || Thread::current()->is_VM_thread(), "wrong thread"); + assert(Thread::current() == biased_locker || Thread::current() == biased_locker->get_active_handshaker(), "wrong thread"); markWord mark = obj->mark(); assert(mark.biased_locker() == biased_locker && --- old/src/hotspot/share/runtime/handshake.cpp 2020-01-13 15:15:51.848615468 +0000 +++ new/src/hotspot/share/runtime/handshake.cpp 2020-01-13 15:15:51.030595369 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -37,51 +37,44 @@ #include "utilities/formatBuffer.hpp" #include "utilities/preserveException.hpp" -class HandshakeOperation: public StackObj { -public: - virtual void do_handshake(JavaThread* thread) = 0; -}; -class HandshakeThreadsOperation: public HandshakeOperation { - static Semaphore _done; +class HandshakeOperation: public StackObj { HandshakeClosure* _handshake_cl; + int64_t _pending_threads; bool _executed; + bool _is_direct; public: - HandshakeThreadsOperation(HandshakeClosure* cl) : _handshake_cl(cl), _executed(false) {} + HandshakeOperation(HandshakeClosure* cl) : _handshake_cl(cl), _pending_threads(1), _executed(false), _is_direct(false) {} void do_handshake(JavaThread* thread); - bool thread_has_completed() { return _done.trywait(); } + bool is_completed() { + assert(_pending_threads >= 0, "_pending_threads cannot be negative"); + return _pending_threads == 0; + } + void add_target_count(int count) { Atomic::add(&_pending_threads, count); } bool executed() const { return _executed; } const char* name() { return _handshake_cl->name(); } + void set_isdirect() { _is_direct = true; } + bool is_direct() { return _is_direct; } + #ifdef ASSERT void check_state() { - assert(!_done.trywait(), "Must be zero"); + assert(_pending_threads == 0, "Must be zero"); } #endif }; -Semaphore HandshakeThreadsOperation::_done(0); - class VM_Handshake: public VM_Operation { const jlong _handshake_timeout; public: bool evaluate_at_safepoint() const { return false; } protected: - HandshakeThreadsOperation* const _op; + HandshakeOperation* const _op; - VM_Handshake(HandshakeThreadsOperation* op) : + VM_Handshake(HandshakeOperation* op) : _handshake_timeout(TimeHelper::millis_to_counter(HandshakeTimeout)), _op(op) {} - void set_handshake(JavaThread* target) { - target->set_handshake_operation(_op); - } - - // This method returns true for threads completed their operation - // and true for threads canceled their operation. - // A cancellation can happen if the thread is exiting. - bool poll_for_completed_thread() { return _op->thread_has_completed(); } - bool handshake_has_timed_out(jlong start_time); static void handle_timeout(); }; @@ -121,12 +114,10 @@ class VM_HandshakeOneThread: public VM_Handshake { JavaThread* _target; public: - VM_HandshakeOneThread(HandshakeThreadsOperation* op, JavaThread* target) : + VM_HandshakeOneThread(HandshakeOperation* op, JavaThread* target) : VM_Handshake(op), _target(target) {} void doit() { - DEBUG_ONLY(_op->check_state();) - jlong start_time_ns = 0; if (log_is_enabled(Info, handshake)) { start_time_ns = os::javaTimeNanos(); @@ -134,7 +125,7 @@ ThreadsListHandle tlh; if (tlh.includes(_target)) { - set_handshake(_target); + _target->set_handshake_operation(_op); } else { log_handshake_info(start_time_ns, _op->name(), 0, 0, "(thread dead)"); return; @@ -147,8 +138,8 @@ if (handshake_has_timed_out(timeout_start_time)) { handle_timeout(); } - by_vm_thread = _target->handshake_try_process_by_vmThread(); - } while (!poll_for_completed_thread()); + by_vm_thread = _target->handshake_try_process(_op); + } while (!_op->is_completed()); DEBUG_ONLY(_op->check_state();) log_handshake_info(start_time_ns, _op->name(), 1, by_vm_thread ? 1 : 0); } @@ -160,11 +151,9 @@ class VM_HandshakeAllThreads: public VM_Handshake { public: - VM_HandshakeAllThreads(HandshakeThreadsOperation* op) : VM_Handshake(op) {} + VM_HandshakeAllThreads(HandshakeOperation* op) : VM_Handshake(op) {} void doit() { - DEBUG_ONLY(_op->check_state();) - jlong start_time_ns = 0; if (log_is_enabled(Info, handshake)) { start_time_ns = os::javaTimeNanos(); @@ -174,7 +163,7 @@ JavaThreadIteratorWithHandle jtiwh; int number_of_threads_issued = 0; for (JavaThread *thr = jtiwh.next(); thr != NULL; thr = jtiwh.next()) { - set_handshake(thr); + thr->set_handshake_operation(_op); number_of_threads_issued++; } @@ -182,10 +171,10 @@ log_handshake_info(start_time_ns, _op->name(), 0, 0); return; } + _op->add_target_count(number_of_threads_issued - 1); log_trace(handshake)("Threads signaled, begin processing blocked threads by VMThread"); const jlong start_time = os::elapsed_counter(); - int number_of_threads_completed = 0; do { // Check if handshake operation has timed out if (handshake_has_timed_out(start_time)) { @@ -198,18 +187,12 @@ jtiwh.rewind(); 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_process_by_vmthread. - if (thr->handshake_try_process_by_vmThread()) { + // hence it is skipped in handshake_try_process. + if (thr->handshake_try_process(_op)) { handshake_executed_by_vm_thread++; } } - while (poll_for_completed_thread()) { - // Includes canceled operations by exiting threads. - number_of_threads_completed++; - } - - } while (number_of_threads_issued > number_of_threads_completed); - assert(number_of_threads_issued == number_of_threads_completed, "Must be the same"); + } while (!_op->is_completed()); DEBUG_ONLY(_op->check_state();) log_handshake_info(start_time_ns, _op->name(), number_of_threads_issued, handshake_executed_by_vm_thread); @@ -245,7 +228,7 @@ bool executed() const { return _executed; } }; -void HandshakeThreadsOperation::do_handshake(JavaThread* thread) { +void HandshakeOperation::do_handshake(JavaThread* thread) { jlong start_time_ns = 0; if (log_is_enabled(Debug, handshake, task)) { start_time_ns = os::javaTimeNanos(); @@ -263,15 +246,15 @@ name(), p2i(thread), BOOL_TO_STR(Thread::current()->is_VM_thread()), completion_time); } - // Use the semaphore to inform the VM thread that we have completed the operation - _done.signal(); + // Inform VMThread/Handshaker that we have completed the operation + Atomic::dec(&_pending_threads); - // It is no longer safe to refer to 'this' as the VMThread may have destroyed this operation + // It is no longer safe to refer to 'this' as the VMThread/Handshaker may have destroyed this operation } void Handshake::execute(HandshakeClosure* thread_cl) { if (SafepointMechanism::uses_thread_local_poll()) { - HandshakeThreadsOperation cto(thread_cl); + HandshakeOperation cto(thread_cl); VM_HandshakeAllThreads handshake(&cto); VMThread::execute(&handshake); } else { @@ -280,12 +263,16 @@ } } -bool Handshake::execute(HandshakeClosure* thread_cl, JavaThread* target) { +bool Handshake::execute(HandshakeClosure* thread_cl, JavaThread* target, bool is_direct_handshake) { if (SafepointMechanism::uses_thread_local_poll()) { - HandshakeThreadsOperation cto(thread_cl); - VM_HandshakeOneThread handshake(&cto, target); - VMThread::execute(&handshake); - return handshake.executed(); + HandshakeOperation ho(thread_cl); + if (is_direct_handshake) { + direct_handshake(target, &ho); + } else { + VM_HandshakeOneThread op(&ho, target); + VMThread::execute(&op); + } + return ho.executed(); } else { VM_HandshakeFallbackOperation op(thread_cl, target); VMThread::execute(&op); @@ -293,61 +280,106 @@ } } -HandshakeState::HandshakeState() : _operation(NULL), _semaphore(1), _thread_in_process_handshake(false) { - DEBUG_ONLY(_vmthread_processing_handshake = false;) +void Handshake::direct_handshake(JavaThread* target, HandshakeOperation *op) { + JavaThread *self = (JavaThread*)Thread::current(); + op->set_isdirect(); + + jlong start_time_ns = 0; + if (log_is_enabled(Info, handshake)) { + 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; + } + + bool by_handshaker = false; + while (!op->is_completed()) { + by_handshaker = target->handshake_try_process(op); + // Check for pending handshakes to avoid possible deadlocks where our + // target is trying to handshake us. + if (SafepointMechanism::should_block(self)) { + ThreadBlockInVM tbivm(self); + } + } + DEBUG_ONLY(op->check_state();) + log_handshake_info(start_time_ns, op->name(), 1, by_handshaker ? 1 : 0); } -void HandshakeState::set_operation(JavaThread* target, HandshakeOperation* op) { - _operation = op; - SafepointMechanism::arm_local_poll_release(target); +HandshakeState::HandshakeState() : _operation(NULL), _operation_direct(NULL), _handshake_turn_sem(1), _processing_sem(1), _thread_in_process_handshake(false) { + DEBUG_ONLY(_active_handshaker = NULL;) } -void HandshakeState::clear_handshake(JavaThread* target) { - _operation = NULL; - SafepointMechanism::disarm_if_needed(target, true /* release */); +void HandshakeState::set_operation(HandshakeOperation* op) { + if (!op->is_direct()) { + _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*)Thread::current()); + _operation_direct = op; + } + SafepointMechanism::arm_local_poll_release(_thread); +} + +void HandshakeState::clear_handshake(bool is_direct) { + if (!is_direct) { + _operation = NULL; + } else { + _operation_direct = NULL; + _handshake_turn_sem.signal(); + } } -void HandshakeState::process_self_inner(JavaThread* thread) { - assert(Thread::current() == thread, "should call from thread"); - assert(!thread->is_terminated(), "should not be a terminated thread"); - assert(thread->thread_state() != _thread_blocked, "should not be in a blocked state"); - assert(thread->thread_state() != _thread_in_native, "should not be in native"); +void HandshakeState::process_self_inner() { + assert(Thread::current() == _thread, "should call from thread"); + assert(!_thread->is_terminated(), "should not be a terminated thread"); + assert(_thread->thread_state() != _thread_blocked, "should not be in a blocked state"); + assert(_thread->thread_state() != _thread_in_native, "should not be in native"); do { - ThreadInVMForHandshake tivm(thread); - if (!_semaphore.trywait()) { - _semaphore.wait_with_safepoint_check(thread); + ThreadInVMForHandshake tivm(_thread); + if (!_processing_sem.trywait()) { + _processing_sem.wait_with_safepoint_check(_thread); } + bool is_direct_handshake = false; HandshakeOperation* op = Atomic::load_acquire(&_operation); + if (op == NULL) { + op = Atomic::load_acquire(&_operation_direct); + is_direct_handshake = true; + } if (op != NULL) { - HandleMark hm(thread); - CautiouslyPreserveExceptionMark pem(thread); + HandleMark hm(_thread); + CautiouslyPreserveExceptionMark pem(_thread); // Disarm before execute the operation - clear_handshake(thread); - op->do_handshake(thread); + clear_handshake(is_direct_handshake); + op->do_handshake(_thread); } - _semaphore.signal(); + _processing_sem.signal(); } while (has_operation()); } -bool HandshakeState::vmthread_can_process_handshake(JavaThread* target) { +bool HandshakeState::can_process_handshake() { // handshake_safe may only be called with polls armed. - // VM thread controls this by first claiming the handshake via claim_handshake_for_vmthread. - return SafepointSynchronize::handshake_safe(target); + // Handshaker controls this by first claiming the handshake via claim_handshake(). + return SafepointSynchronize::handshake_safe(_thread); } -static bool possibly_vmthread_can_process_handshake(JavaThread* target) { +bool HandshakeState::possibly_can_process_handshake() { // Note that this method is allowed to produce false positives. - if (target->is_ext_suspended()) { + if (_thread->is_ext_suspended()) { return true; } - if (target->is_terminated()) { + if (_thread->is_terminated()) { return true; } - switch (target->thread_state()) { + switch (_thread->thread_state()) { case _thread_in_native: // native threads are safe if they have no java stack or have walkable stack - return !target->has_last_Java_frame() || target->frame_anchor()->walkable(); + return !_thread->has_last_Java_frame() || _thread->frame_anchor()->walkable(); case _thread_blocked: return true; @@ -357,32 +389,40 @@ } } -bool HandshakeState::claim_handshake_for_vmthread() { - if (!_semaphore.trywait()) { +bool HandshakeState::claim_handshake(bool is_direct) { + if (!_processing_sem.trywait()) { return false; } - if (has_operation()) { + if ((!is_direct && _operation != NULL) || (is_direct && _operation_direct != NULL)){ return true; } - _semaphore.signal(); + _processing_sem.signal(); return false; } -bool HandshakeState::try_process_by_vmThread(JavaThread* target) { - assert(Thread::current()->is_VM_thread(), "should call from vm thread"); +bool HandshakeState::try_process(HandshakeOperation* op) { + bool is_direct = op->is_direct(); - if (!has_operation()) { + if ((!is_direct && _operation == NULL) || (is_direct && _operation_direct == NULL)){ // JT has already cleared its handshake return false; } - if (!possibly_vmthread_can_process_handshake(target)) { + if (!possibly_can_process_handshake()) { // JT is observed in an unsafe state, it must notice the handshake itself return false; } // Claim the semaphore if there still an operation to be executed. - if (!claim_handshake_for_vmthread()) { + if (!claim_handshake(is_direct)) { + return false; + } + + // 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 false; } @@ -390,19 +430,19 @@ // can observe a safe state the thread cannot possibly continue without // getting caught by the semaphore. bool executed = false; - if (vmthread_can_process_handshake(target)) { - guarantee(!_semaphore.trywait(), "we should already own the semaphore"); - log_trace(handshake)("Processing handshake by VMThtread"); - DEBUG_ONLY(_vmthread_processing_handshake = true;) - _operation->do_handshake(target); - DEBUG_ONLY(_vmthread_processing_handshake = false;) - // Disarm after VM thread have executed the operation. - clear_handshake(target); + 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"); + DEBUG_ONLY(_active_handshaker = Thread::current();) + op->do_handshake(_thread); + DEBUG_ONLY(_active_handshaker = NULL;) + // Disarm after we have executed the operation. + clear_handshake(is_direct); executed = true; } // Release the thread - _semaphore.signal(); + _processing_sem.signal(); return executed; } --- old/src/hotspot/share/runtime/handshake.hpp 2020-01-13 15:15:52.809639080 +0000 +++ new/src/hotspot/share/runtime/handshake.hpp 2020-01-13 15:15:51.989618932 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -31,6 +31,7 @@ #include "runtime/semaphore.hpp" class JavaThread; +class HandshakeOperation; // 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 @@ -48,51 +49,52 @@ }; class Handshake : public AllStatic { + static void direct_handshake(JavaThread* target, HandshakeOperation *op); public: // Execution of handshake operation static void execute(HandshakeClosure* hs_cl); - static bool execute(HandshakeClosure* hs_cl, JavaThread* target); + static bool execute(HandshakeClosure* hs_cl, JavaThread* target, bool is_direct_handshake = false); }; -class HandshakeOperation; - // The HandshakeState keep tracks of an ongoing handshake for one JavaThread. // VM thread and JavaThread are serialized with the semaphore making sure // the operation is only done by either VM thread on behalf of the JavaThread // or the JavaThread itself. class HandshakeState { + JavaThread *_thread; HandshakeOperation* volatile _operation; + HandshakeOperation* volatile _operation_direct; - Semaphore _semaphore; + Semaphore _handshake_turn_sem; + Semaphore _processing_sem; bool _thread_in_process_handshake; - bool claim_handshake_for_vmthread(); - bool vmthread_can_process_handshake(JavaThread* target); + bool claim_handshake(bool is_direct); + bool possibly_can_process_handshake(); + bool can_process_handshake(); + void clear_handshake(bool is_direct); + + void process_self_inner(); - void clear_handshake(JavaThread* thread); - - void process_self_inner(JavaThread* thread); public: HandshakeState(); - void set_operation(JavaThread* thread, HandshakeOperation* op); + void set_thread(JavaThread *thread) { _thread = thread; } - bool has_operation() const { - return _operation != NULL; - } + void set_operation(HandshakeOperation* op); + bool has_operation() const { return _operation != NULL || _operation_direct != NULL; } - void process_by_self(JavaThread* thread) { + void process_by_self() { if (!_thread_in_process_handshake) { FlagSetting fs(_thread_in_process_handshake, true); - process_self_inner(thread); + process_self_inner(); } } - - bool try_process_by_vmThread(JavaThread* target); + bool try_process(HandshakeOperation* op); #ifdef ASSERT - bool _vmthread_processing_handshake; - bool is_vmthread_processing_handshake() const { return _vmthread_processing_handshake; } + Thread* _active_handshaker; + Thread* get_active_handshaker() const { return _active_handshaker; } #endif }; --- old/src/hotspot/share/runtime/mutexLocker.cpp 2020-01-13 15:15:53.787663111 +0000 +++ new/src/hotspot/share/runtime/mutexLocker.cpp 2020-01-13 15:15:52.962642840 +0000 @@ -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 @@ -189,7 +189,7 @@ } void assert_locked_or_safepoint_or_handshake(const Mutex* lock, const JavaThread* thread) { - if (Thread::current()->is_VM_thread() && thread->is_vmthread_processing_handshake()) return; + if (Thread::current() == thread->get_active_handshaker()) return; assert_locked_or_safepoint(lock); } #endif --- old/src/hotspot/share/runtime/safepoint.cpp 2020-01-13 15:15:54.791687780 +0000 +++ new/src/hotspot/share/runtime/safepoint.cpp 2020-01-13 15:15:53.966667509 +0000 @@ -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 @@ -494,8 +494,6 @@ assert(!cur_state->is_running(), "Thread not suspended at safepoint"); cur_state->restart(); // TSS _running assert(cur_state->is_running(), "safepoint state has not been reset"); - - SafepointMechanism::disarm_if_needed(current, false /* NO release */); } } // ~JavaThreadIteratorWithHandle @@ -736,7 +734,6 @@ } bool SafepointSynchronize::handshake_safe(JavaThread *thread) { - assert(Thread::current()->is_VM_thread(), "Must be VMThread"); if (thread->is_ext_suspended() || thread->is_terminated()) { return true; } --- old/src/hotspot/share/runtime/safepointMechanism.inline.hpp 2020-01-13 15:15:55.795712449 +0000 +++ new/src/hotspot/share/runtime/safepointMechanism.inline.hpp 2020-01-13 15:15:54.979692399 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -70,19 +70,6 @@ thread->set_polling_page(poll_disarmed_value()); } -void SafepointMechanism::disarm_if_needed(JavaThread* thread, bool memory_order_release) { - JavaThreadState jts = thread->thread_state(); - if (jts == _thread_in_native || jts == _thread_in_native_trans) { - // JavaThread will disarm itself and execute cross_modify_fence() before continuing - return; - } - if (memory_order_release) { - thread->set_polling_page_release(poll_disarmed_value()); - } else { - thread->set_polling_page(poll_disarmed_value()); - } -} - void SafepointMechanism::arm_local_poll_release(JavaThread* thread) { thread->set_polling_page_release(poll_armed_value()); } --- old/src/hotspot/share/runtime/thread.cpp 2020-01-13 15:15:56.744735767 +0000 +++ new/src/hotspot/share/runtime/thread.cpp 2020-01-13 15:15:55.912715324 +0000 @@ -1706,6 +1706,7 @@ _SleepEvent = ParkEvent::Allocate(this); // Setup safepoint state info for this thread ThreadSafepointState::create(this); + _handshake.set_thread(this); debug_only(_java_call_counter = 0); --- old/src/hotspot/share/runtime/thread.hpp 2020-01-13 15:15:57.729759969 +0000 +++ new/src/hotspot/share/runtime/thread.hpp 2020-01-13 15:15:56.904739698 +0000 @@ -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 @@ -1318,7 +1318,7 @@ HandshakeState _handshake; public: void set_handshake_operation(HandshakeOperation* op) { - _handshake.set_operation(this, op); + _handshake.set_operation(op); } bool has_handshake() const { @@ -1326,16 +1326,16 @@ } void handshake_process_by_self() { - _handshake.process_by_self(this); + _handshake.process_by_self(); } - bool handshake_try_process_by_vmThread() { - return _handshake.try_process_by_vmThread(this); + bool handshake_try_process(HandshakeOperation* op) { + return _handshake.try_process(op); } #ifdef ASSERT - bool is_vmthread_processing_handshake() const { - return _handshake.is_vmthread_processing_handshake(); + Thread* get_active_handshaker() const { + return _handshake.get_active_handshaker(); } #endif --- /dev/null 2019-10-31 15:42:43.283000000 +0000 +++ new/test/hotspot/jtreg/runtime/handshake/HandshakeDirectTest.java 2020-01-13 15:15:57.882763728 +0000 @@ -0,0 +1,128 @@ +/* + * 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 HandshakeDirectTest + * @summary This test tries to stress direct handshakes between threads while suspending them. + * @library /testlibrary /test/lib + * @build HandshakeDirectTest + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+SafepointALot -XX:BiasedLockingDecayTime=100000000 -XX:BiasedLockingBulkRebiasThreshold=1000000 -XX:BiasedLockingBulkRevokeThreshold=1000000 HandshakeDirectTest + */ + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.ThreadLocalRandom; +import java.io.*; + +public class HandshakeDirectTest implements Runnable { + static final int WORKING_THREADS = 32; + static final int DIRECT_HANDSHAKES_MARK = 50000; + static Thread[] _working_threads = new Thread[WORKING_THREADS]; + static java.util.concurrent.Semaphore[] _handshake_sem = new java.util.concurrent.Semaphore[WORKING_THREADS]; + static Object[] _locks = new Object[WORKING_THREADS]; + static boolean[] _is_biased = new boolean[WORKING_THREADS]; + static Thread _suspendresume_thread = new Thread(); + static AtomicInteger _handshake_count = new AtomicInteger(0); + + @Override + public void run() { + int me = Integer.parseInt(Thread.currentThread().getName()); + + while (true) { + try { + if (_is_biased[me] == false) { + _handshake_sem[me].acquire(); + synchronized(_locks[me]) { + _is_biased[me] = true; + } + _handshake_sem[me].release(); + } + + // Handshake directly some other worker + int handshakee = ThreadLocalRandom.current().nextInt(0, WORKING_THREADS-1); + if (handshakee == me) { + handshakee = handshakee != 0 ? handshakee - 1 : handshakee + 1; + } + _handshake_sem[handshakee].acquire(); + if (_is_biased[handshakee]) { + // Revoke biased lock + synchronized(_locks[handshakee]) { + _handshake_count.incrementAndGet(); + } + // Create new lock to be biased + _locks[handshakee] = new Object(); + _is_biased[handshakee] = false; + } + _handshake_sem[handshakee].release(); + if (_handshake_count.get() >= DIRECT_HANDSHAKES_MARK) { + break; + } + } catch(InterruptedException ie) { + } + } + } + + public static void main(String... args) throws Exception { + HandshakeDirectTest test = new HandshakeDirectTest(); + + // Initialize semaphores + for (int i = 0; i < WORKING_THREADS; i++) { + _handshake_sem[i] = new java.util.concurrent.Semaphore(1); + } + + // Initialize locks + for (int i = 0; i < WORKING_THREADS; i++) { + _locks[i] = new Object(); + } + + // Fire-up working threads. + for (int i = 0; i < WORKING_THREADS; i++) { + _working_threads[i] = new Thread(test, Integer.toString(i)); + _working_threads[i].setDaemon(true); + _working_threads[i].start(); + } + + // Fire-up suspend-resume thread + Thread _suspendresume_thread = new Thread() { + @Override + public void run() { + while (true) { + int i = ThreadLocalRandom.current().nextInt(0, WORKING_THREADS-1); + _working_threads[i].suspend(); + try { + Thread.sleep(1); // sleep for 1 ms + } catch(InterruptedException ie) { + } + _working_threads[i].resume(); + } + } + }; + _suspendresume_thread.setDaemon(true); + _suspendresume_thread.start(); + + // Wait until the desired number of direct handshakes is reached + while (_handshake_count.get() < DIRECT_HANDSHAKES_MARK) { + Thread.sleep(10); // sleep for 10ms + } + } +}