Chromium Code Reviews| 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 |