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#include "ScriptExecutionContext.h"
34#include "SharedTimer.h"
35#include "ThreadGlobalData.h"
36#include "ThreadTimers.h"
37#include "WorkerRunLoop.h"
38#include "WorkerGlobalScope.h"
39#include "WorkerThread.h"
40#include <wtf/CurrentTime.h>
41
42namespace WebCore {
43
44class WorkerSharedTimer : public SharedTimer {
45public:
46    WorkerSharedTimer()
47        : m_sharedTimerFunction(0)
48        , m_nextFireTime(0)
49    {
50    }
51
52    // SharedTimer interface.
53    virtual void setFiredFunction(void (*function)()) { m_sharedTimerFunction = function; }
54    virtual void setFireInterval(double interval) { m_nextFireTime = interval + currentTime(); }
55    virtual void stop() { m_nextFireTime = 0; }
56
57    bool isActive() { return m_sharedTimerFunction && m_nextFireTime; }
58    double fireTime() { return m_nextFireTime; }
59    void fire() { m_sharedTimerFunction(); }
60
61private:
62    void (*m_sharedTimerFunction)();
63    double m_nextFireTime;
64};
65
66class ModePredicate {
67public:
68    ModePredicate(const String& mode)
69        : m_mode(mode)
70        , m_defaultMode(mode == WorkerRunLoop::defaultMode())
71    {
72    }
73
74    bool isDefaultMode() const
75    {
76        return m_defaultMode;
77    }
78
79    bool operator()(const WorkerRunLoop::Task& task) const
80    {
81        return m_defaultMode || m_mode == task.mode();
82    }
83
84private:
85    String m_mode;
86    bool m_defaultMode;
87};
88
89WorkerRunLoop::WorkerRunLoop()
90    : m_sharedTimer(std::make_unique<WorkerSharedTimer>())
91    , m_nestedCount(0)
92    , m_uniqueId(0)
93{
94}
95
96WorkerRunLoop::~WorkerRunLoop()
97{
98    ASSERT(!m_nestedCount);
99}
100
101String WorkerRunLoop::defaultMode()
102{
103    return String();
104}
105
106class RunLoopSetup {
107    WTF_MAKE_NONCOPYABLE(RunLoopSetup);
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(WorkerGlobalScope* context)
128{
129    RunLoopSetup setup(*this);
130    ModePredicate modePredicate(defaultMode());
131    MessageQueueWaitResult result;
132    do {
133        result = runInMode(context, modePredicate, WaitForMessage);
134    } while (result != MessageQueueTerminated);
135    runCleanupTasks(context);
136}
137
138MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerGlobalScope* context, const String& mode, WaitMode waitMode)
139{
140    RunLoopSetup setup(*this);
141    ModePredicate modePredicate(mode);
142    MessageQueueWaitResult result = runInMode(context, modePredicate, waitMode);
143    return result;
144}
145
146MessageQueueWaitResult WorkerRunLoop::runInMode(WorkerGlobalScope* context, const ModePredicate& predicate, WaitMode waitMode)
147{
148    ASSERT(context);
149    ASSERT(context->thread().threadID() == currentThread());
150
151    double absoluteTime = 0.0;
152    if (waitMode == WaitForMessage)
153        absoluteTime = (predicate.isDefaultMode() && m_sharedTimer->isActive()) ? m_sharedTimer->fireTime() : MessageQueue<Task>::infiniteTime();
154    MessageQueueWaitResult result;
155    auto task = m_messageQueue.waitForMessageFilteredWithTimeout(result, predicate, absoluteTime);
156
157    // 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.
158
159    switch (result) {
160    case MessageQueueTerminated:
161        break;
162
163    case MessageQueueMessageReceived:
164        task->performTask(*this, context);
165        break;
166
167    case MessageQueueTimeout:
168        if (!context->isClosing())
169            m_sharedTimer->fire();
170        break;
171    }
172
173    return result;
174}
175
176void WorkerRunLoop::runCleanupTasks(WorkerGlobalScope* context)
177{
178    ASSERT(context);
179    ASSERT(context->thread().threadID() == currentThread());
180    ASSERT(m_messageQueue.killed());
181
182    while (true) {
183        auto task = m_messageQueue.tryGetMessageIgnoringKilled();
184        if (!task)
185            return;
186        task->performTask(*this, context);
187    }
188}
189
190void WorkerRunLoop::terminate()
191{
192    m_messageQueue.kill();
193}
194
195void WorkerRunLoop::postTask(ScriptExecutionContext::Task task)
196{
197    postTaskForMode(WTF::move(task), defaultMode());
198}
199
200void WorkerRunLoop::postTaskAndTerminate(ScriptExecutionContext::Task task)
201{
202    m_messageQueue.appendAndKill(Task::create(WTF::move(task), defaultMode().isolatedCopy()));
203}
204
205void WorkerRunLoop::postTaskForMode(ScriptExecutionContext::Task task, const String& mode)
206{
207    m_messageQueue.append(Task::create(WTF::move(task), mode.isolatedCopy()));
208}
209
210std::unique_ptr<WorkerRunLoop::Task> WorkerRunLoop::Task::create(ScriptExecutionContext::Task task, const String& mode)
211{
212    return std::unique_ptr<Task>(new Task(WTF::move(task), mode));
213}
214
215void WorkerRunLoop::Task::performTask(const WorkerRunLoop& runLoop, WorkerGlobalScope* context)
216{
217    if ((!context->isClosing() && !runLoop.terminated()) || m_task.isCleanupTask())
218        m_task.performTask(*context);
219}
220
221WorkerRunLoop::Task::Task(ScriptExecutionContext::Task task, const String& mode)
222    : m_task(WTF::move(task))
223    , m_mode(mode.isolatedCopy())
224{
225}
226
227} // namespace WebCore
228