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

Side by Side 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 unified diff | Download patch
« no previous file with comments | « Source/core/workers/WorkerThread.cpp ('k') | Source/platform/PlatformThreadData.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 "public/platform/WebScheduler.h"
16 #include "wtf/ThreadingPrimitives.h"
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19
20 using testing::_;
21 using testing::Invoke;
22 using testing::Return;
23 using testing::Mock;
24
25 namespace blink {
26
27 namespace {
28 class MockWorkerLoaderProxy : public WorkerLoaderProxy {
29 public:
30 MockWorkerLoaderProxy() : WorkerLoaderProxy(nullptr) { }
31 ~MockWorkerLoaderProxy() override { }
32
33 MOCK_METHOD1(postTaskToLoader, void(PassOwnPtr<ExecutionContextTask>));
34 MOCK_METHOD1(postTaskToWorkerGlobalScope, bool(PassOwnPtr<ExecutionContextTa sk>));
35 };
36
37 class MockWorkerReportingProxy : public WorkerReportingProxy {
38 public:
39 MockWorkerReportingProxy() { }
40 ~MockWorkerReportingProxy() override { }
41
42 MOCK_METHOD5(reportException, void(const String& errorMessage, int lineNumbe r, int columnNumber, const String& sourceURL, int exceptionId));
43 MOCK_METHOD1(reportConsoleMessage, void(PassRefPtrWillBeRawPtr<ConsoleMessag e>));
44 MOCK_METHOD1(postMessageToPageInspector, void(const String&));
45 MOCK_METHOD0(postWorkerConsoleAgentEnabled, void());
46 MOCK_METHOD1(didEvaluateWorkerScript, void(bool success));
47 MOCK_METHOD1(workerGlobalScopeStarted, void(WorkerGlobalScope*));
48 MOCK_METHOD0(workerGlobalScopeClosed, void());
49 MOCK_METHOD0(workerThreadTerminated, void());
50 MOCK_METHOD0(willDestroyWorkerGlobalScope, void());
51 };
52
53 class FakeWorkerGlobalScope : public WorkerGlobalScope {
54 public:
55 typedef WorkerGlobalScope Base;
56
57 FakeWorkerGlobalScope(const KURL& url, const String& userAgent, WorkerThread * thread, const SecurityOrigin* starterOrigin, PassOwnPtrWillBeRawPtr<WorkerClie nts> workerClients)
58 : WorkerGlobalScope(url, userAgent, thread, monotonicallyIncreasingTime( ), starterOrigin, workerClients)
59 {
60 }
61
62 ~FakeWorkerGlobalScope() override
63 {
64 }
65
66 virtual bool isSharedWorkerGlobalScope() const override { return true; }
67
68 // EventTarget
69 virtual const AtomicString& interfaceName() const override
70 {
71 return EventTargetNames::SharedWorkerGlobalScope;
72 }
73
74 virtual void logExceptionToConsole(const String&, int , const String&, int, int, PassRefPtrWillBeRawPtr<ScriptCallStack>) override
75 {
76 }
77
78 // Setters/Getters for attributes in SharedWorkerGlobalScope.idl
79 DEFINE_ATTRIBUTE_EVENT_LISTENER(connect);
80 String name() const { return "FakeWorkerGlobalScope"; }
81
82 DECLARE_VIRTUAL_TRACE();
83 };
84
85 DEFINE_TRACE(FakeWorkerGlobalScope)
86 {
87 WorkerGlobalScope::trace(visitor);
88 }
89
90 class WorkerThreadForTest : public WorkerThread {
91 public:
92 WorkerThreadForTest(
93 WorkerLoaderProxy* mockWorkerLoaderProxy,
94 WorkerReportingProxy& mockWorkerReportingProxy,
95 PassOwnPtrWillBeRawPtr<WorkerThreadStartupData> workerThreadStartupData)
96 : WorkerThread("Test Thread", mockWorkerLoaderProxy, mockWorkerReporting Proxy, workerThreadStartupData)
97 {
98 }
99
100 ~WorkerThreadForTest() override { }
101
102 // NOTE we use the WebSchduler to post tasks in this test because it's not a ffected by the terminated() method, which
103 // tasks posted via the WorkerTrhead directly are.
104 using WorkerThread::schedulerForTesting;
105 using WorkerThread::threadForTesting;
106
107 MOCK_METHOD1(doIdleGc, bool(double deadlineSeconds));
108 MOCK_METHOD0(terminated, bool());
109
110 PassRefPtrWillBeRawPtr<WorkerGlobalScope> createWorkerGlobalScope(PassOwnPtr <WorkerThreadStartupData> startupData) override
111 {
112 return adoptRefWillBeNoop(new FakeWorkerGlobalScope(startupData->m_scrip tURL, startupData->m_userAgent, this, startupData->m_starterOrigin, startupData- >m_workerClients.release()));
113 }
114 };
115
116 class WakeupTask : public WebThread::Task {
117 public:
118 WakeupTask() { }
119
120 ~WakeupTask() override { }
121
122 void run() override { }
123 };
124
125
126 class PostDelayedWakeupTask : public WebThread::Task {
127 public:
128 PostDelayedWakeupTask(WebScheduler* scheduler, long long delay) : m_schedule r(scheduler), m_delay(delay) { }
129
130 ~PostDelayedWakeupTask() override { }
131
132 void run() override
133 {
134 m_scheduler->postTimerTask(FROM_HERE, new WakeupTask(), m_delay);
135 }
136
137 WebScheduler* m_scheduler; // NOT OWNED
138 long long m_delay;
139 };
140
141 class SignalTask : public WebThread::Task {
142 public:
143 SignalTask(ThreadCondition* completion, Mutex* mutex) : m_completion(complet ion), m_mutex(mutex) { }
144
145 ~SignalTask() override { }
146
147 void run() override
148 {
149 WTF::Locker<Mutex> lock(*m_mutex);
150 m_completion->signal();
151 }
152
153 private:
154 ThreadCondition* m_completion; // NOT OWNED
155 Mutex* m_mutex; // NOT OWNED
156 };
157
158 } // namespace
159
160 class WorkerThreadTest : public testing::Test {
161 public:
162 void SetUp() override
163 {
164 m_mockWorkerLoaderProxy = new MockWorkerLoaderProxy();
165 m_mockWorkerReportingProxy = adoptPtr(new MockWorkerReportingProxy());
166 m_securityOrigin = SecurityOrigin::create(KURL(ParsedURLString, "http:// fake.url/"));
167 m_workerThread = adoptRef(new WorkerThreadForTest(
168 m_mockWorkerLoaderProxy.get(),
169 *m_mockWorkerReportingProxy,
170 WorkerThreadStartupData::create(
171 KURL(ParsedURLString, "http://fake.url/"),
172 "fake user agent",
173 "//fake source code",
174 nullptr,
175 DontPauseWorkerGlobalScopeOnStart,
176 "contentSecurityPolicy",
177 ContentSecurityPolicyHeaderTypeReport,
178 m_securityOrigin.get(),
179 WorkerClients::create(),
180 V8CacheOptionsDefault)));
181 ExpectWorkerLifetimeReportingCalls();
182 }
183
184 void StartAndWaitForInit()
185 {
186 ThreadCondition complete;
187 Mutex mutex;
188
189 m_workerThread->start();
190 m_workerThread->threadForTesting()->postTask(FROM_HERE, new SignalTask(& complete, &mutex));
191
192 {
193 WTF::Locker<Mutex> lock(mutex);
194 complete.wait(mutex);
195 }
196 }
197
198 void PostWakeUpTask(long long waitMs)
199 {
200 WebScheduler* scheduler = m_workerThread->schedulerForTesting();
201
202 // The idle task will get posted on an after wake up queue, so we need a nother task
203 // posted at the right time to wake the system up. We don't know the ri ght delay here
204 // since the thread can take a variable length of time to be responsive, however this
205 // isn't a problem when posting a delayed task from within a task on the worker thread.
206 scheduler->postLoadingTask(FROM_HERE, new PostDelayedWakeupTask(schedule r, waitMs));
207 }
208
209 protected:
210 void ExpectWorkerLifetimeReportingCalls()
211 {
212 EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Ti mes(1);
213 EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(true)). Times(1);
214 EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times (1);
215 EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()) .Times(1);
216 }
217
218 RefPtr<SecurityOrigin> m_securityOrigin;
219 RefPtr<MockWorkerLoaderProxy> m_mockWorkerLoaderProxy;
220 OwnPtr<MockWorkerReportingProxy> m_mockWorkerReportingProxy;
221 RefPtr<WorkerThreadForTest> m_workerThread;
222 };
223
224 TEST_F(WorkerThreadTest, GcOccursWhileIdle)
225 {
226 ThreadCondition gcDone;
227 Mutex mutex;
228
229 ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke(
230 [&gcDone, &mutex](double)
231 {
232 WTF::Locker<Mutex> lock(mutex);
233 gcDone.signal();
234 return false;
235 }));
236
237 EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1);
238 EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
239
240 StartAndWaitForInit();
241 PostWakeUpTask(310ul); // 10ms after the quiescent period ends.
242
243 {
244 WTF::Locker<Mutex> lock(mutex);
245 gcDone.wait(mutex);
246 }
247
248 m_workerThread->terminateAndWait();
249 };
250
251 class RepeatingTask : public WebThread::Task {
252 public:
253 RepeatingTask(WebScheduler* scheduler, ThreadCondition* completion, Mutex* m utex)
254 : RepeatingTask(scheduler, completion, mutex, 0) { }
255
256 ~RepeatingTask() override { }
257
258 void run() override
259 {
260 m_taskCount++;
261 if (m_taskCount == 10) {
262 WTF::Locker<Mutex> lock(*m_mutex);
263 m_completion->signal();
264 }
265
266 m_scheduler->postTimerTask(
267 FROM_HERE, new RepeatingTask(m_scheduler, m_completion, m_mutex, m_t askCount), 50ul);
268 m_scheduler->postLoadingTask(FROM_HERE, new WakeupTask());
269
270 }
271
272 private:
273 RepeatingTask(WebScheduler* scheduler, ThreadCondition* completion, Mutex* m utex, int taskCount)
274 : m_scheduler(scheduler)
275 , m_completion(completion)
276 , m_mutex(mutex)
277 , m_taskCount(taskCount)
278 { }
279
280 WebScheduler* m_scheduler; // NOT OWNED
281 ThreadCondition* m_completion;
282 Mutex* m_mutex;
283 int m_taskCount;
284 };
285
286 TEST_F(WorkerThreadTest, GcDoesNotOccurIfGapBetweenDelayedTasksIsTooSmall)
287 {
288 ThreadCondition completion;
289 Mutex mutex;
290
291 EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(0);
292 EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
293
294 StartAndWaitForInit();
295
296 WebScheduler* scheduler = m_workerThread->schedulerForTesting();
297
298 // Post a repeating task that should prevent any GC from happening.
299 scheduler->postLoadingTask(FROM_HERE, new RepeatingTask(scheduler, &completi on, &mutex));
300
301 {
302 WTF::Locker<Mutex> lock(mutex);
303 completion.wait(mutex);
304 }
305
306 // Make sure doIdleGc has not been called by this stage.
307 Mock::VerifyAndClearExpectations(m_workerThread.get());
308
309 EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
310 m_workerThread->terminateAndWait();
311 }
312
313 TEST_F(WorkerThreadTest, LongGcDeadline_NoFutureTasks)
314 {
315 ThreadCondition gcDone;
316 Mutex mutex;
317 double deadlineLength = 0;
318
319 ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke(
320 [&gcDone, &mutex, &deadlineLength](double deadline)
321 {
322 WTF::Locker<Mutex> lock(mutex);
323 gcDone.signal();
324 deadlineLength = deadline -Platform::current()->monotonicallyIncreas ingTime();
325 return false;
326 }));
327
328 EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1);
329 EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
330
331 StartAndWaitForInit();
332 PostWakeUpTask(310ul);
333
334 {
335 WTF::Locker<Mutex> lock(mutex);
336 gcDone.wait(mutex);
337
338 // The deadline should be close to 1s in duration if there are no tasks that need to run soon.
339 EXPECT_GT(deadlineLength, 0.9);
340 }
341
342 m_workerThread->terminateAndWait();
343 }
344
345 TEST_F(WorkerThreadTest, LongGcDeadline_NextTaskAfterIdlePeriod)
346 {
347 ThreadCondition gcDone;
348 Mutex mutex;
349 double deadlineLength = 0;
350
351 ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke(
352 [&gcDone, &mutex, &deadlineLength](double deadline)
353 {
354 WTF::Locker<Mutex> lock(mutex);
355 gcDone.signal();
356 deadlineLength = deadline -Platform::current()->monotonicallyIncreas ingTime();
357 return false;
358 }));
359
360 EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1);
361 EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
362
363 StartAndWaitForInit();
364 PostWakeUpTask(310ul);
365 PostWakeUpTask(675ul); // Task that runs shortly after the 50ms idle period ends.
366
367 {
368 WTF::Locker<Mutex> lock(mutex);
369 gcDone.wait(mutex);
370
371 // The worker thread calls canExceedIdleDeadlineIfRequired which only co nsiders if
372 // there are any delayed tasks scheduled for the current long idle perio d. Since the
373 // next task is in the following idle period, a long gc deadline is allo wed.
374 EXPECT_GT(deadlineLength, 0.9);
375 }
376
377 m_workerThread->terminateAndWait();
378 }
379
380 TEST_F(WorkerThreadTest, ShortGcDeadline)
381 {
382 ThreadCondition gcDone;
383 Mutex mutex;
384 double deadlineLength = 0;
385
386 ON_CALL(*m_workerThread, doIdleGc(_)).WillByDefault(Invoke(
387 [&gcDone, &mutex, &deadlineLength](double deadline)
388 {
389 WTF::Locker<Mutex> lock(mutex);
390 gcDone.signal();
391 deadlineLength = deadline -Platform::current()->monotonicallyIncreas ingTime();
392 return false;
393 }));
394
395 EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(1);
396 EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(false));
397
398 StartAndWaitForInit();
399 PostWakeUpTask(310ul);
400 PostWakeUpTask(625ul); // Task that runs during the idle period.
401
402 {
403 WTF::Locker<Mutex> lock(mutex);
404 gcDone.wait(mutex);
405
406 // The deadline should be < 50ms if there's a task that needs to run dur ing the idle period.
407 EXPECT_LT(deadlineLength, 0.025);
408 }
409
410 m_workerThread->terminateAndWait();
411 }
412
413 class PostDelayedSignalTask : public WebThread::Task {
414 public:
415 PostDelayedSignalTask(WebScheduler* scheduler, ThreadCondition* completion, Mutex* mutex, long long delay)
416 : m_scheduler(scheduler)
417 , m_completion(completion)
418 , m_mutex(mutex)
419 , m_delay(delay) { }
420
421 ~PostDelayedSignalTask() override { }
422
423 void run() override
424 {
425 m_scheduler->postTimerTask(FROM_HERE, new SignalTask(m_completion, m_mut ex), m_delay);
426 }
427
428 WebScheduler* m_scheduler; // NOT OWNED
429 ThreadCondition* m_completion; // NOT OWNED
430 Mutex* m_mutex; // NOT OWNED
431 long long m_delay;
432 };
433
434 TEST_F(WorkerThreadTest, TerminationPreventsGc)
435 {
436 ThreadCondition completion;
437 Mutex mutex;
438
439 EXPECT_CALL(*m_workerThread, doIdleGc(_)).Times(0);
440 EXPECT_CALL(*m_workerThread, terminated()).WillRepeatedly(Return(true));
441
442 StartAndWaitForInit();
443 PostWakeUpTask(310ul); // 10ms after the quiescent period ends.
444
445 // Give pleanty of time for the GC to run before exiting.
446 WebScheduler* scheduler = m_workerThread->schedulerForTesting();
447 scheduler->postLoadingTask(FROM_HERE, new PostDelayedSignalTask(scheduler, & completion, &mutex, 700ul));
448
449 {
450 WTF::Locker<Mutex> lock(mutex);
451 completion.wait(mutex);
452 }
453
454 m_workerThread->terminateAndWait();
455 };
456
457 } // namespace blink
OLDNEW
« 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