Upstream version 10.38.220.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / workers / WorkerThread.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26
27 #include "config.h"
28
29 #include "core/workers/WorkerThread.h"
30
31 #include "bindings/core/v8/ScriptSourceCode.h"
32 #include "core/dom/Microtask.h"
33 #include "core/inspector/InspectorInstrumentation.h"
34 #include "core/inspector/WorkerInspectorController.h"
35 #include "core/workers/DedicatedWorkerGlobalScope.h"
36 #include "core/workers/WorkerClients.h"
37 #include "core/workers/WorkerReportingProxy.h"
38 #include "core/workers/WorkerThreadStartupData.h"
39 #include "platform/PlatformThreadData.h"
40 #include "platform/Task.h"
41 #include "platform/ThreadTimers.h"
42 #include "platform/heap/ThreadState.h"
43 #include "platform/weborigin/KURL.h"
44 #include "public/platform/Platform.h"
45 #include "public/platform/WebThread.h"
46 #include "public/platform/WebWaitableEvent.h"
47 #include "public/platform/WebWorkerRunLoop.h"
48 #include "wtf/Noncopyable.h"
49 #include "wtf/text/WTFString.h"
50
51 #include <utility>
52
53 namespace blink {
54
55 namespace {
56 const int64 kShortIdleHandlerDelayMs = 1000;
57 const int64 kLongIdleHandlerDelayMs = 10*1000;
58
59 class MicrotaskRunner : public WebThread::TaskObserver {
60 public:
61     virtual void willProcessTask() OVERRIDE { }
62     virtual void didProcessTask() OVERRIDE
63     {
64         Microtask::performCheckpoint();
65     }
66 };
67
68 } // namespace
69
70 static Mutex& threadSetMutex()
71 {
72     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
73     return mutex;
74 }
75
76 static HashSet<WorkerThread*>& workerThreads()
77 {
78     DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ());
79     return threads;
80 }
81
82 unsigned WorkerThread::workerThreadCount()
83 {
84     MutexLocker lock(threadSetMutex());
85     return workerThreads().size();
86 }
87
88 class WorkerSharedTimer : public SharedTimer {
89 public:
90     explicit WorkerSharedTimer(WorkerThread* workerThread)
91         : m_workerThread(workerThread)
92         , m_nextFireTime(0.0)
93         , m_running(false)
94     { }
95
96     typedef void (*SharedTimerFunction)();
97     virtual void setFiredFunction(SharedTimerFunction func)
98     {
99         m_sharedTimerFunction = func;
100         if (!m_sharedTimerFunction)
101             m_nextFireTime = 0.0;
102     }
103
104     virtual void setFireInterval(double interval)
105     {
106         ASSERT(m_sharedTimerFunction);
107
108         // See BlinkPlatformImpl::setSharedTimerFireInterval for explanation of
109         // why ceil is used in the interval calculation.
110         int64 delay = static_cast<int64>(ceil(interval * 1000));
111
112         if (delay < 0) {
113             delay = 0;
114             m_nextFireTime = 0.0;
115         }
116
117         m_running = true;
118         m_nextFireTime = currentTime() + interval;
119         m_workerThread->postDelayedTask(createSameThreadTask(&WorkerSharedTimer::OnTimeout, this), delay);
120     }
121
122     virtual void stop()
123     {
124         m_running = false;
125     }
126
127     double nextFireTime() { return m_nextFireTime; }
128
129 private:
130     void OnTimeout()
131     {
132         ASSERT(m_workerThread->workerGlobalScope());
133         if (m_sharedTimerFunction && m_running && !m_workerThread->workerGlobalScope()->isClosing())
134             m_sharedTimerFunction();
135     }
136
137     WorkerThread* m_workerThread;
138     SharedTimerFunction m_sharedTimerFunction;
139     double m_nextFireTime;
140     bool m_running;
141 };
142
143 class WorkerThreadTask : public blink::WebThread::Task {
144     WTF_MAKE_NONCOPYABLE(WorkerThreadTask); WTF_MAKE_FAST_ALLOCATED;
145 public:
146     static PassOwnPtr<WorkerThreadTask> create(const WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask> task, bool isInstrumented)
147     {
148         return adoptPtr(new WorkerThreadTask(workerThread, task, isInstrumented));
149     }
150
151     virtual ~WorkerThreadTask() { }
152
153     virtual void run() OVERRIDE
154     {
155         WorkerGlobalScope* workerGlobalScope = m_workerThread.workerGlobalScope();
156         // Tasks could be put on the message loop after the cleanup task,
157         // ensure none of those are ran.
158         if (!workerGlobalScope)
159             return;
160
161         if (m_isInstrumented)
162             InspectorInstrumentation::willPerformExecutionContextTask(workerGlobalScope, m_task.get());
163         if ((!workerGlobalScope->isClosing() && !m_workerThread.terminated()) || m_task->isCleanupTask())
164             m_task->performTask(workerGlobalScope);
165         if (m_isInstrumented)
166             InspectorInstrumentation::didPerformExecutionContextTask(workerGlobalScope);
167     }
168
169 private:
170     WorkerThreadTask(const WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask> task, bool isInstrumented)
171         : m_workerThread(workerThread)
172         , m_task(task)
173         , m_isInstrumented(isInstrumented)
174     {
175         if (m_isInstrumented)
176             m_isInstrumented = !m_task->taskNameForInstrumentation().isEmpty();
177         if (m_isInstrumented)
178             InspectorInstrumentation::didPostExecutionContextTask(m_workerThread.workerGlobalScope(), m_task.get());
179     }
180
181     const WorkerThread& m_workerThread;
182     OwnPtr<ExecutionContextTask> m_task;
183     bool m_isInstrumented;
184 };
185
186 class RunDebuggerQueueTask FINAL : public ExecutionContextTask {
187 public:
188     static PassOwnPtr<RunDebuggerQueueTask> create(WorkerThread* thread)
189     {
190         return adoptPtr(new RunDebuggerQueueTask(thread));
191     }
192     virtual void performTask(ExecutionContext* context) OVERRIDE
193     {
194         ASSERT(context->isWorkerGlobalScope());
195         m_thread->runDebuggerTask(WorkerThread::DontWaitForMessage);
196     }
197
198 private:
199     explicit RunDebuggerQueueTask(WorkerThread* thread) : m_thread(thread) { }
200
201     WorkerThread* m_thread;
202 };
203
204 WorkerThread::WorkerThread(WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, PassOwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData)
205     : m_terminated(false)
206     , m_workerLoaderProxy(workerLoaderProxy)
207     , m_workerReportingProxy(workerReportingProxy)
208     , m_startupData(startupData)
209     , m_shutdownEvent(adoptPtr(blink::Platform::current()->createWaitableEvent()))
210     , m_terminationEvent(adoptPtr(blink::Platform::current()->createWaitableEvent()))
211 {
212     MutexLocker lock(threadSetMutex());
213     workerThreads().add(this);
214 }
215
216 WorkerThread::~WorkerThread()
217 {
218     MutexLocker lock(threadSetMutex());
219     ASSERT(workerThreads().contains(this));
220     workerThreads().remove(this);
221 }
222
223 void WorkerThread::start()
224 {
225     if (m_thread)
226         return;
227
228     m_thread = adoptPtr(blink::Platform::current()->createThread("WebCore: Worker"));
229     m_thread->postTask(new Task(WTF::bind(&WorkerThread::initialize, this)));
230 }
231
232 void WorkerThread::interruptAndDispatchInspectorCommands()
233 {
234     MutexLocker locker(m_workerInspectorControllerMutex);
235     if (m_workerInspectorController)
236         m_workerInspectorController->interruptAndDispatchInspectorCommands();
237 }
238
239 void WorkerThread::initialize()
240 {
241     KURL scriptURL = m_startupData->m_scriptURL;
242     String sourceCode = m_startupData->m_sourceCode;
243     WorkerThreadStartMode startMode = m_startupData->m_startMode;
244
245     {
246         MutexLocker lock(m_threadCreationMutex);
247
248         // The worker was terminated before the thread had a chance to run.
249         if (m_terminated) {
250             // Notify the proxy that the WorkerGlobalScope has been disposed of.
251             // This can free this thread object, hence it must not be touched afterwards.
252             m_workerReportingProxy.workerThreadTerminated();
253             return;
254         }
255
256         m_microtaskRunner = adoptPtr(new MicrotaskRunner);
257         m_thread->addTaskObserver(m_microtaskRunner.get());
258         m_pendingGCRunner = adoptPtr(new PendingGCRunner);
259         m_messageLoopInterruptor = adoptPtr(new MessageLoopInterruptor(m_thread.get()));
260         m_thread->addTaskObserver(m_pendingGCRunner.get());
261         ThreadState::attach();
262         ThreadState::current()->addInterruptor(m_messageLoopInterruptor.get());
263         m_workerGlobalScope = createWorkerGlobalScope(m_startupData.release());
264
265         m_sharedTimer = adoptPtr(new WorkerSharedTimer(this));
266         PlatformThreadData::current().threadTimers().setSharedTimer(m_sharedTimer.get());
267     }
268
269     // The corresponding call to didStopWorkerRunLoop is in
270     // ~WorkerScriptController.
271     blink::Platform::current()->didStartWorkerRunLoop(blink::WebWorkerRunLoop(this));
272
273     // Notify proxy that a new WorkerGlobalScope has been created and started.
274     m_workerReportingProxy.workerGlobalScopeStarted(m_workerGlobalScope.get());
275
276     WorkerScriptController* script = m_workerGlobalScope->script();
277     if (!script->isExecutionForbidden())
278         script->initializeContextIfNeeded();
279     InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), startMode);
280     script->evaluate(ScriptSourceCode(sourceCode, scriptURL));
281
282     postInitialize();
283
284     postDelayedTask(createSameThreadTask(&WorkerThread::idleHandler, this), kShortIdleHandlerDelayMs);
285 }
286
287 void WorkerThread::cleanup()
288 {
289
290     // This should be called before we start the shutdown procedure.
291     workerReportingProxy().willDestroyWorkerGlobalScope();
292
293     // The below assignment will destroy the context, which will in turn notify messaging proxy.
294     // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them.
295     // If Oilpan is enabled, we detach of the context/global scope, with the final heap cleanup below sweeping it out.
296 #if !ENABLE(OILPAN)
297     ASSERT(m_workerGlobalScope->hasOneRef());
298 #endif
299     m_workerGlobalScope->dispose();
300     m_workerGlobalScope = nullptr;
301
302     ThreadState::current()->removeInterruptor(m_messageLoopInterruptor.get());
303
304     // Detach the ThreadState, cleaning out the thread's heap by
305     // performing a final GC. The cleanup operation will at the end
306     // assert that the heap is empty. If the heap does not become
307     // empty, there are still pointers into the heap and those
308     // pointers will be dangling after thread termination because we
309     // are destroying the heap. It is important to detach while the
310     // thread is still valid. In particular, finalizers for objects in
311     // the heap for this thread will need to access thread local data.
312     ThreadState::detach();
313
314     m_thread->removeTaskObserver(m_microtaskRunner.get());
315     m_microtaskRunner = nullptr;
316     m_thread->removeTaskObserver(m_pendingGCRunner.get());
317     m_pendingGCRunner = nullptr;
318     m_messageLoopInterruptor = nullptr;
319
320     // Notify the proxy that the WorkerGlobalScope has been disposed of.
321     // This can free this thread object, hence it must not be touched afterwards.
322     workerReportingProxy().workerThreadTerminated();
323
324     m_terminationEvent->signal();
325
326     // Clean up PlatformThreadData before WTF::WTFThreadData goes away!
327     PlatformThreadData::current().destroy();
328 }
329
330 class WorkerThreadShutdownFinishTask : public ExecutionContextTask {
331 public:
332     static PassOwnPtr<WorkerThreadShutdownFinishTask> create()
333     {
334         return adoptPtr(new WorkerThreadShutdownFinishTask());
335     }
336
337     virtual void performTask(ExecutionContext *context)
338     {
339         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
340         workerGlobalScope->clearInspector();
341         // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed.
342         workerGlobalScope->clearScript();
343         workerGlobalScope->thread()->m_thread->postTask(new Task(WTF::bind(&WorkerThread::cleanup, workerGlobalScope->thread())));
344     }
345
346     virtual bool isCleanupTask() const { return true; }
347 };
348
349 class WorkerThreadShutdownStartTask : public ExecutionContextTask {
350 public:
351     static PassOwnPtr<WorkerThreadShutdownStartTask> create()
352     {
353         return adoptPtr(new WorkerThreadShutdownStartTask());
354     }
355
356     virtual void performTask(ExecutionContext *context)
357     {
358         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
359         workerGlobalScope->stopFetch();
360         workerGlobalScope->stopActiveDOMObjects();
361         PlatformThreadData::current().threadTimers().setSharedTimer(nullptr);
362
363         // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects,
364         // which become dangling once Heap is destroyed.
365         workerGlobalScope->removeAllEventListeners();
366
367         // Stick a shutdown command at the end of the queue, so that we deal
368         // with all the cleanup tasks the databases post first.
369         workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create());
370     }
371
372     virtual bool isCleanupTask() const { return true; }
373 };
374
375 void WorkerThread::stop()
376 {
377     // Prevent the deadlock between GC and an attempt to stop a thread.
378     ThreadState::SafePointScope safePointScope(ThreadState::HeapPointersOnStack);
379     stopInternal();
380 }
381
382 void WorkerThread::stopInShutdownSequence()
383 {
384     stopInternal();
385 }
386
387 void WorkerThread::stopInternal()
388 {
389     // Protect against this method and initialize() racing each other.
390     MutexLocker lock(m_threadCreationMutex);
391
392     // If stop has already been called, just return.
393     if (m_terminated)
394         return;
395     m_terminated = true;
396
397     // Signal the thread to notify that the thread's stopping.
398     if (m_shutdownEvent)
399         m_shutdownEvent->signal();
400
401     if (!m_workerGlobalScope)
402         return;
403
404     // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever.
405     m_workerGlobalScope->script()->scheduleExecutionTermination();
406     m_workerGlobalScope->wasRequestedToTerminate();
407     InspectorInstrumentation::didKillAllExecutionContextTasks(m_workerGlobalScope.get());
408     m_debuggerMessageQueue.kill();
409     postTask(WorkerThreadShutdownStartTask::create());
410 }
411
412 void WorkerThread::terminateAndWaitForAllWorkers()
413 {
414     // Keep this lock to prevent WorkerThread instances from being destroyed.
415     MutexLocker lock(threadSetMutex());
416     HashSet<WorkerThread*> threads = workerThreads();
417     for (HashSet<WorkerThread*>::iterator itr = threads.begin(); itr != threads.end(); ++itr)
418         (*itr)->stopInShutdownSequence();
419
420     for (HashSet<WorkerThread*>::iterator itr = threads.begin(); itr != threads.end(); ++itr)
421         (*itr)->terminationEvent()->wait();
422 }
423
424 bool WorkerThread::isCurrentThread() const
425 {
426     return m_thread && m_thread->isCurrentThread();
427 }
428
429 void WorkerThread::idleHandler()
430 {
431     ASSERT(m_workerGlobalScope.get());
432     int64 delay = kLongIdleHandlerDelayMs;
433
434     // Do a script engine idle notification if the next event is distant enough.
435     const double kMinIdleTimespan = 0.3;
436     if (m_sharedTimer->nextFireTime() == 0.0 || m_sharedTimer->nextFireTime() > currentTime() + kMinIdleTimespan) {
437         bool hasMoreWork = !m_workerGlobalScope->idleNotification();
438         if (hasMoreWork)
439             delay = kShortIdleHandlerDelayMs;
440     }
441
442     postDelayedTask(createSameThreadTask(&WorkerThread::idleHandler, this), delay);
443 }
444
445 void WorkerThread::postTask(PassOwnPtr<ExecutionContextTask> task)
446 {
447     m_thread->postTask(WorkerThreadTask::create(*this, task, true).leakPtr());
448 }
449
450 void WorkerThread::postDelayedTask(PassOwnPtr<ExecutionContextTask> task, long long delayMs)
451 {
452     m_thread->postDelayedTask(WorkerThreadTask::create(*this, task, true).leakPtr(), delayMs);
453 }
454
455 void WorkerThread::postDebuggerTask(PassOwnPtr<ExecutionContextTask> task)
456 {
457     m_debuggerMessageQueue.append(WorkerThreadTask::create(*this, task, false));
458     postTask(RunDebuggerQueueTask::create(this));
459 }
460
461 MessageQueueWaitResult WorkerThread::runDebuggerTask(WaitMode waitMode)
462 {
463     ASSERT(isCurrentThread());
464     MessageQueueWaitResult result;
465     double absoluteTime = MessageQueue<blink::WebThread::Task>::infiniteTime();
466     OwnPtr<blink::WebThread::Task> task;
467     {
468         if (waitMode == DontWaitForMessage)
469             absoluteTime = 0.0;
470         ThreadState::SafePointScope safePointScope(ThreadState::NoHeapPointersOnStack);
471         task = m_debuggerMessageQueue.waitForMessageWithTimeout(result, absoluteTime);
472     }
473
474     if (result == MessageQueueMessageReceived) {
475         InspectorInstrumentation::willProcessTask(workerGlobalScope());
476         task->run();
477         InspectorInstrumentation::didProcessTask(workerGlobalScope());
478     }
479
480     return result;
481 }
482
483 void WorkerThread::willEnterNestedLoop()
484 {
485     InspectorInstrumentation::willEnterNestedRunLoop(m_workerGlobalScope.get());
486 }
487
488 void WorkerThread::didLeaveNestedLoop()
489 {
490     InspectorInstrumentation::didLeaveNestedRunLoop(m_workerGlobalScope.get());
491 }
492
493 void WorkerThread::setWorkerInspectorController(WorkerInspectorController* workerInspectorController)
494 {
495     MutexLocker locker(m_workerInspectorControllerMutex);
496     m_workerInspectorController = workerInspectorController;
497 }
498
499 } // namespace blink