From de32acf9ac02a62e7d3e3a61cef3a7bbb38fa6cb Mon Sep 17 00:00:00 2001 From: Derek Xu Date: Tue, 15 Apr 2025 09:07:59 -0700 Subject: [PATCH] [VM] Introduce ListQueue class TEST=list_queue_test.cc Change-Id: Ie85055475f35a9fdc4f31e61d5f1f5d04eb707a8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420220 Reviewed-by: Ben Konyi Commit-Queue: Derek Xu --- runtime/bin/builtin_impl_sources.gni | 1 + runtime/bin/list_queue_test.cc | 69 +++++++++++++++++++++++ runtime/platform/list_queue.h | 80 +++++++++++++++++++++++++++ runtime/platform/platform_sources.gni | 1 + 4 files changed, 151 insertions(+) create mode 100644 runtime/bin/list_queue_test.cc create mode 100644 runtime/platform/list_queue.h diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni index 2d814c753e7..8e18c0a845b 100644 --- a/runtime/bin/builtin_impl_sources.gni +++ b/runtime/bin/builtin_impl_sources.gni @@ -64,6 +64,7 @@ builtin_impl_tests = [ "eventhandler_test.cc", "file_test.cc", "hashmap_test.cc", + "list_queue_test.cc", "priority_heap_test.cc", "snapshot_utils_test.cc", "test_utils.cc", diff --git a/runtime/bin/list_queue_test.cc b/runtime/bin/list_queue_test.cc new file mode 100644 index 00000000000..e67643ebbb3 --- /dev/null +++ b/runtime/bin/list_queue_test.cc @@ -0,0 +1,69 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include + +#include "platform/list_queue.h" +#include "vm/unit_test.h" + +namespace dart { + +void ExpectContentsToBe(const ListQueue& actual, + const std::vector& expected) { + intptr_t i = 0; + actual.ForEach([&expected, &i](intptr_t actual_element) { + EXPECT(actual_element == expected[i]); + ++i; + }); +} + +VM_UNIT_TEST_CASE(ListQueue_PublicMethods) { + ListQueue l; + intptr_t front = -1; + + l.PushBack(1); + ExpectContentsToBe(l, {1}); + EXPECT(l.Length() == 1); + + front = l.PopFront(); + EXPECT(front == 1); + ExpectContentsToBe(l, {}); + EXPECT(l.Length() == 0); + + l.PushBack(2); + ExpectContentsToBe(l, {2}); + EXPECT(l.Length() == 1); + + l.PushBack(3); + ExpectContentsToBe(l, {2, 3}); + EXPECT(l.Length() == 2); + + front = l.PopFront(); + EXPECT(front == 2); + ExpectContentsToBe(l, {3}); + EXPECT(l.Length() == 1); + + l.PushBack(4); + ExpectContentsToBe(l, {3, 4}); + EXPECT(l.Length() == 2); +} + +VM_UNIT_TEST_CASE(ListQueue_Grow) { + const intptr_t kInitialCapacity = ListQueue::kInitialCapacity; + + ListQueue l; + l.PushBack(1); + ExpectContentsToBe(l, {1}); + l.PopFront(); + ExpectContentsToBe(l, {}); + + // Force |l| to grow by adding more than |kInitialCapacity| elements to it. + for (intptr_t i = 0; i <= kInitialCapacity + 3; ++i) { + l.PushBack(123); + } + + ExpectContentsToBe(l, std::vector(kInitialCapacity + 4, 123)); +} + +} // namespace dart diff --git a/runtime/platform/list_queue.h b/runtime/platform/list_queue.h new file mode 100644 index 00000000000..69d75c0a212 --- /dev/null +++ b/runtime/platform/list_queue.h @@ -0,0 +1,80 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNTIME_PLATFORM_LIST_QUEUE_H_ +#define RUNTIME_PLATFORM_LIST_QUEUE_H_ + +#include +#include +#include + +#include "platform/assert.h" +#include "platform/globals.h" + +// A queue backed by a circular buffer similar to dart:collection's ListQueue. +template +class ListQueue { + public: + static constexpr intptr_t kInitialCapacity = 64; + + ListQueue() + : buffer_(std::make_unique(kInitialCapacity)), + capacity_(kInitialCapacity) {} + + void PushBack(T&& element) { + buffer_[tail_] = std::move(element); + tail_ = (tail_ + 1) % capacity_; + if (head_ == tail_) { + Grow(); + } + ++length_; + } + + T&& PopFront() { + ASSERT(head_ != tail_); + T&& element = std::move(buffer_[head_]); + head_ = (head_ + 1) % capacity_; + --length_; + return std::move(element); + } + + // The number of elements currently in the queue. + intptr_t Length() { return length_; } + + void ForEach(std::function callback) const { + for (intptr_t i = head_; i != tail_; i = (i + 1) % capacity_) { + callback(buffer_[i]); + } + } + + private: + std::unique_ptr buffer_; + intptr_t capacity_; + intptr_t length_ = 0; + intptr_t head_ = 0; + intptr_t tail_ = 0; + + void Grow() { + intptr_t split = capacity_ - head_; + + intptr_t new_capacity = capacity_ << 1; + std::unique_ptr new_buffer = std::make_unique(new_capacity); + + for (intptr_t i = 0; i < split; ++i) { + new_buffer[i] = std::move(buffer_[head_ + i]); + } + for (intptr_t i = split; i < split + head_; ++i) { + new_buffer[i] = std::move(buffer_[i - split]); + } + + head_ = 0; + tail_ = capacity_; + buffer_.swap(new_buffer); + capacity_ = new_capacity; + } + + DISALLOW_COPY_AND_ASSIGN(ListQueue); +}; + +#endif // RUNTIME_PLATFORM_LIST_QUEUE_H_ diff --git a/runtime/platform/platform_sources.gni b/runtime/platform/platform_sources.gni index 0e0c12dd75c..27b58ef8a7c 100644 --- a/runtime/platform/platform_sources.gni +++ b/runtime/platform/platform_sources.gni @@ -18,6 +18,7 @@ platform_sources = [ "growable_array.h", "hashmap.cc", "hashmap.h", + "list_queue.h", "lockers.h", "memory_sanitizer.h", "safe_stack.h",