// Copyright 2020 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/heap/cppgc/gc-invoker.h" #include "include/cppgc/platform.h" #include "src/heap/cppgc/heap.h" #include "test/unittests/heap/cppgc/test-platform.h" #include "testing/gmock/include/gmock/gmock-matchers.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace cppgc { namespace internal { namespace { class MockGarbageCollector : public GarbageCollector { public: MOCK_METHOD(void, CollectGarbage, (GCConfig), (override)); MOCK_METHOD(void, StartIncrementalGarbageCollection, (GCConfig), (override)); MOCK_METHOD(size_t, epoch, (), (const, override)); MOCK_METHOD(const EmbedderStackState*, override_stack_state, (), (const, override)); }; class MockTaskRunner : public cppgc::TaskRunner { public: MOCK_METHOD(void, PostTask, (std::unique_ptr), (override)); MOCK_METHOD(void, PostNonNestableTask, (std::unique_ptr), (override)); MOCK_METHOD(void, PostDelayedTask, (std::unique_ptr, double), (override)); MOCK_METHOD(void, PostNonNestableDelayedTask, (std::unique_ptr, double), (override)); MOCK_METHOD(void, PostIdleTask, (std::unique_ptr), (override)); bool IdleTasksEnabled() override { return true; } bool NonNestableTasksEnabled() const override { return true; } bool NonNestableDelayedTasksEnabled() const override { return true; } }; class MockPlatform : public cppgc::Platform { public: explicit MockPlatform(std::shared_ptr runner) : runner_(std::move(runner)), tracing_controller_(std::make_unique()) {} PageAllocator* GetPageAllocator() override { return nullptr; } double MonotonicallyIncreasingTime() override { return 0.0; } std::shared_ptr GetForegroundTaskRunner() override { return runner_; } TracingController* GetTracingController() override { return tracing_controller_.get(); } private: std::shared_ptr runner_; std::unique_ptr tracing_controller_; }; } // namespace TEST(GCInvokerTest, PrecideGCIsInvokedSynchronously) { MockPlatform platform(nullptr); MockGarbageCollector gc; GCInvoker invoker(&gc, &platform, cppgc::Heap::StackSupport::kNoConservativeStackScan); EXPECT_CALL(gc, CollectGarbage(::testing::Field( &GCConfig::stack_state, StackState::kNoHeapPointers))); invoker.CollectGarbage(GCConfig::PreciseAtomicConfig()); } TEST(GCInvokerTest, ConservativeGCIsInvokedSynchronouslyWhenSupported) { MockPlatform platform(nullptr); MockGarbageCollector gc; GCInvoker invoker(&gc, &platform, cppgc::Heap::StackSupport::kSupportsConservativeStackScan); EXPECT_CALL( gc, CollectGarbage(::testing::Field( &GCConfig::stack_state, StackState::kMayContainHeapPointers))); invoker.CollectGarbage(GCConfig::ConservativeAtomicConfig()); } TEST(GCInvokerTest, ConservativeGCIsScheduledAsPreciseGCViaPlatform) { std::shared_ptr runner = std::shared_ptr(new MockTaskRunner()); MockPlatform platform(runner); MockGarbageCollector gc; GCInvoker invoker(&gc, &platform, cppgc::Heap::StackSupport::kNoConservativeStackScan); EXPECT_CALL(gc, epoch).WillOnce(::testing::Return(0)); EXPECT_CALL(*static_cast(runner.get()), PostNonNestableTask(::testing::_)); invoker.CollectGarbage(GCConfig::ConservativeAtomicConfig()); } TEST(GCInvokerTest, ConservativeGCIsInvokedAsPreciseGCViaPlatform) { testing::TestPlatform platform; MockGarbageCollector gc; GCInvoker invoker(&gc, &platform, cppgc::Heap::StackSupport::kNoConservativeStackScan); EXPECT_CALL(gc, epoch).WillRepeatedly(::testing::Return(0)); EXPECT_CALL(gc, CollectGarbage); invoker.CollectGarbage(GCConfig::ConservativeAtomicConfig()); platform.RunAllForegroundTasks(); } TEST(GCInvokerTest, IncrementalGCIsStarted) { // Since StartIncrementalGarbageCollection doesn't scan the stack, support for // conservative stack scanning should not matter. MockPlatform platform(nullptr); MockGarbageCollector gc; // Conservative stack scanning supported. GCInvoker invoker_with_support( &gc, &platform, cppgc::Heap::StackSupport::kSupportsConservativeStackScan); EXPECT_CALL( gc, StartIncrementalGarbageCollection(::testing::Field( &GCConfig::stack_state, StackState::kMayContainHeapPointers))); invoker_with_support.StartIncrementalGarbageCollection( GCConfig::ConservativeIncrementalConfig()); // Conservative stack scanning *not* supported. GCInvoker invoker_without_support( &gc, &platform, cppgc::Heap::StackSupport::kNoConservativeStackScan); EXPECT_CALL(gc, StartIncrementalGarbageCollection(::testing::Field( &GCConfig::stack_state, StackState::kMayContainHeapPointers))) .Times(0); invoker_without_support.StartIncrementalGarbageCollection( GCConfig::ConservativeIncrementalConfig()); } } // namespace internal } // namespace cppgc