| // 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_LIBPLATFORM_DEFAULT_JOB_H_ |
| #define V8_LIBPLATFORM_DEFAULT_JOB_H_ |
| |
| #include <atomic> |
| #include <memory> |
| |
| #include "include/libplatform/libplatform-export.h" |
| #include "include/v8-platform.h" |
| #include "src/base/platform/condition-variable.h" |
| #include "src/base/platform/mutex.h" |
| |
| namespace v8 { |
| namespace platform { |
| |
| class V8_PLATFORM_EXPORT DefaultJobState |
| : public std::enable_shared_from_this<DefaultJobState> { |
| public: |
| class JobDelegate : public v8::JobDelegate { |
| public: |
| explicit JobDelegate(DefaultJobState* outer, bool is_joining_thread = false) |
| : outer_(outer), is_joining_thread_(is_joining_thread) {} |
| ~JobDelegate(); |
| |
| void NotifyConcurrencyIncrease() override { |
| outer_->NotifyConcurrencyIncrease(); |
| } |
| bool ShouldYield() override { |
| // Thread-safe but may return an outdated result. |
| return outer_->is_canceled_.load(std::memory_order_relaxed); |
| } |
| uint8_t GetTaskId() override; |
| bool IsJoiningThread() const override { return is_joining_thread_; } |
| |
| private: |
| static constexpr uint8_t kInvalidTaskId = |
| std::numeric_limits<uint8_t>::max(); |
| |
| DefaultJobState* outer_; |
| uint8_t task_id_ = kInvalidTaskId; |
| bool is_joining_thread_; |
| }; |
| |
| DefaultJobState(Platform* platform, std::unique_ptr<JobTask> job_task, |
| TaskPriority priority, size_t num_worker_threads); |
| virtual ~DefaultJobState(); |
| |
| void NotifyConcurrencyIncrease(); |
| uint8_t AcquireTaskId(); |
| void ReleaseTaskId(uint8_t task_id); |
| |
| void Join(); |
| void CancelAndWait(); |
| void CancelAndDetach(); |
| bool IsActive(); |
| |
| // Must be called before running |job_task_| for the first time. If it returns |
| // true, then the worker thread must contribute and must call DidRunTask(), or |
| // false if it should return. |
| bool CanRunFirstTask(); |
| // Must be called after running |job_task_|. Returns true if the worker thread |
| // must contribute again, or false if it should return. |
| bool DidRunTask(); |
| |
| void UpdatePriority(TaskPriority); |
| |
| private: |
| // Called from the joining thread. Waits for the worker count to be below or |
| // equal to max concurrency (will happen when a worker calls |
| // DidRunTask()). Returns true if the joining thread should run a task, or |
| // false if joining was completed and all other workers returned because |
| // there's no work remaining. |
| bool WaitForParticipationOpportunityLockRequired(); |
| |
| // Returns GetMaxConcurrency() capped by the number of threads used by this |
| // job. |
| size_t CappedMaxConcurrency(size_t worker_count) const; |
| |
| void CallOnWorkerThread(TaskPriority priority, std::unique_ptr<Task> task); |
| |
| Platform* const platform_; |
| std::unique_ptr<JobTask> job_task_; |
| |
| // All members below are protected by |mutex_|. |
| base::Mutex mutex_; |
| TaskPriority priority_; |
| // Number of workers running this job. |
| size_t active_workers_ = 0; |
| // Number of posted tasks that aren't running this job yet. |
| size_t pending_tasks_ = 0; |
| // Indicates if the job is canceled. |
| std::atomic_bool is_canceled_{false}; |
| // Number of worker threads available to schedule the worker task. |
| size_t num_worker_threads_; |
| // Signaled when a worker returns. |
| base::ConditionVariable worker_released_condition_; |
| |
| std::atomic<uint32_t> assigned_task_ids_{0}; |
| }; |
| |
| class V8_PLATFORM_EXPORT DefaultJobHandle : public JobHandle { |
| public: |
| explicit DefaultJobHandle(std::shared_ptr<DefaultJobState> state); |
| ~DefaultJobHandle() override; |
| |
| void NotifyConcurrencyIncrease() override { |
| state_->NotifyConcurrencyIncrease(); |
| } |
| |
| void Join() override; |
| void Cancel() override; |
| void CancelAndDetach() override; |
| bool IsCompleted() override { return !IsActive(); } |
| bool IsActive() override; |
| bool IsRunning() override { return IsValid(); } |
| bool IsValid() override { return state_ != nullptr; } |
| |
| bool UpdatePriorityEnabled() const override { return true; } |
| |
| void UpdatePriority(TaskPriority) override; |
| |
| private: |
| std::shared_ptr<DefaultJobState> state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DefaultJobHandle); |
| }; |
| |
| class DefaultJobWorker : public Task { |
| public: |
| DefaultJobWorker(std::weak_ptr<DefaultJobState> state, JobTask* job_task) |
| : state_(std::move(state)), job_task_(job_task) {} |
| ~DefaultJobWorker() override = default; |
| |
| void Run() override { |
| auto shared_state = state_.lock(); |
| if (!shared_state) return; |
| if (!shared_state->CanRunFirstTask()) return; |
| do { |
| // Scope of |delegate| must not outlive DidRunTask() so that associated |
| // state is freed before the worker becomes inactive. |
| DefaultJobState::JobDelegate delegate(shared_state.get()); |
| job_task_->Run(&delegate); |
| } while (shared_state->DidRunTask()); |
| } |
| |
| private: |
| friend class DefaultJob; |
| |
| std::weak_ptr<DefaultJobState> state_; |
| JobTask* job_task_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DefaultJobWorker); |
| }; |
| |
| } // namespace platform |
| } // namespace v8 |
| |
| #endif // V8_LIBPLATFORM_DEFAULT_JOB_H_ |