diff --git a/BUILD.gn b/BUILD.gn index 28e0f6f55e..ee64b91760 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -4213,6 +4213,7 @@ v8_source_set("cppgc_base") { "include/cppgc/allocation.h", "include/cppgc/common.h", "include/cppgc/custom-space.h", + "include/cppgc/default-platform.h", "include/cppgc/garbage-collected.h", "include/cppgc/heap.h", "include/cppgc/internal/api-constants.h", @@ -4226,7 +4227,6 @@ v8_source_set("cppgc_base") { "include/cppgc/internal/process-heap.h", "include/cppgc/internal/write-barrier.h", "include/cppgc/liveness-broker.h", - "include/cppgc/liveness-broker.h", "include/cppgc/macros.h", "include/cppgc/member.h", "include/cppgc/persistent.h", @@ -4238,6 +4238,7 @@ v8_source_set("cppgc_base") { "include/cppgc/visitor.h", "include/v8config.h", "src/heap/cppgc/allocation.cc", + "src/heap/cppgc/default-platform.cc", "src/heap/cppgc/free-list.cc", "src/heap/cppgc/free-list.h", "src/heap/cppgc/garbage-collector.h", @@ -4839,6 +4840,19 @@ v8_executable("cppgc_for_v8_embedders") { ] } +v8_executable("cppgc_standalone") { + sources = [ "samples/cppgc/cppgc-standalone.cc" ] + + configs = [ + # Note: don't use :internal_config here because this target will get + # the :external_config applied to it by virtue of depending on :cppgc, and + # you can't have both applied to the same target. + ":internal_config_base", + ] + + deps = [ ":cppgc" ] +} + template("v8_fuzzer") { name = target_name forward_variables_from(invoker, "*") diff --git a/include/cppgc/default-platform.h b/include/cppgc/default-platform.h new file mode 100644 index 0000000000..24b1cd1498 --- /dev/null +++ b/include/cppgc/default-platform.h @@ -0,0 +1,76 @@ +// 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 INCLUDE_CPPGC_DEFAULT_PLATFORM_H_ +#define INCLUDE_CPPGC_DEFAULT_PLATFORM_H_ + +#include +#include // NOLINT(build/c++11) +#include + +#include "cppgc/platform.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +/** + * Default task runner implementation. Keep posted tasks in a list that can be + * processed by calling RunSingleTask() or RunUntilIdle(). + */ +class V8_EXPORT DefaultTaskRunner final : public cppgc::TaskRunner { + public: + DefaultTaskRunner() = default; + + DefaultTaskRunner(const DefaultTaskRunner&) = delete; + DefaultTaskRunner& operator=(const DefaultTaskRunner&) = delete; + + void PostTask(std::unique_ptr task) override; + void PostNonNestableTask(std::unique_ptr task) override; + void PostDelayedTask(std::unique_ptr task, double) override; + void PostNonNestableDelayedTask(std::unique_ptr task, + double) override; + + void PostIdleTask(std::unique_ptr task) override; + bool IdleTasksEnabled() override { return true; } + + bool RunSingleTask(); + bool RunSingleIdleTask(double duration_in_seconds); + + void RunUntilIdle(); + + private: + std::vector> tasks_; + std::vector> 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 GetForegroundTaskRunner() final; + + std::unique_ptr PostJob( + cppgc::TaskPriority priority, + std::unique_ptr job_task) final; + + void WaitAllForegroundTasks(); + void WaitAllBackgroundTasks(); + + private: + std::unique_ptr page_allocator_; + std::shared_ptr foreground_task_runner_; + std::vector> job_threads_; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_DEFAULT_PLATFORM_H_ diff --git a/include/cppgc/platform.h b/include/cppgc/platform.h index 72694c6ee1..b6c21bdec0 100644 --- a/include/cppgc/platform.h +++ b/include/cppgc/platform.h @@ -14,6 +14,7 @@ namespace cppgc { // V8-specific. using IdleTask = v8::IdleTask; using JobHandle = v8::JobHandle; +using JobDelegate = v8::JobDelegate; using JobTask = v8::JobTask; using PageAllocator = v8::PageAllocator; using Task = v8::Task; diff --git a/samples/cppgc/cppgc-standalone.cc b/samples/cppgc/cppgc-standalone.cc new file mode 100644 index 0000000000..f8cb4020c3 --- /dev/null +++ b/samples/cppgc/cppgc-standalone.cc @@ -0,0 +1,64 @@ +// 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 +#include +#include +#include +#include + +#include +#include +#include + +/** + * This sample program shows how to set up a stand-alone cppgc heap. + */ + +/** + * Simple string rope to illustrate allocation and garbage collection below. + * The rope keeps the next parts alive via regular managed reference. + */ +class Rope final : public cppgc::GarbageCollected { + public: + explicit Rope(std::string part, Rope* next = nullptr) + : part_(part), next_(next) {} + + void Trace(cppgc::Visitor* visitor) const { visitor->Trace(next_); } + + private: + std::string part_; + cppgc::Member next_; + + friend std::ostream& operator<<(std::ostream& os, const Rope& rope) { + os << rope.part_; + if (rope.next_) { + os << *rope.next_; + } + return os; + } +}; + +int main(int argc, char* argv[]) { + // Create a default platform that is used by cppgc::Heap for execution and + // backend allocation. + auto cppgc_platform = std::make_shared(); + // Initialize the process. This must happen before any + // cppgc::Heap::Create() calls. + cppgc::InitializeProcess(cppgc_platform->GetPageAllocator()); + // Create a managed heap. + std::unique_ptr heap = cppgc::Heap::Create(cppgc_platform); + // Allocate a string rope on the managed heap. + auto* greeting = cppgc::MakeGarbageCollected( + heap->GetAllocationHandle(), "Hello ", + cppgc::MakeGarbageCollected(heap->GetAllocationHandle(), "World!")); + // Manually trigger garbage collection. The object greeting is held alive + // through conservative stack scanning. + heap->ForceGarbageCollectionSlow("CppGC stand-alone example", "Testing"); + std::cout << *greeting << std::endl; + // Gracefully shutdown the process. + cppgc::ShutdownProcess(); + return 0; +} diff --git a/src/heap/cppgc/default-platform.cc b/src/heap/cppgc/default-platform.cc new file mode 100644 index 0000000000..792bf38189 --- /dev/null +++ b/src/heap/cppgc/default-platform.cc @@ -0,0 +1,133 @@ +// 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 // NOLINT(build/c++11) +#include // NOLINT(build/c++11) + +#include "src/base/page-allocator.h" + +namespace cppgc { + +namespace { + +// Simple implementation of JobTask based on std::thread. +class DefaultJobHandle : public JobHandle { + public: + explicit DefaultJobHandle(std::shared_ptr thread) + : thread_(std::move(thread)) {} + + void NotifyConcurrencyIncrease() override {} + void Join() override { + if (thread_->joinable()) thread_->join(); + } + void Cancel() override { Join(); } + bool IsRunning() override { return thread_->joinable(); } + + private: + std::shared_ptr thread_; +}; + +} // namespace + +void DefaultTaskRunner::PostTask(std::unique_ptr task) { + tasks_.push_back(std::move(task)); +} + +void DefaultTaskRunner::PostNonNestableTask(std::unique_ptr task) { + PostTask(std::move(task)); +} + +void DefaultTaskRunner::PostDelayedTask(std::unique_ptr task, + double) { + PostTask(std::move(task)); +} + +void DefaultTaskRunner::PostNonNestableDelayedTask( + std::unique_ptr task, double) { + PostTask(std::move(task)); +} + +void DefaultTaskRunner::PostIdleTask(std::unique_ptr 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::infinity()); + } + idle_tasks_.clear(); +} + +DefaultPlatform::DefaultPlatform() + : page_allocator_(std::make_unique()), + foreground_task_runner_(std::make_shared()) {} + +DefaultPlatform::~DefaultPlatform() noexcept { WaitAllBackgroundTasks(); } + +cppgc::PageAllocator* DefaultPlatform::GetPageAllocator() { + return page_allocator_.get(); +} + +double DefaultPlatform::MonotonicallyIncreasingTime() { + return std::chrono::duration( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); +} + +std::shared_ptr DefaultPlatform::GetForegroundTaskRunner() { + return foreground_task_runner_; +} + +std::unique_ptr DefaultPlatform::PostJob( + cppgc::TaskPriority priority, std::unique_ptr job_task) { + auto thread = std::make_shared([task = std::move(job_task)] { + class SimpleDelegate final : public cppgc::JobDelegate { + public: + bool ShouldYield() override { return false; } + void NotifyConcurrencyIncrease() override {} + } delegate; + + if (task) task->Run(&delegate); + }); + job_threads_.push_back(thread); + return std::make_unique(std::move(thread)); +} + +void DefaultPlatform::WaitAllForegroundTasks() { + foreground_task_runner_->RunUntilIdle(); +} + +void DefaultPlatform::WaitAllBackgroundTasks() { + for (auto& thread : job_threads_) { + thread->join(); + } + job_threads_.clear(); +} + +} // namespace cppgc