OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "config.h" | |
6 #include "core/workers/WorkerThread.h" | |
7 | |
8 #include "bindings/core/v8/ScriptWrappable.h" | |
9 #include "core/inspector/ConsoleMessage.h" | |
10 #include "core/workers/SharedWorkerGlobalScope.h" | |
11 #include "core/workers/SharedWorkerThread.h" | |
12 #include "core/workers/WorkerGlobalScope.h" | |
13 #include "core/workers/WorkerReportingProxy.h" | |
14 #include "core/workers/WorkerThreadStartupData.h" | |
15 #include <gmock/gmock.h> | |
16 #include <gtest/gtest.h> | |
17 | |
18 using testing::_; | |
19 using testing::Invoke; | |
20 using testing::Return; | |
21 using testing::Mock; | |
22 | |
23 namespace blink { | |
24 | |
25 namespace { | |
26 class MockWorkerLoaderProxy : public WorkerLoaderProxy { | |
27 public: | |
28 MockWorkerLoaderProxy() : WorkerLoaderProxy(nullptr) { } | |
29 ~MockWorkerLoaderProxy() override { } | |
30 | |
31 MOCK_METHOD1(postTaskToLoader, void(PassOwnPtr<ExecutionContextTask>)); | |
32 MOCK_METHOD1(postTaskToWorkerGlobalScope, bool(PassOwnPtr<ExecutionContextTa sk>)); | |
33 }; | |
34 | |
35 class MockWorkerReportingProxy : public WorkerReportingProxy { | |
36 public: | |
37 MockWorkerReportingProxy() { } | |
38 ~MockWorkerReportingProxy() override { } | |
39 | |
40 MOCK_METHOD5(reportException, void(const String& errorMessage, int lineNumbe r, int columnNumber, const String& sourceURL, int exceptionId)); | |
41 MOCK_METHOD1(reportConsoleMessage, void(PassRefPtrWillBeRawPtr<ConsoleMessag e>)); | |
42 MOCK_METHOD1(postMessageToPageInspector, void(const String&)); | |
43 MOCK_METHOD0(postWorkerConsoleAgentEnabled, void()); | |
44 MOCK_METHOD1(didEvaluateWorkerScript, void(bool success)); | |
45 MOCK_METHOD1(workerGlobalScopeStarted, void(WorkerGlobalScope*)); | |
46 MOCK_METHOD0(workerGlobalScopeClosed, void()); | |
47 MOCK_METHOD0(workerThreadTerminated, void()); | |
48 MOCK_METHOD0(willDestroyWorkerGlobalScope, void()); | |
49 }; | |
50 | |
51 class FakeWorkerGlobalScope : public WorkerGlobalScope { | |
52 public: | |
53 typedef WorkerGlobalScope Base; | |
54 | |
55 FakeWorkerGlobalScope(const KURL& url, const String& userAgent, WorkerThread * thread, const SecurityOrigin* starterOrigin, PassOwnPtrWillBeRawPtr<WorkerClie nts> workerClients) | |
56 : WorkerGlobalScope(url, userAgent, thread, monotonicallyIncreasingTime( ), starterOrigin, workerClients) | |
57 { | |
58 } | |
59 | |
60 ~FakeWorkerGlobalScope() override | |
61 { | |
62 } | |
63 | |
64 virtual bool isSharedWorkerGlobalScope() const override { return true; } | |
65 | |
66 // EventTarget | |
67 virtual const AtomicString& interfaceName() const override | |
68 { | |
69 return EventTargetNames::SharedWorkerGlobalScope; | |
70 } | |
71 | |
72 virtual void logExceptionToConsole(const String&, int , const String&, int, int, PassRefPtrWillBeRawPtr<ScriptCallStack>) override | |
73 { | |
74 } | |
75 | |
76 // Setters/Getters for attributes in SharedWorkerGlobalScope.idl | |
77 DEFINE_ATTRIBUTE_EVENT_LISTENER(connect); | |
78 String name() const { return "FakeWorkerGlobalScope"; } | |
79 | |
80 DECLARE_VIRTUAL_TRACE(); | |
81 }; | |
82 | |
83 DEFINE_TRACE(FakeWorkerGlobalScope) | |
84 { | |
85 WorkerGlobalScope::trace(visitor); | |
86 } | |
87 | |
88 class WorkerThreadForTest : public WorkerThread { | |
89 public: | |
90 WorkerThreadForTest( | |
91 WorkerLoaderProxy* mockWorkerLoaderProxy, | |
92 WorkerReportingProxy& mockWorkerReportingProxy, | |
93 PassOwnPtrWillBeRawPtr<WorkerThreadStartupData> workerThreadStartupData) | |
94 : WorkerThread("Test Thread", mockWorkerLoaderProxy, mockWorkerReporting Proxy, workerThreadStartupData) | |
95 { | |
96 } | |
97 | |
98 ~WorkerThreadForTest() override { } | |
99 | |
100 using WorkerThread::threadForTesting; | |
101 | |
102 MOCK_METHOD1(doIdleGc, bool(double deadlineSeconds)); | |
103 | |
104 PassRefPtrWillBeRawPtr<WorkerGlobalScope> createWorkerGlobalScope(PassOwnPtr <WorkerThreadStartupData> startupData) override | |
105 { | |
106 return adoptRefWillBeNoop(new FakeWorkerGlobalScope(startupData->m_scrip tURL, startupData->m_userAgent, this, startupData->m_starterOrigin, startupData- >m_workerClients.release())); | |
107 } | |
108 }; | |
109 | |
110 class WakeupTask : public WebThread::Task { | |
111 public: | |
112 WakeupTask() { } | |
113 | |
114 ~WakeupTask() override { } | |
115 | |
116 void run() override { } | |
117 }; | |
118 | |
119 | |
120 class PostDelayedWakeupTask : public WebThread::Task { | |
121 public: | |
122 PostDelayedWakeupTask(WebThreadSupportingGC* gc, long long delay) : m_gc(gc) , m_delay(delay) { } | |
123 | |
124 ~PostDelayedWakeupTask() override { } | |
125 | |
126 void run() override | |
127 { | |
128 m_gc->postDelayedTask(FROM_HERE, new WakeupTask(), m_delay); | |
129 } | |
130 | |
131 WebThreadSupportingGC* m_gc; | |
132 long long m_delay; | |
133 }; | |
134 | |
135 } // namespace | |
136 | |
137 class WorkerThreadTest : public testing::Test { | |
138 public: | |
139 void SetUp() override | |
140 { | |
141 m_mockWorkerLoaderProxy = new MockWorkerLoaderProxy(); | |
142 m_mockWorkerReportingProxy = adoptPtr(new MockWorkerReportingProxy()); | |
143 m_securityOrigin = SecurityOrigin::create(KURL(ParsedURLString, "http:// fake.url/")); | |
144 m_workerThread = adoptRef(new WorkerThreadForTest( | |
145 m_mockWorkerLoaderProxy.get(), | |
146 *m_mockWorkerReportingProxy, | |
147 WorkerThreadStartupData::create( | |
148 KURL(ParsedURLString, "http://fake.url/"), | |
149 "fake user agent", | |
150 "//fake source code", | |
151 nullptr, | |
152 DontPauseWorkerGlobalScopeOnStart, | |
153 "contentSecurityPolicy", | |
154 ContentSecurityPolicyHeaderTypeReport, | |
155 m_securityOrigin.get(), | |
156 WorkerClients::create(), | |
157 V8CacheOptionsDefault))); | |
158 ExpectWorkerLifetimeReportingCalls(); | |
159 } | |
160 | |
161 void PostWakeUpTask(long long waitMs) | |
162 { | |
163 WebThreadSupportingGC* thread = m_workerThread->threadForTesting(); | |
164 | |
165 // The idle task will get posted on an after wake up queue, so we need a nother task | |
166 // posted at the right time to wake the system up. We don't know the ri ght delay here | |
167 // since the thread can take a variable length of time to be responsive, however this | |
168 // isn't a problem when posting a delayed task from within a task on the worker thread. | |
169 thread->postTask(FROM_HERE, new PostDelayedWakeupTask(thread, waitMs)); | |
170 } | |
171 | |
172 protected: | |
173 void ExpectWorkerLifetimeReportingCalls() | |
174 { | |
175 EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Ti mes(1); | |
176 EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(true)). Times(1); | |
177 EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times (1); | |
178 EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()) .Times(1); | |
179 } | |
180 | |
181 RefPtr<SecurityOrigin> m_securityOrigin; | |
182 RefPtr<MockWorkerLoaderProxy> m_mockWorkerLoaderProxy; | |
183 OwnPtr<MockWorkerReportingProxy> m_mockWorkerReportingProxy; | |
184 RefPtr<WorkerThreadForTest> m_workerThread; | |
185 }; | |
186 | |
187 TEST_F(WorkerThreadTest, GcOccursWhileIdle) | |
188 { | |
189 bool gcDone = false; | |
Sami
2015/04/16 14:23:38
Should this be some kind of thread safe primitive
alex clarke (OOO till 29th)
2015/04/16 17:20:01
I added some mutexes and a ThreadCondition
| |
190 | |
191 ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke( | |
192 [&gcDone](double) | |
193 { | |
194 gcDone = true; | |
195 return false; | |
196 })); | |
197 | |
198 EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1); | |
199 | |
200 m_workerThread->start(); | |
201 | |
202 PostWakeUpTask(310ul); // 10ms after the quiescent period ends. | |
203 | |
204 while (!gcDone) | |
205 Platform::current()->yieldCurrentThread(); | |
206 | |
207 m_workerThread->terminateAndWait(); | |
208 }; | |
209 | |
210 class RepeatingTask : public WebThread::Task { | |
211 public: | |
212 RepeatingTask(WebThreadSupportingGC* thread, int* taskCount) | |
213 : m_thread(thread) | |
214 , m_taskCount(taskCount) | |
215 { } | |
216 | |
217 ~RepeatingTask() override { } | |
218 | |
219 void run() override | |
220 { | |
221 (*m_taskCount)++; | |
222 m_thread->postDelayedTask(FROM_HERE, new RepeatingTask(m_thread, m_taskC ount), 50ul); | |
223 m_thread->postTask(FROM_HERE, new WakeupTask()); | |
224 } | |
225 | |
226 private: | |
227 WebThreadSupportingGC* m_thread; // NOT OWNED | |
228 int* m_taskCount; | |
229 }; | |
230 | |
231 TEST_F(WorkerThreadTest, GcDoesNotOccurIfGapBetweenDelayedTasksIsTooSmall) | |
232 { | |
233 EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(0); | |
234 | |
235 m_workerThread->start(); | |
236 | |
237 WebThreadSupportingGC* thread = m_workerThread->threadForTesting(); | |
238 | |
239 // Post a repeating task that should prevent any GC from happening. | |
240 int taskCount = 0; | |
241 thread->postTask(FROM_HERE, new RepeatingTask(thread, &taskCount)); | |
242 | |
243 while (taskCount < 10) | |
Sami
2015/04/16 14:23:38
Is this thread safe?
alex clarke (OOO till 29th)
2015/04/16 17:20:01
Done.
| |
244 Platform::current()->yieldCurrentThread(); | |
245 | |
246 // Make sure doIdleGc has not been called by this stage. | |
247 Mock::VerifyAndClearExpectations(m_workerThread.get()); | |
248 m_workerThread->terminateAndWait(); | |
249 } | |
250 | |
251 TEST_F(WorkerThreadTest, LongGcDeadline) | |
252 { | |
253 bool gcDone = false; | |
254 double deadlineLength = 0; | |
255 | |
256 ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke( | |
257 [&gcDone, &deadlineLength](double deadline) | |
258 { | |
259 deadlineLength = deadline -Platform::current()->monotonicallyIncreas ingTime(); | |
260 gcDone = true; | |
261 return false; | |
262 })); | |
263 | |
264 EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1); | |
265 | |
266 m_workerThread->start(); | |
267 | |
268 PostWakeUpTask(310ul); | |
269 | |
270 while (!gcDone) | |
271 Platform::current()->yieldCurrentThread(); | |
272 | |
273 // The deadline should be close to 1s in duration if there are no tasks that need to run soon. | |
274 EXPECT_GT(deadlineLength, 0.9); | |
275 | |
276 m_workerThread->terminateAndWait(); | |
277 } | |
278 | |
279 TEST_F(WorkerThreadTest, ShortGcDeadline) | |
280 { | |
281 bool gcDone = false; | |
282 double deadlineLength = 0; | |
283 | |
284 ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke( | |
285 [&gcDone, &deadlineLength](double deadline) | |
286 { | |
287 deadlineLength = deadline -Platform::current()->monotonicallyIncreas ingTime(); | |
288 gcDone = true; | |
289 return false; | |
290 })); | |
291 | |
292 EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1); | |
293 | |
294 m_workerThread->start(); | |
295 | |
296 PostWakeUpTask(310ul); | |
297 PostWakeUpTask(650ul); // Task that run shortly after the idle period starts . | |
298 | |
299 while (!gcDone) | |
300 Platform::current()->yieldCurrentThread(); | |
301 | |
302 // The deadlin should be short if there's a task that needs to run shortly a fter the idle period starts. | |
303 EXPECT_LT(deadlineLength, 0.05); | |
304 | |
305 m_workerThread->terminateAndWait(); | |
306 } | |
307 | |
308 } // namespace blink | |
OLD | NEW |