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
33#if ENABLE(WORKERS)
34
35#include "ScriptExecutionContext.h"
36#include "SharedTimer.h"
37#include "ThreadGlobalData.h"
38#include "ThreadTimers.h"
39#include "WorkerRunLoop.h"
40#include "WorkerContext.h"
41#include "WorkerThread.h"
42
43namespace WebCore {
44
45class WorkerSharedTimer : public SharedTimer {
46public:
47    WorkerSharedTimer()
48        : m_sharedTimerFunction(0)
49        , m_nextFireTime(0)
50    {
51    }
52
53    // SharedTimer interface.
54    virtual void setFiredFunction(void (*function)()) { m_sharedTimerFunction = function; }
55    virtual void setFireTime(double fireTime) { m_nextFireTime = fireTime; }
56    virtual void stop() { m_nextFireTime = 0; }
57
58    bool isActive() { return m_sharedTimerFunction && m_nextFireTime; }
59    double fireTime() { return m_nextFireTime; }
60    void fire() { m_sharedTimerFunction(); }
61
62private:
63    void (*m_sharedTimerFunction)();
64    double m_nextFireTime;
65};
66
67class ModePredicate {
68public:
69    ModePredicate(const String& mode)
70        : m_mode(mode)
71        , m_defaultMode(mode == WorkerRunLoop::defaultMode())
72    {
73    }
74
75    bool isDefaultMode() const
76    {
77        return m_defaultMode;
78    }
79
80    bool operator()(WorkerRunLoop::Task* task) const
81    {
82        return m_defaultMode || m_mode == task->mode();
83    }
84
85private:
86    String m_mode;
87    bool m_defaultMode;
88};
89
90WorkerRunLoop::WorkerRunLoop()
91    : m_sharedTimer(new WorkerSharedTimer)
92    , m_nestedCount(0)
93    , m_uniqueId(0)
94{
95}
96
97WorkerRunLoop::~WorkerRunLoop()
98{
99    ASSERT(!m_nestedCount);
100}
101
102String WorkerRunLoop::defaultMode()
103{
104    return String();
105}
106
107class RunLoopSetup : public Noncopyable {
108public:
109    RunLoopSetup(WorkerRunLoop& runLoop)
110        : m_runLoop(runLoop)
111    {
112        if (!m_runLoop.m_nestedCount)
113            threadGlobalData().threadTimers().setSharedTimer(m_runLoop.m_sharedTimer.get());
114        m_runLoop.m_nestedCount++;
115    }
116
117    ~RunLoopSetup()
118    {
119        m_runLoop.m_nestedCount--;
120        if (!m_runLoop.m_nestedCount)
121            threadGlobalData().threadTimers().setSharedTimer(0);
122    }
123private:
124    WorkerRunLoop& m_runLoop;
125};
126
127void WorkerRunLoop::run(WorkerContext* context)
128{
129    RunLoopSetup setup(*this);
130    ModePredicate modePredicate(defaultMode());
131    MessageQueueWaitResult result;
132    do {
133        result = runInMode(context, modePredicate);
134    } while (result != MessageQueueTerminated);
135}
136
137MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerContext* context, const String& mode)
138{
139    RunLoopSetup setup(*this);
140    ModePredicate modePredicate(mode);
141    MessageQueueWaitResult result = runInMode(context, modePredicate);
142    return result;
143}
144
145MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerContext* context, const ModePredicate& predicate)
146{
147    ASSERT(context);
148    ASSERT(context->thread());
149    ASSERT(context->thread()->threadID() == currentThread());
150
151    double absoluteTime = (predicate.isDefaultMode() && m_sharedTimer->isActive()) ? m_sharedTimer->fireTime() : MessageQueue<Task>::infiniteTime();
152    MessageQueueWaitResult result;
153    OwnPtr<WorkerRunLoop::Task> task = m_messageQueue.waitForMessageFilteredWithTimeout(result, predicate, absoluteTime);
154
155    // If the context is closing, don't execute any further JavaScript tasks (per section 4.1.1 of the Web Workers spec).  However, there may be implementation cleanup tasks in the queue, so keep running through it.
156
157    switch (result) {
158    case MessageQueueTerminated:
159        break;
160
161    case MessageQueueMessageReceived:
162        task->performTask(context);
163        break;
164
165    case MessageQueueTimeout:
166        if (!context->isClosing())
167            m_sharedTimer->fire();
168        break;
169    }
170
171    return result;
172}
173
174void WorkerRunLoop::terminate()
175{
176    m_messageQueue.kill();
177}
178
179void WorkerRunLoop::postTask(PassOwnPtr<ScriptExecutionContext::Task> task)
180{
181    postTaskForMode(task, defaultMode());
182}
183
184void WorkerRunLoop::postTaskForMode(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
185{
186    m_messageQueue.append(Task::create(task, mode.crossThreadString()));
187}
188
189PassOwnPtr<WorkerRunLoop::Task> WorkerRunLoop::Task::create(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
190{
191    return new Task(task, mode);
192}
193
194void WorkerRunLoop::Task::performTask(ScriptExecutionContext* context)
195{
196    WorkerContext* workerContext = static_cast<WorkerContext *>(context);
197    if (!workerContext->isClosing() || m_task->isCleanupTask())
198        m_task->performTask(context);
199}
200
201WorkerRunLoop::Task::Task(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
202    : m_task(task)
203    , m_mode(mode.crossThreadString())
204{
205}
206
207
208} // namespace WebCore
209
210#endif // ENABLE(WORKERS)
211