| /* |
| * Copyright (c) 2020, 2023, 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_JFR_UTILITIES_JFRVERSIONSYSTEM_INLINE_HPP |
| #define SHARE_JFR_UTILITIES_JFRVERSIONSYSTEM_INLINE_HPP |
| |
| #include "jfr/utilities/jfrVersionSystem.hpp" |
| |
| #include "runtime/atomic.hpp" |
| #include "runtime/os.hpp" |
| |
| inline JfrVersionSystem::JfrVersionSystem() : _tip(), _head(nullptr) { |
| _tip._value = 1; |
| } |
| |
| inline JfrVersionSystem::~JfrVersionSystem() { |
| reset(); |
| } |
| |
| inline void JfrVersionSystem::reset() { |
| NodePtr node = _head; |
| while (node != nullptr) { |
| NodePtr next = node->_next; |
| delete node; |
| node = next; |
| } |
| _head = nullptr; |
| _tip._value = 1; |
| } |
| |
| inline JfrVersionSystem::Type JfrVersionSystem::tip() const { |
| return Atomic::load(&_tip._value); |
| } |
| |
| inline JfrVersionSystem::Type JfrVersionSystem::inc_tip() { |
| traceid cmp; |
| traceid xchg; |
| do { |
| cmp = _tip._value; |
| xchg = cmp + 1; |
| } while (Atomic::cmpxchg(&_tip._value, cmp, xchg) != cmp); |
| return xchg; |
| } |
| |
| inline JfrVersionSystem::NodePtr JfrVersionSystem::acquire() { |
| NodePtr node = _head; |
| // free |
| while (node != nullptr) { |
| if (node->_live || Atomic::cmpxchg(&node->_live, false, true)) { |
| node = node->_next; |
| continue; |
| } |
| DEBUG_ONLY(assert_state(node);) |
| return node; |
| } |
| // new |
| node = new Node(this); |
| NodePtr next; |
| do { |
| next = _head; |
| node->_next = next; |
| } while (Atomic::cmpxchg(&_head, next, node) != next); |
| DEBUG_ONLY(assert_state(node);) |
| return node; |
| } |
| |
| inline JfrVersionSystem::Handle JfrVersionSystem::get() { |
| return Handle::make(acquire()); |
| } |
| |
| inline JfrVersionSystem::Node::Node(JfrVersionSystem* system) : _system(system), _next(nullptr), _version(0), _live(true) {} |
| |
| inline traceid JfrVersionSystem::Node::version() const { |
| return _version; |
| } |
| |
| inline void JfrVersionSystem::Node::set(traceid version) const { |
| Atomic::release_store_fence(&_version, version); |
| } |
| |
| inline void JfrVersionSystem::Node::add_ref() const { |
| _ref_counter.inc(); |
| } |
| |
| inline void JfrVersionSystem::Node::remove_ref() const { |
| if (_ref_counter.dec()) { |
| assert(_live, "invariant"); |
| set(0); |
| _live = false; |
| } |
| } |
| |
| inline void JfrVersionSystem::Node::checkout() { |
| set(_system->tip()); |
| assert(version() != 0, "invariant"); |
| } |
| |
| inline void JfrVersionSystem::Node::commit() { |
| assert(version() != 0, "invariant"); |
| // A commit consist of an atomic increment of the tip. |
| const Type commit_version = _system->inc_tip(); |
| // Release this checkout. |
| set(0); |
| // Await release of checkouts for earlier versions. |
| _system->await(commit_version); |
| } |
| |
| inline JfrVersionSystem::NodePtr |
| JfrVersionSystem::synchronize_with(JfrVersionSystem::Type version, JfrVersionSystem::NodePtr node) const { |
| assert(version <= tip(), "invariant"); |
| while (node != nullptr) { |
| const Type checkedout = Atomic::load_acquire(&node->_version); |
| if (checkedout > 0 && checkedout < version) { |
| return node; |
| } |
| node = node->_next; |
| } |
| return nullptr; |
| } |
| |
| inline void JfrVersionSystem::await(JfrVersionSystem::Type version) { |
| assert(version > 0, "invariant"); |
| static const int backoff_unit_ns = 10; |
| int backoff_factor = 1; |
| NodePtr last = _head; |
| while (true) { |
| last = synchronize_with(version, last); |
| if (last == nullptr) { |
| return; |
| } |
| os::naked_short_nanosleep(backoff_unit_ns * backoff_factor++); |
| } |
| } |
| |
| #ifdef ASSERT |
| inline void JfrVersionSystem::assert_state(const JfrVersionSystem::Node* node) const { |
| assert(node != nullptr, "invariant"); |
| assert(node->_live, "invariant"); |
| assert(node->_version == 0, "invariant"); |
| assert(node->_ref_counter.current() == 0, "invariant"); |
| } |
| #endif // ASSERT |
| |
| #endif // SHARE_JFR_UTILITIES_JFRVERSIONSYSTEM_INLINE_HPP |