Home | History | Annotate | Download | only in workers
      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 "platform/PlatformThreadData.h"
     38 #include "platform/SharedTimer.h"
     39 #include "platform/ThreadTimers.h"
     40 #include "platform/heap/ThreadState.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 isDebuggerQueue = (&queue == &m_debuggerMessageQueue);
    183     bool nextTimeoutEventIsIdleWatchdog;
    184     MessageQueueWaitResult result;
    185     OwnPtr<blink::WebThread::Task> task;
    186     do {
    187         double absoluteTime = 0.0;
    188         nextTimeoutEventIsIdleWatchdog = false;
    189         if (waitMode == WaitForMessage) {
    190             absoluteTime = !isDebuggerQueue && m_sharedTimer->isActive() ? m_sharedTimer->fireTime() : MessageQueue<blink::WebThread::Task>::infiniteTime();
    191 
    192             // Do a script engine idle notification if the next event is distant enough.
    193             const double kMinIdleTimespan = 0.3; // seconds
    194             if (queue.isEmpty() && absoluteTime > currentTime() + kMinIdleTimespan) {
    195                 bool hasMoreWork = !m_context->idleNotification();
    196                 if (hasMoreWork) {
    197                     // Schedule a watchdog, so if there are no events within a particular time interval
    198                     // idle notifications won't stop firing.
    199                     const double kWatchdogInterval = 3; // seconds
    200                     double nextWatchdogTime = currentTime() + kWatchdogInterval;
    201                     if (absoluteTime > nextWatchdogTime) {
    202                         absoluteTime = nextWatchdogTime;
    203                         nextTimeoutEventIsIdleWatchdog = true;
    204                     }
    205                 }
    206             }
    207         }
    208 
    209         {
    210             ThreadState::SafePointScope safePointScope(ThreadState::NoHeapPointersOnStack);
    211             task = queue.waitForMessageWithTimeout(result, absoluteTime);
    212         }
    213     } while (result == MessageQueueTimeout && nextTimeoutEventIsIdleWatchdog);
    214 
    215     // If the context is closing, don't execute any further JavaScript tasks (per section 4.1.1 of the Web Workers spec).
    216     // However, there may be implementation cleanup tasks in the queue, so keep running through it.
    217 
    218     switch (result) {
    219     case MessageQueueTerminated:
    220         break;
    221 
    222     case MessageQueueMessageReceived:
    223         InspectorInstrumentation::willProcessTask(m_context);
    224         task->run();
    225         InspectorInstrumentation::didProcessTask(m_context);
    226         break;
    227 
    228     case MessageQueueTimeout:
    229         ASSERT(!isDebuggerQueue || waitMode != WaitForMessage);
    230         if (!m_context->isClosing())
    231             m_sharedTimer->fire();
    232         break;
    233     }
    234 
    235     return result;
    236 }
    237 
    238 void WorkerRunLoop::runCleanupTasks()
    239 {
    240     ASSERT(m_context);
    241     ASSERT(m_context->thread());
    242     ASSERT(m_context->thread()->isCurrentThread());
    243     ASSERT(m_messageQueue.killed());
    244     ASSERT(m_debuggerMessageQueue.killed());
    245 
    246     while (true) {
    247         OwnPtr<blink::WebThread::Task> task = m_debuggerMessageQueue.tryGetMessageIgnoringKilled();
    248         if (!task)
    249             task = m_messageQueue.tryGetMessageIgnoringKilled();
    250         if (!task)
    251             return;
    252         task->run();
    253     }
    254 }
    255 
    256 void WorkerRunLoop::terminate()
    257 {
    258     m_messageQueue.kill();
    259     m_debuggerMessageQueue.kill();
    260 }
    261 
    262 bool WorkerRunLoop::postTask(PassOwnPtr<ExecutionContextTask> task)
    263 {
    264     return m_messageQueue.append(WorkerRunLoopTask::create(*this, task));
    265 }
    266 
    267 void WorkerRunLoop::postTaskAndTerminate(PassOwnPtr<ExecutionContextTask> task)
    268 {
    269     m_debuggerMessageQueue.kill();
    270     m_messageQueue.appendAndKill(WorkerRunLoopTask::create(*this, task));
    271 }
    272 
    273 bool WorkerRunLoop::postDebuggerTask(PassOwnPtr<ExecutionContextTask> task)
    274 {
    275     bool posted = m_debuggerMessageQueue.append(WorkerRunLoopTask::create(*this, task));
    276     if (posted)
    277         postTask(TickleDebuggerQueueTask::create(this));
    278     return posted;
    279 }
    280 
    281 } // namespace WebCore
    282