source: webkit/trunk/JavaScriptCore/wtf/ThreadingWin.cpp@ 31694

Last change on this file since 31694 was 31694, checked in by Adam Roben, 17 years ago

Windows build fix

  • wtf/ThreadingWin.cpp: Back out some changes I didn't mean to land.
  • Property svn:eol-style set to native
File size: 13.0 KB
Line 
1/*
2 * Copyright (C) 2007, 2008 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 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * =============================================================================
29 * Note: The implementation of condition variables under the Windows
30 * plaform was based on that of the excellent BOOST C++ library. It
31 * has been rewritten to fit in with the WebKit architecture and to
32 * use its coding conventions.
33 * =============================================================================
34 *
35 * The Boost license is virtually identical to the Apple variation at the
36 * top of this file, but is included here for completeness:
37 *
38 * Boost Software License - Version 1.0 - August 17th, 2003
39 *
40 * Permission is hereby granted, free of charge, to any person or organization
41 * obtaining a copy of the software and accompanying documentation covered by
42 * this license (the "Software") to use, reproduce, display, distribute,
43 * execute, and transmit the Software, and to prepare derivative works of the
44 * Software, and to permit third-parties to whom the Software is furnished to
45 * do so, all subject to the following:
46 *
47 * The copyright notices in the Software and this entire statement, including
48 * the above license grant, this restriction and the following disclaimer,
49 * must be included in all copies of the Software, in whole or in part, and
50 * all derivative works of the Software, unless such copies or derivative
51 * works are solely in the form of machine-executable object code generated by
52 * a source language processor.
53 *
54 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
55 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
56 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
57 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
58 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
59 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
60 * DEALINGS IN THE SOFTWARE.
61 */
62
63#include "config.h"
64#include "Threading.h"
65
66#include <windows.h>
67#include <wtf/HashMap.h>
68#include <wtf/MathExtras.h>
69
70namespace WTF {
71
72Mutex* atomicallyInitializedStaticMutex;
73
74static ThreadIdentifier mainThreadIdentifier;
75
76void initializeThreading()
77{
78 if (!atomicallyInitializedStaticMutex) {
79 atomicallyInitializedStaticMutex = new Mutex;
80 wtf_random_init();
81 mainThreadIdentifier = currentThread();
82 }
83}
84
85static Mutex& threadMapMutex()
86{
87 static Mutex mutex;
88 return mutex;
89}
90
91static HashMap<DWORD, HANDLE>& threadMap()
92{
93 static HashMap<DWORD, HANDLE> map;
94 return map;
95}
96
97static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle)
98{
99 MutexLocker locker(threadMapMutex());
100 threadMap().add(threadID, threadHandle);
101}
102
103static HANDLE threadHandleForIdentifier(ThreadIdentifier id)
104{
105 MutexLocker locker(threadMapMutex());
106 return threadMap().get(id);
107}
108
109static void clearThreadHandleForIdentifier(ThreadIdentifier id)
110{
111 MutexLocker locker(threadMapMutex());
112 ASSERT(threadMap().contains(id));
113 threadMap().remove(id);
114}
115
116ThreadIdentifier createThread(ThreadFunction entryPoint, void* data)
117{
118 DWORD threadIdentifier = 0;
119 ThreadIdentifier threadID = 0;
120 HANDLE hEvent = ::CreateEvent(0, FALSE, FALSE, 0);
121 HANDLE threadHandle = ::CreateThread(0, 0, (LPTHREAD_START_ROUTINE)entryPoint, data, 0, &threadIdentifier);
122 if (!threadHandle) {
123 LOG_ERROR("Failed to create thread at entry point %p with data %p", entryPoint, data);
124 return 0;
125 }
126
127 threadID = static_cast<ThreadIdentifier>(threadIdentifier);
128 storeThreadHandleByIdentifier(threadIdentifier, threadHandle);
129
130 return threadID;
131}
132
133int waitForThreadCompletion(ThreadIdentifier threadID, void** result)
134{
135 ASSERT(threadID);
136
137 HANDLE threadHandle = threadHandleForIdentifier(threadID);
138 if (!threadHandle)
139 LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID);
140
141 DWORD joinResult = ::WaitForSingleObject(threadHandle, INFINITE);
142 if (joinResult == WAIT_FAILED)
143 LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID);
144
145 ::CloseHandle(threadHandle);
146 clearThreadHandleForIdentifier(threadID);
147
148 return joinResult;
149}
150
151void detachThread(ThreadIdentifier threadID)
152{
153 ASSERT(threadID);
154
155 HANDLE threadHandle = threadHandleForIdentifier(threadID);
156 if (threadHandle)
157 ::CloseHandle(threadHandle);
158 clearThreadHandleForIdentifier(threadID);
159}
160
161ThreadIdentifier currentThread()
162{
163 return static_cast<ThreadIdentifier>(::GetCurrentThreadId());
164}
165
166bool isMainThread()
167{
168 return currentThread() == mainThreadIdentifier;
169}
170
171Mutex::Mutex()
172{
173 m_mutex.m_recursionCount = 0;
174 ::InitializeCriticalSection(&m_mutex.m_internalMutex);
175}
176
177Mutex::~Mutex()
178{
179 ::DeleteCriticalSection(&m_mutex.m_internalMutex);
180}
181
182void Mutex::lock()
183{
184 ::EnterCriticalSection(&m_mutex.m_internalMutex);
185 ++m_mutex.m_recursionCount;
186}
187
188bool Mutex::tryLock()
189{
190 // This method is modeled after the behavior of pthread_mutex_trylock,
191 // which will return an error if the lock is already owned by the
192 // current thread. Since the primitive Win32 'TryEnterCriticalSection'
193 // treats this as a successful case, it changes the behavior of several
194 // tests in WebKit that check to see if the current thread already
195 // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord)
196 DWORD result = ::TryEnterCriticalSection(&m_mutex.m_internalMutex);
197
198 if (result != 0) { // We got the lock
199 // If this thread already had the lock, we must unlock and
200 // return false so that we mimic the behavior of POSIX's
201 // pthread_mutex_trylock:
202 if (m_mutex.m_recursionCount > 0) {
203 ::LeaveCriticalSection(&m_mutex.m_internalMutex);
204 return false;
205 }
206
207 ++m_mutex.m_recursionCount;
208 return true;
209 }
210
211 return false;
212}
213
214void Mutex::unlock()
215{
216 --m_mutex.m_recursionCount;
217 ::LeaveCriticalSection(&m_mutex.m_internalMutex);
218}
219
220static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1);
221
222ThreadCondition::ThreadCondition()
223{
224 m_condition.m_timedOut = 0;
225 m_condition.m_blocked = 0;
226 m_condition.m_waitingForRemoval = 0;
227 m_condition.m_gate = ::CreateSemaphore(0, 1, 1, 0);
228 m_condition.m_queue = ::CreateSemaphore(0, 0, MaxSemaphoreCount, 0);
229 m_condition.m_mutex = ::CreateMutex(0, 0, 0);
230
231 if (!m_condition.m_gate || !m_condition.m_queue || !m_condition.m_mutex) {
232 if (m_condition.m_gate)
233 ::CloseHandle(m_condition.m_gate);
234 if (m_condition.m_queue)
235 ::CloseHandle(m_condition.m_queue);
236 if (m_condition.m_mutex)
237 ::CloseHandle(m_condition.m_mutex);
238 }
239}
240
241ThreadCondition::~ThreadCondition()
242{
243 ::CloseHandle(m_condition.m_gate);
244 ::CloseHandle(m_condition.m_queue);
245 ::CloseHandle(m_condition.m_mutex);
246}
247
248void ThreadCondition::wait(Mutex& mutex)
249{
250 PlatformMutex& cs = mutex.impl();
251
252 // Enter the wait state.
253 DWORD res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
254 ASSERT(res == WAIT_OBJECT_0);
255 ++m_condition.m_blocked;
256 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
257 ASSERT(res);
258
259 ::LeaveCriticalSection(&cs.m_internalMutex);
260
261 res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
262 ASSERT(res == WAIT_OBJECT_0);
263
264 res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
265 ASSERT(res == WAIT_OBJECT_0);
266 size_t wasWaiting = m_condition.m_waitingForRemoval;
267 size_t wasTimedOut = m_condition.m_timedOut;
268 if (wasWaiting != 0) {
269 if (--m_condition.m_waitingForRemoval == 0) {
270 if (m_condition.m_blocked != 0) {
271 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); // open m_gate
272 ASSERT(res);
273 wasWaiting = 0;
274 }
275 else if (m_condition.m_timedOut != 0)
276 m_condition.m_timedOut = 0;
277 }
278 } else if (++m_condition.m_timedOut == ((std::numeric_limits<unsigned>::max)() / 2)) {
279 // timeout occured, normalize the m_condition.m_timedOut count
280 // this may occur if many calls to wait with a timeout are made and
281 // no call to notify_* is made
282 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
283 ASSERT(res == WAIT_OBJECT_0);
284 m_condition.m_blocked -= m_condition.m_timedOut;
285 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
286 ASSERT(res);
287 m_condition.m_timedOut = 0;
288 }
289 res = ::ReleaseMutex(m_condition.m_mutex);
290 ASSERT(res);
291
292 if (wasWaiting == 1) {
293 for (/**/ ; wasTimedOut; --wasTimedOut) {
294 // better now than spurious later
295 res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
296 ASSERT(res == WAIT_OBJECT_0);
297 }
298 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
299 ASSERT(res);
300 }
301
302 ::EnterCriticalSection (&cs.m_internalMutex);
303}
304
305bool ThreadCondition::timedWait(Mutex& mutex, double interval)
306{
307 // Empty for now
308 ASSERT(false);
309 return false;
310}
311
312void ThreadCondition::signal()
313{
314 unsigned signals = 0;
315
316 DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
317 ASSERT(res == WAIT_OBJECT_0);
318
319 if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
320 if (m_condition.m_blocked == 0) {
321 res = ::ReleaseMutex(m_condition.m_mutex);
322 ASSERT(res);
323 return;
324 }
325
326 ++m_condition.m_waitingForRemoval;
327 --m_condition.m_blocked;
328
329 signals = 1;
330 } else {
331 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
332 ASSERT(res == WAIT_OBJECT_0);
333 if (m_condition.m_blocked > m_condition.m_timedOut) {
334 if (m_condition.m_timedOut != 0) {
335 m_condition.m_blocked -= m_condition.m_timedOut;
336 m_condition.m_timedOut = 0;
337 }
338 signals = m_condition.m_waitingForRemoval = 1;
339 --m_condition.m_blocked;
340 } else {
341 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
342 ASSERT(res);
343 }
344 }
345
346 res =::ReleaseMutex(m_condition.m_mutex);
347 ASSERT(res);
348
349 if (signals) {
350 res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
351 ASSERT(res);
352 }
353}
354
355void ThreadCondition::broadcast()
356{
357 unsigned signals = 0;
358
359 WORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
360 ASSERT(res == WAIT_OBJECT_0);
361
362 if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
363 if (m_condition.m_blocked == 0) {
364 res = ::ReleaseMutex(m_condition.m_mutex);
365 ASSERT(res);
366 return;
367 }
368
369 m_condition.m_waitingForRemoval += (signals = m_condition.m_blocked);
370 m_condition.m_blocked = 0;
371 } else {
372 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
373 ASSERT(res == WAIT_OBJECT_0);
374 if (m_condition.m_blocked > m_condition.m_timedOut) {
375 if (m_condition.m_timedOut != 0) {
376 m_condition.m_blocked -= m_condition.m_timedOut;
377 m_condition.m_timedOut = 0;
378 }
379 signals = m_condition.m_waitingForRemoval = m_condition.m_blocked;
380 m_condition.m_blocked = 0;
381 } else {
382 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
383 ASSERT(res);
384 }
385 }
386
387 res = ::ReleaseMutex(m_condition.m_mutex);
388 ASSERT(res);
389
390 if (signals) {
391 res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
392 ASSERT(res);
393 }
394}
395
396} // namespace WTF
Note: See TracBrowser for help on using the repository browser.