cppgc: Use libplatform as default platform

Bug: chromium:1056170
Change-Id: I4214978f31ae754e4940dfca4182ada202d17c01
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2456688
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Hannes Payer <hpayer@chromium.org>
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70412}
This commit is contained in:
Omer Katz 2020-10-08 13:16:20 +02:00 committed by Commit Bot
parent c212d10da0
commit 65bec1aa79
14 changed files with 85 additions and 610 deletions

View File

@ -4374,8 +4374,6 @@ v8_source_set("cppgc_base") {
"src/heap/cppgc/allocation.cc",
"src/heap/cppgc/concurrent-marker.cc",
"src/heap/cppgc/concurrent-marker.h",
"src/heap/cppgc/default-job.h",
"src/heap/cppgc/default-platform.cc",
"src/heap/cppgc/free-list.cc",
"src/heap/cppgc/free-list.h",
"src/heap/cppgc/garbage-collector.h",
@ -4457,6 +4455,7 @@ v8_source_set("cppgc_base") {
public_deps = [
":v8_cppgc_shared",
":v8_libbase",
":v8_libplatform",
]
}

View File

@ -4,4 +4,5 @@ include_rules = [
"+v8-platform.h",
"+cppgc",
"-src",
"+libplatform/libplatform.h",
]

View File

@ -9,74 +9,49 @@
#include <vector>
#include "cppgc/platform.h"
#include "libplatform/libplatform.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
class DefaultJob;
} // namespace internal
/**
* Default task runner implementation. Keep posted tasks in a list that can be
* processed by calling RunSingleTask() or RunUntilIdle().
* Platform provided by cppgc. Uses V8's DefaultPlatform provided by
* libplatform internally.Exception: GetForegroundTaskRunner(), see below.
*/
class V8_EXPORT DefaultTaskRunner final : public cppgc::TaskRunner {
class V8_EXPORT DefaultPlatform : public Platform {
public:
DefaultTaskRunner() = default;
using IdleTaskSupport = v8::platform::IdleTaskSupport;
explicit DefaultPlatform(
int thread_pool_size = 0,
IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled)
: v8_platform_(v8::platform::NewDefaultPlatform(thread_pool_size,
idle_task_support)) {}
DefaultTaskRunner(const DefaultTaskRunner&) = delete;
DefaultTaskRunner& operator=(const DefaultTaskRunner&) = delete;
cppgc::PageAllocator* GetPageAllocator() override {
return v8_platform_->GetPageAllocator();
}
void PostTask(std::unique_ptr<cppgc::Task> task) override;
void PostDelayedTask(std::unique_ptr<cppgc::Task> task, double) override;
double MonotonicallyIncreasingTime() override {
return v8_platform_->MonotonicallyIncreasingTime();
}
bool NonNestableTasksEnabled() const final { return false; }
bool NonNestableDelayedTasksEnabled() const final { return false; }
void PostNonNestableTask(std::unique_ptr<cppgc::Task> task) override;
void PostNonNestableDelayedTask(std::unique_ptr<cppgc::Task> task,
double) override;
std::shared_ptr<cppgc::TaskRunner> GetForegroundTaskRunner() override {
// V8's default platform creates a new task runner when passed the
// v8::Isolate pointer the first time. For non-default platforms this will
// require getting the appropriate task runner.
return v8_platform_->GetForegroundTaskRunner(kNoIsolate);
}
void PostIdleTask(std::unique_ptr<cppgc::IdleTask> task) override;
bool IdleTasksEnabled() override { return true; }
bool RunSingleTask();
bool RunSingleIdleTask(double duration_in_seconds);
void RunUntilIdle();
private:
std::vector<std::unique_ptr<cppgc::Task>> tasks_;
std::vector<std::unique_ptr<cppgc::IdleTask>> idle_tasks_;
};
/**
* Default platform implementation that uses std::thread for spawning job tasks.
*/
class V8_EXPORT DefaultPlatform final : public Platform {
public:
DefaultPlatform();
~DefaultPlatform() noexcept override;
cppgc::PageAllocator* GetPageAllocator() final;
double MonotonicallyIncreasingTime() final;
std::shared_ptr<cppgc::TaskRunner> GetForegroundTaskRunner() final;
// DefaultPlatform does not support job priorities. All jobs would be
// assigned the same priority regardless of the cppgc::TaskPriority parameter.
std::unique_ptr<cppgc::JobHandle> PostJob(
cppgc::TaskPriority priority,
std::unique_ptr<cppgc::JobTask> job_task) final;
std::unique_ptr<cppgc::JobTask> job_task) override {
return v8_platform_->PostJob(priority, std::move(job_task));
}
void WaitAllForegroundTasks();
void WaitAllBackgroundTasks();
protected:
static constexpr v8::Isolate* kNoIsolate = nullptr;
private:
std::unique_ptr<PageAllocator> page_allocator_;
std::shared_ptr<DefaultTaskRunner> foreground_task_runner_;
std::vector<std::shared_ptr<internal::DefaultJob>> jobs_;
std::unique_ptr<v8::Platform> v8_platform_;
};
} // namespace cppgc

