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
46QT_USE_NAMESPACE
47
48class ThreadPrivate : public QThread {
49public:
50 ThreadPrivate(ThreadFunction entryPoint, void* data);
51 void run();
52 void* getReturnValue() { return m_returnValue; }
53private:
54 void* m_data;
55 ThreadFunction m_entryPoint;
56 void* m_returnValue;
57};
58
59ThreadPrivate::ThreadPrivate(ThreadFunction entryPoint, void* data)
60 : m_data(data)
61 , m_entryPoint(entryPoint)
62 , m_returnValue(0)
63{
64}
65
66void ThreadPrivate::run()
67{
68 m_returnValue = m_entryPoint(m_data);
69}
70
71class ThreadMonitor : public QObject {
72 Q_OBJECT
73public:
74 static ThreadMonitor * instance()
75 {
76 static ThreadMonitor *instance = new ThreadMonitor();
77 return instance;
78 }
79
80public Q_SLOTS:
81 void threadFinished()
82 {
83 sender()->deleteLater();
84 }
85};
86
87static Mutex* atomicallyInitializedStaticMutex;
88
89static ThreadIdentifier mainThreadIdentifier;
90
91static Mutex& threadMapMutex()
92{
93 static Mutex mutex;
94 return mutex;
95}
96
97static HashMap<ThreadIdentifier, QThread*>& threadMap()
98{
99 static HashMap<ThreadIdentifier, QThread*> map;
100 return map;
101}
102
103static ThreadIdentifier identifierByQthreadHandle(QThread*& thread)
104{
105 MutexLocker locker(threadMapMutex());
106
107 HashMap<ThreadIdentifier, QThread*>::iterator i = threadMap().begin();
108 for (; i != threadMap().end(); ++i) {
109 if (i->second == thread)
110 return i->first;
111 }
112
113 return 0;
114}
115
116static ThreadIdentifier establishIdentifierForThread(QThread*& thread)
117{
118 ASSERT(!identifierByQthreadHandle(thread));
119
120 MutexLocker locker(threadMapMutex());
121
122 static ThreadIdentifier identifierCount = 1;
123
124 threadMap().add(identifierCount, thread);
125
126 return identifierCount++;
127}
128
129static void clearThreadForIdentifier(ThreadIdentifier id)
130{
131 MutexLocker locker(threadMapMutex());
132
133 ASSERT(threadMap().contains(id));
134
135 threadMap().remove(id);
136}
137
138static QThread* threadForIdentifier(ThreadIdentifier id)
139{
140 MutexLocker locker(threadMapMutex());
141
142 return threadMap().get(id);
143}
144
145void initializeThreading()
146{
147 if (!atomicallyInitializedStaticMutex) {
148 atomicallyInitializedStaticMutex = new Mutex;
149 threadMapMutex();
150 initializeRandomNumberGenerator();
151 QThread* mainThread = QCoreApplication::instance()->thread();
152 mainThreadIdentifier = identifierByQthreadHandle(mainThread);
153 if (!mainThreadIdentifier)
154 mainThreadIdentifier = establishIdentifierForThread(mainThread);
155 initializeMainThread();
156 }
157}
158
159void lockAtomicallyInitializedStaticMutex()
160{
161 ASSERT(atomicallyInitializedStaticMutex);
162 atomicallyInitializedStaticMutex->lock();
163}
164
165void unlockAtomicallyInitializedStaticMutex()
166{
167 atomicallyInitializedStaticMutex->unlock();
168}
169
170ThreadIdentifier createThreadInternal(ThreadFunction entryPoint, void* data, const char*)
171{
172 ThreadPrivate* thread = new ThreadPrivate(entryPoint, data);
173 if (!thread) {
174 LOG_ERROR("Failed to create thread at entry point %p with data %p", entryPoint, data);
175 return 0;
176 }
177
178 QObject::connect(thread, SIGNAL(finished()), ThreadMonitor::instance(), SLOT(threadFinished()));
179
180 thread->start();
181
182 QThread* threadRef = static_cast<QThread*>(thread);
183
184 return establishIdentifierForThread(threadRef);
185}
186
187void initializeCurrentThreadInternal(const char*)
188{
189}
190
191int waitForThreadCompletion(ThreadIdentifier threadID, void** result)
192{
193 ASSERT(threadID);
194
195 QThread* thread = threadForIdentifier(threadID);
196
197 bool res = thread->wait();
198
199 clearThreadForIdentifier(threadID);
200 if (result)
201 *result = static_cast<ThreadPrivate*>(thread)->getReturnValue();
202
203 return !res;
204}
205
206void detachThread(ThreadIdentifier threadID)
207{
208 ASSERT(threadID);
209 clearThreadForIdentifier(threadID);
210}
211
212ThreadIdentifier currentThread()
213{
214 QThread* currentThread = QThread::currentThread();
215 if (ThreadIdentifier id = identifierByQthreadHandle(currentThread))
216 return id;
217 return establishIdentifierForThread(currentThread);
218}
219
220bool isMainThread()
221{
222 return QThread::currentThread() == QCoreApplication::instance()->thread();
223}
224
225Mutex::Mutex()
226 : m_mutex(new QMutex())
227{
228}
229
230Mutex::~Mutex()
231{
232 delete m_mutex;
233}
234
235void Mutex::lock()
236{
237 m_mutex->lock();
238}
239
240bool Mutex::tryLock()
241{
242 return m_mutex->tryLock();
243}
244
245void Mutex::unlock()
246{
247 m_mutex->unlock();
248}
249
250ThreadCondition::ThreadCondition()
251 : m_condition(new QWaitCondition())
252{
253}
254
255ThreadCondition::~ThreadCondition()
256{
257 delete m_condition;
258}
259
260void ThreadCondition::wait(Mutex& mutex)
261{
262 m_condition->wait(mutex.impl());
263}
264
265bool ThreadCondition::timedWait(Mutex& mutex, double absoluteTime)
266{
267 double currentTime = WTF::currentTime();
268
269 // Time is in the past - return immediately.
270 if (absoluteTime < currentTime)
271 return false;
272
273 // Time is too far in the future (and would overflow unsigned long) - wait forever.
274 if (absoluteTime - currentTime > static_cast<double>(INT_MAX) / 1000.0) {
275 wait(mutex);
276 return true;
277 }
278
279 double intervalMilliseconds = (absoluteTime - currentTime) * 1000.0;
280 return m_condition->wait(mutex.impl(), static_cast<unsigned long>(intervalMilliseconds));
281}
282
283void ThreadCondition::signal()
284{
285 m_condition->wakeOne();
286}
287
288void ThreadCondition::broadcast()
289{
290 m_condition->wakeAll();
291}
292
293} // namespace WebCore
294
295#include "ThreadingQt.moc"
296
297#endif
298