On Thu, 24 Sep 2020 08:18:12 GMT, Robbin Ehn <r...@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