View File

@ -3,12 +3,12 @@
// found in the LICENSE file.
#include <include/cppgc/allocation.h>
#include <include/cppgc/default-platform.h>
#include <include/cppgc/garbage-collected.h>
#include <include/cppgc/heap.h>
#include <include/cppgc/member.h>
#include <include/cppgc/platform.h>
#include <include/cppgc/visitor.h>
#include <include/libplatform/libplatform.h>
#include <include/v8.h>
#include <iostream>
@ -21,42 +21,6 @@
* platform for cppgc.
*/
/**
* Platform used by cppgc. Can just redirect to v8::Platform for most calls.
* Exception: GetForegroundTaskRunner(), see below.
*
* This example uses V8's default platform implementation to drive the cppgc
* platform.
*/
class Platform final : public cppgc::Platform {
public:
Platform() : v8_platform_(v8::platform::NewDefaultPlatform()) {}
cppgc::PageAllocator* GetPageAllocator() final {
return v8_platform_->GetPageAllocator();
}
double MonotonicallyIncreasingTime() final {
return v8_platform_->MonotonicallyIncreasingTime();
}
std::shared_ptr<cppgc::TaskRunner> GetForegroundTaskRunner() final {
// V8's default platform creates a new task runner when passed the
// v8::Isolate pointer the first time. For non-default platforms this will
// require getting the appropriate task runner.
return v8_platform_->GetForegroundTaskRunner(nullptr);
}
std::unique_ptr<cppgc::JobHandle> PostJob(
cppgc::TaskPriority priority,
std::unique_ptr<cppgc::JobTask> job_task) final {
return v8_platform_->PostJob(priority, std::move(job_task));
}
private:
std::unique_ptr<v8::Platform> v8_platform_;
};
/**
* Simple string rope to illustrate allocation and garbage collection below. The
* rope keeps the next parts alive via regular managed reference.
@ -86,7 +50,7 @@ std::ostream& operator<<(std::ostream& os, const Rope& rope) {
int main(int argc, char* argv[]) {
// Create a platform that is used by cppgc::Heap for execution and backend
// allocation.
auto cppgc_platform = std::make_shared<Platform>();
auto cppgc_platform = std::make_shared<cppgc::DefaultPlatform>();
// Initialize the process. This must happen before any cppgc::Heap::Create()
// calls.
cppgc::InitializeProcess(cppgc_platform->GetPageAllocator());

View File

@ -161,7 +161,7 @@ ConcurrentMarkerBase::ConcurrentMarkerBase(
platform_(platform) {}
void ConcurrentMarkerBase::Start() {
if (!platform_) return;
DCHECK(platform_);
concurrent_marking_handle_ =
platform_->PostJob(v8::TaskPriority::kUserVisible,
std::make_unique<ConcurrentMarkingTask>(*this));

View File

@ -1,193 +0,0 @@
// 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.
#ifndef V8_HEAP_CPPGC_DEFAULT_JOB_H_
#define V8_HEAP_CPPGC_DEFAULT_JOB_H_
#include <atomic>
#include <map>
#include <memory>
#include <unordered_set>
#include <vector>
#include "include/cppgc/platform.h"
#include "src/base/logging.h"
#include "src/base/platform/mutex.h"
namespace cppgc {
namespace internal {
template <typename Job>
class DefaultJobFactory {
public:
static std::shared_ptr<Job> Create(std::unique_ptr<cppgc::JobTask> job_task) {
std::shared_ptr<Job> job =
std::make_shared<Job>(typename Job::Key(), std::move(job_task));
job->NotifyConcurrencyIncrease();
return job;
}
};
template <typename Thread>
class DefaultJobImpl {
public:
class JobDelegate;
class JobHandle;
class Key {
private:
Key() {}
template <typename Job>
friend class DefaultJobFactory;
};
DefaultJobImpl(Key, std::unique_ptr<cppgc::JobTask> job_task)
: job_task_(std::move(job_task)) {}
~DefaultJobImpl() {
Cancel();
DCHECK_EQ(0, active_threads_.load(std::memory_order_relaxed));
}
void NotifyConcurrencyIncrease();
void Join() {
for (std::shared_ptr<Thread>& thread : job_threads_) thread->Join();
job_threads_.clear();
can_run_.store(false, std::memory_order_relaxed);
}
void Cancel() {
can_run_.store(false, std::memory_order_relaxed);
Join();
}
void CancelAndDetach() { can_run_.store(false, std::memory_order_relaxed); }
bool IsCompleted() const { return !IsRunning(); }
bool IsRunning() const {
uint8_t active_threads = active_threads_.load(std::memory_order_relaxed);
return (active_threads + job_task_->GetMaxConcurrency(active_threads)) > 0;
}
bool CanRun() const { return can_run_.load(std::memory_order_relaxed); }
void RunJobTask() {
DCHECK_NOT_NULL(job_task_);
NotifyJobThreadStart();
JobDelegate delegate(this);
job_task_->Run(&delegate);
NotifyJobThreadEnd();
}
protected:
virtual std::shared_ptr<Thread> CreateThread(DefaultJobImpl*) = 0;
void NotifyJobThreadStart() {
active_threads_.fetch_add(1, std::memory_order_relaxed);
}
void NotifyJobThreadEnd() {
active_threads_.fetch_sub(1, std::memory_order_relaxed);
}
void GuaranteeAvailableIds(uint8_t max_threads) {
if (max_threads <= highest_thread_count_) return;
v8::base::MutexGuard guard(&ids_lock_);
while (highest_thread_count_ < max_threads) {
available_ids_.push_back(++highest_thread_count_);
}
}
std::unique_ptr<cppgc::JobTask> job_task_;
std::vector<std::shared_ptr<Thread>> job_threads_;
std::atomic_bool can_run_{true};
std::atomic<uint8_t> active_threads_{0};
// Task id management.
v8::base::Mutex ids_lock_;
std::vector<uint8_t> available_ids_;
uint8_t highest_thread_count_ = -1;
};
template <typename Thread>
class DefaultJobImpl<Thread>::JobDelegate final : public cppgc::JobDelegate {
public:
explicit JobDelegate(DefaultJobImpl* job) : job_(job) {}
~JobDelegate() { ReleaseTaskId(); }
bool ShouldYield() override { return !job_->CanRun(); }
void NotifyConcurrencyIncrease() override {
job_->NotifyConcurrencyIncrease();
}
uint8_t GetTaskId() override {
AcquireTaskId();
return job_thread_id_;
}
private:
void AcquireTaskId() {
if (job_thread_id_ != kInvalidTaskId) return;
v8::base::MutexGuard guard(&job_->ids_lock_);
job_thread_id_ = job_->available_ids_.back();
DCHECK_NE(kInvalidTaskId, job_thread_id_);
job_->available_ids_.pop_back();
}
void ReleaseTaskId() {
if (job_thread_id_ == kInvalidTaskId) return;
v8::base::MutexGuard guard(&job_->ids_lock_);
job_->available_ids_.push_back(job_thread_id_);
}
DefaultJobImpl* const job_;
static constexpr uint8_t kInvalidTaskId = std::numeric_limits<uint8_t>::max();
uint8_t job_thread_id_ = kInvalidTaskId;
};
template <typename Thread>
void DefaultJobImpl<Thread>::NotifyConcurrencyIncrease() {
DCHECK(CanRun());
static const size_t kMaxThreads = Thread::GetMaxSupportedConcurrency();
uint8_t current_active_threads =
active_threads_.load(std::memory_order_relaxed);
size_t max_threads = std::min(
kMaxThreads, job_task_->GetMaxConcurrency(current_active_threads));
if (current_active_threads >= max_threads) return;
DCHECK_LT(max_threads, std::numeric_limits<uint8_t>::max());
GuaranteeAvailableIds(max_threads);
for (uint8_t new_threads = max_threads - current_active_threads;
new_threads > 0; --new_threads) {
std::shared_ptr<Thread> thread = CreateThread(this);
job_threads_.push_back(thread);
}
}
template <typename Thread>
class DefaultJobImpl<Thread>::JobHandle final : public cppgc::JobHandle {
public:
explicit JobHandle(std::shared_ptr<DefaultJobImpl> job)
: job_(std::move(job)) {
DCHECK_NOT_NULL(job_);
}
void NotifyConcurrencyIncrease() override {
job_->NotifyConcurrencyIncrease();
}
void Join() override { job_->Join(); }
void Cancel() override { job_->Cancel(); }
void CancelAndDetach() override { job_->CancelAndDetach(); }
bool IsCompleted() override { return job_->IsCompleted(); }
bool IsRunning() override { return job_->IsRunning(); }
// DefaultJobImpl doesn't support priorities.
bool UpdatePriorityEnabled() const override { return false; }
void UpdatePriority(TaskPriority) override { UNREACHABLE(); }
private:
std::shared_ptr<DefaultJobImpl> job_;
};
} // namespace internal
} // namespace cppgc
#endif // V8_HEAP_CPPGC_DEFAULT_JOB_H_

View File

@ -1,143 +0,0 @@
// 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 "include/cppgc/default-platform.h"
#include <chrono> // NOLINT(build/c++11)
#include <thread> // NOLINT(build/c++11)
#include "src/base/logging.h"
#include "src/base/page-allocator.h"
#include "src/base/sys-info.h"
#include "src/heap/cppgc/default-job.h"
namespace cppgc {
namespace internal {
// Default implementation of Jobs based on std::thread.
namespace {
class DefaultJobThread final : private std::thread {
public:
template <typename Function>
explicit DefaultJobThread(Function function)
: std::thread(std::move(function)) {}
~DefaultJobThread() { DCHECK(!joinable()); }
void Join() { join(); }
static size_t GetMaxSupportedConcurrency() {
return v8::base::SysInfo::NumberOfProcessors() - 1;
}
};
} // namespace
class DefaultJob final : public DefaultJobImpl<DefaultJobThread> {
public:
DefaultJob(Key key, std::unique_ptr<cppgc::JobTask> job_task)
: DefaultJobImpl(key, std::move(job_task)) {}
std::shared_ptr<DefaultJobThread> CreateThread(DefaultJobImpl* job) final {
return std::make_shared<DefaultJobThread>([job = this] {
DCHECK_NOT_NULL(job);
job->RunJobTask();
});
}
};
} // namespace internal
void DefaultTaskRunner::PostTask(std::unique_ptr<cppgc::Task> task) {
tasks_.push_back(std::move(task));
}
void DefaultTaskRunner::PostDelayedTask(std::unique_ptr<cppgc::Task> task,
double) {
PostTask(std::move(task));
}
void DefaultTaskRunner::PostNonNestableTask(std::unique_ptr<cppgc::Task>) {
UNREACHABLE();
}
void DefaultTaskRunner::PostNonNestableDelayedTask(std::unique_ptr<cppgc::Task>,
double) {
UNREACHABLE();
}
void DefaultTaskRunner::PostIdleTask(std::unique_ptr<cppgc::IdleTask> task) {
idle_tasks_.push_back(std::move(task));
}
bool DefaultTaskRunner::RunSingleTask() {
if (!tasks_.size()) return false;
tasks_.back()->Run();
tasks_.pop_back();
return true;
}
bool DefaultTaskRunner::RunSingleIdleTask(double deadline_in_seconds) {
if (!idle_tasks_.size()) return false;
idle_tasks_.back()->Run(deadline_in_seconds);
idle_tasks_.pop_back();
return true;
}
void DefaultTaskRunner::RunUntilIdle() {
for (auto& task : tasks_) {
task->Run();
}
tasks_.clear();
for (auto& task : idle_tasks_) {
task->Run(std::numeric_limits<double>::infinity());
}
idle_tasks_.clear();
}
DefaultPlatform::DefaultPlatform()
: page_allocator_(std::make_unique<v8::base::PageAllocator>()),
foreground_task_runner_(std::make_shared<DefaultTaskRunner>()) {}
DefaultPlatform::~DefaultPlatform() noexcept { WaitAllBackgroundTasks(); }
cppgc::PageAllocator* DefaultPlatform::GetPageAllocator() {
return page_allocator_.get();
}
double DefaultPlatform::MonotonicallyIncreasingTime() {
return std::chrono::duration<double>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
}
std::shared_ptr<cppgc::TaskRunner> DefaultPlatform::GetForegroundTaskRunner() {
return foreground_task_runner_;
}
std::unique_ptr<cppgc::JobHandle> DefaultPlatform::PostJob(
cppgc::TaskPriority priority, std::unique_ptr<cppgc::JobTask> job_task) {
std::shared_ptr<internal::DefaultJob> job =
internal::DefaultJobFactory<internal::DefaultJob>::Create(
std::move(job_task));
jobs_.push_back(job);
return std::make_unique<internal::DefaultJob::JobHandle>(std::move(job));
}
void DefaultPlatform::WaitAllForegroundTasks() {
foreground_task_runner_->RunUntilIdle();
}
void DefaultPlatform::WaitAllBackgroundTasks() {
for (auto& job : jobs_) {
job->Join();
}
jobs_.clear();
}
} // namespace cppgc

View File

@ -261,8 +261,8 @@ void MarkerBase::VisitRoots(MarkingConfig::StackState stack_state) {
}
void MarkerBase::ScheduleIncrementalMarkingTask() {
if (!platform_ || !foreground_task_runner_ || incremental_marking_handle_)
return;
DCHECK(platform_);
if (!foreground_task_runner_ || incremental_marking_handle_) return;
incremental_marking_handle_ =
IncrementalMarkingTask::Post(foreground_task_runner_.get(), this);
}

View File

@ -504,13 +504,11 @@ class Sweeper::SweeperImpl final {
void FinishIfRunning() {
if (!is_in_progress_) return;
if (concurrent_sweeper_handle_ &&
if (concurrent_sweeper_handle_ && concurrent_sweeper_handle_->IsRunning() &&
concurrent_sweeper_handle_->UpdatePriorityEnabled()) {
DCHECK(concurrent_sweeper_handle_->IsRunning());
concurrent_sweeper_handle_->UpdatePriority(
cppgc::TaskPriority::kUserBlocking);
}
Finish();
}
@ -533,6 +531,10 @@ class Sweeper::SweeperImpl final {
stats_collector_->NotifySweepingCompleted();
}
void WaitForConcurrentSweepingForTesting() {
if (concurrent_sweeper_handle_) concurrent_sweeper_handle_->Join();
}
private:
class IncrementalSweepTask : public cppgc::IdleTask {
public:
@ -572,14 +574,17 @@ class Sweeper::SweeperImpl final {
};
void ScheduleIncrementalSweeping() {
if (!platform_ || !foreground_task_runner_) return;
DCHECK(platform_);
if (!foreground_task_runner_ ||
!foreground_task_runner_->IdleTasksEnabled())
return;
incremental_sweeper_handle_ =
IncrementalSweepTask::Post(this, foreground_task_runner_.get());
}
void ScheduleConcurrentSweeping() {
if (!platform_) return;
DCHECK(platform_);
concurrent_sweeper_handle_ = platform_->PostJob(
cppgc::TaskPriority::kUserVisible,
@ -588,7 +593,8 @@ class Sweeper::SweeperImpl final {
void CancelSweepers() {
if (incremental_sweeper_handle_) incremental_sweeper_handle_.Cancel();
if (concurrent_sweeper_handle_) concurrent_sweeper_handle_->Cancel();
if (concurrent_sweeper_handle_ && concurrent_sweeper_handle_->IsRunning())
concurrent_sweeper_handle_->Cancel();
}
void SynchronizeAndFinalizeConcurrentSweeping() {
@ -616,6 +622,9 @@ Sweeper::~Sweeper() = default;
void Sweeper::Start(Config config) { impl_->Start(config); }
void Sweeper::FinishIfRunning() { impl_->FinishIfRunning(); }
void Sweeper::WaitForConcurrentSweepingForTesting() {
impl_->WaitForConcurrentSweepingForTesting();
}
} // namespace internal
} // namespace cppgc

View File

@ -17,6 +17,7 @@ namespace internal {
class StatsCollector;
class RawHeap;
class ConcurrentSweeperTest;
class V8_EXPORT_PRIVATE Sweeper final {
public:
@ -33,8 +34,12 @@ class V8_EXPORT_PRIVATE Sweeper final {
void FinishIfRunning();
private:
void WaitForConcurrentSweepingForTesting();
class SweeperImpl;
std::unique_ptr<SweeperImpl> impl_;
friend class ConcurrentSweeperTest;
};
} // namespace internal

View File

@ -61,6 +61,8 @@ class NonFinalizable : public GarbageCollected<NonFinalizable<Size>> {
using NormalNonFinalizable = NonFinalizable<32>;
using LargeNonFinalizable = NonFinalizable<kLargeObjectSizeThreshold * 2>;
} // namespace
class ConcurrentSweeperTest : public testing::TestWithHeap {
public:
ConcurrentSweeperTest() { g_destructor_callcount = 0; }
@ -76,6 +78,12 @@ class ConcurrentSweeperTest : public testing::TestWithHeap {
sweeper.Start(Sweeper::Config::kIncrementalAndConcurrent);
}
void WaitForConcurrentSweeping() {
Heap* heap = Heap::From(GetHeap());
Sweeper& sweeper = heap->sweeper();
sweeper.WaitForConcurrentSweepingForTesting();
}
void FinishSweeping() {
Heap* heap = Heap::From(GetHeap());
Sweeper& sweeper = heap->sweeper();
@ -126,8 +134,6 @@ class ConcurrentSweeperTest : public testing::TestWithHeap {
}
};
} // namespace
TEST_F(ConcurrentSweeperTest, BackgroundSweepOfNormalPage) {
// Non finalizable objects are swept right away.
using GCedType = NormalNonFinalizable;
@ -145,7 +151,7 @@ TEST_F(ConcurrentSweeperTest, BackgroundSweepOfNormalPage) {
StartSweeping();
// Wait for concurrent sweeping to finish.
GetPlatform().WaitAllBackgroundTasks();
WaitForConcurrentSweeping();
#if !defined(CPPGC_YOUNG_GENERATION)
// Check that the marked object was unmarked.
@ -184,7 +190,7 @@ TEST_F(ConcurrentSweeperTest, BackgroundSweepOfLargePage) {
StartSweeping();
// Wait for concurrent sweeping to finish.
GetPlatform().WaitAllBackgroundTasks();
WaitForConcurrentSweeping();
#if !defined(CPPGC_YOUNG_GENERATION)
// Check that the marked object was unmarked.
@ -224,7 +230,7 @@ TEST_F(ConcurrentSweeperTest, DeferredFinalizationOfNormalPage) {
StartSweeping();
// Wait for concurrent sweeping to finish.
GetPlatform().WaitAllBackgroundTasks();
WaitForConcurrentSweeping();
// Check that pages are not returned right away.
for (auto* page : pages) {
@ -256,7 +262,7 @@ TEST_F(ConcurrentSweeperTest, DeferredFinalizationOfLargePage) {
StartSweeping();
// Wait for concurrent sweeping to finish.
GetPlatform().WaitAllBackgroundTasks();
WaitForConcurrentSweeping();
// Check that the page is not returned to the space.
EXPECT_EQ(space->end(), std::find(space->begin(), space->end(), page));
@ -302,7 +308,7 @@ TEST_F(ConcurrentSweeperTest, IncrementalSweeping) {
EXPECT_TRUE(marked_large_header.IsMarked());
// Wait for incremental sweeper to finish.
GetPlatform().WaitAllForegroundTasks();
GetPlatform().RunAllForegroundTasks();
EXPECT_EQ(2u, g_destructor_callcount);
#if !defined(CPPGC_YOUNG_GENERATION)

View File

@ -105,7 +105,7 @@ TEST(GCInvokerTest, ConservativeGCIsInvokedAsPreciseGCViaPlatform) {
EXPECT_CALL(gc, epoch).WillRepeatedly(::testing::Return(0));
EXPECT_CALL(gc, CollectGarbage);
invoker.CollectGarbage(GarbageCollector::Config::ConservativeAtomicConfig());
platform.WaitAllForegroundTasks();
platform.RunAllForegroundTasks();
}
TEST(GCInvokerTest, IncrementalGCIsStarted) {

View File

@ -4,132 +4,29 @@
#include "test/unittests/heap/cppgc/test-platform.h"
#include "include/libplatform/libplatform.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "src/heap/cppgc/default-job.h"
namespace cppgc {
namespace internal {
namespace testing {
namespace {
class TestJobThread final : public v8::base::Thread {
public:
using id = uint8_t;
explicit TestJobThread(TestJob* job) : Thread(Options("job")), job_(job) {}
void Run() final;
static size_t GetMaxSupportedConcurrency() { return 4u; }
private:
TestJob* const job_;
};
} // namespace
// Default implementation of Jobs based on std::thread.
class TestJob final : public DefaultJobImpl<TestJobThread> {
public:
explicit TestJob(Key key, std::unique_ptr<cppgc::JobTask> job_task)
: DefaultJobImpl(key, std::move(job_task)) {}
std::shared_ptr<TestJobThread> CreateThread(DefaultJobImpl* job) final {
std::shared_ptr<TestJobThread> thread =
std::make_shared<TestJobThread>(this);
const bool thread_started = thread->Start();
USE(thread_started);
DCHECK(thread_started);
return thread;
}
};
void TestJobThread::Run() {
DCHECK_NOT_NULL(job_);
job_->RunJobTask();
}
void TestTaskRunner::PostTask(std::unique_ptr<cppgc::Task> task) {
tasks_.push_back(std::move(task));
}
void TestTaskRunner::PostNonNestableTask(std::unique_ptr<cppgc::Task> task) {
PostTask(std::move(task));
}
void TestTaskRunner::PostDelayedTask(std::unique_ptr<cppgc::Task> task,
double) {
PostTask(std::move(task));
}
void TestTaskRunner::PostNonNestableDelayedTask(
std::unique_ptr<cppgc::Task> task, double) {
PostTask(std::move(task));
}
void TestTaskRunner::PostIdleTask(std::unique_ptr<cppgc::IdleTask> task) {
idle_tasks_.push_back(std::move(task));
}
bool TestTaskRunner::RunSingleTask() {
if (!tasks_.size()) return false;
tasks_.back()->Run();
tasks_.pop_back();
return true;
}
bool TestTaskRunner::RunSingleIdleTask(double deadline_in_seconds) {
if (!idle_tasks_.size()) return false;
idle_tasks_.back()->Run(deadline_in_seconds);
idle_tasks_.pop_back();
return true;
}
void TestTaskRunner::RunUntilIdle() {
for (auto& task : tasks_) {
task->Run();
}
tasks_.clear();
for (auto& task : idle_tasks_) {
task->Run(std::numeric_limits<double>::infinity());
}
idle_tasks_.clear();
}
TestPlatform::TestPlatform()
: foreground_task_runner_(std::make_unique<TestTaskRunner>()) {}
TestPlatform::~TestPlatform() V8_NOEXCEPT { WaitAllBackgroundTasks(); }
: DefaultPlatform(0, DefaultPlatform::IdleTaskSupport::kEnabled) {}
std::unique_ptr<cppgc::JobHandle> TestPlatform::PostJob(
cppgc::TaskPriority, std::unique_ptr<cppgc::JobTask> job_task) {
if (AreBackgroundTasksDisabled()) return {};
std::shared_ptr<TestJob> job =
DefaultJobFactory<TestJob>::Create(std::move(job_task));
jobs_.push_back(job);
return std::make_unique<TestJob::JobHandle>(std::move(job));
cppgc::TaskPriority priority, std::unique_ptr<cppgc::JobTask> job_task) {
if (AreBackgroundTasksDisabled()) return nullptr;
return v8_platform_->PostJob(priority, std::move(job_task));
}
double TestPlatform::MonotonicallyIncreasingTime() {
return v8::base::TimeTicks::HighResolutionNow().ToInternalValue() /
static_cast<double>(v8::base::Time::kMicrosecondsPerSecond);
}
void TestPlatform::WaitAllForegroundTasks() {
foreground_task_runner_->RunUntilIdle();
}
void TestPlatform::WaitAllBackgroundTasks() {
for (auto& job : jobs_) {
job->Join();
void TestPlatform::RunAllForegroundTasks() {
v8::platform::PumpMessageLoop(v8_platform_.get(), kNoIsolate);
if (GetForegroundTaskRunner()->IdleTasksEnabled()) {
v8::platform::RunIdleTasks(v8_platform_.get(), kNoIsolate,
std::numeric_limits<double>::max());
}
jobs_.clear();
}
TestPlatform::DisableBackgroundTasksScope::DisableBackgroundTasksScope(

View File

@ -5,45 +5,14 @@
#ifndef V8_UNITTESTS_HEAP_CPPGC_TEST_PLATFORM_H_
#define V8_UNITTESTS_HEAP_CPPGC_TEST_PLATFORM_H_
#include <memory>
#include <vector>
#include "include/cppgc/platform.h"
#include "src/base/page-allocator.h"
#include "src/base/platform/platform.h"
#include "include/cppgc/default-platform.h"
#include "src/base/compiler-specific.h"
namespace cppgc {
namespace internal {
namespace testing {
class TestJob;
class TestTaskRunner : public cppgc::TaskRunner {
public:
void PostTask(std::unique_ptr<cppgc::Task> task) override;
void PostDelayedTask(std::unique_ptr<cppgc::Task> task, double) override;
bool NonNestableTasksEnabled() const override { return true; }
void PostNonNestableTask(std::unique_ptr<cppgc::Task> task) override;
bool NonNestableDelayedTasksEnabled() const override { return true; }
void PostNonNestableDelayedTask(std::unique_ptr<cppgc::Task> task,
double) override;
bool IdleTasksEnabled() override { return true; }
void PostIdleTask(std::unique_ptr<cppgc::IdleTask> task) override;
bool RunSingleTask();
bool RunSingleIdleTask(double duration_in_seconds);
void RunUntilIdle();
private:
std::vector<std::unique_ptr<cppgc::Task>> tasks_;
std::vector<std::unique_ptr<cppgc::IdleTask>> idle_tasks_;
};
class TestPlatform : public Platform {
class TestPlatform : public DefaultPlatform {
public:
class DisableBackgroundTasksScope {
public:
@ -55,32 +24,18 @@ class TestPlatform : public Platform {
};
TestPlatform();
~TestPlatform() V8_NOEXCEPT override;
PageAllocator* GetPageAllocator() override { return &page_allocator_; }
std::shared_ptr<cppgc::TaskRunner> GetForegroundTaskRunner() override {
return foreground_task_runner_;
}
// TestPlatform does not support job priorities. All jobs would be assigned
// the same priority regardless of the cppgc::TaskPriority parameter.
std::unique_ptr<cppgc::JobHandle> PostJob(
cppgc::TaskPriority, std::unique_ptr<cppgc::JobTask> job_task) override;
cppgc::TaskPriority priority,
std::unique_ptr<cppgc::JobTask> job_task) final;
double MonotonicallyIncreasingTime() override;
void WaitAllForegroundTasks();
void WaitAllBackgroundTasks();
void RunAllForegroundTasks();
private:
bool AreBackgroundTasksDisabled() const {
return disabled_background_tasks_ > 0;
}
v8::base::PageAllocator page_allocator_;
std::shared_ptr<TestTaskRunner> foreground_task_runner_;
std::vector<std::shared_ptr<TestJob>> jobs_;
size_t disabled_background_tasks_ = 0;
};