Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / workers / WorkerRunLoop.cpp
1 /*
2  * Copyright (C) 2009 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "core/workers/WorkerRunLoop.h"
33
34 #include "core/inspector/InspectorInstrumentation.h"
35 #include "core/workers/WorkerGlobalScope.h"
36 #include "core/workers/WorkerThread.h"
37 #include "heap/ThreadState.h"
38 #include "platform/PlatformThreadData.h"
39 #include "platform/SharedTimer.h"
40 #include "platform/ThreadTimers.h"
41 #include "wtf/CurrentTime.h"
42
43 namespace WebCore {
44
45 class WorkerRunLoopTask : public blink::WebThread::Task {
46     WTF_MAKE_NONCOPYABLE(WorkerRunLoopTask); WTF_MAKE_FAST_ALLOCATED;
47 public:
48     static PassOwnPtr<WorkerRunLoopTask> create(const WorkerRunLoop& runLoop, PassOwnPtr<ExecutionContextTask> task)
49     {
50         return adoptPtr(new WorkerRunLoopTask(runLoop, task));
51     }
52
53     virtual ~WorkerRunLoopTask() { }
54
55     virtual void run() OVERRIDE
56     {
57         WorkerGlobalScope* workerGlobalScope = m_runLoop.context();
58         if ((!workerGlobalScope->isClosing() && !m_runLoop.terminated()) || m_task->isCleanupTask())
59             m_task->performTask(workerGlobalScope);
60     }
61
62 private:
63     WorkerRunLoopTask(const WorkerRunLoop& runLoop, PassOwnPtr<ExecutionContextTask> task)
64         : m_runLoop(runLoop)
65         , m_task(task)
66     {
67     }
68
69     const WorkerRunLoop& m_runLoop;
70     OwnPtr<ExecutionContextTask> m_task;
71 };
72
73 class TickleDebuggerQueueTask FINAL : public ExecutionContextTask {
74 public:
75     static PassOwnPtr<TickleDebuggerQueueTask> create(WorkerRunLoop* loop)
76     {
77         return adoptPtr(new TickleDebuggerQueueTask(loop));
78     }
79     virtual void performTask(ExecutionContext* context) OVERRIDE
80     {
81         ASSERT(context->isWorkerGlobalScope());
82         m_loop->runDebuggerTask(WorkerRunLoop::DontWaitForMessage);
83     }
84
85 private:
86     explicit TickleDebuggerQueueTask(WorkerRunLoop* loop) : m_loop(loop) { }
87
88     WorkerRunLoop* m_loop;
89 };
90
91 class WorkerSharedTimer : public SharedTimer {
92 public:
93     WorkerSharedTimer()
94         : m_sharedTimerFunction(0)
95         , m_nextFireTime(0)
96     {
97     }
98
99     // SharedTimer interface.
100     virtual void setFiredFunction(void (*function)()) { m_sharedTimerFunction = function; }
101     virtual void setFireInterval(double interval) { m_nextFireTime = interval + currentTime(); }
102     virtual void stop() { m_nextFireTime = 0; }
103
104     bool isActive() { return m_sharedTimerFunction && m_nextFireTime; }
105     double fireTime() { return m_nextFireTime; }
106     void fire() { m_sharedTimerFunction(); }
107
108 private:
109     void (*m_sharedTimerFunction)();
110     double m_nextFireTime;
111 };
112
113 WorkerRunLoop::WorkerRunLoop()
114     : m_sharedTimer(adoptPtr(new WorkerSharedTimer))
115     , m_context(0)
116     , m_nestedCount(0)
117 {
118 }
119
120 WorkerRunLoop::~WorkerRunLoop()
121 {
122     ASSERT(!m_nestedCount);
123 }
124
125 class RunLoopSetup {
126     WTF_MAKE_NONCOPYABLE(RunLoopSetup);
127 public:
128     RunLoopSetup(WorkerRunLoop& runLoop, WorkerGlobalScope* context)
129         : m_runLoop(runLoop)
130         , m_context(context)
131     {
132         if (!m_runLoop.m_nestedCount)
133             PlatformThreadData::current().threadTimers().setSharedTimer(m_runLoop.m_sharedTimer.get());
134         m_runLoop.m_nestedCount++;
135         InspectorInstrumentation::willEnterNestedRunLoop(m_context);
136     }
137
138     ~RunLoopSetup()
139     {
140         m_runLoop.m_nestedCount--;
141         if (!m_runLoop.m_nestedCount)
142             PlatformThreadData::current().threadTimers().setSharedTimer(0);
143         InspectorInstrumentation::didLeaveNestedRunLoop(m_context);
144     }
145 private:
146     WorkerRunLoop& m_runLoop;
147     WorkerGlobalScope* m_context;
148 };
149
150 void WorkerRunLoop::setWorkerGlobalScope(WorkerGlobalScope* context)
151 {
152     ASSERT(!m_context);
153     ASSERT(context);
154     m_context = context;
155 }
156
157 void WorkerRunLoop::run()
158 {
159     ASSERT(m_context);
160     RunLoopSetup setup(*this, m_context);
161     MessageQueueWaitResult result;
162     do {
163         ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack);
164         result = run(m_messageQueue, WaitForMessage);
165     } while (result != MessageQueueTerminated);
166     runCleanupTasks();
167 }
168
169 MessageQueueWaitResult WorkerRunLoop::runDebuggerTask(WaitMode waitMode)
170 {
171     ASSERT(m_context);
172     RunLoopSetup setup(*this, m_context);
173     return run(m_debuggerMessageQueue, waitMode);
174 }
175
176 MessageQueueWaitResult WorkerRunLoop::run(MessageQueue<blink::WebThread::Task>& queue, WaitMode waitMode)
177 {
178     ASSERT(m_context);
179     ASSERT(m_context->thread());
180     ASSERT(m_context->thread()->isCurrentThread());
181
182     bool nextTimeoutEventIsIdleWatchdog;
183     MessageQueueWaitResult result;
184     OwnPtr<blink::WebThread::Task> task;
185     do {
186         double absoluteTime = 0.0;
187         nextTimeoutEventIsIdleWatchdog = false;
188         if (waitMode == WaitForMessage) {
189             absoluteTime = m_sharedTimer->isActive() ? m_sharedTimer->fireTime() : MessageQueue<blink::WebThread::Task>::infiniteTime();
190
191             // Do a script engine idle notification if the next event is distant enough.
192             const double kMinIdleTimespan = 0.3; // seconds
193             if (queue.isEmpty() && absoluteTime > currentTime() + kMinIdleTimespan) {
194                 bool hasMoreWork = !m_context->idleNotification();
195                 if (hasMoreWork) {
196                     // Schedule a watchdog, so if there are no events within a particular time interval
197                     // idle notifications won't stop firing.
198                     const double kWatchdogInterval = 3; // seconds
199                     double nextWatchdogTime = currentTime() + kWatchdogInterval;
200                     if (absoluteTime > nextWatchdogTime) {
201                         absoluteTime = nextWatchdogTime;
202                         nextTimeoutEventIsIdleWatchdog = true;
203                     }
204                 }
205             }
206         }
207
208         {
209             ThreadState::SafePointScope safePointScope(ThreadState::NoHeapPointersOnStack);
210             task = queue.waitForMessageWithTimeout(result, absoluteTime);
211         }
212     } while (result == MessageQueueTimeout && nextTimeoutEventIsIdleWatchdog);
213
214     // If the context is closing, don't execute any further JavaScript tasks (per section 4.1.1 of the Web Workers spec).
215     // However, there may be implementation cleanup tasks in the queue, so keep running through it.
216
217     switch (result) {
218     case MessageQueueTerminated:
219         break;
220
221     case MessageQueueMessageReceived:
222         InspectorInstrumentation::willProcessTask(m_context);
223         task->run();
224         InspectorInstrumentation::didProcessTask(m_context);
225         break;
226
227     case MessageQueueTimeout:
228         if (!m_context->isClosing())
229             m_sharedTimer->fire();
230         break;
231     }
232
233     return result;
234 }
235
236 void WorkerRunLoop::runCleanupTasks()
237 {
238     ASSERT(m_context);
239     ASSERT(m_context->thread());
240     ASSERT(m_context->thread()->isCurrentThread());
241     ASSERT(m_messageQueue.killed());
242     ASSERT(m_debuggerMessageQueue.killed());
243
244     while (true) {
245         OwnPtr<blink::WebThread::Task> task = m_debuggerMessageQueue.tryGetMessageIgnoringKilled();
246         if (!task)
247             task = m_messageQueue.tryGetMessageIgnoringKilled();
248         if (!task)
249             return;
250         task->run();
251     }
252 }
253
254 void WorkerRunLoop::terminate()
255 {
256     m_messageQueue.kill();
257     m_debuggerMessageQueue.kill();
258 }
259
260 bool WorkerRunLoop::postTask(PassOwnPtr<ExecutionContextTask> task)
261 {
262     return m_messageQueue.append(WorkerRunLoopTask::create(*this, task));
263 }
264
265 void WorkerRunLoop::postTaskAndTerminate(PassOwnPtr<ExecutionContextTask> task)
266 {
267     m_debuggerMessageQueue.kill();
268     m_messageQueue.appendAndKill(WorkerRunLoopTask::create(*this, task));
269 }
270
271 bool WorkerRunLoop::postDebuggerTask(PassOwnPtr<ExecutionContextTask> task)
272 {
273     bool posted = m_debuggerMessageQueue.append(WorkerRunLoopTask::create(*this, task));
274     if (posted)
275         postTask(TickleDebuggerQueueTask::create(this));
276     return posted;
277 }
278
279 } // namespace WebCore