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

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

Issue 2011763002: Worker: Attempt to gracefully terminate WorkerThread as much as possible (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@reorder_functions
Patch Set: address review comments Created 4 years, 7 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
Index: third_party/WebKit/Source/core/workers/WorkerThreadTest.cpp
diff --git a/third_party/WebKit/Source/core/workers/WorkerThreadTest.cpp b/third_party/WebKit/Source/core/workers/WorkerThreadTest.cpp
index 8fb299c9d7693bf4a41fbf1664d1646ac85b57d8..6e11f3b36c539da3f853da174c516960ebe1a370 100644
--- a/third_party/WebKit/Source/core/workers/WorkerThreadTest.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerThreadTest.cpp
@@ -5,19 +5,16 @@
#include "core/workers/WorkerThread.h"
#include "core/workers/WorkerThreadTestHelper.h"
-#include "public/platform/WebScheduler.h"
+#include "platform/testing/UnitTestHelpers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::AtMost;
-using testing::Invoke;
-using testing::Return;
-using testing::Mock;
namespace blink {
-class WorkerThreadTest : public testing::Test {
+class WorkerThreadTest : public ::testing::Test {
public:
void SetUp() override
{
@@ -36,21 +33,33 @@ public:
void start()
{
- startWithSourceCode("//fake source code");
+ m_workerThread->startWithSourceCode(m_securityOrigin.get(), "//fake source code");
}
- void startWithSourceCode(const String& source)
+ void startWithSourceCodeNotToFinish()
{
- m_workerThread->startWithSourceCode(m_securityOrigin.get(), source);
+ // Use a JavaScript source code that makes an infinite loop so that we
+ // can catch some kind of issues as a timeout.
+ m_workerThread->startWithSourceCode(m_securityOrigin.get(), "while(true) {}");
}
- void waitForInit()
+ void waitForShutdown()
{
- m_workerThread->waitForInit();
+ m_workerThread->m_shutdownEvent->wait();
+ }
+
+ void setForceTerminationDelayInMs(long long forceTerminationDelayInMs)
+ {
+ m_workerThread->setForceTerminationDelayInMsForTesting(forceTerminationDelayInMs);
+ }
+
+ bool isForceTerminationTaskScheduled()
+ {
+ return m_workerThread->m_scheduledForceTerminationTask.get();
}
protected:
- void expectWorkerLifetimeReportingCalls()
+ void expectReportingCalls()
{
EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Times(1);
EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(true)).Times(1);
@@ -58,51 +67,188 @@ protected:
EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()).Times(1);
}
+ void expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting()
+ {
+ EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Times(AtMost(1));
+ EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(_)).Times(AtMost(1));
+ EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times(AtMost(1));
+ EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()).Times(AtMost(1));
+ }
+
+ void expectReportingCallsForWorkerForciblyTerminated()
+ {
+ EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Times(1);
+ EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(false)).Times(1);
+ EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times(1);
+ EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()).Times(1);
+ }
+
RefPtr<SecurityOrigin> m_securityOrigin;
OwnPtr<MockWorkerLoaderProxyProvider> m_mockWorkerLoaderProxyProvider;
OwnPtr<MockWorkerReportingProxy> m_mockWorkerReportingProxy;
OwnPtr<WorkerThreadForTest> m_workerThread;
};
-TEST_F(WorkerThreadTest, StartAndStop)
+TEST_F(WorkerThreadTest, StartAndTerminate_AsyncTerminate)
{
- expectWorkerLifetimeReportingCalls();
+ expectReportingCalls();
start();
- waitForInit();
+ m_workerThread->waitForInit();
+
+ // The worker thread is not being blocked, so the worker thread should be
+ // gracefully shut down.
+ m_workerThread->terminate();
+ EXPECT_TRUE(isForceTerminationTaskScheduled());
+ waitForShutdown();
+ EXPECT_EQ(WorkerThread::ExitCode::GracefullyTerminated, m_workerThread->exitCode());
+}
+
+TEST_F(WorkerThreadTest, StartAndTerminate_SyncTerminate)
+{
+ expectReportingCalls();
+ start();
+ m_workerThread->waitForInit();
m_workerThread->terminateAndWait();
+ EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->exitCode());
+}
+
+TEST_F(WorkerThreadTest, StartAndTerminateImmediately_AsyncTerminate)
+{
+ expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting();
+ start();
+
+ // There are two possible cases depending on timing:
+ // (1) If the thread hasn't been initialized on the worker thread yet,
+ // terminate() should not attempt to shut down the thread.
+ // (2) If the thread has already been initialized on the worker thread,
+ // terminate() should gracefully shut down the thread.
+ m_workerThread->terminate();
+ waitForShutdown();
+ WorkerThread::ExitCode exitCode = m_workerThread->exitCode();
+ EXPECT_TRUE(WorkerThread::ExitCode::TerminatedBeforeStarting == exitCode || WorkerThread::ExitCode::GracefullyTerminated == exitCode);
}
-TEST_F(WorkerThreadTest, StartAndStopImmediately)
+TEST_F(WorkerThreadTest, StartAndTerminateImmediately_SyncTerminate)
{
- EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_))
- .Times(AtMost(1));
- EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(_))
- .Times(AtMost(1));
- EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated())
- .Times(AtMost(1));
- EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope())
- .Times(AtMost(1));
+ expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting();
start();
+
+ // There are two possible cases depending on timing:
+ // (1) If the thread hasn't been initialized on the worker thread yet,
+ // terminateAndWait() should not attempt to shut down the thread.
+ // (2) If the thread has already been initialized on the worker thread,
+ // terminateAndWait() should synchronously forcibly terminates the isolate.
+ m_workerThread->terminateAndWait();
+ WorkerThread::ExitCode exitCode = m_workerThread->exitCode();
+ EXPECT_TRUE(WorkerThread::ExitCode::TerminatedBeforeStarting == exitCode || WorkerThread::ExitCode::SyncForciblyTerminated == exitCode);
+}
+
+TEST_F(WorkerThreadTest, TerminateBeforeStart_AsyncTerminate)
+{
+ m_workerThread->terminate();
+ EXPECT_EQ(WorkerThread::ExitCode::TerminatedBeforeStarting, m_workerThread->exitCode());
+ EXPECT_FALSE(isForceTerminationTaskScheduled());
+
+ // Sync termination requests after the thread is terminated should not block
+ // the main thread.
+ m_workerThread->terminateAndWait();
+ WorkerThread::terminateAndWaitForAllWorkers();
+}
+
+TEST_F(WorkerThreadTest, TerminateBeforeStart_SyncTerminate)
+{
+ // Terminating the worker thread that hasn't started yet does not block the
+ // main thread.
+ m_workerThread->terminateAndWait();
+ EXPECT_EQ(WorkerThread::ExitCode::TerminatedBeforeStarting, m_workerThread->exitCode());
+
+ // Async termination request after the thread is terminated should not
+ // schedule a delayed task.
+ m_workerThread->terminate();
+ EXPECT_FALSE(isForceTerminationTaskScheduled());
+
+ // Sync termination requests after the thread is terminated should not block
+ // the main thread.
m_workerThread->terminateAndWait();
+ WorkerThread::terminateAndWaitForAllWorkers();
}
-TEST_F(WorkerThreadTest, StartAndStopOnScriptLoaded)
+TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_SyncForciblyTerminate)
{
- // Use a JavaScript source code that makes an infinite loop so that we can
- // catch some kind of issues as a timeout.
- const String source("while(true) {}");
-
- EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_))
- .Times(AtMost(1));
- EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(_))
- .Times(AtMost(1));
- EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated())
- .Times(AtMost(1));
- EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope())
- .Times(AtMost(1));
- startWithSourceCode(source);
+ expectReportingCallsForWorkerForciblyTerminated();
+ startWithSourceCodeNotToFinish();
m_workerThread->waitUntilScriptLoaded();
+
+ // terminateAndWait() synchronously terminates the isolate.
m_workerThread->terminateAndWait();
+ EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->exitCode());
+}
+
+TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_AsyncForciblyTerminate)
+{
+ const long long kForceTerminationDelayInMs = 10;
+ setForceTerminationDelayInMs(kForceTerminationDelayInMs);
+
+ expectReportingCallsForWorkerForciblyTerminated();
+ startWithSourceCodeNotToFinish();
+ m_workerThread->waitUntilScriptLoaded();
+
+ // terminate() schedules a force termination task.
+ m_workerThread->terminate();
+ EXPECT_TRUE(isForceTerminationTaskScheduled());
+ EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode());
+
+ // Wait until the force termination task runs.
+ testing::runDelayedTasks(kForceTerminationDelayInMs);
+ waitForShutdown();
+ EXPECT_EQ(WorkerThread::ExitCode::AsyncForciblyTerminated, m_workerThread->exitCode());
}
+TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_AsyncForciblyTerminate_MultipleTimes)
+{
+ const long long kForceTerminationDelayInMs = 10;
+ setForceTerminationDelayInMs(kForceTerminationDelayInMs);
+
+ expectReportingCallsForWorkerForciblyTerminated();
+ startWithSourceCodeNotToFinish();
+ m_workerThread->waitUntilScriptLoaded();
+
+ // terminate() schedules a force termination task.
+ m_workerThread->terminate();
+ EXPECT_TRUE(isForceTerminationTaskScheduled());
+ EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode());
+
+ // Multiple terminate() calls should not take effect.
+ m_workerThread->terminate();
+ m_workerThread->terminate();
+ EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode());
+
+ // Wait until the force termination task runs.
+ testing::runDelayedTasks(kForceTerminationDelayInMs);
+ waitForShutdown();
+ EXPECT_EQ(WorkerThread::ExitCode::AsyncForciblyTerminated, m_workerThread->exitCode());
+}
+
+TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_SyncForciblyTerminateAfterTerminationTaskIsScheduled)
+{
+ const long long kForceTerminationDelayInMs = 10;
+ setForceTerminationDelayInMs(kForceTerminationDelayInMs);
+
+ expectReportingCallsForWorkerForciblyTerminated();
+ startWithSourceCodeNotToFinish();
+ m_workerThread->waitUntilScriptLoaded();
+
+ // terminate() schedules a force termination task.
+ m_workerThread->terminate();
+ EXPECT_TRUE(isForceTerminationTaskScheduled());
+ EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode());
+
+ // terminateAndWait() should overtake the scheduled force termination task.
+ m_workerThread->terminateAndWait();
+ EXPECT_FALSE(isForceTerminationTaskScheduled());
+ EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->exitCode());
+}
+
+// TODO(nhiroki): Add tests for terminateAndWaitForAllWorkers.
+
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698