RFR: 8238761: Asynchronous handshakes [v8]
David Holmes
dholmes at openjdk.java.net
Mon Sep 28 06:32:48 UTC 2020
On Thu, 24 Sep 2020 08:18:12 GMT, Robbin Ehn <rehn at openjdk.org> wrote:
>> This patch implements asynchronous handshake, which changes how handshakes works by default. Asynchronous handshakes
>> are target only executed, which they may never be executed. (target may block on socket for the rest of VM lifetime)
>> Since we have several use-cases for them we can have many handshake pending. (should be very rare) To be able handle an
>> arbitrary amount of handshakes this patch adds a per JavaThread queue and heap allocated HandshakeOperations. It's a
>> singly linked list where you push/insert to the end and pop/get from the front. Inserts are done via CAS on first
>> pointer, no lock needed. Pops are done while holding the per handshake state lock, and when working on the first
>> pointer also CAS. The thread grabbing the handshake state lock for a JavaThread will pop and execute all handshake
>> operations matching the filter. The JavaThread itself uses no filter and any other thread uses the filter of everything
>> except asynchronous handshakes. In this initial change-set there is no need to do any other filtering. If needed
>> filtering can easily be exposed as a virtual method on the HandshakeClosure, but note that filtering causes handshake
>> operation to be done out-order. Since the filter determins who execute the operation and not the invoked method, there
>> is now only one method to call when handshaking one thread. Some comments about the changes:
>> - HandshakeClosure uses ThreadClosure, since it neat to use the same closure for both alla JavThreads do and Handshake
>> all threads. With heap allocating it cannot extends StackObj. I tested several ways to fix this, but those very much
>> worse then this.
>>
>> - I added a is_handshake_safe_for for checking if it's current thread is operating on itself or the handshaker of that
>> thread.
>>
>> - Simplified JVM TI with a JvmtiHandshakeClosure and also made them not needing a JavaThread when executing as a
>> handshaker on a JavaThread, e.g. VM Thread can execute the handshake operation.
>>
>> - Added WB testing method.
>>
>> - Removed VM_HandshakeOneThread, the VM thread uses the same call path as direct handshakes did.
>>
>> - Changed the handshake semaphores to mutex to be able to handle deadlocks with lock ranking.
>>
>> - VM_HandshakeAllThreadsis still a VM operation, since we do support half of the threads being handshaked before a
>> safepoint and half of them after, in many handshake all operations.
>>
>> - ThreadInVMForHandshake do not need to do a fenced transistion since this is always a transistion from unsafe to unsafe.
>>
>> - Added NoSafepointVerifyer, we are thinking about supporting safepoints inside handshake, but it's not needed at the
>> moment. To make sure that gets well tested if added the NoSafepointVerifyer will raise eyebrows.
>>
>> - Added ttyLocker::break_tty_lock_for_safepoint(os::current_thread_id()); due to lock rank.
>>
>> - Added filtered queue and gtest for it.
>>
>> Passes multiple t1-8 runs.
>> Been through some pre-reviwing.
>
> Robbin Ehn has updated the pull request incrementally with one additional commit since the last revision:
>
> Add constructor and comment. Previous renames caused confusing, improved names once more and moved non-public to private
Still some naming issues to resolve and an editing pass on various new comments.
Thanks,
David
src/hotspot/share/runtime/handshake.cpp line 51:
> 49: private:
> 50: // Must use AsyncHandshakeOperation when using AsyncHandshakeClosure.
> 51: HandshakeOperation(AsyncHandshakeClosure* cl, JavaThread* target, jlong start_ns) {};
I'm really not understanding the way you have implemented the guard I requested. How does declaring the private
constructor prevent a call to HandShakeOperation(someAsync, target) ?
src/hotspot/share/runtime/handshake.cpp line 202:
> 200: }
> 201:
> 202: static void log_handshake_info(jlong start_time_ns, const char* name, int targets, int non_self_executed, const
> char* extra = NULL) {
The name "non_self_executed" is much clearer - thanks. But this now highlights that the log message itself doesn't make
complete sense as it refers to "requesting thread" when it could be a range of possible threads not just a single
requesting thread.
src/hotspot/share/runtime/handshake.cpp line 239:
> 237: // Keeps count on how many of own emitted handshakes
> 238: // this thread execute.
> 239: int emitted_handshakes_executed = 0;
I suggest:
// Keeps count of how many handshakes were actually executed
// by this thread.
int handshakes_executed = 0;
src/hotspot/share/runtime/handshake.cpp line 326:
> 324: // Keeps count on how many of own emitted handshakes
> 325: // this thread execute.
> 326: int emitted_handshakes_executed = 0;
See previous comment.
src/hotspot/share/runtime/handshake.hpp line 79:
> 77: // Provides mutual exclusion to this state and queue.
> 78: Mutex _lock;
> 79: // Set to the thread executing the handshake operation during the execution.
s/the execution/its execution/
Or just delete "during the execution"
src/hotspot/share/runtime/handshake.hpp line 87:
> 85: void process_self_inner();
> 86:
> 87: bool have_non_self_executable_operation();
API naming consistency is still a problem here. We have:
- pop_for_self()
- pop()
but
- has_operation() // for use by self/handshakee
- have_non_self_executable_operation()
why not:
- has_operation_for_self()
- has_operation()
?
src/hotspot/share/runtime/handshake.hpp line 100:
> 98: }
> 99:
> 100: // Both _queue and _lock must be check. If a thread have seen this _handshakee
s/check/checked/
s/have/has/
src/hotspot/share/runtime/handshake.hpp line 103:
> 101: // as safe it will execute all possible handshake operations in a loop while
> 102: // holding _lock. We use lock free addition to the queue, which means it is
> 103: // possible to the queue to be seen as empty by _handshakee but as non-empty
s/to/for/
src/hotspot/share/runtime/handshake.hpp line 105:
> 103: // possible to the queue to be seen as empty by _handshakee but as non-empty
> 104: // by the thread executing in the loop. To avoid the _handshakee eliding
> 105: // stopping while handshake operations are being executed, the _handshakee
s/eliding stopping/continuing/ ?
src/hotspot/share/runtime/handshake.hpp line 106:
> 104: // by the thread executing in the loop. To avoid the _handshakee eliding
> 105: // stopping while handshake operations are being executed, the _handshakee
> 106: // must take slow if _lock is held and make sure the queue is empty otherwise
Unclear what "slow" refers to - "take the slow path"? But what path is that?
src/hotspot/share/runtime/handshake.hpp line 107:
> 105: // stopping while handshake operations are being executed, the _handshakee
> 106: // must take slow if _lock is held and make sure the queue is empty otherwise
> 107: // try process it.
s/try/try to/
but I'm not sure how this follows the "otherwise" in this sentence.
src/hotspot/share/utilities/filterQueue.hpp line 32:
> 30:
> 31: // The FilterQueue is FIFO with the ability to skip over queued items.
> 32: // The skipping is controlled by using a filter when poping.
s/poping/popping/
src/hotspot/share/utilities/filterQueue.hpp line 33:
> 31: // The FilterQueue is FIFO with the ability to skip over queued items.
> 32: // The skipping is controlled by using a filter when poping.
> 33: // It also supports lock free pushes, while poping (including contain())
s/poping/popping/
s/contain()/contains()/
src/hotspot/share/utilities/filterQueue.hpp line 63:
> 61:
> 62: // Applies the match_func to the items in the queue until match_func returns
> 63: // true and then return false, or there is no more items and then returns
Surely "true and then returns true"?
s/there is/there are/
src/hotspot/share/utilities/filterQueue.hpp line 64:
> 62: // Applies the match_func to the items in the queue until match_func returns
> 63: // true and then return false, or there is no more items and then returns
> 64: // false. Any pushed item while executing may or may not have match_func
s/pushed item/item pushed/
I know what you are trying to say about concurrent pushes but it sounds too non-deterministic - any concurrent push
that happens before contains() reaches the end of the queue will have the match_func applied. So in that sense "while
executing" only applies to pushes that will be seen; any push not seen had to have happened after execution was
complete.
src/hotspot/share/utilities/filterQueue.hpp line 66:
> 64: // false. Any pushed item while executing may or may not have match_func
> 65: // applied. The method is not re-entrant and must be executed mutually
> 66: // exclusive other contains and pops calls.
// exclusive to other contains() and pop() calls.
src/hotspot/share/utilities/filterQueue.hpp line 77:
> 75:
> 76: // Applies the match_func to all items in the queue returns the item which
> 77: // match_func return true for and was inserted first. Any pushed item while
// Applies the match_func to each item in the queue, in order of insertion, and
// returns the first item for which match_func returns true. Returns false if there are
// no matches or the queue is empty.
src/hotspot/share/utilities/filterQueue.hpp line 80:
> 78: // executing may or may not have be popped, if popped it was the first
> 79: // inserted match. The method is not re-entrant and must be executed mutual
> 80: // exclusive with other contains and pops calls.
See comments on contains() to ensure pop() and contains() use consistent terminology. Thanks.
src/hotspot/share/runtime/handshake.cpp line 389:
> 387: };
> 388:
> 389: static bool non_self_queue_filter(HandshakeOperation* op) {
The name "non_self_queue_filter" is really awkward - sorry. But I think we're going to have to revisit the way
filtering is named and done once we try to generalise it anyway, so I'll let this pass.
-------------
Changes requested by dholmes (Reviewer).
PR: https://git.openjdk.java.net/jdk/pull/151
More information about the serviceability-dev
mailing list