cbe1cfa249
Pushing unresolved variables at the front was an optimization for the case where we didn't have an end pointer. That forces us to do an O(<new elements>) walk to rescope variables. The implementation was more generic and even did O(<all elements>). Now that we have an end pointer we can simply push at the end and MoveTail which is O(1). Change-Id: I65cd5752b432223d95cd529452a064d8dcc812e1 Reviewed-on: https://chromium-review.googlesource.com/c/1351010 Commit-Queue: Toon Verwaest <verwaest@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#57868}
321 lines
9.1 KiB
C++
321 lines
9.1 KiB
C++
// Copyright 2018 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <iterator>
|
|
|
|
#include "src/v8.h"
|
|
|
|
#include "src/base/threaded-list.h"
|
|
#include "testing/gtest-support.h"
|
|
|
|
namespace v8 {
|
|
namespace base {
|
|
|
|
struct ThreadedListTestNode {
|
|
ThreadedListTestNode() : next_(nullptr), other_next_(nullptr) {}
|
|
|
|
ThreadedListTestNode** next() { return &next_; }
|
|
|
|
ThreadedListTestNode* next_;
|
|
|
|
struct OtherTraits {
|
|
static ThreadedListTestNode** start(ThreadedListTestNode** h) { return h; }
|
|
static ThreadedListTestNode* const* start(ThreadedListTestNode* const* h) {
|
|
return h;
|
|
}
|
|
static ThreadedListTestNode** next(ThreadedListTestNode* t) {
|
|
return t->other_next();
|
|
}
|
|
};
|
|
|
|
ThreadedListTestNode** other_next() { return &other_next_; }
|
|
|
|
ThreadedListTestNode* other_next_;
|
|
};
|
|
|
|
struct ThreadedListTest : public ::testing::Test {
|
|
static const size_t INIT_NODES = 5;
|
|
ThreadedListTest() {}
|
|
|
|
void SetUp() override {
|
|
for (size_t i = 0; i < INIT_NODES; i++) {
|
|
nodes[i] = ThreadedListTestNode();
|
|
}
|
|
|
|
for (size_t i = 0; i < INIT_NODES; i++) {
|
|
list.Add(&nodes[i]);
|
|
normal_next_list.Add(&nodes[i]);
|
|
}
|
|
|
|
// Verify if setup worked
|
|
CHECK(list.Verify());
|
|
CHECK_EQ(list.LengthForTest(), INIT_NODES);
|
|
CHECK(normal_next_list.Verify());
|
|
CHECK_EQ(normal_next_list.LengthForTest(), INIT_NODES);
|
|
|
|
extra_test_node_0 = ThreadedListTestNode();
|
|
extra_test_node_1 = ThreadedListTestNode();
|
|
extra_test_node_2 = ThreadedListTestNode();
|
|
|
|
extra_test_list.Add(&extra_test_node_0);
|
|
extra_test_list.Add(&extra_test_node_1);
|
|
extra_test_list.Add(&extra_test_node_2);
|
|
CHECK_EQ(extra_test_list.LengthForTest(), 3);
|
|
CHECK(extra_test_list.Verify());
|
|
|
|
normal_extra_test_list.Add(&extra_test_node_0);
|
|
normal_extra_test_list.Add(&extra_test_node_1);
|
|
normal_extra_test_list.Add(&extra_test_node_2);
|
|
CHECK_EQ(normal_extra_test_list.LengthForTest(), 3);
|
|
CHECK(normal_extra_test_list.Verify());
|
|
}
|
|
|
|
void TearDown() override {
|
|
// Check if the normal list threaded through next is still untouched.
|
|
CHECK(normal_next_list.Verify());
|
|
CHECK_EQ(normal_next_list.LengthForTest(), INIT_NODES);
|
|
CHECK_EQ(normal_next_list.AtForTest(0), &nodes[0]);
|
|
CHECK_EQ(normal_next_list.AtForTest(4), &nodes[4]);
|
|
CHECK(normal_extra_test_list.Verify());
|
|
CHECK_EQ(normal_extra_test_list.LengthForTest(), 3);
|
|
CHECK_EQ(normal_extra_test_list.AtForTest(0), &extra_test_node_0);
|
|
CHECK_EQ(normal_extra_test_list.AtForTest(2), &extra_test_node_2);
|
|
|
|
list.Clear();
|
|
extra_test_list.Clear();
|
|
}
|
|
|
|
ThreadedListTestNode nodes[INIT_NODES];
|
|
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> list;
|
|
ThreadedList<ThreadedListTestNode> normal_next_list;
|
|
|
|
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
|
|
extra_test_list;
|
|
ThreadedList<ThreadedListTestNode> normal_extra_test_list;
|
|
ThreadedListTestNode extra_test_node_0;
|
|
ThreadedListTestNode extra_test_node_1;
|
|
ThreadedListTestNode extra_test_node_2;
|
|
};
|
|
|
|
TEST_F(ThreadedListTest, Add) {
|
|
CHECK_EQ(list.LengthForTest(), 5);
|
|
ThreadedListTestNode new_node;
|
|
// Add to existing list
|
|
list.Add(&new_node);
|
|
list.Verify();
|
|
CHECK_EQ(list.LengthForTest(), 6);
|
|
CHECK_EQ(list.AtForTest(5), &new_node);
|
|
|
|
list.Clear();
|
|
CHECK_EQ(list.LengthForTest(), 0);
|
|
|
|
new_node = ThreadedListTestNode();
|
|
// Add to empty list
|
|
list.Add(&new_node);
|
|
list.Verify();
|
|
CHECK_EQ(list.LengthForTest(), 1);
|
|
CHECK_EQ(list.AtForTest(0), &new_node);
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, AddFront) {
|
|
CHECK_EQ(list.LengthForTest(), 5);
|
|
ThreadedListTestNode new_node;
|
|
// AddFront to existing list
|
|
list.AddFront(&new_node);
|
|
list.Verify();
|
|
CHECK_EQ(list.LengthForTest(), 6);
|
|
CHECK_EQ(list.first(), &new_node);
|
|
|
|
list.Clear();
|
|
CHECK_EQ(list.LengthForTest(), 0);
|
|
|
|
new_node = ThreadedListTestNode();
|
|
// AddFront to empty list
|
|
list.AddFront(&new_node);
|
|
list.Verify();
|
|
CHECK_EQ(list.LengthForTest(), 1);
|
|
CHECK_EQ(list.first(), &new_node);
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, DropHead) {
|
|
CHECK_EQ(extra_test_list.LengthForTest(), 3);
|
|
CHECK_EQ(extra_test_list.first(), &extra_test_node_0);
|
|
extra_test_list.DropHead();
|
|
extra_test_list.Verify();
|
|
CHECK_EQ(extra_test_list.first(), &extra_test_node_1);
|
|
CHECK_EQ(extra_test_list.LengthForTest(), 2);
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, Append) {
|
|
auto initial_extra_list_end = extra_test_list.end();
|
|
CHECK_EQ(list.LengthForTest(), 5);
|
|
list.Append(std::move(extra_test_list));
|
|
list.Verify();
|
|
extra_test_list.Verify();
|
|
CHECK(extra_test_list.is_empty());
|
|
CHECK_EQ(list.LengthForTest(), 8);
|
|
CHECK_EQ(list.AtForTest(4), &nodes[4]);
|
|
CHECK_EQ(list.AtForTest(5), &extra_test_node_0);
|
|
CHECK_EQ(list.end(), initial_extra_list_end);
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, AppendOutOfScope) {
|
|
ThreadedListTestNode local_extra_test_node_0;
|
|
CHECK_EQ(list.LengthForTest(), 5);
|
|
{
|
|
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
|
|
scoped_extra_test_list;
|
|
|
|
list.Append(std::move(scoped_extra_test_list));
|
|
}
|
|
list.Add(&local_extra_test_node_0);
|
|
|
|
list.Verify();
|
|
CHECK_EQ(list.LengthForTest(), 6);
|
|
CHECK_EQ(list.AtForTest(4), &nodes[4]);
|
|
CHECK_EQ(list.AtForTest(5), &local_extra_test_node_0);
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, Prepend) {
|
|
CHECK_EQ(list.LengthForTest(), 5);
|
|
list.Prepend(std::move(extra_test_list));
|
|
list.Verify();
|
|
extra_test_list.Verify();
|
|
CHECK(extra_test_list.is_empty());
|
|
CHECK_EQ(list.LengthForTest(), 8);
|
|
CHECK_EQ(list.first(), &extra_test_node_0);
|
|
CHECK_EQ(list.AtForTest(2), &extra_test_node_2);
|
|
CHECK_EQ(list.AtForTest(3), &nodes[0]);
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, Clear) {
|
|
CHECK_NE(list.LengthForTest(), 0);
|
|
list.Clear();
|
|
CHECK_EQ(list.LengthForTest(), 0);
|
|
CHECK_NULL(list.first());
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, MoveAssign) {
|
|
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list;
|
|
CHECK_EQ(extra_test_list.LengthForTest(), 3);
|
|
m_list = std::move(extra_test_list);
|
|
|
|
m_list.Verify();
|
|
CHECK_EQ(m_list.first(), &extra_test_node_0);
|
|
CHECK_EQ(m_list.LengthForTest(), 3);
|
|
|
|
// move assign from empty list
|
|
extra_test_list.Clear();
|
|
CHECK_EQ(extra_test_list.LengthForTest(), 0);
|
|
m_list = std::move(extra_test_list);
|
|
CHECK_EQ(m_list.LengthForTest(), 0);
|
|
|
|
m_list.Verify();
|
|
CHECK_NULL(m_list.first());
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, MoveCtor) {
|
|
CHECK_EQ(extra_test_list.LengthForTest(), 3);
|
|
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list(
|
|
std::move(extra_test_list));
|
|
|
|
m_list.Verify();
|
|
CHECK_EQ(m_list.LengthForTest(), 3);
|
|
CHECK_EQ(m_list.first(), &extra_test_node_0);
|
|
|
|
// move construct from empty list
|
|
extra_test_list.Clear();
|
|
CHECK_EQ(extra_test_list.LengthForTest(), 0);
|
|
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list2(
|
|
std::move(extra_test_list));
|
|
CHECK_EQ(m_list2.LengthForTest(), 0);
|
|
|
|
m_list2.Verify();
|
|
CHECK_NULL(m_list2.first());
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, Remove) {
|
|
CHECK_EQ(list.LengthForTest(), 5);
|
|
|
|
// Remove first
|
|
CHECK_EQ(list.first(), &nodes[0]);
|
|
list.Remove(&nodes[0]);
|
|
list.Verify();
|
|
CHECK_EQ(list.first(), &nodes[1]);
|
|
CHECK_EQ(list.LengthForTest(), 4);
|
|
|
|
// Remove middle
|
|
list.Remove(&nodes[2]);
|
|
list.Verify();
|
|
CHECK_EQ(list.LengthForTest(), 3);
|
|
CHECK_EQ(list.first(), &nodes[1]);
|
|
CHECK_EQ(list.AtForTest(1), &nodes[3]);
|
|
|
|
// Remove last
|
|
list.Remove(&nodes[4]);
|
|
list.Verify();
|
|
CHECK_EQ(list.LengthForTest(), 2);
|
|
CHECK_EQ(list.first(), &nodes[1]);
|
|
CHECK_EQ(list.AtForTest(1), &nodes[3]);
|
|
|
|
// Remove rest
|
|
list.Remove(&nodes[1]);
|
|
list.Remove(&nodes[3]);
|
|
list.Verify();
|
|
CHECK_EQ(list.LengthForTest(), 0);
|
|
|
|
// Remove not found
|
|
list.Remove(&nodes[4]);
|
|
list.Verify();
|
|
CHECK_EQ(list.LengthForTest(), 0);
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, Rewind) {
|
|
CHECK_EQ(extra_test_list.LengthForTest(), 3);
|
|
for (auto iter = extra_test_list.begin(); iter != extra_test_list.end();
|
|
++iter) {
|
|
if (*iter == &extra_test_node_2) {
|
|
extra_test_list.Rewind(iter);
|
|
break;
|
|
}
|
|
}
|
|
CHECK_EQ(extra_test_list.LengthForTest(), 2);
|
|
auto iter = extra_test_list.begin();
|
|
CHECK_EQ(*iter, &extra_test_node_0);
|
|
std::advance(iter, 1);
|
|
CHECK_EQ(*iter, &extra_test_node_1);
|
|
|
|
extra_test_list.Rewind(extra_test_list.begin());
|
|
CHECK_EQ(extra_test_list.LengthForTest(), 0);
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, IterComp) {
|
|
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> c_list =
|
|
std::move(extra_test_list);
|
|
bool found_first;
|
|
for (auto iter = c_list.begin(); iter != c_list.end(); ++iter) {
|
|
// This triggers the operator== on the iterator
|
|
if (iter == c_list.begin()) {
|
|
found_first = true;
|
|
}
|
|
}
|
|
CHECK(found_first);
|
|
}
|
|
|
|
TEST_F(ThreadedListTest, ConstIterComp) {
|
|
const ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
|
|
c_list = std::move(extra_test_list);
|
|
bool found_first;
|
|
for (auto iter = c_list.begin(); iter != c_list.end(); ++iter) {
|
|
// This triggers the operator== on the iterator
|
|
if (iter == c_list.begin()) {
|
|
found_first = true;
|
|
}
|
|
}
|
|
CHECK(found_first);
|
|
}
|
|
|
|
} // namespace base
|
|
} // namespace v8
|