| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "platform/heap/SafePoint.h" | 5 #include "platform/heap/SafePoint.h" |
| 6 | 6 |
| 7 #include "platform/heap/Heap.h" | 7 #include "platform/heap/Heap.h" |
| 8 #include "wtf/Atomics.h" | |
| 9 #include "wtf/CurrentTime.h" | |
| 10 | 8 |
| 11 namespace blink { | 9 namespace blink { |
| 12 | 10 |
| 13 using PushAllRegistersCallback = void (*)(SafePointBarrier*, | 11 using PushAllRegistersCallback = void (*)(SafePointBarrier*, |
| 14 ThreadState*, | 12 ThreadState*, |
| 15 intptr_t*); | 13 intptr_t*); |
| 16 extern "C" void pushAllRegisters(SafePointBarrier*, | 14 extern "C" void pushAllRegisters(SafePointBarrier*, |
| 17 ThreadState*, | 15 ThreadState*, |
| 18 PushAllRegistersCallback); | 16 PushAllRegistersCallback); |
| 19 | 17 |
| 20 static double lockingTimeout() { | 18 SafePointBarrier::SafePointBarrier() {} |
| 21 // Wait time for parking all threads is at most 100 ms. | |
| 22 return 0.100; | |
| 23 } | |
| 24 | |
| 25 SafePointBarrier::SafePointBarrier() | |
| 26 : m_unparkedThreadCount(0), m_parkingRequested(0) {} | |
| 27 | 19 |
| 28 SafePointBarrier::~SafePointBarrier() {} | 20 SafePointBarrier::~SafePointBarrier() {} |
| 29 | 21 |
| 30 bool SafePointBarrier::parkOthers() { | |
| 31 ASSERT(ThreadState::current()->isAtSafePoint()); | |
| 32 | |
| 33 ThreadState* current = ThreadState::current(); | |
| 34 // Lock threadAttachMutex() to prevent threads from attaching. | |
| 35 current->lockThreadAttachMutex(); | |
| 36 const ThreadStateSet& threads = current->heap().threads(); | |
| 37 | |
| 38 MutexLocker locker(m_mutex); | |
| 39 atomicAdd(&m_unparkedThreadCount, threads.size()); | |
| 40 releaseStore(&m_parkingRequested, 1); | |
| 41 | |
| 42 for (ThreadState* state : threads) { | |
| 43 if (state == current) | |
| 44 continue; | |
| 45 | |
| 46 for (auto& interruptor : state->interruptors()) | |
| 47 interruptor->requestInterrupt(); | |
| 48 } | |
| 49 | |
| 50 while (acquireLoad(&m_unparkedThreadCount) > 0) { | |
| 51 double expirationTime = currentTime() + lockingTimeout(); | |
| 52 if (!m_parked.timedWait(m_mutex, expirationTime)) { | |
| 53 // One of the other threads did not return to a safepoint within the | |
| 54 // maximum time we allow for threads to be parked. Abandon the GC and | |
| 55 // resume the currently parked threads. | |
| 56 resumeOthers(true); | |
| 57 return false; | |
| 58 } | |
| 59 } | |
| 60 return true; | |
| 61 } | |
| 62 | |
| 63 void SafePointBarrier::resumeOthers(bool barrierLocked) { | |
| 64 ThreadState* current = ThreadState::current(); | |
| 65 const ThreadStateSet& threads = current->heap().threads(); | |
| 66 atomicSubtract(&m_unparkedThreadCount, threads.size()); | |
| 67 releaseStore(&m_parkingRequested, 0); | |
| 68 | |
| 69 if (UNLIKELY(barrierLocked)) { | |
| 70 m_resume.broadcast(); | |
| 71 } else { | |
| 72 // FIXME: Resumed threads will all contend for m_mutex just | |
| 73 // to unlock it later which is a waste of resources. | |
| 74 MutexLocker locker(m_mutex); | |
| 75 m_resume.broadcast(); | |
| 76 } | |
| 77 | |
| 78 current->unlockThreadAttachMutex(); | |
| 79 ASSERT(ThreadState::current()->isAtSafePoint()); | |
| 80 } | |
| 81 | |
| 82 void SafePointBarrier::checkAndPark(ThreadState* state, | |
| 83 SafePointAwareMutexLocker* locker) { | |
| 84 ASSERT(!state->sweepForbidden()); | |
| 85 if (acquireLoad(&m_parkingRequested)) { | |
| 86 // If we are leaving the safepoint from a SafePointAwareMutexLocker | |
| 87 // call out to release the lock before going to sleep. This enables the | |
| 88 // lock to be acquired in the sweep phase, e.g. during weak processing | |
| 89 // or finalization. The SafePointAwareLocker will reenter the safepoint | |
| 90 // and reacquire the lock after leaving this safepoint. | |
| 91 if (locker) | |
| 92 locker->reset(); | |
| 93 pushAllRegisters(this, state, parkAfterPushRegisters); | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 void SafePointBarrier::enterSafePoint(ThreadState* state) { | 22 void SafePointBarrier::enterSafePoint(ThreadState* state) { |
| 98 ASSERT(!state->sweepForbidden()); | 23 ASSERT(!state->sweepForbidden()); |
| 99 pushAllRegisters(this, state, enterSafePointAfterPushRegisters); | 24 pushAllRegisters(this, state, enterSafePointAfterPushRegisters); |
| 100 } | 25 } |
| 101 | 26 |
| 102 void SafePointBarrier::leaveSafePoint(ThreadState* state, | 27 void SafePointBarrier::leaveSafePoint(ThreadState* state, |
| 103 SafePointAwareMutexLocker* locker) { | 28 SafePointAwareMutexLocker* locker) { |
| 104 if (atomicIncrement(&m_unparkedThreadCount) > 0) | |
| 105 checkAndPark(state, locker); | |
| 106 } | |
| 107 | |
| 108 void SafePointBarrier::doPark(ThreadState* state, intptr_t* stackEnd) { | |
| 109 state->recordStackEnd(stackEnd); | |
| 110 MutexLocker locker(m_mutex); | |
| 111 if (!atomicDecrement(&m_unparkedThreadCount)) | |
| 112 m_parked.signal(); | |
| 113 while (acquireLoad(&m_parkingRequested)) | |
| 114 m_resume.wait(m_mutex); | |
| 115 atomicIncrement(&m_unparkedThreadCount); | |
| 116 } | 29 } |
| 117 | 30 |
| 118 void SafePointBarrier::doEnterSafePoint(ThreadState* state, | 31 void SafePointBarrier::doEnterSafePoint(ThreadState* state, |
| 119 intptr_t* stackEnd) { | 32 intptr_t* stackEnd) { |
| 120 state->recordStackEnd(stackEnd); | 33 state->recordStackEnd(stackEnd); |
| 121 state->copyStackUntilSafePointScope(); | 34 state->copyStackUntilSafePointScope(); |
| 122 if (!atomicDecrement(&m_unparkedThreadCount)) { | |
| 123 MutexLocker locker(m_mutex); | |
| 124 m_parked.signal(); // Safe point reached. | |
| 125 } | |
| 126 } | 35 } |
| 127 | 36 |
| 128 } // namespace blink | 37 } // namespace blink |
| OLD | NEW |