blob: de34aca07c4f111ddbccdbe1ab6a9c8bdc2b31f7 [file] [edit]
/*
* Copyright (c) 2015, 2025, 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_GC_Z_ZRANGEREGISTRY_INLINE_HPP
#define SHARE_GC_Z_ZRANGEREGISTRY_INLINE_HPP
#include "gc/z/zRangeRegistry.hpp"
#include "gc/z/zAddress.inline.hpp"
#include "gc/z/zList.inline.hpp"
#include "gc/z/zLock.inline.hpp"
template <typename Range>
void ZRangeRegistry<Range>::move_into(const Range& range) {
assert(!range.is_null(), "Invalid range");
assert(check_limits(range), "Range outside limits");
const offset start = range.start();
const offset_end end = range.end();
const size_t size = range.size();
ZListIterator<Node> iter(&_list);
for (Node* node; iter.next(&node);) {
if (node->start() < start) {
continue;
}
Node* const prev = _list.prev(node);
if (prev != nullptr && start == prev->end()) {
if (end == node->start()) {
// Merge with prev and current ranges
grow_from_back(prev->range(), size);
grow_from_back(prev->range(), node->size());
_list.remove(node);
delete node;
} else {
// Merge with prev range
grow_from_back(prev->range(), size);
}
} else if (end == node->start()) {
// Merge with current range
grow_from_front(node->range(), size);
} else {
// Insert range before current range
assert(end < node->start(), "Areas must not overlap");
Node* const new_node = new Node(start, size);
_list.insert_before(node, new_node);
}
// Done
return;
}
// Insert last
Node* const last = _list.last();
if (last != nullptr && start == last->end()) {
// Merge with last range
grow_from_back(last->range(), size);
} else {
// Insert new node last
Node* const new_node = new Node(start, size);
_list.insert_last(new_node);
}
}
template <typename Range>
void ZRangeRegistry<Range>::insert_inner(const Range& range) {
if (_callbacks._prepare_for_hand_back != nullptr) {
_callbacks._prepare_for_hand_back(range);
}
move_into(range);
}
template <typename Range>
void ZRangeRegistry<Range>::register_inner(const Range& range) {
move_into(range);
}
template <typename Range>
void ZRangeRegistry<Range>::grow_from_front(Range* range, size_t size) {
if (_callbacks._grow != nullptr) {
const Range from = *range;
const Range to = Range(from.start() - size, from.size() + size);
_callbacks._grow(from, to);
}
range->grow_from_front(size);
}
template <typename Range>
void ZRangeRegistry<Range>::grow_from_back(Range* range, size_t size) {
if (_callbacks._grow != nullptr) {
const Range from = *range;
const Range to = Range(from.start(), from.size() + size);
_callbacks._grow(from, to);
}
range->grow_from_back(size);
}
template <typename Range>
Range ZRangeRegistry<Range>::shrink_from_front(Range* range, size_t size) {
if (_callbacks._shrink != nullptr) {
const Range from = *range;
const Range to = from.last_part(size);
_callbacks._shrink(from, to);
}
return range->shrink_from_front(size);
}
template <typename Range>
Range ZRangeRegistry<Range>::shrink_from_back(Range* range, size_t size) {
if (_callbacks._shrink != nullptr) {
const Range from = *range;
const Range to = from.first_part(from.size() - size);
_callbacks._shrink(from, to);
}
return range->shrink_from_back(size);
}
template <typename Range>
Range ZRangeRegistry<Range>::remove_from_low_inner(size_t size) {
ZListIterator<Node> iter(&_list);
for (Node* node; iter.next(&node);) {
if (node->size() >= size) {
Range range;
if (node->size() == size) {
// Exact match, remove range
_list.remove(node);
range = *node->range();
delete node;
} else {
// Larger than requested, shrink range
range = shrink_from_front(node->range(), size);
}
if (_callbacks._prepare_for_hand_out != nullptr) {
_callbacks._prepare_for_hand_out(range);
}
return range;
}
}
// Out of memory
return Range();
}
template <typename Range>
Range ZRangeRegistry<Range>::remove_from_low_at_most_inner(size_t size) {
Node* const node = _list.first();
if (node == nullptr) {
// List is empty
return Range();
}
Range range;
if (node->size() <= size) {
// Smaller than or equal to requested, remove range
_list.remove(node);
range = *node->range();
delete node;
} else {
// Larger than requested, shrink range
range = shrink_from_front(node->range(), size);
}
if (_callbacks._prepare_for_hand_out) {
_callbacks._prepare_for_hand_out(range);
}
return range;
}
template <typename Range>
size_t ZRangeRegistry<Range>::remove_from_low_many_at_most_inner(size_t size, ZArray<Range>* out) {
size_t to_remove = size;
while (to_remove > 0) {
const Range range = remove_from_low_at_most_inner(to_remove);
if (range.is_null()) {
// The requested amount is not available
return size - to_remove;
}
to_remove -= range.size();
out->append(range);
}
return size;
}
template <typename Range>
ZRangeRegistry<Range>::Callbacks::Callbacks()
: _prepare_for_hand_out(nullptr),
_prepare_for_hand_back(nullptr),
_grow(nullptr),
_shrink(nullptr) {}
template <typename Range>
ZRangeRegistry<Range>::ZRangeRegistry()
: _list(),
_callbacks(),
_limits() {}
template <typename Range>
void ZRangeRegistry<Range>::register_callbacks(const Callbacks& callbacks) {
_callbacks = callbacks;
}
template <typename Range>
void ZRangeRegistry<Range>::register_range(const Range& range) {
ZLocker<ZLock> locker(&_lock);
register_inner(range);
}
template <typename Range>
bool ZRangeRegistry<Range>::unregister_first(Range* out) {
// Unregistering a range doesn't call a "prepare_to_hand_out" callback
// because the range is unregistered and not handed out to be used.
ZLocker<ZLock> locker(&_lock);
if (_list.is_empty()) {
return false;
}
// Don't invoke the "prepare_to_hand_out" callback
Node* const node = _list.remove_first();
// Return the range
*out = *node->range();
delete node;
return true;
}
template <typename Range>
inline bool ZRangeRegistry<Range>::is_empty() const {
return _list.is_empty();
}
template <typename Range>
bool ZRangeRegistry<Range>::is_contiguous() const {
return _list.size() == 1;
}
template <typename Range>
void ZRangeRegistry<Range>::anchor_limits() {
assert(_limits.is_null(), "Should only anchor limits once");
if (_list.is_empty()) {
return;
}
const offset start = _list.first()->start();
const size_t size = _list.last()->end() - start;
_limits = Range(start, size);
}
template <typename Range>
bool ZRangeRegistry<Range>::limits_contain(const Range& range) const {
if (_limits.is_null() || range.is_null()) {
return false;
}
return range.start() >= _limits.start() && range.end() <= _limits.end();
}
template <typename Range>
bool ZRangeRegistry<Range>::check_limits(const Range& range) const {
if (_limits.is_null()) {
// Limits not anchored
return true;
}
// Otherwise, check that other is within the limits
return limits_contain(range);
}
template <typename Range>
typename ZRangeRegistry<Range>::offset ZRangeRegistry<Range>::peek_low_address() const {
ZLocker<ZLock> locker(&_lock);
const Node* const node = _list.first();
if (node != nullptr) {
return node->start();
}
// Out of memory
return offset::invalid;
}
template <typename Range>
typename ZRangeRegistry<Range>::offset_end ZRangeRegistry<Range>::peak_high_address_end() const {
ZLocker<ZLock> locker(&_lock);
const Node* const node = _list.last();
if (node != nullptr) {
return node->end();
}
// Out of memory
return offset_end::invalid;
}
template <typename Range>
void ZRangeRegistry<Range>::insert(const Range& range) {
ZLocker<ZLock> locker(&_lock);
insert_inner(range);
}
template <typename Range>
void ZRangeRegistry<Range>::insert_and_remove_from_low_many(const Range& range, ZArray<Range>* out) {
ZLocker<ZLock> locker(&_lock);
const size_t size = range.size();
// Insert the range
insert_inner(range);
// Remove (hopefully) at a lower address
const size_t removed = remove_from_low_many_at_most_inner(size, out);
// This should always succeed since we freed the same amount.
assert(removed == size, "must succeed");
}
template <typename Range>
Range ZRangeRegistry<Range>::insert_and_remove_from_low_exact_or_many(size_t size, ZArray<Range>* in_out) {
ZLocker<ZLock> locker(&_lock);
size_t inserted = 0;
// Insert everything
ZArrayIterator<Range> iter(in_out);
for (Range mem; iter.next(&mem);) {
insert_inner(mem);
inserted += mem.size();
}
// Clear stored memory so that we can populate it below
in_out->clear();
// Try to find and remove a contiguous chunk
Range range = remove_from_low_inner(size);
if (!range.is_null()) {
return range;
}
// Failed to find a contiguous chunk, split it up into smaller chunks and
// only remove up to as much that has been inserted.
size_t removed = remove_from_low_many_at_most_inner(inserted, in_out);
assert(removed == inserted, "Should be able to get back as much as we previously inserted");
return Range();
}
template <typename Range>
Range ZRangeRegistry<Range>::remove_from_low(size_t size) {
ZLocker<ZLock> locker(&_lock);
Range range = remove_from_low_inner(size);
return range;
}
template <typename Range>
Range ZRangeRegistry<Range>::remove_from_low_at_most(size_t size) {
ZLocker<ZLock> lock(&_lock);
Range range = remove_from_low_at_most_inner(size);
return range;
}
template <typename Range>
size_t ZRangeRegistry<Range>::remove_from_low_many_at_most(size_t size, ZArray<Range>* out) {
ZLocker<ZLock> lock(&_lock);
return remove_from_low_many_at_most_inner(size, out);
}
template <typename Range>
Range ZRangeRegistry<Range>::remove_from_high(size_t size) {
ZLocker<ZLock> locker(&_lock);
ZListReverseIterator<Node> iter(&_list);
for (Node* node; iter.next(&node);) {
if (node->size() >= size) {
Range range;
if (node->size() == size) {
// Exact match, remove range
_list.remove(node);
range = *node->range();
delete node;
} else {
// Larger than requested, shrink range
range = shrink_from_back(node->range(), size);
}
if (_callbacks._prepare_for_hand_out != nullptr) {
_callbacks._prepare_for_hand_out(range);
}
return range;
}
}
// Out of memory
return Range();
}
template <typename Range>
void ZRangeRegistry<Range>::transfer_from_low(ZRangeRegistry* other, size_t size) {
assert(other->_list.is_empty(), "Should only be used for initialization");
ZLocker<ZLock> locker(&_lock);
size_t to_move = size;
ZListIterator<Node> iter(&_list);
for (Node* node; iter.next(&node);) {
Node* to_transfer;
if (node->size() <= to_move) {
// Smaller than or equal to requested, remove range
_list.remove(node);
to_transfer = node;
} else {
// Larger than requested, shrink range
const Range range = shrink_from_front(node->range(), to_move);
to_transfer = new Node(range);
}
// Insert into the other list
//
// The from list is sorted, the other list starts empty, and the inserts
// come in sort order, so we can insert_last here.
other->_list.insert_last(to_transfer);
to_move -= to_transfer->size();
if (to_move == 0) {
break;
}
}
assert(to_move == 0, "Should have transferred requested size");
}
#endif // SHARE_GC_Z_ZRANGEREGISTRY_INLINE_HPP