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 "wtf/CurrentTime.h"
     41 
     42 namespace WebCore {
     43 
     44 class WorkerRunLoop::Task {
     45     WTF_MAKE_NONCOPYABLE(Task); WTF_MAKE_FAST_ALLOCATED;
     46 public:
     47     static PassOwnPtr<Task> create(PassOwnPtr<ExecutionContextTask> task, const String& mode)
     48     {
     49         return adoptPtr(new Task(task, mode));
     50     }
     51     const String& mode() const { return m_mode; }
     52     void performTask(const WorkerRunLoop& runLoop, ExecutionContext* context)
     53     {
     54         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
     55         if ((!workerGlobalScope->isClosing() && !runLoop.terminated()) || m_task->isCleanupTask())
     56             m_task->performTask(context);
     57     }
     58 
     59 private:
     60     Task(PassOwnPtr<ExecutionContextTask> task, const String& mode)
     61         : m_task(task)
     62         , m_mode(mode.isolatedCopy())
     63     {
     64     }
     65 
     66     OwnPtr<ExecutionContextTask> m_task;
     67     String m_mode;
     68 };
     69 
     70 class WorkerSharedTimer : public SharedTimer {
     71 public:
     72     WorkerSharedTimer()
     73         : m_sharedTimerFunction(0)
     74         , m_nextFireTime(0)
     75     {
     76     }
     77 
     78     // SharedTimer interface.
     79     virtual void setFiredFunction(void (*function)()) { m_sharedTimerFunction = function; }
     80     virtual void setFireInterval(double interval) { m_nextFireTime = interval + currentTime(); }
     81     virtual void stop() { m_nextFireTime = 0; }
     82 
     83     bool isActive() { return m_sharedTimerFunction && m_nextFireTime; }
     84     double fireTime() { return m_nextFireTime; }
     85     void fire() { m_sharedTimerFunction(); }
     86 
     87 private:
     88     void (*m_sharedTimerFunction)();
     89     double m_nextFireTime;
     90 };
     91 
     92 class ModePredicate {
     93 public:
     94     ModePredicate(const String& mode)
     95         : m_mode(mode)
     96         , m_defaultMode(mode == WorkerRunLoop::defaultMode())
     97     {
     98     }
     99 
    100     bool isDefaultMode() const
    101     {
    102         return m_defaultMode;
    103     }
    104 
    105     bool operator()(WorkerRunLoop::Task* task) const
    106     {
    107         return m_defaultMode || m_mode == task->mode();
    108     }
    109 
    110 private:
    111     String m_mode;
    112     bool m_defaultMode;
    113 };
    114 
    115 WorkerRunLoop::WorkerRunLoop()
    116     : m_sharedTimer(adoptPtr(new WorkerSharedTimer))
    117     , m_nestedCount(0)
    118     , m_uniqueId(0)
    119 {
    120 }
    121 
    122 WorkerRunLoop::~WorkerRunLoop()
    123 {
    124     ASSERT(!m_nestedCount);
    125 }
    126 
    127 String WorkerRunLoop::defaultMode()
    128 {
    129     return String();
    130 }
    131 
    132 class RunLoopSetup {
    133     WTF_MAKE_NONCOPYABLE(RunLoopSetup);
    134 public:
    135     RunLoopSetup(WorkerRunLoop& runLoop, WorkerGlobalScope* context)
    136         : m_runLoop(runLoop)
    137         , m_context(context)
    138     {
    139         if (!m_runLoop.m_nestedCount)
    140             PlatformThreadData::current().threadTimers().setSharedTimer(m_runLoop.m_sharedTimer.get());
    141         m_runLoop.m_nestedCount++;
    142         InspectorInstrumentation::willEnterNestedRunLoop(m_context);
    143     }
    144 
    145     ~RunLoopSetup()
    146     {
    147         m_runLoop.m_nestedCount--;
    148         if (!m_runLoop.m_nestedCount)
    149             PlatformThreadData::current().threadTimers().setSharedTimer(0);
    150         InspectorInstrumentation::didLeaveNestedRunLoop(m_context);
    151     }
    152 private:
    153     WorkerRunLoop& m_runLoop;
    154     WorkerGlobalScope* m_context;
    155 };
    156 
    157 void WorkerRunLoop::run(WorkerGlobalScope* context)
    158 {
    159     RunLoopSetup setup(*this, context);
    160     ModePredicate modePredicate(defaultMode());
    161     MessageQueueWaitResult result;
    162     do {
    163         result = runInMode(context, modePredicate, WaitForMessage);
    164     } while (result != MessageQueueTerminated);
    165     runCleanupTasks(context);
    166 }
    167 
    168 MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerGlobalScope* context, const String& mode, WaitMode waitMode)
    169 {
    170     RunLoopSetup setup(*this, context);
    171     ModePredicate modePredicate(mode);
    172     MessageQueueWaitResult result = runInMode(context, modePredicate, waitMode);
    173     return result;
    174 }
    175 
    176 MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerGlobalScope* context, const ModePredicate& predicate, WaitMode waitMode)
    177 {
    178     ASSERT(context);
    179     ASSERT(context->thread());
    180     ASSERT(context->thread()->isCurrentThread());
    181 
    182     bool nextTimeoutEventIsIdleWatchdog;
    183     MessageQueueWaitResult result;
    184     OwnPtr<WorkerRunLoop::Task> task;
    185     do {
    186         double absoluteTime = 0.0;
    187         nextTimeoutEventIsIdleWatchdog = false;
    188         if (waitMode == WaitForMessage) {
    189             absoluteTime = (predicate.isDefaultMode() && m_sharedTimer->isActive()) ? m_sharedTimer->fireTime() : MessageQueue<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 (m_messageQueue.isEmpty() && absoluteTime > currentTime() + kMinIdleTimespan) {
    194                 bool hasMoreWork = !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         task = m_messageQueue.waitForMessageFilteredWithTimeout(result, predicate, absoluteTime);
    208     } while (result == MessageQueueTimeout && nextTimeoutEventIsIdleWatchdog);
    209 
    210     // If the context is closing, don't execute any further JavaScript tasks (per section 4.1.1 of the Web Workers spec).
    211     // However, there may be implementation cleanup tasks in the queue, so keep running through it.
    212 
    213     switch (result) {
    214     case MessageQueueTerminated:
    215         break;
    216 
    217     case MessageQueueMessageReceived:
    218         InspectorInstrumentation::willProcessTask(context);
    219         task->performTask(*this, context);
    220         InspectorInstrumentation::didProcessTask(context);
    221         break;
    222 
    223     case MessageQueueTimeout:
    224         if (!context->isClosing())
    225             m_sharedTimer->fire();
    226         break;
    227     }
    228 
    229     return result;
    230 }
    231 
    232 void WorkerRunLoop::runCleanupTasks(WorkerGlobalScope* context)
    233 {
    234     ASSERT(context);
    235     ASSERT(context->thread());
    236     ASSERT(context->thread()->isCurrentThread());
    237     ASSERT(m_messageQueue.killed());
    238 
    239     while (true) {
    240         OwnPtr<WorkerRunLoop::Task> task = m_messageQueue.tryGetMessageIgnoringKilled();
    241         if (!task)
    242             return;
    243         task->performTask(*this, context);
    244     }
    245 }
    246 
    247 void WorkerRunLoop::terminate()
    248 {
    249     m_messageQueue.kill();
    250 }
    251 
    252 bool WorkerRunLoop::postTask(PassOwnPtr<ExecutionContextTask> task)
    253 {
    254     return postTaskForMode(task, defaultMode());
    255 }
    256 
    257 bool WorkerRunLoop::postTask(const Closure& closure)
    258 {
    259     return postTask(CallClosureTask::create(closure));
    260 }
    261 
    262 void WorkerRunLoop::postTaskAndTerminate(PassOwnPtr<ExecutionContextTask> task)
    263 {
    264     m_messageQueue.appendAndKill(Task::create(task, defaultMode().isolatedCopy()));
    265 }
    266 
    267 bool WorkerRunLoop::postTaskForMode(PassOwnPtr<ExecutionContextTask> task, const String& mode)
    268 {
    269     return m_messageQueue.append(Task::create(task, mode.isolatedCopy()));
    270 }
    271 
    272 bool WorkerRunLoop::postTaskForMode(const Closure& closure, const String& mode)
    273 {
    274     return postTaskForMode(CallClosureTask::create(closure), mode);
    275 }
    276 
    277 } // namespace WebCore
    278