Changeset 259683 in webkit


Ignore:
Timestamp:
Apr 7, 2020 4:24:52 PM (4 years ago)
Author:
ysuzuki@apple.com
Message:

[JSC] Collect-continuously thread should take m_collectContinuouslyLock while it is waking up concurrent collector thread
https://bugs.webkit.org/show_bug.cgi?id=210163

Reviewed by Saam Barati.

JSTests:

  • stress/collect-continuously-should-not-wake-concurrent-collector-after-prevent-collection-is-called.js: Added.

(let.theCode):

Source/JavaScriptCore:

Collect-Continuously thread has fancy race issue.

In Heap::preventCollection, we first take m_collectContinuouslyLock to ensure collect-continuously thread is not working, and then
we ensure collector thread is stopped by using waitForCollector. However our collect-continuously thread is implemented like this.

while (!m_shouldStopCollectingContinuously) {

{ (A)

LockHolder locker(*m_threadLock);
if (m_requests.isEmpty()) {

m_requests.append(WTF::nullopt);
m_lastGrantedTicket++;
m_threadCondition->notifyOne(locker); (B) WAKING UP concurrent collector thread.

}

}

{

LockHolder locker(m_collectContinuouslyLock);
...
while (!hasElapsed(timeToWakeUp) && !m_shouldStopCollectingContinuously)

m_collectContinuouslyCondition.waitUntil(m_collectContinuouslyLock, timeToWakeUp);

}

}

Even if m_collectContinuouslyLock is taken, collect-continuously thread is still able to wake up concurrent collector thread
since (B)'s code is not guarded by m_collectContinuouslyLock. The following sequence can happen,

  1. The main thread calls Heap::preventCollection to ensure all collection is stopped.
  2. The collect-continuously thread is at (A) point.
  3. The main thread takes m_collectContinuouslyLock. This is OK.
  4. The main thread calls waitForCollector to ensure that concurrent collector thread is stopped.
  5. The collect-continuously thread executes (B). It is allowed since this is not guarded by m_collectContinuouslyLock. So, concurrent collector starts working.
  6. While the main thread called Heap::preventCollection, concurrent collector starts collection!

We should guard (A)'s block with m_collectContinuouslyLock too.

  • heap/Heap.cpp:

(JSC::Heap::notifyIsSafeToCollect):

Location:
trunk
Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r259681 r259683  
     12020-04-07  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] Collect-continuously thread should take m_collectContinuouslyLock while it is waking up concurrent collector thread
     4        https://bugs.webkit.org/show_bug.cgi?id=210163
     5
     6        Reviewed by Saam Barati.
     7
     8        * stress/collect-continuously-should-not-wake-concurrent-collector-after-prevent-collection-is-called.js: Added.
     9        (let.theCode):
     10
    1112020-04-07  Saam Barati  <sbarati@apple.com>
    212
  • trunk/Source/JavaScriptCore/ChangeLog

    r259681 r259683  
     12020-04-07  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] Collect-continuously thread should take m_collectContinuouslyLock while it is waking up concurrent collector thread
     4        https://bugs.webkit.org/show_bug.cgi?id=210163
     5
     6        Reviewed by Saam Barati.
     7
     8        Collect-Continuously thread has fancy race issue.
     9
     10        In Heap::preventCollection, we first take m_collectContinuouslyLock to ensure collect-continuously thread is not working, and then
     11        we ensure collector thread is stopped by using waitForCollector. However our collect-continuously thread is implemented like this.
     12
     13                while (!m_shouldStopCollectingContinuously) {
     14                    { // (A)
     15                        LockHolder locker(*m_threadLock);
     16                        if (m_requests.isEmpty()) {
     17                            m_requests.append(WTF::nullopt);
     18                            m_lastGrantedTicket++;
     19                            m_threadCondition->notifyOne(locker);  // (B) WAKING UP concurrent collector thread.
     20                        }
     21                    }
     22
     23                    {
     24                        LockHolder locker(m_collectContinuouslyLock);
     25                        ...
     26                        while (!hasElapsed(timeToWakeUp) && !m_shouldStopCollectingContinuously)
     27                            m_collectContinuouslyCondition.waitUntil(m_collectContinuouslyLock, timeToWakeUp);
     28                    }
     29                }
     30
     31        Even if m_collectContinuouslyLock is taken, collect-continuously thread is still able to wake up concurrent collector thread
     32        since (B)'s code is not guarded by m_collectContinuouslyLock. The following sequence can happen,
     33
     34            1. The main thread calls Heap::preventCollection to ensure all collection is stopped.
     35            2. The collect-continuously thread is at (A) point.
     36            3. The main thread takes m_collectContinuouslyLock. This is OK.
     37            4. The main thread calls waitForCollector to ensure that concurrent collector thread is stopped.
     38            5. The collect-continuously thread executes (B). It is allowed since this is not guarded by m_collectContinuouslyLock. So, concurrent collector starts working.
     39            6. While the main thread called Heap::preventCollection, concurrent collector starts collection!
     40
     41        We should guard (A)'s block with m_collectContinuouslyLock too.
     42
     43        * heap/Heap.cpp:
     44        (JSC::Heap::notifyIsSafeToCollect):
     45
    1462020-04-07  Saam Barati  <sbarati@apple.com>
    247
  • trunk/Source/JavaScriptCore/heap/Heap.cpp

    r258478 r259683  
    29002900                MonotonicTime initialTime = MonotonicTime::now();
    29012901                Seconds period = Seconds::fromMilliseconds(Options::collectContinuouslyPeriodMS());
    2902                 while (!m_shouldStopCollectingContinuously) {
     2902                while (true) {
     2903                    LockHolder locker(m_collectContinuouslyLock);
    29032904                    {
    29042905                        LockHolder locker(*m_threadLock);
     
    29102911                    }
    29112912                   
    2912                     {
    2913                         LockHolder locker(m_collectContinuouslyLock);
    2914                         Seconds elapsed = MonotonicTime::now() - initialTime;
    2915                         Seconds elapsedInPeriod = elapsed % period;
    2916                         MonotonicTime timeToWakeUp =
    2917                             initialTime + elapsed - elapsedInPeriod + period;
    2918                         while (!hasElapsed(timeToWakeUp) && !m_shouldStopCollectingContinuously) {
    2919                             m_collectContinuouslyCondition.waitUntil(
    2920                                 m_collectContinuouslyLock, timeToWakeUp);
    2921                         }
     2913                    Seconds elapsed = MonotonicTime::now() - initialTime;
     2914                    Seconds elapsedInPeriod = elapsed % period;
     2915                    MonotonicTime timeToWakeUp =
     2916                        initialTime + elapsed - elapsedInPeriod + period;
     2917                    while (!hasElapsed(timeToWakeUp) && !m_shouldStopCollectingContinuously) {
     2918                        m_collectContinuouslyCondition.waitUntil(
     2919                            m_collectContinuouslyLock, timeToWakeUp);
    29222920                    }
     2921                    if (m_shouldStopCollectingContinuously)
     2922                        break;
    29232923                }
    29242924            }, ThreadType::GarbageCollection);
Note: See TracChangeset for help on using the changeset viewer.