24
26
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
27
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
28
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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
* =============================================================================
35
* The Boost license is virtually identical to the Apple variation at the
36
* top of this file, but is included here for completeness:
38
* Boost Software License - Version 1.0 - August 17th, 2003
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:
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.
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.
32
* There are numerous academic and practical works on how to implement pthread_cond_wait/pthread_cond_signal/pthread_cond_broadcast
33
* functions on Win32. Here is one example: http://www.cs.wustl.edu/~schmidt/win32-cv-1.html which is widely credited as a 'starting point'
34
* of modern attempts. There are several more or less proven implementations, one in Boost C++ library (http://www.boost.org) and another
35
* in pthreads-win32 (http://sourceware.org/pthreads-win32/).
37
* The number of articles and discussions is the evidence of significant difficulties in implementing these primitives correctly.
38
* The brief search of revisions, ChangeLog entries, discussions in comp.programming.threads and other places clearly documents
39
* numerous pitfalls and performance problems the authors had to overcome to arrive to the suitable implementations.
40
* Optimally, WebKit would use one of those supported/tested libraries directly. To roll out our own implementation is impractical,
41
* if even for the lack of sufficient testing. However, a faithful reproduction of the code from one of the popular supported
42
* libraries seems to be a good compromise.
44
* The early Boost implementation (http://www.boxbackup.org/trac/browser/box/nick/win/lib/win32/boost_1_32_0/libs/thread/src/condition.cpp?rev=30)
45
* is identical to pthreads-win32 (http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32).
46
* Current Boost uses yet another (although seemingly equivalent) algorithm which came from their 'thread rewrite' effort.
48
* This file includes timedWait/signal/broadcast implementations translated to WebKit coding style from the latest algorithm by
49
* Alexander Terekhov and Louis Thomas, as captured here: http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32
50
* It replaces the implementation of their previous algorithm, also documented in the same source above.
51
* The naming and comments are left very close to original to enable easy cross-check.
53
* The corresponding Pthreads-win32 License is included below, and CONTRIBUTORS file which it refers to is added to
54
* source directory (as CONTRIBUTORS.pthreads-win32).
58
* Pthreads-win32 - POSIX Threads Library for Win32
59
* Copyright(C) 1998 John E. Bossom
60
* Copyright(C) 1999,2005 Pthreads-win32 contributors
62
* Contact Email: rpj@callisto.canberra.edu.au
64
* The current list of contributors is contained
65
* in the file CONTRIBUTORS included with the source
66
* code distribution. The list can also be seen at the
67
* following World Wide Web location:
68
* http://sources.redhat.com/pthreads-win32/contributors.html
70
* This library is free software; you can redistribute it and/or
71
* modify it under the terms of the GNU Lesser General Public
72
* License as published by the Free Software Foundation; either
73
* version 2 of the License, or (at your option) any later version.
75
* This library is distributed in the hope that it will be useful,
76
* but WITHOUT ANY WARRANTY; without even the implied warranty of
77
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
78
* Lesser General Public License for more details.
80
* You should have received a copy of the GNU Lesser General Public
81
* License along with this library in the file COPYING.LIB;
82
* if not, write to the Free Software Foundation, Inc.,
83
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
63
86
#include "config.h"
290
326
void Mutex::unlock()
292
328
--m_mutex.m_recursionCount;
293
::LeaveCriticalSection(&m_mutex.m_internalMutex);
329
LeaveCriticalSection(&m_mutex.m_internalMutex);
332
bool PlatformCondition::timedWait(PlatformMutex& mutex, DWORD durationMilliseconds)
334
// Enter the wait state.
335
DWORD res = WaitForSingleObject(m_blockLock, INFINITE);
336
ASSERT(res == WAIT_OBJECT_0);
338
res = ReleaseSemaphore(m_blockLock, 1, 0);
341
LeaveCriticalSection(&mutex.m_internalMutex);
343
// Main wait - use timeout.
344
bool timedOut = (WaitForSingleObject(m_blockQueue, durationMilliseconds) == WAIT_TIMEOUT);
346
res = WaitForSingleObject(m_unblockLock, INFINITE);
347
ASSERT(res == WAIT_OBJECT_0);
349
int signalsLeft = m_waitersToUnblock;
351
if (m_waitersToUnblock)
352
--m_waitersToUnblock;
353
else if (++m_waitersGone == (INT_MAX / 2)) { // timeout/canceled or spurious semaphore
354
// timeout or spurious wakeup occured, normalize the m_waitersGone count
355
// this may occur if many calls to wait with a timeout are made and
356
// no call to notify_* is made
357
res = WaitForSingleObject(m_blockLock, INFINITE);
358
ASSERT(res == WAIT_OBJECT_0);
359
m_waitersBlocked -= m_waitersGone;
360
res = ReleaseSemaphore(m_blockLock, 1, 0);
365
res = ReleaseMutex(m_unblockLock);
368
if (signalsLeft == 1) {
369
res = ReleaseSemaphore(m_blockLock, 1, 0); // Open the gate.
373
EnterCriticalSection (&mutex.m_internalMutex);
378
void PlatformCondition::signal(bool unblockAll)
380
unsigned signalsToIssue = 0;
382
DWORD res = WaitForSingleObject(m_unblockLock, INFINITE);
383
ASSERT(res == WAIT_OBJECT_0);
385
if (m_waitersToUnblock) { // the gate is already closed
386
if (!m_waitersBlocked) { // no-op
387
res = ReleaseMutex(m_unblockLock);
393
signalsToIssue = m_waitersBlocked;
394
m_waitersToUnblock += m_waitersBlocked;
395
m_waitersBlocked = 0;
398
++m_waitersToUnblock;
401
} else if (m_waitersBlocked > m_waitersGone) {
402
res = WaitForSingleObject(m_blockLock, INFINITE); // Close the gate.
403
ASSERT(res == WAIT_OBJECT_0);
404
if (m_waitersGone != 0) {
405
m_waitersBlocked -= m_waitersGone;
409
signalsToIssue = m_waitersBlocked;
410
m_waitersToUnblock = m_waitersBlocked;
411
m_waitersBlocked = 0;
414
m_waitersToUnblock = 1;
418
res = ReleaseMutex(m_unblockLock);
423
res = ReleaseMutex(m_unblockLock);
426
if (signalsToIssue) {
427
res = ReleaseSemaphore(m_blockQueue, signalsToIssue, 0);
296
432
static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1);
298
434
ThreadCondition::ThreadCondition()
300
m_condition.m_timedOut = 0;
301
m_condition.m_blocked = 0;
302
m_condition.m_waitingForRemoval = 0;
303
m_condition.m_gate = ::CreateSemaphore(0, 1, 1, 0);
304
m_condition.m_queue = ::CreateSemaphore(0, 0, MaxSemaphoreCount, 0);
305
m_condition.m_mutex = ::CreateMutex(0, 0, 0);
436
m_condition.m_waitersGone = 0;
437
m_condition.m_waitersBlocked = 0;
438
m_condition.m_waitersToUnblock = 0;
439
m_condition.m_blockLock = CreateSemaphore(0, 1, 1, 0);
440
m_condition.m_blockQueue = CreateSemaphore(0, 0, MaxSemaphoreCount, 0);
441
m_condition.m_unblockLock = CreateMutex(0, 0, 0);
307
if (!m_condition.m_gate || !m_condition.m_queue || !m_condition.m_mutex) {
308
if (m_condition.m_gate)
309
::CloseHandle(m_condition.m_gate);
310
if (m_condition.m_queue)
311
::CloseHandle(m_condition.m_queue);
312
if (m_condition.m_mutex)
313
::CloseHandle(m_condition.m_mutex);
443
if (!m_condition.m_blockLock || !m_condition.m_blockQueue || !m_condition.m_unblockLock) {
444
if (m_condition.m_blockLock)
445
CloseHandle(m_condition.m_blockLock);
446
if (m_condition.m_blockQueue)
447
CloseHandle(m_condition.m_blockQueue);
448
if (m_condition.m_unblockLock)
449
CloseHandle(m_condition.m_unblockLock);
317
453
ThreadCondition::~ThreadCondition()
319
::CloseHandle(m_condition.m_gate);
320
::CloseHandle(m_condition.m_queue);
321
::CloseHandle(m_condition.m_mutex);
455
CloseHandle(m_condition.m_blockLock);
456
CloseHandle(m_condition.m_blockQueue);
457
CloseHandle(m_condition.m_unblockLock);
324
460
void ThreadCondition::wait(Mutex& mutex)
326
PlatformMutex& cs = mutex.impl();
328
// Enter the wait state.
329
DWORD res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
330
ASSERT(res == WAIT_OBJECT_0);
331
++m_condition.m_blocked;
332
res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
335
::LeaveCriticalSection(&cs.m_internalMutex);
337
res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
338
ASSERT(res == WAIT_OBJECT_0);
340
res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
341
ASSERT(res == WAIT_OBJECT_0);
342
size_t wasWaiting = m_condition.m_waitingForRemoval;
343
size_t wasTimedOut = m_condition.m_timedOut;
344
if (wasWaiting != 0) {
345
if (--m_condition.m_waitingForRemoval == 0) {
346
if (m_condition.m_blocked != 0) {
347
res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); // open m_gate
351
else if (m_condition.m_timedOut != 0)
352
m_condition.m_timedOut = 0;
354
} else if (++m_condition.m_timedOut == ((std::numeric_limits<unsigned>::max)() / 2)) {
355
// timeout occured, normalize the m_condition.m_timedOut count
356
// this may occur if many calls to wait with a timeout are made and
357
// no call to notify_* is made
358
res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
359
ASSERT(res == WAIT_OBJECT_0);
360
m_condition.m_blocked -= m_condition.m_timedOut;
361
res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
363
m_condition.m_timedOut = 0;
365
res = ::ReleaseMutex(m_condition.m_mutex);
368
if (wasWaiting == 1) {
369
for (/**/ ; wasTimedOut; --wasTimedOut) {
370
// better now than spurious later
371
res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
372
ASSERT(res == WAIT_OBJECT_0);
374
res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
378
::EnterCriticalSection (&cs.m_internalMutex);
462
m_condition.timedWait(mutex.impl(), INFINITE);
381
bool ThreadCondition::timedWait(Mutex& mutex, double interval)
465
bool ThreadCondition::timedWait(Mutex& mutex, double absoluteTime)
467
double currentTime = WTF::currentTime();
469
// Time is in the past - return immediately.
470
if (absoluteTime < currentTime)
473
// Time is too far in the future (and would overflow unsigned long) - wait forever.
474
if (absoluteTime - currentTime > static_cast<double>(INT_MAX) / 1000.0) {
479
double intervalMilliseconds = (absoluteTime - currentTime) * 1000.0;
480
return m_condition.timedWait(mutex.impl(), static_cast<unsigned long>(intervalMilliseconds));
388
483
void ThreadCondition::signal()
390
unsigned signals = 0;
392
DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
393
ASSERT(res == WAIT_OBJECT_0);
395
if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
396
if (m_condition.m_blocked == 0) {
397
res = ::ReleaseMutex(m_condition.m_mutex);
402
++m_condition.m_waitingForRemoval;
403
--m_condition.m_blocked;
407
res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
408
ASSERT(res == WAIT_OBJECT_0);
409
if (m_condition.m_blocked > m_condition.m_timedOut) {
410
if (m_condition.m_timedOut != 0) {
411
m_condition.m_blocked -= m_condition.m_timedOut;
412
m_condition.m_timedOut = 0;
414
signals = m_condition.m_waitingForRemoval = 1;
415
--m_condition.m_blocked;
417
res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
422
res =::ReleaseMutex(m_condition.m_mutex);
426
res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
485
m_condition.signal(false); // Unblock only 1 thread.
431
488
void ThreadCondition::broadcast()
433
unsigned signals = 0;
435
DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
436
ASSERT(res == WAIT_OBJECT_0);
438
if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
439
if (m_condition.m_blocked == 0) {
440
res = ::ReleaseMutex(m_condition.m_mutex);
445
m_condition.m_waitingForRemoval += (signals = m_condition.m_blocked);
446
m_condition.m_blocked = 0;
448
res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
449
ASSERT(res == WAIT_OBJECT_0);
450
if (m_condition.m_blocked > m_condition.m_timedOut) {
451
if (m_condition.m_timedOut != 0) {
452
m_condition.m_blocked -= m_condition.m_timedOut;
453
m_condition.m_timedOut = 0;
455
signals = m_condition.m_waitingForRemoval = m_condition.m_blocked;
456
m_condition.m_blocked = 0;
458
res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
463
res = ::ReleaseMutex(m_condition.m_mutex);
467
res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
490
m_condition.signal(true); // Unblock all threads.
472
493
} // namespace WTF