Commit 6c85c148 authored by mlippautz's avatar mlippautz Committed by Commit bot

Add lock-based unbounded queue

...based on the 2-lock algorithm by M. Scott and M. Michael (1992).

BUG=chromium:524425
LOG=N

Review URL: https://codereview.chromium.org/1448283004

Cr-Commit-Position: refs/heads/master@{#32078}
parent 95ff2971
// Copyright 2015 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.
#ifndef V8_LOCKED_QUEUE_INL_
#define V8_LOCKED_QUEUE_INL_
#include "src/atomic-utils.h"
#include "src/locked-queue.h"
namespace v8 {
namespace internal {
template <typename Record>
struct LockedQueue<Record>::Node : Malloced {
Node() : next(nullptr) {}
Record value;
AtomicValue<Node*> next;
};
template <typename Record>
inline LockedQueue<Record>::LockedQueue() {
head_ = new Node();
CHECK(head_ != nullptr);
tail_ = head_;
}
template <typename Record>
inline LockedQueue<Record>::~LockedQueue() {
// Destroy all remaining nodes. Note that we do not destroy the actual values.
Node* old_node = nullptr;
Node* cur_node = head_;
while (cur_node != nullptr) {
old_node = cur_node;
cur_node = cur_node->next.Value();
delete old_node;
}
}
template <typename Record>
inline void LockedQueue<Record>::Enqueue(const Record& record) {
Node* n = new Node();
CHECK(n != nullptr);
n->value = record;
{
base::LockGuard<base::Mutex> guard(&tail_mutex_);
tail_->next.SetValue(n);
tail_ = n;
}
}
template <typename Record>
inline bool LockedQueue<Record>::Dequeue(Record* record) {
Node* old_head = nullptr;
{
base::LockGuard<base::Mutex> guard(&head_mutex_);
old_head = head_;
Node* const next_node = head_->next.Value();
if (next_node == nullptr) return false;
*record = next_node->value;
head_ = next_node;
}
delete old_head;
return true;
}
template <typename Record>
inline bool LockedQueue<Record>::IsEmpty() const {
base::LockGuard<base::Mutex> guard(&head_mutex_);
return head_->next.Value() == nullptr;
}
template <typename Record>
inline bool LockedQueue<Record>::Peek(Record* record) const {
base::LockGuard<base::Mutex> guard(&head_mutex_);
Node* const next_node = head_->next.Value();
if (next_node == nullptr) return false;
*record = next_node->value;
return true;
}
} // namespace internal
} // namespace v8
#endif // V8_LOCKED_QUEUE_INL_
// Copyright 2015 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.
#ifndef V8_LOCKED_QUEUE_
#define V8_LOCKED_QUEUE_
#include "src/allocation.h"
#include "src/base/platform/platform.h"
namespace v8 {
namespace internal {
// Simple lock-based unbounded size queue (multi producer; multi consumer) based
// on "Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue
// Algorithms" by M. Scott and M. Michael.
// See:
// https://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html
template <typename Record>
class LockedQueue final BASE_EMBEDDED {
public:
inline LockedQueue();
inline ~LockedQueue();
inline void Enqueue(const Record& record);
inline bool Dequeue(Record* record);
inline bool IsEmpty() const;
inline bool Peek(Record* record) const;
private:
struct Node;
mutable base::Mutex head_mutex_;
base::Mutex tail_mutex_;
Node* head_;
Node* tail_;
DISALLOW_COPY_AND_ASSIGN(LockedQueue);
};
} // namespace internal
} // namespace v8
#endif // V8_LOCKED_QUEUE_
// Copyright 2015 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 "src/locked-queue-inl.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
typedef int Record;
} // namespace
namespace v8 {
namespace internal {
TEST(LockedQueue, ConstructorEmpty) {
LockedQueue<Record> queue;
EXPECT_TRUE(queue.IsEmpty());
}
TEST(LockedQueue, SingleRecordEnqueueDequeue) {
LockedQueue<Record> queue;
EXPECT_TRUE(queue.IsEmpty());
queue.Enqueue(1);
EXPECT_FALSE(queue.IsEmpty());
Record a = -1;
bool success = queue.Dequeue(&a);
EXPECT_TRUE(success);
EXPECT_EQ(a, 1);
EXPECT_TRUE(queue.IsEmpty());
}
TEST(LockedQueue, Peek) {
LockedQueue<Record> queue;
EXPECT_TRUE(queue.IsEmpty());
queue.Enqueue(1);
EXPECT_FALSE(queue.IsEmpty());
Record a = -1;
bool success = queue.Peek(&a);
EXPECT_TRUE(success);
EXPECT_EQ(a, 1);
EXPECT_FALSE(queue.IsEmpty());
success = queue.Dequeue(&a);
EXPECT_TRUE(success);
EXPECT_EQ(a, 1);
EXPECT_TRUE(queue.IsEmpty());
}
TEST(LockedQueue, PeekOnEmpty) {
LockedQueue<Record> queue;
EXPECT_TRUE(queue.IsEmpty());
Record a = -1;
bool success = queue.Peek(&a);
EXPECT_FALSE(success);
}
TEST(LockedQueue, MultipleRecords) {
LockedQueue<Record> queue;
EXPECT_TRUE(queue.IsEmpty());
queue.Enqueue(1);
EXPECT_FALSE(queue.IsEmpty());
for (int i = 2; i <= 5; ++i) {
queue.Enqueue(i);
EXPECT_FALSE(queue.IsEmpty());
}
Record rec = 0;
for (int i = 1; i <= 4; ++i) {
EXPECT_FALSE(queue.IsEmpty());
queue.Dequeue(&rec);
EXPECT_EQ(i, rec);
}
for (int i = 6; i <= 12; ++i) {
queue.Enqueue(i);
EXPECT_FALSE(queue.IsEmpty());
}
for (int i = 5; i <= 12; ++i) {
EXPECT_FALSE(queue.IsEmpty());
queue.Dequeue(&rec);
EXPECT_EQ(i, rec);
}
EXPECT_TRUE(queue.IsEmpty());
}
} // namespace internal
} // namespace v8
......@@ -110,6 +110,7 @@
'heap/memory-reducer-unittest.cc',
'heap/heap-unittest.cc',
'heap/scavenge-job-unittest.cc',
'locked-queue-unittest.cc',
'run-all-unittests.cc',
'runtime/runtime-interpreter-unittest.cc',
'test-utils.h',
......
......@@ -854,6 +854,8 @@
'../../src/layout-descriptor.h',
'../../src/list-inl.h',
'../../src/list.h',
'../../src/locked-queue-inl.h',
'../../src/locked-queue.h',
'../../src/log-inl.h',
'../../src/log-utils.cc',
'../../src/log-utils.h',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment