Commit 48701743 authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

Add a helper class for measuring memory usage of workers in a renderer

V8WorkerMemoryReporter posts a memory measurement task on each worker
thread and then collects the results. A memory measurement is performed
using V8's MeasureMemory API. The new class will be used by
V8PerContextMemoryReporter to serve requests from PerformanceManager.

Design doc: https://bit.ly/30Y8Js6

Bug: 1085129
Change-Id: Ic02c9e41f74fb5f4a890d19c59cf222d1aa7ca82
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2332275Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarJoe Mason <joenotcharles@chromium.org>
Reviewed-by: default avatarHiroki Nakagawa <nhiroki@chromium.org>
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798056}
parent 6bc98538
......@@ -67,6 +67,8 @@ component("controller") {
sources += [
"performance_manager/v8_per_frame_memory_reporter_impl.cc",
"performance_manager/v8_per_frame_memory_reporter_impl.h",
"performance_manager/v8_worker_memory_reporter.cc",
"performance_manager/v8_worker_memory_reporter.h",
]
}
if (is_win) {
......@@ -182,8 +184,10 @@ source_set("blink_unittests_sources") {
"user_level_memory_pressure_signal_generator_test.cc",
]
} else {
sources +=
[ "performance_manager/v8_per_frame_memory_reporter_impl_test.cc" ]
sources += [
"performance_manager/v8_per_frame_memory_reporter_impl_test.cc",
"performance_manager/v8_worker_memory_reporter_test.cc",
]
}
if (is_linux || is_chromeos || is_android || is_mac || is_win) {
......
// Copyright 2020 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/controller/performance_manager/v8_worker_memory_reporter.h"
#include <memory>
#include <utility>
#include "base/check.h"
#include "base/time/time.h"
#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
#include "third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h"
#include "third_party/blink/renderer/core/workers/worker_thread.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
namespace WTF {
template <>
struct CrossThreadCopier<blink::V8WorkerMemoryReporter::WorkerMemoryUsage>
: public CrossThreadCopierPassThrough<
blink::V8WorkerMemoryReporter::WorkerMemoryUsage> {
STATIC_ONLY(CrossThreadCopier);
};
} // namespace WTF
namespace blink {
const base::TimeDelta V8WorkerMemoryReporter::kTimeout =
base::TimeDelta::FromSeconds(60);
namespace {
// This delegate is provided to v8::Isolate::MeasureMemory API.
// V8 calls MeasurementComplete with the measurement result.
//
// All functions of this delegate are called on the worker thread.
class WorkerMeasurementDelegate : public v8::MeasureMemoryDelegate {
public:
WorkerMeasurementDelegate(
base::WeakPtr<V8WorkerMemoryReporter> worker_memory_reporter,
WorkerThread* worker_thread)
: worker_memory_reporter_(std::move(worker_memory_reporter)),
worker_thread_(worker_thread) {
DCHECK(worker_thread_->IsCurrentThread());
}
~WorkerMeasurementDelegate() override;
// v8::MeasureMemoryDelegate overrides.
bool ShouldMeasure(v8::Local<v8::Context> context) override { return true; }
void MeasurementComplete(
const std::vector<std::pair<v8::Local<v8::Context>, size_t>>&
context_sizes,
size_t unattributed_size) override;
private:
void NotifyMeasurementSuccess(
V8WorkerMemoryReporter::WorkerMemoryUsage memory_usage);
void NotifyMeasurementFailure();
base::WeakPtr<V8WorkerMemoryReporter> worker_memory_reporter_;
WorkerThread* worker_thread_;
bool did_notify_ = false;
};
WorkerMeasurementDelegate::~WorkerMeasurementDelegate() {
DCHECK(worker_thread_->IsCurrentThread());
if (!did_notify_) {
// This may happen if the worker shuts down before completing
// memory measurement.
NotifyMeasurementFailure();
}
}
void WorkerMeasurementDelegate::MeasurementComplete(
const std::vector<std::pair<v8::Local<v8::Context>, size_t>>& context_sizes,
size_t unattributed_size) {
DCHECK(worker_thread_->IsCurrentThread());
WorkerOrWorkletGlobalScope* global_scope = worker_thread_->GlobalScope();
DCHECK(global_scope);
DCHECK_LE(context_sizes.size(), 1u);
size_t bytes = unattributed_size;
for (auto& context_size : context_sizes) {
bytes += context_size.second;
}
NotifyMeasurementSuccess(V8WorkerMemoryReporter::WorkerMemoryUsage{
To<WorkerGlobalScope>(global_scope)->GetWorkerToken(), bytes});
}
void WorkerMeasurementDelegate::NotifyMeasurementFailure() {
DCHECK(worker_thread_->IsCurrentThread());
DCHECK(!did_notify_);
V8WorkerMemoryReporter::NotifyMeasurementFailure(worker_thread_,
worker_memory_reporter_);
did_notify_ = true;
}
void WorkerMeasurementDelegate::NotifyMeasurementSuccess(
V8WorkerMemoryReporter::WorkerMemoryUsage memory_usage) {
DCHECK(worker_thread_->IsCurrentThread());
DCHECK(!did_notify_);
V8WorkerMemoryReporter::NotifyMeasurementSuccess(
worker_thread_, worker_memory_reporter_, memory_usage);
did_notify_ = true;
}
} // anonymous namespace
// static
void V8WorkerMemoryReporter::GetMemoryUsage(ResultCallback callback,
v8::MeasureMemoryExecution mode) {
DCHECK(IsMainThread());
// The private constructor prevents us from using std::make_unique here.
std::unique_ptr<V8WorkerMemoryReporter> worker_memory_reporter(
new V8WorkerMemoryReporter(std::move(callback)));
// Worker tasks get a weak pointer to the instance for passing it back
// to the main thread in OnMeasurementSuccess and OnMeasurementFailure.
// Worker tasks never dereference the weak pointer.
unsigned worker_count = WorkerThread::CallOnAllWorkerThreads(
&V8WorkerMemoryReporter::StartMeasurement, TaskType::kInternalDefault,
worker_memory_reporter->GetWeakPtr(), mode);
if (worker_count == 0) {
Thread::Current()->GetTaskRunner()->PostTask(
FROM_HERE, WTF::Bind(&V8WorkerMemoryReporter::InvokeCallback,
std::move(worker_memory_reporter)));
return;
}
worker_memory_reporter->SetWorkerCount(worker_count);
// Transfer the ownership of the instance to the timeout task.
Thread::Current()->GetTaskRunner()->PostDelayedTask(
FROM_HERE,
WTF::Bind(&V8WorkerMemoryReporter::OnTimeout,
std::move(worker_memory_reporter)),
kTimeout);
}
// static
void V8WorkerMemoryReporter::StartMeasurement(
WorkerThread* worker_thread,
base::WeakPtr<V8WorkerMemoryReporter> worker_memory_reporter,
v8::MeasureMemoryExecution measurement_mode) {
DCHECK(worker_thread->IsCurrentThread());
WorkerOrWorkletGlobalScope* global_scope = worker_thread->GlobalScope();
DCHECK(global_scope);
v8::Isolate* isolate = worker_thread->GetIsolate();
if (global_scope->IsWorkerGlobalScope()) {
auto delegate = std::make_unique<WorkerMeasurementDelegate>(
std::move(worker_memory_reporter), worker_thread);
isolate->MeasureMemory(std::move(delegate), measurement_mode);
} else {
// TODO(ulan): Add support for worklets once we get tokens for them. We
// need to careful to not trigger GC on a worklet because usually worklets
// are soft real-time and are written to avoid GC.
// For now we simply notify a failure so that the main thread doesn't wait
// for a response from the worklet.
NotifyMeasurementFailure(worker_thread, worker_memory_reporter);
}
}
// static
void V8WorkerMemoryReporter::NotifyMeasurementSuccess(
WorkerThread* worker_thread,
base::WeakPtr<V8WorkerMemoryReporter> worker_memory_reporter,
WorkerMemoryUsage memory_usage) {
DCHECK(worker_thread->IsCurrentThread());
PostCrossThreadTask(
*Thread::MainThread()->GetTaskRunner(), FROM_HERE,
CrossThreadBindOnce(&V8WorkerMemoryReporter::OnMeasurementSuccess,
worker_memory_reporter, memory_usage));
}
// static
void V8WorkerMemoryReporter::NotifyMeasurementFailure(
WorkerThread* worker_thread,
base::WeakPtr<V8WorkerMemoryReporter> worker_memory_reporter) {
DCHECK(worker_thread->IsCurrentThread());
PostCrossThreadTask(
*Thread::MainThread()->GetTaskRunner(), FROM_HERE,
CrossThreadBindOnce(&V8WorkerMemoryReporter::OnMeasurementFailure,
worker_memory_reporter));
}
void V8WorkerMemoryReporter::OnMeasurementFailure() {
DCHECK(IsMainThread());
if (state_ == State::kDone)
return;
++failure_count_;
if (success_count_ + failure_count_ == worker_count_) {
InvokeCallback();
DCHECK_EQ(state_, State::kDone);
}
}
void V8WorkerMemoryReporter::OnMeasurementSuccess(
WorkerMemoryUsage memory_usage) {
DCHECK(IsMainThread());
if (state_ == State::kDone)
return;
result_.workers.emplace_back(memory_usage);
++success_count_;
if (success_count_ + failure_count_ == worker_count_) {
InvokeCallback();
DCHECK_EQ(state_, State::kDone);
}
}
void V8WorkerMemoryReporter::SetWorkerCount(unsigned worker_count) {
DCHECK(IsMainThread());
DCHECK_EQ(0u, worker_count_);
worker_count_ = worker_count;
}
void V8WorkerMemoryReporter::OnTimeout() {
DCHECK(IsMainThread());
if (state_ == State::kDone)
return;
InvokeCallback();
DCHECK_EQ(state_, State::kDone);
}
void V8WorkerMemoryReporter::InvokeCallback() {
DCHECK(IsMainThread());
DCHECK_EQ(state_, State::kWaiting);
std::move(callback_).Run(std::move(result_));
state_ = State::kDone;
}
} // namespace blink
// Copyright 2020 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CONTROLLER_PERFORMANCE_MANAGER_V8_WORKER_MEMORY_REPORTER_H_
#define THIRD_PARTY_BLINK_RENDERER_CONTROLLER_PERFORMANCE_MANAGER_V8_WORKER_MEMORY_REPORTER_H_
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/renderer/controller/controller_export.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "v8/include/v8.h"
namespace blink {
class WorkerThread;
// This class measures memory usage of all workers in the renderer process.
// Memory measurement is performed by posting a task on each worker thread
// and then combining the results. Unresponsive workers (workers that never
// go back to the message loop to run tasks) are skipped after a timeout.
// The entry point is `GetMemoryUsage` that internally works as follows:
// 1. Create an instance of V8WorkerMemoryReporter.
// 2. Post a memory measurement task on each worker thread using
// WorkerThread::CallOnAllWorkerThreads. Each task has a weak reference
// to V8WorkerMemoryReporter.
// 3. Post a timeout task on the main thread and transfer the ownership of
// V8WorkerMemoryReporter to the task.
// 4. Each worker task starts memory measurement using V8's MeasureMemory.
// 5. Once a measurement succeeds (or fails) a task is posted on the main
// thread that records the result.
// 6. Once results from all workers are collected, the callback is invoked.
// 7. If there is an unresponsive worker, then the timeout task will eventually
// run and invoke the callback with partial results.
class CONTROLLER_EXPORT V8WorkerMemoryReporter {
public:
struct WorkerMemoryUsage {
WorkerToken token;
size_t bytes;
};
struct Result {
Vector<WorkerMemoryUsage> workers;
};
using ResultCallback = base::OnceCallback<void(const Result&)>;
// This function should be called on the main thread. Upon completion
// the given callback is also invoked on the main thread.
static void GetMemoryUsage(ResultCallback callback,
v8::MeasureMemoryExecution mode);
// These functions are called by WorkerMeasurementDelegate on a worker thread.
static void NotifyMeasurementSuccess(WorkerThread*,
base::WeakPtr<V8WorkerMemoryReporter>,
WorkerMemoryUsage memory_usage);
static void NotifyMeasurementFailure(WorkerThread*,
base::WeakPtr<V8WorkerMemoryReporter>);
private:
// The initial state is kWaiting.
// Transition from kWaiting to kDone happens on two events:
// A. Responses from all workers are collected. A response is either a
// successful measurement or a failure.
// B. The timeout task runs.
// Upon transition to kDone, the callback is invoked with the results.
// All operations become no-ops in the kDone state.
enum class State { kWaiting, kDone };
// V8's MeasureMemory API may take 0-30 seconds depending on the GC schedule.
static const base::TimeDelta kTimeout;
explicit V8WorkerMemoryReporter(ResultCallback callback)
: callback_(std::move(callback)) {}
// This function runs on a worker thread.
static void StartMeasurement(WorkerThread*,
base::WeakPtr<V8WorkerMemoryReporter>,
v8::MeasureMemoryExecution);
// Functions that run on the main thread.
void OnTimeout();
void OnMeasurementFailure();
void OnMeasurementSuccess(WorkerMemoryUsage memory_usage);
void InvokeCallback();
base::WeakPtr<V8WorkerMemoryReporter> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void SetWorkerCount(unsigned worker_count);
Result result_;
ResultCallback callback_;
State state_ = State::kWaiting;
unsigned worker_count_ = 0;
unsigned success_count_ = 0;
unsigned failure_count_ = 0;
base::WeakPtrFactory<V8WorkerMemoryReporter> weak_factory_{this};
FRIEND_TEST_ALL_PREFIXES(V8WorkerMemoryReporterTest, OnMeasurementSuccess);
FRIEND_TEST_ALL_PREFIXES(V8WorkerMemoryReporterTest, OnMeasurementFailure);
FRIEND_TEST_ALL_PREFIXES(V8WorkerMemoryReporterTest, OnTimeout);
FRIEND_TEST_ALL_PREFIXES(V8WorkerMemoryReporterTest, OnTimeoutNoop);
FRIEND_TEST_ALL_PREFIXES(V8WorkerMemoryReporterTest, GetMemoryUsage);
FRIEND_TEST_ALL_PREFIXES(V8WorkerMemoryReporterTestWithMockPlatform,
GetMemoryUsageTimeout);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CONTROLLER_PERFORMANCE_MANAGER_V8_WORKER_MEMORY_REPORTER_H_
// Copyright 2020 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/controller/performance_manager/v8_worker_memory_reporter.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker_test.h"
#include "third_party/blink/renderer/core/workers/worker_thread.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
class V8WorkerMemoryReporterTest : public ::testing::Test {
public:
using Result = V8WorkerMemoryReporter::Result;
using WorkerMemoryUsage = V8WorkerMemoryReporter::WorkerMemoryUsage;
};
class V8WorkerMemoryReporterTestWithDedicatedWorker
: public DedicatedWorkerTest {};
class V8WorkerMemoryReporterTestWithMockPlatform
: public V8WorkerMemoryReporterTestWithDedicatedWorker {
public:
void SetUp() override {
EnablePlatform();
V8WorkerMemoryReporterTestWithDedicatedWorker::SetUp();
}
};
class MockCallback {
public:
MOCK_METHOD(void, Callback, (const V8WorkerMemoryReporter::Result&));
};
bool operator==(const V8WorkerMemoryReporter::WorkerMemoryUsage& lhs,
const V8WorkerMemoryReporter::WorkerMemoryUsage& rhs) {
return lhs.token == rhs.token && lhs.bytes == rhs.bytes;
}
bool operator==(const V8WorkerMemoryReporter::Result& lhs,
const V8WorkerMemoryReporter::Result& rhs) {
return lhs.workers == rhs.workers;
}
class MemoryUsageChecker {
public:
enum class CallbackAction { kExitRunLoop, kNone };
MemoryUsageChecker(size_t worker_count,
size_t bytes_per_worker_lower_bound,
CallbackAction callback_action)
: worker_count_(worker_count),
bytes_per_worker_lower_bound_(bytes_per_worker_lower_bound),
callback_action_(callback_action) {}
void Callback(const V8WorkerMemoryReporter::Result& result) {
EXPECT_EQ(worker_count_, result.workers.size());
size_t expected_counts[2] = {0, 1};
EXPECT_THAT(expected_counts, testing::Contains(worker_count_));
if (worker_count_ == 1) {
EXPECT_LE(bytes_per_worker_lower_bound_, result.workers[0].bytes);
}
called_ = true;
if (callback_action_ == CallbackAction::kExitRunLoop) {
test::ExitRunLoop();
}
}
bool IsCalled() { return called_; }
private:
bool called_ = false;
size_t worker_count_;
size_t bytes_per_worker_lower_bound_;
CallbackAction callback_action_;
};
TEST_F(V8WorkerMemoryReporterTest, OnMeasurementSuccess) {
MockCallback mock_callback;
V8WorkerMemoryReporter reporter(
WTF::Bind(&MockCallback::Callback, WTF::Unretained(&mock_callback)));
reporter.SetWorkerCount(6);
Result result = {Vector<WorkerMemoryUsage>(
{WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 1},
WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 2},
WorkerMemoryUsage{WorkerToken(SharedWorkerToken()), 3},
WorkerMemoryUsage{WorkerToken(SharedWorkerToken()), 4},
WorkerMemoryUsage{WorkerToken(ServiceWorkerToken()), 4},
WorkerMemoryUsage{WorkerToken(ServiceWorkerToken()), 5}})};
EXPECT_CALL(mock_callback, Callback(result)).Times(1);
for (auto& worker : result.workers) {
reporter.OnMeasurementSuccess(worker);
}
}
TEST_F(V8WorkerMemoryReporterTest, OnMeasurementFailure) {
MockCallback mock_callback;
V8WorkerMemoryReporter reporter(
WTF::Bind(&MockCallback::Callback, WTF::Unretained(&mock_callback)));
reporter.SetWorkerCount(3);
Result result = {Vector<WorkerMemoryUsage>(
{WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 1},
WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 2}})};
EXPECT_CALL(mock_callback, Callback(result)).Times(1);
reporter.OnMeasurementSuccess(result.workers[0]);
reporter.OnMeasurementFailure();
reporter.OnMeasurementSuccess(result.workers[1]);
}
TEST_F(V8WorkerMemoryReporterTest, OnTimeout) {
MockCallback mock_callback;
V8WorkerMemoryReporter reporter(
WTF::Bind(&MockCallback::Callback, WTF::Unretained(&mock_callback)));
reporter.SetWorkerCount(4);
Result result = {Vector<WorkerMemoryUsage>(
{WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 1},
WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 2}})};
EXPECT_CALL(mock_callback, Callback(result)).Times(1);
reporter.OnMeasurementSuccess(result.workers[0]);
reporter.OnMeasurementSuccess(result.workers[1]);
reporter.OnTimeout();
reporter.OnMeasurementSuccess(
WorkerMemoryUsage{WorkerToken(SharedWorkerToken()), 2});
reporter.OnMeasurementFailure();
}
TEST_F(V8WorkerMemoryReporterTest, OnTimeoutNoop) {
MockCallback mock_callback;
V8WorkerMemoryReporter reporter(
WTF::Bind(&MockCallback::Callback, WTF::Unretained(&mock_callback)));
reporter.SetWorkerCount(2);
Result result = {Vector<WorkerMemoryUsage>(
{WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 1},
WorkerMemoryUsage{WorkerToken(DedicatedWorkerToken()), 2}})};
EXPECT_CALL(mock_callback, Callback(result)).Times(1);
reporter.OnMeasurementSuccess(result.workers[0]);
reporter.OnMeasurementSuccess(result.workers[1]);
reporter.OnTimeout();
}
TEST_F(V8WorkerMemoryReporterTestWithDedicatedWorker, GetMemoryUsage) {
const String source_code = "globalThis.array = new Array(1000000).fill(0);";
StartWorker(source_code);
WaitUntilWorkerIsRunning();
constexpr size_t kBytesPerArrayElement = 4;
constexpr size_t kArrayLength = 1000000;
MemoryUsageChecker checker(1, kBytesPerArrayElement * kArrayLength,
MemoryUsageChecker::CallbackAction::kExitRunLoop);
V8WorkerMemoryReporter::GetMemoryUsage(
WTF::Bind(&MemoryUsageChecker::Callback, WTF::Unretained(&checker)),
v8::MeasureMemoryExecution::kEager);
test::EnterRunLoop();
EXPECT_TRUE(checker.IsCalled());
}
TEST_F(V8WorkerMemoryReporterTestWithMockPlatform, GetMemoryUsageTimeout) {
const String source_code = "while(true);";
StartWorker(source_code);
// Since the worker is in infinite loop and does not process tasks,
// we cannot call WaitUntilWorkerIsRunning here as that would block.
MemoryUsageChecker checker(0, 0, MemoryUsageChecker::CallbackAction::kNone);
V8WorkerMemoryReporter::GetMemoryUsage(
WTF::Bind(&MemoryUsageChecker::Callback, WTF::Unretained(&checker)),
v8::MeasureMemoryExecution::kEager);
platform()->RunForPeriodSeconds(V8WorkerMemoryReporter::kTimeout.InSeconds() +
1);
EXPECT_TRUE(checker.IsCalled());
}
} // namespace blink
......@@ -1431,6 +1431,7 @@ source_set("unit_tests") {
"typed_arrays/array_buffer/array_buffer_contents_test.cc",
"url/url_search_params_test.cc",
"workers/dedicated_worker_test.cc",
"workers/dedicated_worker_test.h",
"workers/main_thread_worklet_test.cc",
"workers/threaded_worklet_test.cc",
"workers/worker_thread_test.cc",
......
......@@ -2,6 +2,8 @@
// 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/dedicated_worker_test.h"
#include <bitset>
#include <memory>
#include "base/single_thread_task_runner.h"
......@@ -165,42 +167,57 @@ class DedicatedWorkerMessagingProxyForTest
scoped_refptr<const SecurityOrigin> security_origin_;
};
class DedicatedWorkerTest : public PageTestBase {
public:
DedicatedWorkerTest() = default;
void DedicatedWorkerTest::SetUp() {
PageTestBase::SetUp(IntSize());
worker_messaging_proxy_ =
MakeGarbageCollected<DedicatedWorkerMessagingProxyForTest>(
GetFrame().DomWindow());
}
void SetUp() override {
PageTestBase::SetUp(IntSize());
worker_messaging_proxy_ =
MakeGarbageCollected<DedicatedWorkerMessagingProxyForTest>(
GetFrame().DomWindow());
}
void DedicatedWorkerTest::TearDown() {
GetWorkerThread()->TerminateForTesting();
GetWorkerThread()->WaitForShutdownForTesting();
}
void TearDown() override {
GetWorkerThread()->Terminate();
GetWorkerThread()->WaitForShutdownForTesting();
}
void DedicatedWorkerTest::DispatchMessageEvent() {
BlinkTransferableMessage message;
WorkerMessagingProxy()->PostMessageToWorkerGlobalScope(std::move(message));
}
void DispatchMessageEvent() {
BlinkTransferableMessage message;
WorkerMessagingProxy()->PostMessageToWorkerGlobalScope(std::move(message));
}
DedicatedWorkerMessagingProxyForTest*
DedicatedWorkerTest::WorkerMessagingProxy() {
return worker_messaging_proxy_.Get();
}
DedicatedWorkerMessagingProxyForTest* WorkerMessagingProxy() {
return worker_messaging_proxy_.Get();
}
DedicatedWorkerThreadForTest* DedicatedWorkerTest::GetWorkerThread() {
return worker_messaging_proxy_->GetDedicatedWorkerThread();
}
DedicatedWorkerThreadForTest* GetWorkerThread() {
return worker_messaging_proxy_->GetDedicatedWorkerThread();
}
void DedicatedWorkerTest::StartWorker(const String& source_code) {
WorkerMessagingProxy()->StartWithSourceCode(source_code);
}
private:
Persistent<DedicatedWorkerMessagingProxyForTest> worker_messaging_proxy_;
};
namespace {
void PostExitRunLoopTaskOnParent(WorkerThread* worker_thread) {
PostCrossThreadTask(*worker_thread->GetParentTaskRunnerForTesting(),
FROM_HERE, CrossThreadBindOnce(&test::ExitRunLoop));
}
} // anonymous namespace
void DedicatedWorkerTest::WaitUntilWorkerIsRunning() {
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
CrossThreadBindOnce(&PostExitRunLoopTaskOnParent,
CrossThreadUnretained(GetWorkerThread())));
test::EnterRunLoop();
}
TEST_F(DedicatedWorkerTest, PendingActivity_NoActivityAfterContextDestroyed) {
const String source_code = "// Do nothing";
WorkerMessagingProxy()->StartWithSourceCode(source_code);
StartWorker(source_code);
EXPECT_TRUE(WorkerMessagingProxy()->HasPendingActivity());
......@@ -212,7 +229,7 @@ TEST_F(DedicatedWorkerTest, PendingActivity_NoActivityAfterContextDestroyed) {
TEST_F(DedicatedWorkerTest, UseCounter) {
Page::InsertOrdinaryPageForTesting(&GetPage());
const String source_code = "// Do nothing";
WorkerMessagingProxy()->StartWithSourceCode(source_code);
StartWorker(source_code);
// This feature is randomly selected.
const WebFeature kFeature1 = WebFeature::kRequestFileSystem;
......@@ -259,7 +276,7 @@ TEST_F(DedicatedWorkerTest, UseCounter) {
TEST_F(DedicatedWorkerTest, TaskRunner) {
const String source_code = "// Do nothing";
WorkerMessagingProxy()->StartWithSourceCode(source_code);
StartWorker(source_code);
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
......
// Copyright 2020 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_DEDICATED_WORKER_TEST_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_DEDICATED_WORKER_TEST_H_
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
namespace blink {
class DedicatedWorkerThreadForTest;
class DedicatedWorkerMessagingProxyForTest;
class DedicatedWorkerTest : public PageTestBase {
public:
DedicatedWorkerTest() = default;
void SetUp() override;
void TearDown() override;
void DispatchMessageEvent();
DedicatedWorkerMessagingProxyForTest* WorkerMessagingProxy();
DedicatedWorkerThreadForTest* GetWorkerThread();
void StartWorker(const String& source_code);
void WaitUntilWorkerIsRunning();
private:
Persistent<DedicatedWorkerMessagingProxyForTest> worker_messaging_proxy_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_DEDICATED_WORKER_TEST_H_
\ No newline at end of file
......@@ -200,17 +200,21 @@ class CORE_EXPORT WorkerThread : public Thread::TaskObserver {
// adds the current WorkerThread* as the first parameter |function|.
// This only calls |function| for threads for which Start() was already
// called.
// Returns the number of workers that are scheduled to run the function.
template <typename FunctionType, typename... Parameters>
static void CallOnAllWorkerThreads(FunctionType function,
TaskType task_type,
Parameters&&... parameters) {
static unsigned CallOnAllWorkerThreads(FunctionType function,
TaskType task_type,
Parameters&&... parameters) {
MutexLocker lock(ThreadSetMutex());
unsigned called_worker_count = 0;
for (WorkerThread* thread : WorkerThreads()) {
PostCrossThreadTask(
*thread->GetTaskRunner(task_type), FROM_HERE,
CrossThreadBindOnce(function, WTF::CrossThreadUnretained(thread),
parameters...));
++called_worker_count;
}
return called_worker_count;
}
int GetWorkerThreadId() const { return worker_thread_id_; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment