blob: 6d070dbd01e6d3c1e0d4abe2d09a5b014ca10867 [file] [log] [blame]
// Copyright 2018 The Chromium 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 "third_party/blink/renderer/core/workers/experimental/task_worklet.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/workers/experimental/thread_pool_thread.h"
#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
#include "third_party/blink/renderer/core/workers/threaded_worklet_messaging_proxy.h"
#include "third_party/blink/renderer/core/workers/threaded_worklet_object_proxy.h"
#include "third_party/blink/renderer/core/workers/worker_backing_thread.h"
#include "third_party/blink/renderer/core/workers/worker_thread.h"
namespace blink {
class TaskWorkletMessagingProxy final : public ThreadedWorkletMessagingProxy {
public:
explicit TaskWorkletMessagingProxy(ExecutionContext* context)
: ThreadedWorkletMessagingProxy(context) {}
~TaskWorkletMessagingProxy() override = default;
std::unique_ptr<WorkerThread> CreateWorkerThread() override {
return std::make_unique<ThreadPoolThread>(GetExecutionContext(),
WorkletObjectProxy(),
ThreadPoolThread::kWorklet);
}
ThreadPoolThread* GetWorkerThread() const {
return static_cast<ThreadPoolThread*>(
ThreadedMessagingProxyBase::GetWorkerThread());
}
};
const char TaskWorklet::kSupplementName[] = "TaskWorklet";
TaskWorklet* TaskWorklet::From(LocalDOMWindow& window) {
TaskWorklet* task_worklet =
Supplement<LocalDOMWindow>::From<TaskWorklet>(window);
if (!task_worklet) {
task_worklet = MakeGarbageCollected<TaskWorklet>(window.document());
Supplement<LocalDOMWindow>::ProvideTo(window, task_worklet);
}
return task_worklet;
}
static const size_t kMaxTaskWorkletThreads = 4;
TaskWorklet::TaskWorklet(Document* document) : Worklet(document) {}
Task* TaskWorklet::postTask(ScriptState* script_state,
V8Function* function,
const Vector<ScriptValue>& arguments,
ExceptionState& exception_state) {
if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"This frame is already detached");
return nullptr;
}
// TODO(japhet): Here and below: it's unclear what task type should be used,
// and whether the API should allow it to be configured. Using kIdleTask as a
// placeholder for now.
Task* task =
MakeGarbageCollected<Task>(script_state, this, function, arguments,
TaskType::kIdleTask, exception_state);
if (exception_state.HadException())
return nullptr;
return task;
}
Task* TaskWorklet::postTask(ScriptState* script_state,
const String& function_name,
const Vector<ScriptValue>& arguments,
ExceptionState& exception_state) {
if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"This frame is already detached");
return nullptr;
}
Task* task =
MakeGarbageCollected<Task>(script_state, this, function_name, arguments,
TaskType::kIdleTask, exception_state);
if (exception_state.HadException())
return nullptr;
return task;
}
ThreadPoolThread* TaskWorklet::GetLeastBusyThread() {
DCHECK(IsMainThread());
ThreadPoolThread* least_busy_thread = nullptr;
size_t lowest_task_count = std::numeric_limits<std::size_t>::max();
for (auto& proxy : proxies_) {
ThreadPoolThread* current_thread =
static_cast<TaskWorkletMessagingProxy*>(proxy.Get())->GetWorkerThread();
size_t current_task_count = current_thread->GetTasksInProgressCount();
// If there's an idle thread, use it.
if (current_task_count == 0)
return current_thread;
if (current_task_count < lowest_task_count) {
least_busy_thread = current_thread;
lowest_task_count = current_task_count;
}
}
if (proxies_.size() == kMaxTaskWorkletThreads)
return least_busy_thread;
auto* proxy = static_cast<TaskWorkletMessagingProxy*>(CreateGlobalScope());
proxies_.push_back(proxy);
return proxy->GetWorkerThread();
}
// TODO(japhet): This causes all of the backing threads to be created when
// addModule() is first called. Sort out lazy global scope creation.
// Note that if the function variant of postTask() is called first, global
// scopes will be created lazily; it's only module loading that needs upfront
// global scope creation, presumably because we don't have a way to replay
// module loads from WorkletModuleResponsesMap yet.
bool TaskWorklet::NeedsToCreateGlobalScope() {
return GetNumberOfGlobalScopes() < kMaxTaskWorkletThreads;
}
WorkletGlobalScopeProxy* TaskWorklet::CreateGlobalScope() {
DCHECK_LT(GetNumberOfGlobalScopes(), kMaxTaskWorkletThreads);
TaskWorkletMessagingProxy* proxy =
MakeGarbageCollected<TaskWorkletMessagingProxy>(GetExecutionContext());
proxy->Initialize(MakeGarbageCollected<WorkerClients>(), ModuleResponsesMap(),
WorkerBackingThreadStartupData::CreateDefault());
return proxy;
}
// We select a global scope without this getting called.
wtf_size_t TaskWorklet::SelectGlobalScope() {
NOTREACHED();
return 0u;
}
void TaskWorklet::Trace(blink::Visitor* visitor) {
Worklet::Trace(visitor);
Supplement<LocalDOMWindow>::Trace(visitor);
}
} // namespace blink