1/*
2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1.  Redistributions of source code must retain the above copyright
10 *     notice, this list of conditions and the following disclaimer.
11 * 2.  Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 *     its contributors may be used to endorse or promote products derived
16 *     from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29#include "config.h"
30#include "Threading.h"
31
32#if !ENABLE(SINGLE_THREADED)
33
34#include "CurrentTime.h"
35#include "HashMap.h"
36#include "MainThread.h"
37#include "RandomNumberSeed.h"
38
39#include <QCoreApplication>
40#include <QMutex>
41#include <QThread>
42#include <QWaitCondition>
43
44namespace WTF {
45
46class ThreadPrivate : public QThread {
47public:
48    ThreadPrivate(ThreadFunction entryPoint, void* data);
49    void run();
50    void* getReturnValue() { return m_returnValue; }
51private:
52    void* m_data;
53    ThreadFunction m_entryPoint;
54    void* m_returnValue;
55};
56
57ThreadPrivate::ThreadPrivate(ThreadFunction entryPoint, void* data)
58    : m_data(data)
59    , m_entryPoint(entryPoint)
60    , m_returnValue(0)
61{
62}
63
64void ThreadPrivate::run()
65{
66    m_returnValue = m_entryPoint(m_data);
67}
68
69class ThreadMonitor : public QObject {
70    Q_OBJECT
71public:
72    static ThreadMonitor * instance()
73    {
74        static ThreadMonitor *instance = new ThreadMonitor();
75        return instance;
76    }
77
78public Q_SLOTS:
79    void threadFinished()
80    {
81        sender()->deleteLater();
82    }
83};
84
85static Mutex* atomicallyInitializedStaticMutex;
86
87static Mutex& threadMapMutex()
88{
89    static Mutex mutex;
90    return mutex;
91}
92
93static HashMap<ThreadIdentifier, QThread*>& threadMap()
94{
95    static HashMap<ThreadIdentifier, QThread*> map;
96    return map;
97}
98
99static ThreadIdentifier identifierByQthreadHandle(QThread*& thread)
100{
101    MutexLocker locker(threadMapMutex());
102
103    HashMap<ThreadIdentifier, QThread*>::iterator i = threadMap().begin();
104    for (; i != threadMap().end(); ++i) {
105        if (i->second == thread)
106            return i->first;
107    }
108
109    return 0;
110}
111
112static ThreadIdentifier establishIdentifierForThread(QThread*& thread)
113{
114    ASSERT(!identifierByQthreadHandle(thread));
115
116    MutexLocker locker(threadMapMutex());
117
118    static ThreadIdentifier identifierCount = 1;
119
120    threadMap().add(identifierCount, thread);
121
122    return identifierCount++;
123}
124
125static void clearThreadForIdentifier(ThreadIdentifier id)
126{
127    MutexLocker locker(threadMapMutex());
128
129    ASSERT(threadMap().contains(id));
130
131    threadMap().remove(id);
132}
133
134static QThread* threadForIdentifier(ThreadIdentifier id)
135{
136    MutexLocker locker(threadMapMutex());
137
138    return threadMap().get(id);
139}
140
141void initializeThreading()
142{
143    if (!atomicallyInitializedStaticMutex) {
144        atomicallyInitializedStaticMutex = new Mutex;
145        threadMapMutex();
146        initializeRandomNumberGenerator();
147    }
148}
149
150void lockAtomicallyInitializedStaticMutex()
151{
152    ASSERT(atomicallyInitializedStaticMutex);
153    atomicallyInitializedStaticMutex->lock();
154}
155
156void unlockAtomicallyInitializedStaticMutex()
157{
158    atomicallyInitializedStaticMutex->unlock();
159}
160
161ThreadIdentifier createThreadInternal(ThreadFunction entryPoint, void* data, const char*)
162{
163    ThreadPrivate* thread = new ThreadPrivate(entryPoint, data);
164    if (!thread) {
165        LOG_ERROR("Failed to create thread at entry point %p with data %p", entryPoint, data);
166        return 0;
167    }
168
169    QObject::connect(thread, SIGNAL(finished()), ThreadMonitor::instance(), SLOT(threadFinished()));
170
171    thread->start();
172
173    QThread* threadRef = static_cast<QThread*>(thread);
174
175    return establishIdentifierForThread(threadRef);
176}
177
178void initializeCurrentThreadInternal(const char*)
179{
180}
181
182int waitForThreadCompletion(ThreadIdentifier threadID, void** result)
183{
184    ASSERT(threadID);
185
186    QThread* thread = threadForIdentifier(threadID);
187
188    bool res = thread->wait();
189
190    clearThreadForIdentifier(threadID);
191    if (result)
192        *result = static_cast<ThreadPrivate*>(thread)->getReturnValue();
193
194    return !res;
195}
196
197void detachThread(ThreadIdentifier threadID)
198{
199    ASSERT(threadID);
200    clearThreadForIdentifier(threadID);
201}
202
203ThreadIdentifier currentThread()
204{
205    QThread* currentThread = QThread::currentThread();
206    if (ThreadIdentifier id = identifierByQthreadHandle(currentThread))
207        return id;
208    return establishIdentifierForThread(currentThread);
209}
210
211void yield()
212{
213    QThread::yieldCurrentThread();
214}
215
216Mutex::Mutex()
217    : m_mutex(new QMutex())
218{
219}
220
221Mutex::~Mutex()
222{
223    delete m_mutex;
224}
225
226void Mutex::lock()
227{
228    m_mutex->lock();
229}
230
231bool Mutex::tryLock()
232{
233    return m_mutex->tryLock();
234}
235
236void Mutex::unlock()
237{
238    m_mutex->unlock();
239}
240
241ThreadCondition::ThreadCondition()
242    : m_condition(new QWaitCondition())
243{
244}
245
246ThreadCondition::~ThreadCondition()
247{
248    delete m_condition;
249}
250
251void ThreadCondition::wait(Mutex& mutex)
252{
253    m_condition->wait(mutex.impl());
254}
255
256bool ThreadCondition::timedWait(Mutex& mutex, double absoluteTime)
257{
258    double currentTime = WTF::currentTime();
259
260    // Time is in the past - return immediately.
261    if (absoluteTime < currentTime)
262        return false;
263
264    // Time is too far in the future (and would overflow unsigned long) - wait forever.
265    if (absoluteTime - currentTime > static_cast<double>(INT_MAX) / 1000.0) {
266        wait(mutex);
267        return true;
268    }
269
270    double intervalMilliseconds = (absoluteTime - currentTime) * 1000.0;
271    return m_condition->wait(mutex.impl(), static_cast<unsigned long>(intervalMilliseconds));
272}
273
274void ThreadCondition::signal()
275{
276    m_condition->wakeOne();
277}
278
279void ThreadCondition::broadcast()
280{
281    m_condition->wakeAll();
282}
283
284} // namespace WebCore
285
286#include "ThreadingQt.moc"
287
288#endif
289