Changeset 229515 in webkit


Ignore:
Timestamp:
Mar 11, 2018 12:03:30 AM (6 years ago)
Author:
Yusuke Suzuki
Message:

[Win] Use SRWLOCK and CONDITION_VARIABLE to simplify implementation
https://bugs.webkit.org/show_bug.cgi?id=183541

Reviewed by Darin Adler.

After Windows Vista, Windows offers SRWLOCK and CONDITION_VARIABLE.
They can simplify the implementation of our WTF::Mutex and WTF::ThreadCondition.

C++ std::mutex and std::condition_variable uses std::chrono for their timed
functions. Since std::chrono is not overflow-aware, we cannot reliably use
this functionalities. This is why we still keep WTF::Mutex and WTF::ThreadCondition.
They are used for ParkingLot.

  • wtf/ThreadingPrimitives.h:
  • wtf/ThreadingWin.cpp:

(WTF::Mutex::Mutex):
(WTF::Mutex::~Mutex):
(WTF::Mutex::lock):
(WTF::Mutex::tryLock):
(WTF::Mutex::unlock):
(WTF::absoluteTimeToWaitTimeoutInterval):
(WTF::ThreadCondition::ThreadCondition):
(WTF::ThreadCondition::~ThreadCondition):
(WTF::ThreadCondition::wait):
(WTF::ThreadCondition::timedWait):
(WTF::ThreadCondition::signal):
(WTF::ThreadCondition::broadcast):
(WTF::PlatformCondition::timedWait): Deleted.
(WTF::PlatformCondition::signal): Deleted.

