| // Copyright 2017 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_ITEM_PARALLEL_JOB_H_ |
| #define V8_HEAP_ITEM_PARALLEL_JOB_H_ |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "src/base/atomic-utils.h" |
| #include "src/base/logging.h" |
| #include "src/base/macros.h" |
| #include "src/common/globals.h" |
| #include "src/tasks/cancelable-task.h" |
| |
| namespace v8 { |
| |
| namespace base { |
| class Semaphore; |
| } // namespace base |
| |
| namespace internal { |
| |
| class Counters; |
| class Isolate; |
| |
| // This class manages background tasks that process a set of items in parallel. |
| // The first task added is executed on the same thread as |job.Run()| is called. |
| // All other tasks are scheduled in the background. |
| // |
| // - Items need to inherit from ItemParallelJob::Item. |
| // - Tasks need to inherit from ItemParallelJob::Task. |
| // |
| // Items need to be marked as finished after processing them. Task and Item |
| // ownership is transferred to the job. |
| class V8_EXPORT_PRIVATE ItemParallelJob { |
| public: |
| class Task; |
| |
| class V8_EXPORT_PRIVATE Item { |
| public: |
| Item() = default; |
| virtual ~Item() = default; |
| |
| // Marks an item as being finished. |
| void MarkFinished() { CHECK_EQ(kProcessing, state_.exchange(kFinished)); } |
| |
| private: |
| enum ProcessingState : uintptr_t { kAvailable, kProcessing, kFinished }; |
| |
| bool TryMarkingAsProcessing() { |
| ProcessingState available = kAvailable; |
| return state_.compare_exchange_strong(available, kProcessing); |
| } |
| bool IsFinished() { return state_ == kFinished; } |
| |
| std::atomic<ProcessingState> state_{kAvailable}; |
| |
| friend class ItemParallelJob; |
| friend class ItemParallelJob::Task; |
| |
| DISALLOW_COPY_AND_ASSIGN(Item); |
| }; |
| |
| class V8_EXPORT_PRIVATE Task : public CancelableTask { |
| public: |
| enum class Runner { kForeground, kBackground }; |
| explicit Task(Isolate* isolate); |
| ~Task() override = default; |
| |
| virtual void RunInParallel(Runner runner) = 0; |
| |
| protected: |
| // Retrieves a new item that needs to be processed. Returns |nullptr| if |
| // all items are processed. Upon returning an item, the task is required |
| // to process the item and mark the item as finished after doing so. |
| template <class ItemType> |
| ItemType* GetItem() { |
| while (items_considered_++ != items_->size()) { |
| // Wrap around. |
| if (cur_index_ == items_->size()) { |
| cur_index_ = 0; |
| } |
| Item* item = (*items_)[cur_index_++]; |
| if (item->TryMarkingAsProcessing()) { |
| return static_cast<ItemType*>(item); |
| } |
| } |
| return nullptr; |
| } |
| |
| private: |
| friend class ItemParallelJob; |
| friend class Item; |
| |
| // Sets up state required before invoking Run(). If |
| // |start_index is >= items_.size()|, this task will not process work items |
| // (some jobs have more tasks than work items in order to parallelize post- |
| // processing, e.g. scavenging). |
| void SetupInternal(base::Semaphore* on_finish, std::vector<Item*>* items, |
| size_t start_index); |
| void WillRunOnForeground(); |
| // We don't allow overriding this method any further. |
| void RunInternal() final; |
| |
| std::vector<Item*>* items_ = nullptr; |
| size_t cur_index_ = 0; |
| size_t items_considered_ = 0; |
| Runner runner_ = Runner::kBackground; |
| base::Semaphore* on_finish_ = nullptr; |
| |
| DISALLOW_COPY_AND_ASSIGN(Task); |
| }; |
| |
| ItemParallelJob(CancelableTaskManager* cancelable_task_manager, |
| base::Semaphore* pending_tasks); |
| |
| ~ItemParallelJob(); |
| |
| // Adds a task to the job. Transfers ownership to the job. |
| void AddTask(Task* task) { tasks_.push_back(std::unique_ptr<Task>(task)); } |
| |
| // Adds an item to the job. Transfers ownership to the job. |
| void AddItem(Item* item) { items_.push_back(item); } |
| |
| int NumberOfItems() const { return static_cast<int>(items_.size()); } |
| int NumberOfTasks() const { return static_cast<int>(tasks_.size()); } |
| |
| // Runs this job. |
| void Run(); |
| |
| private: |
| std::vector<Item*> items_; |
| std::vector<std::unique_ptr<Task>> tasks_; |
| CancelableTaskManager* cancelable_task_manager_; |
| base::Semaphore* pending_tasks_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ItemParallelJob); |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_HEAP_ITEM_PARALLEL_JOB_H_ |