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