1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "modules/compositorworker/CompositorWorkerManager.h"
8 #include "bindings/core/v8/ScriptSourceCode.h"
9 #include "bindings/core/v8/V8GCController.h"
10 #include "core/inspector/ConsoleMessage.h"
11 #include "core/testing/DummyPageHolder.h"
12 #include "core/workers/WorkerLoaderProxy.h"
13 #include "core/workers/WorkerObjectProxy.h"
14 #include "core/workers/WorkerThreadStartupData.h"
15 #include "modules/compositorworker/CompositorWorkerThread.h"
16 #include "platform/NotImplemented.h"
17 #include "platform/ThreadSafeFunctional.h"
18 #include "platform/heap/Handle.h"
19 #include "platform/testing/UnitTestHelpers.h"
20 #include "public/platform/Platform.h"
21 #include "public/platform/WebWaitableEvent.h"
22 #include <gtest/gtest.h>
27 class TestCompositorWorkerThread
: public CompositorWorkerThread
{
29 TestCompositorWorkerThread(WorkerLoaderProxyProvider
* loaderProxyProvider
, WorkerObjectProxy
& objectProxy
, double timeOrigin
, WebWaitableEvent
* startEvent
)
30 : CompositorWorkerThread(WorkerLoaderProxy::create(loaderProxyProvider
), objectProxy
, timeOrigin
)
31 , m_startEvent(startEvent
)
35 ~TestCompositorWorkerThread() override
{ }
37 void setCallbackAfterV8Termination(PassOwnPtr
<Function
<void()>> callback
)
39 m_v8TerminationCallback
= callback
;
44 void didStartRunLoop() override
46 m_startEvent
->signal();
48 void terminateV8Execution() override
50 CompositorWorkerThread::terminateV8Execution();
51 if (m_v8TerminationCallback
)
52 (*m_v8TerminationCallback
)();
54 void willDestroyIsolate() override
56 V8GCController::collectAllGarbageForTesting(v8::Isolate::GetCurrent());
57 CompositorWorkerThread::willDestroyIsolate();
60 WebWaitableEvent
* m_startEvent
;
61 OwnPtr
<Function
<void()>> m_v8TerminationCallback
;
64 // A null WorkerObjectProxy, supplied when creating CompositorWorkerThreads.
65 class TestCompositorWorkerObjectProxy
: public WorkerObjectProxy
{
67 static PassOwnPtr
<TestCompositorWorkerObjectProxy
> create(ExecutionContext
* context
)
69 return adoptPtr(new TestCompositorWorkerObjectProxy(context
));
72 // (Empty) WorkerReportingProxy implementation:
73 virtual void reportException(const String
& errorMessage
, int lineNumber
, int columnNumber
, const String
& sourceURL
, int exceptionId
) { }
74 void reportConsoleMessage(PassRefPtrWillBeRawPtr
<ConsoleMessage
>) override
{ }
75 void postMessageToPageInspector(const String
&) override
{ }
76 void postWorkerConsoleAgentEnabled() override
{ }
78 void didEvaluateWorkerScript(bool success
) override
{ }
79 void workerGlobalScopeStarted(WorkerGlobalScope
*) override
{ }
80 void workerGlobalScopeClosed() override
{ }
81 void workerThreadTerminated() override
{ }
82 void willDestroyWorkerGlobalScope() override
{ }
84 TestCompositorWorkerObjectProxy(ExecutionContext
* context
)
85 : WorkerObjectProxy(context
, nullptr)
92 class CompositorWorkerManagerTest
: public ::testing::Test
{
96 m_page
= DummyPageHolder::create();
97 m_objectProxy
= TestCompositorWorkerObjectProxy::create(&m_page
->document());
98 m_securityOrigin
= SecurityOrigin::create(KURL(ParsedURLString
, "http://fake.url/"));
101 void TearDown() override
103 ASSERT(!managerHasThread());
104 ASSERT(!managerHasIsolate());
108 PassRefPtr
<TestCompositorWorkerThread
> createCompositorWorker(WebWaitableEvent
* startEvent
)
110 TestCompositorWorkerThread
* workerThread
= new TestCompositorWorkerThread(nullptr, *m_objectProxy
, 0, startEvent
);
111 OwnPtrWillBeRawPtr
<WorkerClients
> clients
= nullptr;
112 workerThread
->start(WorkerThreadStartupData::create(
113 KURL(ParsedURLString
, "http://fake.url/"),
115 "//fake source code",
117 DontPauseWorkerGlobalScopeOnStart
,
118 adoptPtr(new Vector
<CSPHeaderAndType
>()),
119 m_securityOrigin
.get(),
121 V8CacheOptionsDefault
));
122 return adoptRef(workerThread
);
125 void createWorkerAdapter(RefPtr
<CompositorWorkerThread
>* workerThread
, WebWaitableEvent
* creationEvent
)
127 *workerThread
= createCompositorWorker(creationEvent
);
130 // Attempts to run some simple script for |worker|.
131 void checkWorkerCanExecuteScript(WorkerThread
* worker
)
133 OwnPtr
<WebWaitableEvent
> waitEvent
= adoptPtr(Platform::current()->createWaitableEvent());
134 worker
->backingThread().platformThread().taskRunner()->postTask(FROM_HERE
, threadSafeBind(&CompositorWorkerManagerTest::executeScriptInWorker
, AllowCrossThreadAccess(this),
135 AllowCrossThreadAccess(worker
), AllowCrossThreadAccess(waitEvent
.get())));
139 void waitForWaitableEventAfterIteratingCurrentLoop(WebWaitableEvent
* waitEvent
)
141 testing::runPendingTasks();
145 bool managerHasThread() const
147 return CompositorWorkerManager::instance()->m_thread
;
150 bool managerHasIsolate() const
152 return CompositorWorkerManager::instance()->m_isolate
;
156 void executeScriptInWorker(WorkerThread
* worker
, WebWaitableEvent
* waitEvent
)
158 WorkerScriptController
* scriptController
= worker
->workerGlobalScope()->script();
159 bool evaluateResult
= scriptController
->evaluate(ScriptSourceCode("var counter = 0; ++counter;"));
160 ASSERT_UNUSED(evaluateResult
, evaluateResult
);
164 OwnPtr
<DummyPageHolder
> m_page
;
165 RefPtr
<SecurityOrigin
> m_securityOrigin
;
166 OwnPtr
<WorkerObjectProxy
> m_objectProxy
;
169 TEST_F(CompositorWorkerManagerTest
, Basic
)
171 OwnPtr
<WebWaitableEvent
> creationEvent
= adoptPtr(Platform::current()->createWaitableEvent());
172 RefPtr
<CompositorWorkerThread
> compositorWorker
= createCompositorWorker(creationEvent
.get());
173 waitForWaitableEventAfterIteratingCurrentLoop(creationEvent
.get());
174 checkWorkerCanExecuteScript(compositorWorker
.get());
175 compositorWorker
->terminateAndWait();
178 // Tests that the same WebThread is used for new workers if the WebThread is still alive.
179 TEST_F(CompositorWorkerManagerTest
, CreateSecondAndTerminateFirst
)
181 // Create the first worker and wait until it is initialized.
182 OwnPtr
<WebWaitableEvent
> firstCreationEvent
= adoptPtr(Platform::current()->createWaitableEvent());
183 RefPtr
<CompositorWorkerThread
> firstWorker
= createCompositorWorker(firstCreationEvent
.get());
184 WebThreadSupportingGC
* firstThread
= &CompositorWorkerManager::instance()->compositorWorkerThread();
186 waitForWaitableEventAfterIteratingCurrentLoop(firstCreationEvent
.get());
187 v8::Isolate
* firstIsolate
= firstWorker
->isolate();
188 ASSERT(firstIsolate
);
190 // Create the second worker and immediately destroy the first worker.
191 OwnPtr
<WebWaitableEvent
> secondCreationEvent
= adoptPtr(Platform::current()->createWaitableEvent());
192 RefPtr
<CompositorWorkerThread
> secondWorker
= createCompositorWorker(secondCreationEvent
.get());
193 firstWorker
->terminateAndWait();
195 // Wait until the second worker is initialized. Verify that the second worker is using the same
196 // thread and Isolate as the first worker.
197 WebThreadSupportingGC
* secondThread
= &CompositorWorkerManager::instance()->compositorWorkerThread();
198 ASSERT(secondThread
);
199 waitForWaitableEventAfterIteratingCurrentLoop(secondCreationEvent
.get());
200 EXPECT_EQ(firstThread
, secondThread
);
202 v8::Isolate
* secondIsolate
= secondWorker
->isolate();
203 ASSERT(secondIsolate
);
204 EXPECT_EQ(firstIsolate
, secondIsolate
);
206 // Verify that the worker can still successfully execute script.
207 checkWorkerCanExecuteScript(secondWorker
.get());
209 secondWorker
->terminateAndWait();
212 static void checkCurrentIsolate(v8::Isolate
* isolate
, WebWaitableEvent
* event
)
214 EXPECT_EQ(v8::Isolate::GetCurrent(), isolate
);
218 // Tests that a new WebThread is created if all existing workers are terminated before a new worker is created.
219 TEST_F(CompositorWorkerManagerTest
, TerminateFirstAndCreateSecond
)
221 // Create the first worker, wait until it is initialized, and terminate it.
222 OwnPtr
<WebWaitableEvent
> creationEvent
= adoptPtr(Platform::current()->createWaitableEvent());
223 RefPtr
<CompositorWorkerThread
> compositorWorker
= createCompositorWorker(creationEvent
.get());
224 WebThreadSupportingGC
* firstThread
= &CompositorWorkerManager::instance()->compositorWorkerThread();
225 waitForWaitableEventAfterIteratingCurrentLoop(creationEvent
.get());
226 ASSERT(compositorWorker
->isolate());
227 compositorWorker
->terminateAndWait();
229 // Create the second worker. Verify that the second worker lives in a different WebThread since the first
230 // thread will have been destroyed after destroying the first worker.
231 creationEvent
= adoptPtr(Platform::current()->createWaitableEvent());
232 compositorWorker
= createCompositorWorker(creationEvent
.get());
233 WebThreadSupportingGC
* secondThread
= &CompositorWorkerManager::instance()->compositorWorkerThread();
234 EXPECT_NE(firstThread
, secondThread
);
235 waitForWaitableEventAfterIteratingCurrentLoop(creationEvent
.get());
237 // Jump over to the worker's thread to verify that the Isolate is set up correctly and execute script.
238 OwnPtr
<WebWaitableEvent
> checkEvent
= adoptPtr(Platform::current()->createWaitableEvent());
239 secondThread
->platformThread().taskRunner()->postTask(FROM_HERE
, threadSafeBind(&checkCurrentIsolate
, AllowCrossThreadAccess(compositorWorker
->isolate()), AllowCrossThreadAccess(checkEvent
.get())));
240 waitForWaitableEventAfterIteratingCurrentLoop(checkEvent
.get());
241 checkWorkerCanExecuteScript(compositorWorker
.get());
243 compositorWorker
->terminateAndWait();
246 // Tests that v8::Isolate and WebThread are correctly set-up if a worker is created while another is terminating.
247 TEST_F(CompositorWorkerManagerTest
, CreatingSecondDuringTerminationOfFirst
)
249 OwnPtr
<WebWaitableEvent
> firstCreationEvent
= adoptPtr(Platform::current()->createWaitableEvent());
250 RefPtr
<TestCompositorWorkerThread
> firstWorker
= createCompositorWorker(firstCreationEvent
.get());
251 waitForWaitableEventAfterIteratingCurrentLoop(firstCreationEvent
.get());
252 v8::Isolate
* firstIsolate
= firstWorker
->isolate();
253 ASSERT(firstIsolate
);
255 // Request termination of the first worker, and set-up to make sure the second worker is created right as
256 // the first worker terminates its isolate.
257 OwnPtr
<WebWaitableEvent
> secondCreationEvent
= adoptPtr(Platform::current()->createWaitableEvent());
258 RefPtr
<CompositorWorkerThread
> secondWorker
;
259 firstWorker
->setCallbackAfterV8Termination(bind(&CompositorWorkerManagerTest::createWorkerAdapter
, this, &secondWorker
, secondCreationEvent
.get()));
260 firstWorker
->terminateAndWait();
261 ASSERT(secondWorker
);
263 waitForWaitableEventAfterIteratingCurrentLoop(secondCreationEvent
.get());
264 v8::Isolate
* secondIsolate
= secondWorker
->isolate();
265 ASSERT(secondIsolate
);
266 EXPECT_EQ(firstIsolate
, secondIsolate
);
268 // Verify that the isolate can run some scripts correctly in the second worker.
269 checkWorkerCanExecuteScript(secondWorker
.get());
270 secondWorker
->terminateAndWait();