Changeset 209938 in webkit


Ignore:
Timestamp:
Dec 16, 2016 2:26:09 PM (7 years ago)
Author:
msaboff@apple.com
Message:

REGRESSION: HipChat and Mail sometimes hang beneath JSC::Heap::lastChanceToFinalize()
https://bugs.webkit.org/show_bug.cgi?id=165962

Reviewed by Filip Pizlo.

There is an inherent race in Condition::waitFor() where the timeout can happen just before
a notify from another thread.

Fixed this by adding a condition variable and flag to each AutomaticThread. The flag
is used to signify to a notifying thread that the thread is waiting. That flag is set
in the waiting thread before calling waitFor() and cleared by another thread when it
notifies the thread. The access to that flag happens when the lock is held.
Now the waiting thread checks if the flag after a timeout to see that it in fact should
proceed like a normal notification.

The added condition variable allows us to target a specific thread. We used to keep a list
of waiting threads, now we keep a list of all threads. To notify one thread, we look for
a waiting thread and notify it directly. If we can't find a waiting thread, we start a
sleeping thread.

We notify all threads by waking all waiting threads and starting all sleeping threads.

  • wtf/AutomaticThread.cpp:

(WTF::AutomaticThreadCondition::notifyOne):
(WTF::AutomaticThreadCondition::notifyAll):
(WTF::AutomaticThread::isWaiting):
(WTF::AutomaticThread::notify):
(WTF::AutomaticThread::start):

  • wtf/AutomaticThread.h:
Location:
trunk/Source/WTF
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WTF/ChangeLog

    r209913 r209938  
     12016-12-16  Michael Saboff  <msaboff@apple.com>
     2
     3        REGRESSION: HipChat and Mail sometimes hang beneath JSC::Heap::lastChanceToFinalize()
     4        https://bugs.webkit.org/show_bug.cgi?id=165962
     5
     6        Reviewed by Filip Pizlo.
     7
     8        There is an inherent race in Condition::waitFor() where the timeout can happen just before
     9        a notify from another thread.
     10
     11        Fixed this by adding a condition variable and flag to each AutomaticThread.  The flag
     12        is used to signify to a notifying thread that the thread is waiting.  That flag is set
     13        in the waiting thread before calling waitFor() and cleared by another thread when it
     14        notifies the thread.  The access to that flag happens when the lock is held.
     15        Now the waiting thread checks if the flag after a timeout to see that it in fact should
     16        proceed like a normal notification.
     17
     18        The added condition variable allows us to target a specific thread.  We used to keep a list
     19        of waiting threads, now we keep a list of all threads.  To notify one thread, we look for
     20        a waiting thread and notify it directly.  If we can't find a waiting thread, we start a
     21        sleeping thread.
     22
     23        We notify all threads by waking all waiting threads and starting all sleeping threads.
     24
     25        * wtf/AutomaticThread.cpp:
     26        (WTF::AutomaticThreadCondition::notifyOne):
     27        (WTF::AutomaticThreadCondition::notifyAll):
     28        (WTF::AutomaticThread::isWaiting):
     29        (WTF::AutomaticThread::notify):
     30        (WTF::AutomaticThread::start):
     31        * wtf/AutomaticThread.h:
     32
    1332016-12-15  Myles C. Maxfield  <mmaxfield@apple.com>
    234
  • trunk/Source/WTF/wtf/AutomaticThread.cpp

    r208982 r209938  
    4848void AutomaticThreadCondition::notifyOne(const LockHolder& locker)
    4949{
    50     if (m_condition.notifyOne())
    51         return;
    52    
    53     if (m_threads.isEmpty())
    54         return;
    55    
    56     m_threads.takeLast()->start(locker);
     50    for (AutomaticThread* thread : m_threads) {
     51        if (thread->isWaiting(locker)) {
     52            thread->notify(locker);
     53            return;
     54        }
     55    }
     56
     57    for (AutomaticThread* thread : m_threads) {
     58        if (!thread->hasUnderlyingThread(locker)) {
     59            thread->start(locker);
     60            return;
     61        }
     62    }
     63
     64    m_condition.notifyOne();
    5765}
    5866
     
    6068{
    6169    m_condition.notifyAll();
    62    
    63     for (AutomaticThread* thread : m_threads)
    64         thread->start(locker);
    65     m_threads.clear();
     70
     71    for (AutomaticThread* thread : m_threads) {
     72        if (thread->isWaiting(locker))
     73            thread->notify(locker);
     74        else if (!thread->hasUnderlyingThread(locker))
     75            thread->start(locker);
     76    }
    6677}
    6778
     
    116127    m_isRunning = false;
    117128    return true;
     129}
     130
     131bool AutomaticThread::isWaiting(const LockHolder& locker)
     132{
     133    return hasUnderlyingThread(locker) && m_isWaiting;
     134}
     135
     136bool AutomaticThread::notify(const LockHolder& locker)
     137{
     138    ASSERT_UNUSED(locker, hasUnderlyingThread(locker));
     139    m_isWaiting = false;
     140    return m_waitCondition.notifyOne();
    118141}
    119142
     
    162185            if (!ASSERT_DISABLED) {
    163186                LockHolder locker(*m_lock);
    164                 ASSERT(!m_condition->contains(locker, this));
     187                ASSERT(m_condition->contains(locker, this));
    165188            }
    166189           
     
    181204                        RELEASE_ASSERT(result == PollResult::Wait);
    182205                        // Shut the thread down after one second.
     206                        m_isWaiting = true;
    183207                        bool awokenByNotify =
    184                             m_condition->m_condition.waitFor(*m_lock, 1_s);
    185                         if (!awokenByNotify) {
     208                            m_waitCondition.waitFor(*m_lock, 1_s);
     209                        if (verbose && !awokenByNotify && !m_isWaiting)
     210                            dataLog(RawPointer(this), ": waitFor timed out, but notified via m_isWaiting flag!\n");
     211                        if (m_isWaiting) {
     212                            m_isWaiting = false;
    186213                            if (verbose)
    187214                                dataLog(RawPointer(this), ": Going to sleep!\n");
    188                             m_condition->add(locker, this);
    189215                            return;
    190216                        }
  • trunk/Source/WTF/wtf/AutomaticThread.h

    r208306 r209938  
    121121    // mechanism (set some flag and then notify the condition).
    122122    bool tryStop(const LockHolder&);
    123    
     123
     124    bool isWaiting(const LockHolder&);
     125
     126    bool notify(const LockHolder&);
     127
    124128    void join();
    125129   
     
    178182    RefPtr<AutomaticThreadCondition> m_condition;
    179183    bool m_isRunning { true };
     184    bool m_isWaiting { false };
    180185    bool m_hasUnderlyingThread { false };
     186    Condition m_waitCondition;
    181187    Condition m_isRunningCondition;
    182188};
Note: See TracChangeset for help on using the changeset viewer.