Use local MicrotaskQueue in unittests

MicrotaskQueueTest uses Isolate's default_microtask_queue for testing,
however the instance is shared between test cases, and causes flaky
failure of MicrotaskQueueTest.BufferGrowth.

This CL adds a MicrotaskQueue instance for each test fixture, so that
each test cases use separate ones.

Also, this CL removes the DCHECK that denies non-default MicrotaskQueue
to run, which is unneeded after https://crrev.com/c/1369906.

Bug: v8:8124
Change-Id: I4ff236c327bf0be14f582b3ca8c802fd72661b42
Reviewed-on: https://chromium-review.googlesource.com/c/1417315
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Taiju Tsuiki <tzik@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58901}
This commit is contained in:
tzik 2019-01-17 20:31:28 +09:00 committed by Commit Bot
parent 2bb5b40f75
commit eebdb0f54d
3 changed files with 50 additions and 44 deletions

View File

@ -258,13 +258,6 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Object> Invoke(Isolate* isolate,
Handle<Code> code =
JSEntry(isolate, params.execution_target, params.is_construct);
{
// Non-default MicrotaskQueue is currently unsupported.
// TODO(tzik): Remove this DCHECK once the entry function is ready to handle
// the microtask_queue parameter.
DCHECK_IMPLIES(
params.execution_target == Execution::Target::kRunMicrotasks,
params.microtask_queue == isolate->default_microtask_queue());
// Save and restore context around invocation and block the
// allocation of handles without explicit handle scopes.
SaveContext save(isolate);

View File

@ -36,9 +36,27 @@ class MicrotaskQueueTest : public TestWithNativeContext {
return factory()->NewCallbackTask(runner, data);
}
void TearDown() override {
isolate()->default_microtask_queue()->RunMicrotasks(isolate());
void SetUp() override {
microtask_queue_ = MicrotaskQueue::New(isolate());
native_context()->set_microtask_queue(microtask_queue());
}
void TearDown() override {
if (microtask_queue()) {
microtask_queue()->RunMicrotasks(isolate());
context()->DetachGlobal();
}
}
MicrotaskQueue* microtask_queue() const { return microtask_queue_.get(); }
void ClearTestMicrotaskQueue() {
context()->DetachGlobal();
microtask_queue_ = nullptr;
}
private:
std::unique_ptr<MicrotaskQueue> microtask_queue_;
};
class RecordingVisitor : public RootVisitor {
@ -61,61 +79,57 @@ class RecordingVisitor : public RootVisitor {
// Sanity check. Ensure a microtask is stored in a queue and run.
TEST_F(MicrotaskQueueTest, EnqueueAndRun) {
MicrotaskQueue* microtask_queue = isolate()->default_microtask_queue();
ASSERT_TRUE(microtask_queue);
bool ran = false;
EXPECT_EQ(0, microtask_queue->capacity());
EXPECT_EQ(0, microtask_queue->size());
microtask_queue->EnqueueMicrotask(*NewMicrotask([&ran] {
EXPECT_EQ(0, microtask_queue()->capacity());
EXPECT_EQ(0, microtask_queue()->size());
microtask_queue()->EnqueueMicrotask(*NewMicrotask([&ran] {
EXPECT_FALSE(ran);
ran = true;
}));
EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue->capacity());
EXPECT_EQ(1, microtask_queue->size());
microtask_queue->RunMicrotasks(isolate());
EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue()->capacity());
EXPECT_EQ(1, microtask_queue()->size());
microtask_queue()->RunMicrotasks(isolate());
EXPECT_TRUE(ran);
EXPECT_EQ(0, microtask_queue->size());
EXPECT_EQ(0, microtask_queue()->size());
}
// Check for a buffer growth.
TEST_F(MicrotaskQueueTest, BufferGrowth) {
MicrotaskQueue* microtask_queue = isolate()->default_microtask_queue();
ASSERT_TRUE(microtask_queue);
int count = 0;
// Enqueue and flush the queue first to have non-zero |start_|.
microtask_queue->EnqueueMicrotask(
microtask_queue()->EnqueueMicrotask(
*NewMicrotask([&count] { EXPECT_EQ(0, count++); }));
microtask_queue->RunMicrotasks(isolate());
microtask_queue()->RunMicrotasks(isolate());
EXPECT_LT(0, microtask_queue->capacity());
EXPECT_EQ(0, microtask_queue->size());
EXPECT_EQ(1, microtask_queue->start());
EXPECT_LT(0, microtask_queue()->capacity());
EXPECT_EQ(0, microtask_queue()->size());
EXPECT_EQ(1, microtask_queue()->start());
// Fill the queue with Microtasks.
for (int i = 1; i <= MicrotaskQueue::kMinimumCapacity; ++i) {
microtask_queue->EnqueueMicrotask(
microtask_queue()->EnqueueMicrotask(
*NewMicrotask([&count, i] { EXPECT_EQ(i, count++); }));
}
EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue->capacity());
EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue->size());
EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue()->capacity());
EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue()->size());
// Add another to grow the ring buffer.
microtask_queue->EnqueueMicrotask(*NewMicrotask(
microtask_queue()->EnqueueMicrotask(*NewMicrotask(
[&] { EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 1, count++); }));
EXPECT_LT(MicrotaskQueue::kMinimumCapacity, microtask_queue->capacity());
EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 1, microtask_queue->size());
EXPECT_LT(MicrotaskQueue::kMinimumCapacity, microtask_queue()->capacity());
EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 1, microtask_queue()->size());
// Run all pending Microtasks to ensure they run in the proper order.
microtask_queue->RunMicrotasks(isolate());
microtask_queue()->RunMicrotasks(isolate());
EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 2, count);
}
// MicrotaskQueue instances form a doubly linked list.
TEST_F(MicrotaskQueueTest, InstanceChain) {
ClearTestMicrotaskQueue();
MicrotaskQueue* default_mtq = isolate()->default_microtask_queue();
ASSERT_TRUE(default_mtq);
EXPECT_EQ(default_mtq, default_mtq->next());
@ -145,26 +159,23 @@ TEST_F(MicrotaskQueueTest, InstanceChain) {
// Pending Microtasks in MicrotaskQueues are strong roots. Ensure they are
// visited exactly once.
TEST_F(MicrotaskQueueTest, VisitRoot) {
MicrotaskQueue* microtask_queue = isolate()->default_microtask_queue();
ASSERT_TRUE(microtask_queue);
// Ensure that the ring buffer has separate in-use region.
for (int i = 0; i < MicrotaskQueue::kMinimumCapacity / 2 + 1; ++i) {
microtask_queue->EnqueueMicrotask(*NewMicrotask([] {}));
microtask_queue()->EnqueueMicrotask(*NewMicrotask([] {}));
}
microtask_queue->RunMicrotasks(isolate());
microtask_queue()->RunMicrotasks(isolate());
std::vector<Object> expected;
for (int i = 0; i < MicrotaskQueue::kMinimumCapacity / 2 + 1; ++i) {
Handle<Microtask> microtask = NewMicrotask([] {});
expected.push_back(*microtask);
microtask_queue->EnqueueMicrotask(*microtask);
microtask_queue()->EnqueueMicrotask(*microtask);
}
EXPECT_GT(microtask_queue->start() + microtask_queue->size(),
microtask_queue->capacity());
EXPECT_GT(microtask_queue()->start() + microtask_queue()->size(),
microtask_queue()->capacity());
RecordingVisitor visitor;
microtask_queue->IterateMicrotasks(&visitor);
microtask_queue()->IterateMicrotasks(&visitor);
std::vector<Object> actual = visitor.visited();
std::sort(expected.begin(), expected.end());

View File

@ -222,7 +222,9 @@ class WithInternalIsolateMixin : public TMixin {
Factory* factory() const { return isolate()->factory(); }
Isolate* isolate() const { return TMixin::i_isolate(); }
Handle<Context> native_context() const { return isolate()->native_context(); }
Handle<NativeContext> native_context() const {
return isolate()->native_context();
}
template <typename T = Object>
Handle<T> RunJS(const char* source) {