Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Unified Diff: Source/core/workers/WorkerThreadTest.cpp

Issue 956333002: Refactor TimeBase to post tasks. Workers to use real Idle tasks. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Rebase Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/workers/WorkerThread.cpp ('k') | Source/platform/PlatformThreadData.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/workers/WorkerThreadTest.cpp
diff --git a/Source/core/workers/WorkerThreadTest.cpp b/Source/core/workers/WorkerThreadTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d86c31d7fb2587e2eaa947317ed68bb8b536d600
--- /dev/null
+++ b/Source/core/workers/WorkerThreadTest.cpp
@@ -0,0 +1,457 @@
+// Copyright 2015 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 "config.h"
+#include "core/workers/WorkerThread.h"
+
+#include "bindings/core/v8/ScriptWrappable.h"
+#include "core/inspector/ConsoleMessage.h"
+#include "core/workers/SharedWorkerGlobalScope.h"
+#include "core/workers/SharedWorkerThread.h"
+#include "core/workers/WorkerGlobalScope.h"
+#include "core/workers/WorkerReportingProxy.h"
+#include "core/workers/WorkerThreadStartupData.h"
+#include "public/platform/WebScheduler.h"
+#include "wtf/ThreadingPrimitives.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using testing::_;
+using testing::Invoke;
+using testing::Return;
+using testing::Mock;
+
+namespace blink {
+
+namespace {
+class MockWorkerLoaderProxy : public WorkerLoaderProxy {
+public:
+ MockWorkerLoaderProxy() : WorkerLoaderProxy(nullptr) { }
+ ~MockWorkerLoaderProxy() override { }
+
+ MOCK_METHOD1(postTaskToLoader, void(PassOwnPtr<ExecutionContextTask>));
+ MOCK_METHOD1(postTaskToWorkerGlobalScope, bool(PassOwnPtr<ExecutionContextTask>));
+};
+
+class MockWorkerReportingProxy : public WorkerReportingProxy {
+public:
+ MockWorkerReportingProxy() { }
+ ~MockWorkerReportingProxy() override { }
+
+ MOCK_METHOD5(reportException, void(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, int exceptionId));
+ MOCK_METHOD1(reportConsoleMessage, void(PassRefPtrWillBeRawPtr<ConsoleMessage>));
+ MOCK_METHOD1(postMessageToPageInspector, void(const String&));
+ MOCK_METHOD0(postWorkerConsoleAgentEnabled, void());
+ MOCK_METHOD1(didEvaluateWorkerScript, void(bool success));
+ MOCK_METHOD1(workerGlobalScopeStarted, void(WorkerGlobalScope*));
+ MOCK_METHOD0(workerGlobalScopeClosed, void());
+ MOCK_METHOD0(workerThreadTerminated, void());
+ MOCK_METHOD0(willDestroyWorkerGlobalScope, void());
+};
+
+class FakeWorkerGlobalScope : public WorkerGlobalScope {
+public:
+ typedef WorkerGlobalScope Base;
+
+ FakeWorkerGlobalScope(const KURL& url, const String& userAgent, WorkerThread* thread, const SecurityOrigin* starterOrigin, PassOwnPtrWillBeRawPtr<WorkerClients> workerClients)
+ : WorkerGlobalScope(url, userAgent, thread, monotonicallyIncreasingTime(), starterOrigin, workerClients)
+ {
+ }
+
+ ~FakeWorkerGlobalScope() override
+ {
+ }
+
+ virtual bool isSharedWorkerGlobalScope() const override { return true; }
+
+ // EventTarget
+ virtual const AtomicString& interfaceName() const override
+ {
+ return EventTargetNames::SharedWorkerGlobalScope;
+ }
+
+ virtual void logExceptionToConsole(const String&, int , const String&, int, int, PassRefPtrWillBeRawPtr<ScriptCallStack>) override
+ {
+ }
+
+ // Setters/Getters for attributes in SharedWorkerGlobalScope.idl
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(connect);
+ String name() const { return "FakeWorkerGlobalScope"; }
+
+ DECLARE_VIRTUAL_TRACE();
+};
+
+DEFINE_TRACE(FakeWorkerGlobalScope)
+{
+ WorkerGlobalScope::trace(visitor);
+}
+
+class WorkerThreadForTest : public WorkerThread {
+public:
+ WorkerThreadForTest(
+ WorkerLoaderProxy* mockWorkerLoaderProxy,
+ WorkerReportingProxy& mockWorkerReportingProxy,
+ PassOwnPtrWillBeRawPtr<WorkerThreadStartupData> workerThreadStartupData)
+ : WorkerThread("Test Thread", mockWorkerLoaderProxy, mockWorkerReportingProxy, workerThreadStartupData)
+ {
+ }
+
+ ~WorkerThreadForTest() override { }
+
+ // NOTE we use the WebSchduler to post tasks in this test because it's not affected by the terminated() method, which
+ // tasks posted via the WorkerTrhead directly are.
+ using WorkerThread::schedulerForTesting;
+ using WorkerThread::threadForTesting;
+
+ MOCK_METHOD1(doIdleGc, bool(double deadlineSeconds));
+ MOCK_METHOD0(terminated, bool());
+
+ PassRefPtrWillBeRawPtr<WorkerGlobalScope> createWorkerGlobalScope(PassOwnPtr<WorkerThreadStartupData> startupData) override
+ {
+ return adoptRefWillBeNoop(new FakeWorkerGlobalScope(startupData->m_scriptURL, startupData->m_userAgent, this, startupData->m_starterOrigin, startupData->m_workerClients.release()));
+ }
+};
+
+class WakeupTask : public WebThread::Task {
+public:
+ WakeupTask() { }
+
+ ~WakeupTask() override { }
+
+ void run() override { }
+};
+
+
+class PostDelayedWakeupTask : public WebThread::Task {
+public:
+ PostDelayedWakeupTask(WebScheduler* scheduler, long long delay) : m_scheduler(scheduler), m_delay(delay) { }
+
+ ~PostDelayedWakeupTask() override { }
+
+ void run() override
+ {
+ m_scheduler->postTimerTask(FROM_HERE, new WakeupTask(), m_delay);
+ }
+
+ WebScheduler* m_scheduler; // NOT OWNED
+ long long m_delay;
+};
+
+class SignalTask : public WebThread::Task {
+public:
+ SignalTask(ThreadCondition* completion, Mutex* mutex) : m_completion(completion), m_mutex(mutex) { }
+
+ ~SignalTask() override { }
+
+ void run() override
+ {
+ WTF::Locker<Mutex> lock(*m_mutex);
+ m_completion->signal();
+ }
+
+private:
+ ThreadCondition* m_completion; // NOT OWNED
+ Mutex* m_mutex; // NOT OWNED
+};
+
+} // namespace
+
+class WorkerThreadTest : public testing::Test {
+public:
+ void SetUp() override
+ {
+ m_mockWorkerLoaderProxy = new MockWorkerLoaderProxy();
+ m_mockWorkerReportingProxy = adoptPtr(new MockWorkerReportingProxy());
+ m_securityOrigin = SecurityOrigin::create(KURL(ParsedURLString, "http://fake.url/"));
+ m_workerThread = adoptRef(new WorkerThreadForTest(
+ m_mockWorkerLoaderProxy.get(),
+ *m_mockWorkerReportingProxy,
+ WorkerThreadStartupData::create(
+ KURL(ParsedURLString, "http://fake.url/"),
+ "fake user agent",
+ "//fake source code",
+ nullptr,
+ DontPauseWorkerGlobalScopeOnStart,
+ "contentSecurityPolicy",
+ ContentSecurityPolicyHeaderTypeReport,
+ m_securityOrigin.get(),
+ WorkerClients::create(),
+ V8CacheOptionsDefault)));
+ ExpectWorkerLifetimeReportingCalls();
+ }
+
+ void StartAndWaitForInit()
+ {
+ ThreadCondition complete;
+ Mutex mutex;
+
+ m_workerThread->start();
+ m_workerThread->threadForTesting()->postTask(FROM_HERE, new SignalTask(&complete, &mutex));
+
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ complete.wait(mutex);
+ }
+ }
+
+ void PostWakeUpTask(long long waitMs)
+ {
+ WebScheduler* scheduler = m_workerThread->schedulerForTesting();
+
+ // The idle task will get posted on an after wake up queue, so we need another task
+ // posted at the right time to wake the system up. We don't know the right delay here
+ // since the thread can take a variable length of time to be responsive, however this
+ // isn't a problem when posting a delayed task from within a task on the worker thread.
+ scheduler->postLoadingTask(FROM_HERE, new PostDelayedWakeupTask(scheduler, waitMs));
+ }
+
+protected:
+ void ExpectWorkerLifetimeReportingCalls()
+ {
+ EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Times(1);
+ EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(true)).Times(1);
+ EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times(1);
+ EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()).Times(1);
+ }
+
+ RefPtr<SecurityOrigin> m_securityOrigin;
+ RefPtr<MockWorkerLoaderProxy> m_mockWorkerLoaderProxy;
+ OwnPtr<MockWorkerReportingProxy> m_mockWorkerReportingProxy;
+ RefPtr<WorkerThreadForTest> m_workerThread;
+};
+
+TEST_F(WorkerThreadTest, GcOccursWhileIdle)
+{
+ ThreadCondition gcDone;
+ Mutex mutex;
+
+ ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke(
+ [&gcDone, &mutex](double)
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ gcDone.signal();
+ return false;
+ }));
+
+ EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1);
+ EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
+
+ StartAndWaitForInit();
+ PostWakeUpTask(310ul); // 10ms after the quiescent period ends.
+
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ gcDone.wait(mutex);
+ }
+
+ m_workerThread->terminateAndWait();
+};
+
+class RepeatingTask : public WebThread::Task {
+public:
+ RepeatingTask(WebScheduler* scheduler, ThreadCondition* completion, Mutex* mutex)
+ : RepeatingTask(scheduler, completion, mutex, 0) { }
+
+ ~RepeatingTask() override { }
+
+ void run() override
+ {
+ m_taskCount++;
+ if (m_taskCount == 10) {
+ WTF::Locker<Mutex> lock(*m_mutex);
+ m_completion->signal();
+ }
+
+ m_scheduler->postTimerTask(
+ FROM_HERE, new RepeatingTask(m_scheduler, m_completion, m_mutex, m_taskCount), 50ul);
+ m_scheduler->postLoadingTask(FROM_HERE, new WakeupTask());
+
+ }
+
+private:
+ RepeatingTask(WebScheduler* scheduler, ThreadCondition* completion, Mutex* mutex, int taskCount)
+ : m_scheduler(scheduler)
+ , m_completion(completion)
+ , m_mutex(mutex)
+ , m_taskCount(taskCount)
+ { }
+
+ WebScheduler* m_scheduler; // NOT OWNED
+ ThreadCondition* m_completion;
+ Mutex* m_mutex;
+ int m_taskCount;
+};
+
+TEST_F(WorkerThreadTest, GcDoesNotOccurIfGapBetweenDelayedTasksIsTooSmall)
+{
+ ThreadCondition completion;
+ Mutex mutex;
+
+ EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(0);
+ EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
+
+ StartAndWaitForInit();
+
+ WebScheduler* scheduler = m_workerThread->schedulerForTesting();
+
+ // Post a repeating task that should prevent any GC from happening.
+ scheduler->postLoadingTask(FROM_HERE, new RepeatingTask(scheduler, &completion, &mutex));
+
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ completion.wait(mutex);
+ }
+
+ // Make sure doIdleGc has not been called by this stage.
+ Mock::VerifyAndClearExpectations(m_workerThread.get());
+
+ EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
+ m_workerThread->terminateAndWait();
+}
+
+TEST_F(WorkerThreadTest, LongGcDeadline_NoFutureTasks)
+{
+ ThreadCondition gcDone;
+ Mutex mutex;
+ double deadlineLength = 0;
+
+ ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke(
+ [&gcDone, &mutex, &deadlineLength](double deadline)
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ gcDone.signal();
+ deadlineLength = deadline -Platform::current()->monotonicallyIncreasingTime();
+ return false;
+ }));
+
+ EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1);
+ EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
+
+ StartAndWaitForInit();
+ PostWakeUpTask(310ul);
+
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ gcDone.wait(mutex);
+
+ // The deadline should be close to 1s in duration if there are no tasks that need to run soon.
+ EXPECT_GT(deadlineLength, 0.9);
+ }
+
+ m_workerThread->terminateAndWait();
+}
+
+TEST_F(WorkerThreadTest, LongGcDeadline_NextTaskAfterIdlePeriod)
+{
+ ThreadCondition gcDone;
+ Mutex mutex;
+ double deadlineLength = 0;
+
+ ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke(
+ [&gcDone, &mutex, &deadlineLength](double deadline)
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ gcDone.signal();
+ deadlineLength = deadline -Platform::current()->monotonicallyIncreasingTime();
+ return false;
+ }));
+
+ EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1);
+ EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
+
+ StartAndWaitForInit();
+ PostWakeUpTask(310ul);
+ PostWakeUpTask(675ul); // Task that runs shortly after the 50ms idle period ends.
+
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ gcDone.wait(mutex);
+
+ // The worker thread calls canExceedIdleDeadlineIfRequired which only considers if
+ // there are any delayed tasks scheduled for the current long idle period. Since the
+ // next task is in the following idle period, a long gc deadline is allowed.
+ EXPECT_GT(deadlineLength, 0.9);
+ }
+
+ m_workerThread->terminateAndWait();
+}
+
+TEST_F(WorkerThreadTest, ShortGcDeadline)
+{
+ ThreadCondition gcDone;
+ Mutex mutex;
+ double deadlineLength = 0;
+
+ ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke(
+ [&gcDone, &mutex, &deadlineLength](double deadline)
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ gcDone.signal();
+ deadlineLength = deadline -Platform::current()->monotonicallyIncreasingTime();
+ return false;
+ }));
+
+ EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1);
+ EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
+
+ StartAndWaitForInit();
+ PostWakeUpTask(310ul);
+ PostWakeUpTask(625ul); // Task that runs during the idle period.
+
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ gcDone.wait(mutex);
+
+ // The deadline should be < 50ms if there's a task that needs to run during the idle period.
+ EXPECT_LT(deadlineLength, 0.025);
+ }
+
+ m_workerThread->terminateAndWait();
+}
+
+class PostDelayedSignalTask : public WebThread::Task {
+public:
+ PostDelayedSignalTask(WebScheduler* scheduler, ThreadCondition* completion, Mutex* mutex, long long delay)
+ : m_scheduler(scheduler)
+ , m_completion(completion)
+ , m_mutex(mutex)
+ , m_delay(delay) { }
+
+ ~PostDelayedSignalTask() override { }
+
+ void run() override
+ {
+ m_scheduler->postTimerTask(FROM_HERE, new SignalTask(m_completion, m_mutex), m_delay);
+ }
+
+ WebScheduler* m_scheduler; // NOT OWNED
+ ThreadCondition* m_completion; // NOT OWNED
+ Mutex* m_mutex; // NOT OWNED
+ long long m_delay;
+};
+
+TEST_F(WorkerThreadTest, TerminationPreventsGc)
+{
+ ThreadCondition completion;
+ Mutex mutex;
+
+ EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(0);
+ EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(true));
+
+ StartAndWaitForInit();
+ PostWakeUpTask(310ul); // 10ms after the quiescent period ends.
+
+ // Give pleanty of time for the GC to run before exiting.
+ WebScheduler* scheduler = m_workerThread->schedulerForTesting();
+ scheduler->postLoadingTask(FROM_HERE, new PostDelayedSignalTask(scheduler, &completion, &mutex, 700ul));
+
+ {
+ WTF::Locker<Mutex> lock(mutex);
+ completion.wait(mutex);
+ }
+
+ m_workerThread->terminateAndWait();
+};
+
+} // namespace blink
« no previous file with comments | « Source/core/workers/WorkerThread.cpp ('k') | Source/platform/PlatformThreadData.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698