Location:
trunk/Source/WTF
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WTF/ChangeLog

    r229504 r229515  
     12018-03-11  Yusuke Suzuki  <utatane.tea@gmail.com>
     2
     3        [Win] Use SRWLOCK and CONDITION_VARIABLE to simplify implementation
     4        https://bugs.webkit.org/show_bug.cgi?id=183541
     5
     6        Reviewed by Darin Adler.
     7
     8        After Windows Vista, Windows offers SRWLOCK and CONDITION_VARIABLE.
     9        They can simplify the implementation of our WTF::Mutex and WTF::ThreadCondition.
     10
     11        C++ std::mutex and std::condition_variable uses std::chrono for their timed
     12        functions. Since std::chrono is not overflow-aware, we cannot reliably use
     13        this functionalities. This is why we still keep WTF::Mutex and WTF::ThreadCondition.
     14        They are used for ParkingLot.
     15
     16        * wtf/ThreadingPrimitives.h:
     17        * wtf/ThreadingWin.cpp:
     18        (WTF::Mutex::Mutex):
     19        (WTF::Mutex::~Mutex):
     20        (WTF::Mutex::lock):
     21        (WTF::Mutex::tryLock):
     22        (WTF::Mutex::unlock):
     23        (WTF::absoluteTimeToWaitTimeoutInterval):
     24        (WTF::ThreadCondition::ThreadCondition):
     25        (WTF::ThreadCondition::~ThreadCondition):
     26        (WTF::ThreadCondition::wait):
     27        (WTF::ThreadCondition::timedWait):
     28        (WTF::ThreadCondition::signal):
     29        (WTF::ThreadCondition::broadcast):
     30        (WTF::PlatformCondition::timedWait): Deleted.
     31        (WTF::PlatformCondition::signal): Deleted.
     32
    1332018-03-10  Commit Queue  <commit-queue@webkit.org>
    234
  • trunk/Source/WTF/wtf/ThreadingPrimitives.h

    r228942 r229515  
    5656using ThreadIdentifier = uint32_t;
    5757using PlatformThreadHandle = HANDLE;
    58 struct PlatformMutex {
    59     CRITICAL_SECTION m_internalMutex;
    60     size_t m_recursionCount;
    61 };
    62 struct PlatformCondition {
    63     size_t m_waitersGone;
    64     size_t m_waitersBlocked;
    65     size_t m_waitersToUnblock;
    66     HANDLE m_blockLock;
    67     HANDLE m_blockQueue;
    68     HANDLE m_unblockLock;
     58using PlatformMutex = SRWLOCK;
     59using PlatformCondition = CONDITION_VARIABLE;
     60#else
     61#error "Not supported platform"
     62#endif
    6963
    70     bool timedWait(PlatformMutex&, DWORD durationMilliseconds);
    71     void signal(bool unblockAll);
    72 };
    73 #else
    74 typedef void* PlatformMutex;
    75 typedef void* PlatformCondition;
    76 #endif
    77    
    7864class Mutex {
    79     WTF_MAKE_NONCOPYABLE(Mutex); WTF_MAKE_FAST_ALLOCATED;
     65    WTF_MAKE_NONCOPYABLE(Mutex);
     66    WTF_MAKE_FAST_ALLOCATED;
    8067public:
    8168    WTF_EXPORT_PRIVATE Mutex();
     
    8673    WTF_EXPORT_PRIVATE void unlock();
    8774
    88 public:
    8975    PlatformMutex& impl() { return m_mutex; }
     76
    9077private:
    9178    PlatformMutex m_mutex;
     
    9683class ThreadCondition {
    9784    WTF_MAKE_NONCOPYABLE(ThreadCondition);
     85    WTF_MAKE_FAST_ALLOCATED;
    9886public:
    9987    WTF_EXPORT_PRIVATE ThreadCondition();
     
    11098};
    11199
    112 #if OS(WINDOWS)
    113 // Returns an interval in milliseconds suitable for passing to one of the Win32 wait functions (e.g., ::WaitForSingleObject).
    114 WTF_EXPORT_PRIVATE DWORD absoluteTimeToWaitTimeoutInterval(WallTime absoluteTime);
    115 #endif
    116 
    117100} // namespace WTF
    118101
     
    121104using WTF::ThreadCondition;
    122105
    123 #if OS(WINDOWS)
    124 using WTF::absoluteTimeToWaitTimeoutInterval;
    125 #endif
    126 
    127106#endif // ThreadingPrimitives_h
  • trunk/Source/WTF/wtf/ThreadingWin.cpp

    r229209 r229515  
    368368Mutex::Mutex()
    369369{
    370     m_mutex.m_recursionCount = 0;
    371     InitializeCriticalSection(&m_mutex.m_internalMutex);
     370    InitializeSRWLock(&m_mutex);
    372371}
    373372
    374373Mutex::~Mutex()
    375374{
    376     DeleteCriticalSection(&m_mutex.m_internalMutex);
    377375}
    378376
    379377void Mutex::lock()
    380378{
    381     EnterCriticalSection(&m_mutex.m_internalMutex);
    382     ++m_mutex.m_recursionCount;
    383 }
    384    
    385 #pragma warning(suppress: 26115)
     379    AcquireSRWLockExclusive(&m_mutex);
     380}
     381
    386382bool Mutex::tryLock()
    387383{
    388     // This method is modeled after the behavior of pthread_mutex_trylock,
    389     // which will return an error if the lock is already owned by the
    390     // current thread.  Since the primitive Win32 'TryEnterCriticalSection'
    391     // treats this as a successful case, it changes the behavior of several
    392     // tests in WebKit that check to see if the current thread already
    393     // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord)
    394     DWORD result = TryEnterCriticalSection(&m_mutex.m_internalMutex);
    395    
    396     if (result != 0) {       // We got the lock
    397         // If this thread already had the lock, we must unlock and
    398         // return false so that we mimic the behavior of POSIX's
    399         // pthread_mutex_trylock:
    400         if (m_mutex.m_recursionCount > 0) {
    401             LeaveCriticalSection(&m_mutex.m_internalMutex);
    402             return false;
    403         }
    404 
    405         ++m_mutex.m_recursionCount;
    406         return true;
    407     }
    408 
    409     return false;
     384    return TryAcquireSRWLockExclusive(&m_mutex);
    410385}
    411386
    412387void Mutex::unlock()
    413388{
    414     ASSERT(m_mutex.m_recursionCount);
    415     --m_mutex.m_recursionCount;
    416     LeaveCriticalSection(&m_mutex.m_internalMutex);
    417 }
    418 
    419 bool PlatformCondition::timedWait(PlatformMutex& mutex, DWORD durationMilliseconds)
    420 {
    421     // Enter the wait state.
    422     DWORD res = WaitForSingleObject(m_blockLock, INFINITE);
    423     ASSERT_UNUSED(res, res == WAIT_OBJECT_0);
    424     ++m_waitersBlocked;
    425     res = ReleaseSemaphore(m_blockLock, 1, 0);
    426     ASSERT_UNUSED(res, res);
    427 
    428     --mutex.m_recursionCount;
    429     LeaveCriticalSection(&mutex.m_internalMutex);
    430 
    431     // Main wait - use timeout.
    432     bool timedOut = (WaitForSingleObject(m_blockQueue, durationMilliseconds) == WAIT_TIMEOUT);
    433 
    434     res = WaitForSingleObject(m_unblockLock, INFINITE);
    435     ASSERT_UNUSED(res, res == WAIT_OBJECT_0);
    436 
    437     int signalsLeft = m_waitersToUnblock;
    438 
    439     if (m_waitersToUnblock)
    440         --m_waitersToUnblock;
    441     else if (++m_waitersGone == (INT_MAX / 2)) { // timeout/canceled or spurious semaphore
    442         // timeout or spurious wakeup occured, normalize the m_waitersGone count
    443         // this may occur if many calls to wait with a timeout are made and
    444         // no call to notify_* is made
    445         res = WaitForSingleObject(m_blockLock, INFINITE);
    446         ASSERT_UNUSED(res, res == WAIT_OBJECT_0);
    447         m_waitersBlocked -= m_waitersGone;
    448         res = ReleaseSemaphore(m_blockLock, 1, 0);
    449         ASSERT_UNUSED(res, res);
    450         m_waitersGone = 0;
    451     }
    452 
    453     res = ReleaseMutex(m_unblockLock);
    454     ASSERT_UNUSED(res, res);
    455 
    456     if (signalsLeft == 1) {
    457         res = ReleaseSemaphore(m_blockLock, 1, 0); // Open the gate.
    458         ASSERT_UNUSED(res, res);
    459     }
    460 
    461     EnterCriticalSection (&mutex.m_internalMutex);
    462     ++mutex.m_recursionCount;
    463 
    464     return !timedOut;
    465 }
    466 
    467 void PlatformCondition::signal(bool unblockAll)
    468 {
    469     unsigned signalsToIssue = 0;
    470 
    471     DWORD res = WaitForSingleObject(m_unblockLock, INFINITE);
    472     ASSERT_UNUSED(res, res == WAIT_OBJECT_0);
    473 
    474     if (m_waitersToUnblock) { // the gate is already closed
    475         if (!m_waitersBlocked) { // no-op
    476             res = ReleaseMutex(m_unblockLock);
    477             ASSERT_UNUSED(res, res);
    478             return;
    479         }
    480 
    481         if (unblockAll) {
    482             signalsToIssue = m_waitersBlocked;
    483             m_waitersToUnblock += m_waitersBlocked;
    484             m_waitersBlocked = 0;
    485         } else {
    486             signalsToIssue = 1;
    487             ++m_waitersToUnblock;
    488             --m_waitersBlocked;
    489         }
    490     } else if (m_waitersBlocked > m_waitersGone) {
    491         res = WaitForSingleObject(m_blockLock, INFINITE); // Close the gate.
    492         ASSERT_UNUSED(res, res == WAIT_OBJECT_0);
    493         if (m_waitersGone != 0) {
    494             m_waitersBlocked -= m_waitersGone;
    495             m_waitersGone = 0;
    496         }
    497         if (unblockAll) {
    498             signalsToIssue = m_waitersBlocked;
    499             m_waitersToUnblock = m_waitersBlocked;
    500             m_waitersBlocked = 0;
    501         } else {
    502             signalsToIssue = 1;
    503             m_waitersToUnblock = 1;
    504             --m_waitersBlocked;
    505         }
    506     } else { // No-op.
    507         res = ReleaseMutex(m_unblockLock);
    508         ASSERT_UNUSED(res, res);
    509         return;
    510     }
    511 
    512     res = ReleaseMutex(m_unblockLock);
    513     ASSERT_UNUSED(res, res);
    514 
    515     if (signalsToIssue) {
    516         res = ReleaseSemaphore(m_blockQueue, signalsToIssue, 0);
    517         ASSERT_UNUSED(res, res);
    518     }
    519 }
    520 
    521 static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1);
     389    ReleaseSRWLockExclusive(&m_mutex);
     390}
     391
     392// Returns an interval in milliseconds suitable for passing to one of the Win32 wait functions (e.g., ::WaitForSingleObject).
     393static DWORD absoluteTimeToWaitTimeoutInterval(WallTime absoluteTime)
     394{
     395    WallTime currentTime = WallTime::now();
     396
     397    // Time is in the past - return immediately.
     398    if (absoluteTime < currentTime)
     399        return 0;
     400
     401    // Time is too far in the future (and would overflow unsigned long) - wait forever.
     402    if ((absoluteTime - currentTime) > Seconds::fromMilliseconds(INT_MAX))
     403        return INFINITE;
     404
     405    return static_cast<DWORD>((absoluteTime - currentTime).milliseconds());
     406}
    522407
    523408ThreadCondition::ThreadCondition()
    524409{
    525     m_condition.m_waitersGone = 0;
    526     m_condition.m_waitersBlocked = 0;
    527     m_condition.m_waitersToUnblock = 0;
    528     m_condition.m_blockLock = CreateSemaphore(0, 1, 1, 0);
    529     m_condition.m_blockQueue = CreateSemaphore(0, 0, MaxSemaphoreCount, 0);
    530     m_condition.m_unblockLock = CreateMutex(0, 0, 0);
    531 
    532     if (!m_condition.m_blockLock || !m_condition.m_blockQueue || !m_condition.m_unblockLock) {
    533         if (m_condition.m_blockLock)
    534             CloseHandle(m_condition.m_blockLock);
    535         if (m_condition.m_blockQueue)
    536             CloseHandle(m_condition.m_blockQueue);
    537         if (m_condition.m_unblockLock)
    538             CloseHandle(m_condition.m_unblockLock);
    539     }
     410    InitializeConditionVariable(&m_condition);
    540411}
    541412
    542413ThreadCondition::~ThreadCondition()
    543414{
    544     CloseHandle(m_condition.m_blockLock);
    545     CloseHandle(m_condition.m_blockQueue);
    546     CloseHandle(m_condition.m_unblockLock);
    547415}
    548416
    549417void ThreadCondition::wait(Mutex& mutex)
    550418{
    551     m_condition.timedWait(mutex.impl(), INFINITE);
     419    SleepConditionVariableSRW(&m_condition, &mutex.impl(), INFINITE, 0);
    552420}
    553421
    554422bool ThreadCondition::timedWait(Mutex& mutex, WallTime absoluteTime)
    555423{
     424    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686304(v=vs.85).aspx
    556425    DWORD interval = absoluteTimeToWaitTimeoutInterval(absoluteTime);
    557 
    558426    if (!interval) {
    559427        // Consider the wait to have timed out, even if our condition has already been signaled, to
     
    562430    }
    563431
    564     return m_condition.timedWait(mutex.impl(), interval);
     432    if (SleepConditionVariableSRW(&m_condition, &mutex.impl(), interval, 0))
     433        return true;
     434    ASSERT(GetLastError() == ERROR_TIMEOUT);
     435    return false;
    565436}
    566437
    567438void ThreadCondition::signal()
    568439{
    569     m_condition.signal(false); // Unblock only 1 thread.
     440    WakeConditionVariable(&m_condition);
    570441}
    571442
    572443void ThreadCondition::broadcast()
    573444{
    574     m_condition.signal(true); // Unblock all threads.
    575 }
    576 
    577 DWORD absoluteTimeToWaitTimeoutInterval(WallTime absoluteTime)
    578 {
    579     WallTime currentTime = WallTime::now();
    580 
    581     // Time is in the past - return immediately.
    582     if (absoluteTime < currentTime)
    583         return 0;
    584 
    585     // Time is too far in the future (and would overflow unsigned long) - wait forever.
    586     if ((absoluteTime - currentTime) > Seconds::fromMilliseconds(INT_MAX))
    587         return INFINITE;
    588 
    589     return static_cast<DWORD>((absoluteTime - currentTime).milliseconds());
     445    WakeAllConditionVariable(&m_condition);
    590446}
    591447
Note: See TracChangeset for help on using the changeset viewer.