Path: blob/master/src/hotspot/share/jfr/utilities/jfrConcurrentLinkedListHost.inline.hpp
41149 views
/*1* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#ifndef SHARE_JFR_UTILITIES_JFRCONCURRENTLINKEDLISTHOST_INLINE_HPP25#define SHARE_JFR_UTILITIES_JFRCONCURRENTLINKEDLISTHOST_INLINE_HPP2627#include "jfr/utilities/jfrConcurrentLinkedListHost.hpp"2829#include "jfr/utilities/jfrRelation.hpp"30#include "jfr/utilities/jfrTypes.hpp"31#include "runtime/atomic.hpp"32#include "utilities/globalDefinitions.hpp"3334/*35* The removal marker (i.e. the excision bit) is represented by '( )' as part of state description comments:36* "node --> next" becomes "(node) --> next", when node is logically deleted.37*/38template <typename Node>39inline Node* mark_for_removal(Node* node) {40assert(node != NULL, "invariant");41const Node* next = node->_next;42assert(next != NULL, "invariant");43Node* const unmasked_next = unmask(next);44return next == unmasked_next && cas(&node->_next, unmasked_next, set_excision_bit(unmasked_next)) ? unmasked_next : NULL;45}4647/*48* The insertion marker (i.e. the insertion bit) is represented by '[ ]' as part of state description comments:49* "node --> next" becomes "[node] --> next", in an attempt to convey the node as exlusively reserved.50*/51template <typename Node>52inline bool mark_for_insertion(Node* node, const Node* tail) {53assert(node != NULL, "invariant");54return node->_next == tail && cas(&node->_next, const_cast<Node*>(tail), set_insertion_bit(tail));55}5657/*58* Find a predecessor and successor node pair where successor covers predecessor (adjacency).59*/60template <typename Node, typename VersionHandle, template <typename> class SearchPolicy>61Node* find_adjacent(Node* head, const Node* tail, Node** predecessor, VersionHandle& version_handle, SearchPolicy<Node>& predicate) {62assert(head != NULL, "invariant");63assert(tail != NULL, "invariant");64assert(head != tail, "invariant");65Node* predecessor_next = NULL;66while (true) {67Node* current = head;68version_handle->checkout();69Node* next = Atomic::load_acquire(¤t->_next);70do {71assert(next != NULL, "invariant");72Node* const unmasked_next = unmask(next);73// 1A: Locate the first node to keep as predecessor.74if (!is_marked_for_removal(next)) {75*predecessor = current;76predecessor_next = unmasked_next;77}78// 1B: Locate the next node to keep as successor.79current = unmasked_next;80if (current == tail) break;81next = current->_next;82} while (predicate(current, next));83// current represents the successor node from here on out.84// 2: Check predecessor and successor node pair for adjacency.85if (predecessor_next == current) {86// Invariant: predecessor --> successor87return current;88}89// 3: Successor does not (yet) cover predecessor.90// Invariant: predecessor --> (logically excised nodes) --> successor91// Physically excise one or more logically excised nodes in-between.92if (cas(&(*predecessor)->_next, predecessor_next, current)) {93// Invariant: predecessor --> successor94return current;95}96}97}9899template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>100JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::JfrConcurrentLinkedListHost(Client* client) : _client(client) {}101102template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>103bool JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::initialize() {104return true;105}106107template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>108void JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::insert_head(typename Client::Node* node,109typename Client::Node* head,110const typename Client::Node* tail) const {111Node* predecessor;112Node* successor;113HeadNode<Node> predicate(node);114VersionHandle version_handle = _client->get_version_handle();115while (true) {116// Find an adjacent predecessor and successor node pair.117successor = find_adjacent<Node, VersionHandle, HeadNode>(head, tail, &predecessor, version_handle, predicate);118// Invariant (adjacency): predecessor --> successor119// Invariant (optional: key-based total order): predecessor->key() < key && key <= successor->key().120// We can now attempt to insert the new node in-between.121node->_next = successor;122if (cas(&predecessor->_next, successor, node)) {123// Invariant: predecessor --> node --> successor124// An insert to head is a benign modification and will not need to be committed to the version control system.125return;126}127}128}129130template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>131void JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::insert_tail(typename Client::Node* node,132typename Client::Node* head,133typename Client::Node* last,134const typename Client::Node* tail) const {135assert(node != NULL, "invariant");136assert(head != NULL, "invariant");137assert(last != NULL, "invarinat");138assert(tail != NULL, "invariant");139// Mark the new node to be inserted with the insertion marker already.140node->_next = set_insertion_bit(const_cast<NodePtr>(tail));141// Invariant: [node]--> tail142assert(is_marked_for_insertion(node->_next), "invariant");143NodePtr predecessor;144LastNode<Node> predicate;145VersionHandle version_handle = _client->get_version_handle();146while (true) {147// Find an adjacent predecessor and successor node pair, where the successor == tail148const NodePtr successor = find_adjacent<Node, VersionHandle, LastNode>(last, tail, &predecessor, version_handle, predicate);149assert(successor == tail, "invariant");150// Invariant: predecessor --> successor151// We first attempt to mark the predecessor node to signal our intent of performing an insertion.152if (mark_for_insertion(predecessor, tail)) {153break;154}155}156// Predecessor node is claimed for insertion.157// Invariant: [predecessor] --> tail158assert(is_marked_for_insertion(predecessor->_next), "invariant");159assert(predecessor != head, "invariant");160if (Atomic::load_acquire(&last->_next) == predecessor) {161/* Even after we store the new node into the last->_next field, there is no race162because it is also marked with the insertion bit. */163last->_next = node;164// Invariant: last --> [node] --> tail165OrderAccess::storestore();166// Perform the link with the predecessor node, which by this store becomes visible for removal.167predecessor->_next = node;168// Invariant: predecessor --> [node] --> tail169} else {170assert(last == predecessor, "invariant");171last->_next = node;172// Invariant: last --> [node] --> tail173OrderAccess::storestore();174/* This implies the list is logically empty from the removal perspective.175cas is not needed here because inserts must not come in from the head side176concurrently with inserts from tail which are currently blocked by us.177Invariant (logical): head --> tail. */178head->_next = node;179// Invariant: head --> [node] --> tail180}181OrderAccess::storestore();182// Publish the inserted node by removing the insertion marker.183node->_next = const_cast<NodePtr>(tail);184// Invariant: last --> node --> tail (possibly also head --> node --> tail)185}186187template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>188typename Client::Node* JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::remove(typename Client::Node* head,189const typename Client::Node* tail,190typename Client::Node* last /* NULL */,191bool insert_is_head /* true */) {192assert(head != NULL, "invariant");193assert(tail != NULL, "invariant");194assert(head != tail, "invariant");195NodePtr predecessor;196NodePtr successor;197NodePtr successor_next;198SearchPolicy<Node> predicate;199VersionHandle version_handle = _client->get_version_handle();200while (true) {201// Find an adjacent predecessor and successor node pair.202successor = find_adjacent<Node, VersionHandle, SearchPolicy>(head, tail, &predecessor, version_handle, predicate);203if (successor == tail) {204return NULL;205}206// Invariant: predecessor --> successor207// Invariant (optional: key-based total order): predecessor->key() < key && key <= successor->key()208// It is the successor node that is to be removed.209// We first attempt to reserve (logically excise) the successor node.210successor_next = mark_for_removal(successor);211if (successor_next != NULL) {212break;213}214}215// Invariant: predecessor --> (successor) --> successor_next216// Successor node now logically excised.217assert(is_marked_for_removal(successor->_next), "invariant");218// Now attempt to physically excise the successor node.219// If the cas fails, we can optimize for the slow path if we know we are not performing220// insertions from the head. Then a failed cas results not from a new node being inserted,221// but only because another thread excised us already.222if (!cas(&predecessor->_next, successor, successor_next) && insert_is_head) {223// Physically excise using slow path, can be completed asynchronously by other threads.224Identity<Node> excise(successor);225find_adjacent<Node, VersionHandle, Identity>(head, tail, &predecessor, version_handle, excise);226}227if (last != NULL && Atomic::load_acquire(&last->_next) == successor) {228guarantee(!insert_is_head, "invariant");229guarantee(successor_next == tail, "invariant");230LastNode<Node> excise;231find_adjacent<Node, VersionHandle, LastNode>(last, tail, &predecessor, version_handle, excise);232// Invariant: successor excised from last list233}234// Commit the modification back to the version control system.235// It blocks until all checkouts for versions earlier than the commit have been released.236version_handle->commit();237// At this point we know there can be no references onto the excised node. It is safe, enjoy it.238return successor;239}240241template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>242bool JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::in_list(const typename Client::Node* node,243typename Client::Node* head,244const typename Client::Node* tail) const {245assert(head != NULL, "invariant");246assert(tail != NULL, "invariant");247assert(head != tail, "invariant");248VersionHandle version_handle = _client->get_version_handle();249const Node* current = head;250version_handle->checkout();251const Node* next = Atomic::load_acquire(¤t->_next);252while (true) {253if (!is_marked_for_removal(next)) {254if (current == node) {255return true;256}257}258current = unmask(next);259if (current == tail) break;260next = current->_next;261}262return false;263}264265template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>266template <typename Callback>267inline void JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::iterate(typename Client::Node* head,268const typename Client::Node* tail,269Callback& cb) {270assert(head != NULL, "invariant");271assert(tail != NULL, "invariant");272assert(head != tail, "invariant");273VersionHandle version_handle = _client->get_version_handle();274NodePtr current = head;275version_handle->checkout();276NodePtr next = Atomic::load_acquire(¤t->_next);277while (true) {278if (!is_marked_for_removal(next)) {279if (!cb.process(current)) {280return;281}282}283current = unmask(next);284if (current == tail) break;285next = current->_next;286}287}288289#endif // SHARE_JFR_UTILITIES_JFRCONCURRENTLINKEDLISTHOST_INLINE_HPP290291292