1/*
2 * Copyright (C) 2010 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebCoreThreadRun.h"
28
29#if PLATFORM(IOS)
30
31#include "WebCoreThread.h"
32#include "WebCoreThreadInternal.h"
33#include <condition_variable>
34#include <wtf/Vector.h>
35
36namespace {
37
38class WebThreadBlockState {
39public:
40    WebThreadBlockState()
41        : m_completed(false)
42    {
43    }
44
45    void waitForCompletion()
46    {
47        std::unique_lock<std::mutex> lock(m_stateMutex);
48
49        m_completionConditionVariable.wait(lock, [this] { return m_completed; });
50    }
51
52    void setCompleted()
53    {
54        std::lock_guard<std::mutex> lock(m_stateMutex);
55
56        ASSERT(!m_completed);
57        m_completed = true;
58        m_completionConditionVariable.notify_one();
59    }
60
61private:
62    std::mutex m_stateMutex;
63    std::condition_variable m_completionConditionVariable;
64    bool m_completed;
65};
66
67class WebThreadBlock {
68public:
69    WebThreadBlock(void (^task)(), WebThreadBlockState* state)
70        : m_task(Block_copy(task))
71        , m_state(state)
72    {
73    }
74
75    WebThreadBlock(const WebThreadBlock& other)
76        : m_task(Block_copy(other.m_task))
77        , m_state(other.m_state)
78    {
79    }
80
81    WebThreadBlock& operator=(const WebThreadBlock& other)
82    {
83        void (^oldTask)() = m_task;
84        m_task = Block_copy(other.m_task);
85        Block_release(oldTask);
86        m_state = other.m_state;
87        return *this;
88    }
89
90    ~WebThreadBlock()
91    {
92        Block_release(m_task);
93    }
94
95    void operator()() const
96    {
97        m_task();
98        if (m_state)
99            m_state->setCompleted();
100    }
101
102private:
103    void (^m_task)();
104    WebThreadBlockState* m_state;
105};
106
107}
108
109extern "C" {
110
111typedef WTF::Vector<WebThreadBlock> WebThreadRunQueue;
112
113static std::mutex* runQueueMutex;
114static CFRunLoopSourceRef runSource;
115static WebThreadRunQueue* runQueue;
116
117static void HandleRunSource(void *info)
118{
119    UNUSED_PARAM(info);
120    ASSERT(WebThreadIsCurrent());
121    ASSERT(runQueueMutex);
122    ASSERT(runSource);
123    ASSERT(runQueue);
124
125    WebThreadRunQueue queueCopy;
126    {
127        std::lock_guard<std::mutex> lock(*runQueueMutex);
128        queueCopy = *runQueue;
129        runQueue->clear();
130    }
131
132    for (const auto& block : queueCopy)
133        block();
134}
135
136static void _WebThreadRun(void (^task)(), bool synchronous)
137{
138    if (WebThreadIsCurrent() || !WebThreadIsEnabled()) {
139        task();
140        return;
141    }
142
143    ASSERT(runQueueMutex);
144    ASSERT(runSource);
145    ASSERT(runQueue);
146
147    WebThreadBlockState* state = 0;
148    if (synchronous)
149        state = new WebThreadBlockState;
150
151    {
152        std::lock_guard<std::mutex> lock(*runQueueMutex);
153        runQueue->append(WebThreadBlock(task, state));
154    }
155
156    CFRunLoopSourceSignal(runSource);
157    CFRunLoopWakeUp(WebThreadRunLoop());
158
159    if (synchronous) {
160        state->waitForCompletion();
161        delete state;
162    }
163}
164
165void WebThreadRun(void (^task)())
166{
167    _WebThreadRun(task, false);
168}
169
170void WebThreadRunSync(void (^task)())
171{
172    _WebThreadRun(task, true);
173}
174
175void WebThreadInitRunQueue()
176{
177    ASSERT(!runQueue);
178    ASSERT(!runQueueMutex);
179    ASSERT(!runSource);
180
181    static dispatch_once_t pred;
182    dispatch_once(&pred, ^{
183        runQueue = new WebThreadRunQueue;
184
185        CFRunLoopSourceContext runSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, HandleRunSource};
186        runSource = CFRunLoopSourceCreate(NULL, -1, &runSourceContext);
187        CFRunLoopAddSource(WebThreadRunLoop(), runSource, kCFRunLoopDefaultMode);
188
189        runQueueMutex = std::make_unique<std::mutex>().release();
190    });
191}
192
193}
194
195#endif // PLATFORM(IOS)
196