Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions emulation/rocjitsu/lib/util/include/util/intrusive_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,26 +71,27 @@ template <typename T, typename... Options> class IListIterator {
// Standard C++ iterator types.
using difference_type = std::ptrdiff_t;
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using value_type = std::remove_const_t<T>;
using pointer = T *;
using reference = T &;
// Custom iterator types.
using ConstPointer = const T *;
using ConstReference = const T &;
using NodePointer = IListNode<T, Options...> *;
using ConstNodePointer = const IListNode<T, Options...> *;
using NodeReference = IListNode<T, Options...> &;
using ConstNodeReference = const IListNode<T, Options...> &;
using NodeT = IListNode<std::remove_const_t<T>, Options...>;
using NodePointer = std::conditional_t<std::is_const_v<T>, const NodeT *, NodeT *>;
using ConstNodePointer = const NodeT *;
using NodeReference = std::conditional_t<std::is_const_v<T>, const NodeT &, NodeT &>;

IListIterator() = default;
explicit IListIterator(pointer node_ptr) : node_ptr_(static_cast<pointer>(node_ptr)) {}
explicit IListIterator(pointer node_ptr) : node_ptr_(static_cast<NodePointer>(node_ptr)) {}
explicit IListIterator(reference node_ref) : node_ptr_(static_cast<NodePointer>(&node_ref)) {}
explicit IListIterator(NodeReference node_ref) : node_ptr_(&node_ref) {}
template <typename U>
requires(std::is_const_v<T> && std::same_as<U, std::remove_const_t<T>>)
IListIterator(const IListIterator<U, Options...> &other) : node_ptr_(other.node_pointer()) {}

NodePointer node_pointer() { return node_ptr_; }
ConstNodePointer node_pointer() const { return node_ptr_; }

ConstReference operator*() const { return *static_cast<ConstPointer>(node_pointer()); }
reference operator*() const { return *static_cast<pointer>(node_ptr_); }
Comment thread
atgutier marked this conversation as resolved.
Resolved

friend bool operator==(const IListIterator &lhs, const IListIterator &rhs) {
return lhs.node_ptr_ == rhs.node_ptr_;
Expand Down Expand Up @@ -173,11 +174,15 @@ template <typename T, typename... Options> class IntrusiveList {
/// @brief Return an iterator to the beginning of the list.
/// @returns Iterator pointing to the first element in the list.
Iterator begin() { return ++Iterator(sentinel_); }
ConstIterator begin() const { return ++ConstIterator(sentinel_); }
/// @brief Return an iterator past the end of the list.
/// @details The iterator returned is the sentinel which is pointed to by the next pointer in the
/// last element in the list. This iterator should not be dereferenced.
/// @returns The iterator pointing to the sentinel.
Iterator end() { return Iterator(sentinel_); }
ConstIterator end() const { return ConstIterator(sentinel_); }
ConstIterator cbegin() const { return begin(); }
ConstIterator cend() const { return end(); }
/// @brief Checks if the list is empty.
/// @returns Bool indicating if the list is empty or not.
bool empty() const { return sentinel_.next_ == &sentinel_; }
Expand Down
1 change: 1 addition & 0 deletions emulation/rocjitsu/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ add_executable(
decode_execute_benchmark.cpp
util_conditional_wake_spinlock_test.cpp
util_distributed_shared_mutex_test.cpp
util_intrusive_list_test.cpp
util_observable_shared_mutex_test.cpp
host_mapping_lock_test.cpp
util_result_test.cpp
Expand Down
72 changes: 72 additions & 0 deletions emulation/rocjitsu/tests/util_intrusive_list_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2026 Advanced Micro Devices, Inc.
// SPDX-License-Identifier: MIT

#include "util/intrusive_list.h"

#include <gtest/gtest.h>

#include <array>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <ranges>

namespace {

struct Owner {};

class Entry : public util::IListNode<Entry, util::IListParent<Owner>> {
public:
explicit Entry(int value) : value_(value) {}

int value() const { return value_; }

private:
int value_;
};

using EntryList = util::IntrusiveList<Entry, util::IListParent<Owner>>;

static_assert(std::bidirectional_iterator<EntryList::Iterator>);
static_assert(std::bidirectional_iterator<EntryList::ConstIterator>);
static_assert(std::ranges::bidirectional_range<const EntryList>);
static_assert(std::same_as<std::iter_reference_t<EntryList::ConstIterator>, const Entry &>);
static_assert(std::same_as<std::iter_reference_t<EntryList::Iterator>, Entry &>);
static_assert(std::same_as<std::iterator_traits<EntryList::ConstIterator>::value_type, Entry>);
static_assert(std::convertible_to<EntryList::Iterator, EntryList::ConstIterator>);
static_assert(!std::convertible_to<EntryList::ConstIterator, EntryList::Iterator>);

TEST(IntrusiveListTest, IteratesConstList) {
Entry first(1);
Entry second(2);
Entry third(3);
EntryList list;
const EntryList &const_list = list;

EXPECT_EQ(const_list.begin(), const_list.end());
EXPECT_EQ(const_list.cbegin(), const_list.cend());

list.push_back(first);
list.push_back(second);
list.push_back(third);

EntryList::ConstIterator converted = list.begin();
EXPECT_EQ(converted, const_list.begin());

const std::array<const Entry *, 3> expected{&first, &second, &third};
std::size_t index = 0;
for (const Entry &entry : const_list) {
ASSERT_LT(index, expected.size());
EXPECT_EQ(&entry, expected[index++]);
}
EXPECT_EQ(index, expected.size());

EntryList::ConstIterator it = const_list.cend();
EXPECT_EQ(&*--it, &third);
EXPECT_EQ(&*--it, &second);
EXPECT_EQ(&*--it, &first);
EXPECT_EQ(it, const_list.cbegin());
EXPECT_EQ((*it).value(), 1);
}

} // namespace
Loading