Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 89 uint8_t ThreadState::s_mainThreadStateStorage[sizeof(ThreadState)]; | 89 uint8_t ThreadState::s_mainThreadStateStorage[sizeof(ThreadState)]; |
| 90 SafePointBarrier* ThreadState::s_safePointBarrier = 0; | 90 SafePointBarrier* ThreadState::s_safePointBarrier = 0; |
| 91 bool ThreadState::s_inGC = false; | 91 bool ThreadState::s_inGC = false; |
| 92 | 92 |
| 93 static Mutex& threadAttachMutex() | 93 static Mutex& threadAttachMutex() |
| 94 { | 94 { |
| 95 AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); | 95 AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); |
| 96 return mutex; | 96 return mutex; |
| 97 } | 97 } |
| 98 | 98 |
| 99 static double lockingTimeout() | |
| 100 { | |
| 101 // Wait time for parking all threads is at most 10 MS. | |
| 102 return 0.01; | |
| 103 } | |
| 104 | |
| 105 | |
| 99 typedef void (*PushAllRegistersCallback)(SafePointBarrier*, ThreadState*, intptr _t*); | 106 typedef void (*PushAllRegistersCallback)(SafePointBarrier*, ThreadState*, intptr _t*); |
| 100 extern "C" void pushAllRegisters(SafePointBarrier*, ThreadState*, PushAllRegiste rsCallback); | 107 extern "C" void pushAllRegisters(SafePointBarrier*, ThreadState*, PushAllRegiste rsCallback); |
| 101 | 108 |
| 102 class SafePointBarrier { | 109 class SafePointBarrier { |
| 103 public: | 110 public: |
| 104 SafePointBarrier() : m_canResume(1), m_unparkedThreadCount(0) { } | 111 SafePointBarrier() : m_canResume(1), m_unparkedThreadCount(0) { } |
| 105 ~SafePointBarrier() { } | 112 ~SafePointBarrier() { } |
| 106 | 113 |
| 107 // Request other attached threads that are not at safe points to park themse lves on safepoints. | 114 // Request other attached threads that are not at safe points to park themse lves on safepoints. |
| 108 void parkOthers() | 115 bool parkOthers() |
| 109 { | 116 { |
| 110 ASSERT(ThreadState::current()->isAtSafePoint()); | 117 ASSERT(ThreadState::current()->isAtSafePoint()); |
| 111 | 118 |
| 112 // Lock threadAttachMutex() to prevent threads from attaching. | 119 // Lock threadAttachMutex() to prevent threads from attaching. |
| 113 threadAttachMutex().lock(); | 120 threadAttachMutex().lock(); |
| 114 | 121 |
| 115 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThre ads(); | 122 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThre ads(); |
| 116 | 123 |
| 117 MutexLocker locker(m_mutex); | 124 MutexLocker locker(m_mutex); |
| 118 atomicAdd(&m_unparkedThreadCount, threads.size()); | 125 atomicAdd(&m_unparkedThreadCount, threads.size()); |
| 119 releaseStore(&m_canResume, 0); | 126 releaseStore(&m_canResume, 0); |
| 120 | 127 |
| 121 ThreadState* current = ThreadState::current(); | 128 ThreadState* current = ThreadState::current(); |
| 122 for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end = threads.end(); it != end; ++it) { | 129 for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end = threads.end(); it != end; ++it) { |
| 123 if (*it == current) | 130 if (*it == current) |
| 124 continue; | 131 continue; |
| 125 | 132 |
| 126 const Vector<ThreadState::Interruptor*>& interruptors = (*it)->inter ruptors(); | 133 const Vector<ThreadState::Interruptor*>& interruptors = (*it)->inter ruptors(); |
| 127 for (size_t i = 0; i < interruptors.size(); i++) | 134 for (size_t i = 0; i < interruptors.size(); i++) |
| 128 interruptors[i]->requestInterrupt(); | 135 interruptors[i]->requestInterrupt(); |
| 129 } | 136 } |
| 130 | 137 |
| 131 while (acquireLoad(&m_unparkedThreadCount) > 0) | 138 while (acquireLoad(&m_unparkedThreadCount) > 0) { |
| 132 m_parked.wait(m_mutex); | 139 double expirationTime = currentTime() + lockingTimeout(); |
| 140 if (!m_parked.timedWait(m_mutex, expirationTime)) { | |
| 141 // One of the other threads did not return to a safepoint within the maximum | |
| 142 // time we allow for threads to be parked. Abandon the GC and re sume the | |
| 143 // currently parked threads. | |
| 144 resumeOthers(true); | |
|
haraken
2014/05/01 04:03:56
Nit: It would be more consistent if you can remove
wibling-chromium
2014/05/01 06:51:48
Yes, that would be nice. However I am worried that
haraken
2014/05/01 07:33:50
Yes, but is it problematic that we allow another t
wibling-chromium
2014/05/01 08:25:18
No, that should be okay. However previously we wou
haraken
2014/05/01 08:36:31
Thanks for the clarification, makes sense!
| |
| 145 return false; | |
| 146 } | |
| 147 } | |
| 148 return true; | |
| 133 } | 149 } |
| 134 | 150 |
| 135 void resumeOthers() | 151 void resumeOthers(bool barrierLocked = false) |
| 136 { | 152 { |
| 137 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThre ads(); | 153 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThre ads(); |
| 138 atomicSubtract(&m_unparkedThreadCount, threads.size()); | 154 atomicSubtract(&m_unparkedThreadCount, threads.size()); |
|
haraken
2014/05/01 04:03:56
I guess this will confuse m_unparkedThreadCount. A
wibling-chromium
2014/05/01 06:51:48
I initially read the code the same way, but parkOt
haraken
2014/05/01 07:33:50
Thanks for the clarification. Your explanation sou
| |
| 139 releaseStore(&m_canResume, 1); | 155 releaseStore(&m_canResume, 1); |
| 140 { | 156 |
| 157 // FIXME: Resumed threads will all contend for m_mutex just to unlock it | |
| 158 // later which is a waste of resources. | |
| 159 if (UNLIKELY(barrierLocked)) { | |
| 160 m_resume.broadcast(); | |
| 161 } else { | |
| 141 // FIXME: Resumed threads will all contend for | 162 // FIXME: Resumed threads will all contend for |
| 142 // m_mutex just to unlock it later which is a waste of | 163 // m_mutex just to unlock it later which is a waste of |
| 143 // resources. | 164 // resources. |
| 144 MutexLocker locker(m_mutex); | 165 MutexLocker locker(m_mutex); |
| 145 m_resume.broadcast(); | 166 m_resume.broadcast(); |
| 146 } | 167 } |
| 147 | 168 |
| 148 ThreadState* current = ThreadState::current(); | 169 ThreadState* current = ThreadState::current(); |
| 149 for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end = threads.end(); it != end; ++it) { | 170 for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end = threads.end(); it != end; ++it) { |
| 150 if (*it == current) | 171 if (*it == current) |
| 151 continue; | 172 continue; |
| 152 | 173 |
| 153 const Vector<ThreadState::Interruptor*>& interruptors = (*it)->inter ruptors(); | 174 const Vector<ThreadState::Interruptor*>& interruptors = (*it)->inter ruptors(); |
| 154 for (size_t i = 0; i < interruptors.size(); i++) | 175 for (size_t i = 0; i < interruptors.size(); i++) |
| 155 interruptors[i]->clearInterrupt(); | 176 interruptors[i]->clearInterrupt(); |
| 156 } | 177 } |
| 157 | 178 |
| 158 threadAttachMutex().unlock(); | 179 threadAttachMutex().unlock(); |
| 159 ASSERT(ThreadState::current()->isAtSafePoint()); | 180 ASSERT(ThreadState::current()->isAtSafePoint()); |
| 160 } | 181 } |
| 161 | 182 |
| 183 void checkAndPark(ThreadState* state) | |
|
zerny-chromium
2014/05/01 07:05:38
Nit: avoid reordering/formatting for unchanged cod
wibling-chromium
2014/05/01 08:25:18
Done. I will revert that and do it in a separate c
| |
| 184 { | |
| 185 ASSERT(!state->isSweepInProgress()); | |
| 186 if (!acquireLoad(&m_canResume)) { | |
| 187 pushAllRegisters(this, state, parkAfterPushRegisters); | |
| 188 state->performPendingSweep(); | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 void enterSafePoint(ThreadState* state) | |
| 193 { | |
| 194 ASSERT(!state->isSweepInProgress()); | |
| 195 pushAllRegisters(this, state, enterSafePointAfterPushRegisters); | |
| 196 } | |
| 197 | |
| 198 void leaveSafePoint(ThreadState* state) | |
| 199 { | |
| 200 if (atomicIncrement(&m_unparkedThreadCount) > 0) | |
| 201 checkAndPark(state); | |
| 202 } | |
| 203 | |
| 204 private: | |
| 162 void doPark(ThreadState* state, intptr_t* stackEnd) | 205 void doPark(ThreadState* state, intptr_t* stackEnd) |
| 163 { | 206 { |
| 164 state->recordStackEnd(stackEnd); | 207 state->recordStackEnd(stackEnd); |
| 165 MutexLocker locker(m_mutex); | 208 MutexLocker locker(m_mutex); |
| 166 if (!atomicDecrement(&m_unparkedThreadCount)) | 209 if (!atomicDecrement(&m_unparkedThreadCount)) |
| 167 m_parked.signal(); | 210 m_parked.signal(); |
| 168 while (!acquireLoad(&m_canResume)) | 211 while (!acquireLoad(&m_canResume)) |
| 169 m_resume.wait(m_mutex); | 212 m_resume.wait(m_mutex); |
| 170 atomicIncrement(&m_unparkedThreadCount); | 213 atomicIncrement(&m_unparkedThreadCount); |
| 171 } | 214 } |
| 172 | 215 |
| 173 void checkAndPark(ThreadState* state) | 216 static void parkAfterPushRegisters(SafePointBarrier* barrier, ThreadState* s tate, intptr_t* stackEnd) |
| 174 { | 217 { |
| 175 ASSERT(!state->isSweepInProgress()); | 218 barrier->doPark(state, stackEnd); |
| 176 if (!acquireLoad(&m_canResume)) { | |
| 177 pushAllRegisters(this, state, parkAfterPushRegisters); | |
| 178 state->performPendingSweep(); | |
| 179 } | |
| 180 } | 219 } |
| 181 | 220 |
| 182 void doEnterSafePoint(ThreadState* state, intptr_t* stackEnd) | 221 void doEnterSafePoint(ThreadState* state, intptr_t* stackEnd) |
| 183 { | 222 { |
| 184 state->recordStackEnd(stackEnd); | 223 state->recordStackEnd(stackEnd); |
| 185 state->copyStackUntilSafePointScope(); | 224 state->copyStackUntilSafePointScope(); |
| 186 // m_unparkedThreadCount tracks amount of unparked threads. It is | 225 // m_unparkedThreadCount tracks amount of unparked threads. It is |
| 187 // positive if and only if we have requested other threads to park | 226 // positive if and only if we have requested other threads to park |
| 188 // at safe-points in preparation for GC. The last thread to park | 227 // at safe-points in preparation for GC. The last thread to park |
| 189 // itself will make the counter hit zero and should notify GC thread | 228 // itself will make the counter hit zero and should notify GC thread |
| 190 // that it is safe to proceed. | 229 // that it is safe to proceed. |
| 191 // If no other thread is waiting for other threads to park then | 230 // If no other thread is waiting for other threads to park then |
| 192 // this counter can be negative: if N threads are at safe-points | 231 // this counter can be negative: if N threads are at safe-points |
| 193 // the counter will be -N. | 232 // the counter will be -N. |
| 194 if (!atomicDecrement(&m_unparkedThreadCount)) { | 233 if (!atomicDecrement(&m_unparkedThreadCount)) { |
| 195 MutexLocker locker(m_mutex); | 234 MutexLocker locker(m_mutex); |
| 196 m_parked.signal(); // Safe point reached. | 235 m_parked.signal(); // Safe point reached. |
| 197 } | 236 } |
| 198 } | 237 } |
| 199 | 238 |
| 200 void enterSafePoint(ThreadState* state) | |
| 201 { | |
| 202 ASSERT(!state->isSweepInProgress()); | |
| 203 pushAllRegisters(this, state, enterSafePointAfterPushRegisters); | |
| 204 } | |
| 205 | |
| 206 void leaveSafePoint(ThreadState* state) | |
| 207 { | |
| 208 if (atomicIncrement(&m_unparkedThreadCount) > 0) | |
| 209 checkAndPark(state); | |
| 210 } | |
| 211 | |
| 212 private: | |
| 213 static void parkAfterPushRegisters(SafePointBarrier* barrier, ThreadState* s tate, intptr_t* stackEnd) | |
| 214 { | |
| 215 barrier->doPark(state, stackEnd); | |
| 216 } | |
| 217 | |
| 218 static void enterSafePointAfterPushRegisters(SafePointBarrier* barrier, Thre adState* state, intptr_t* stackEnd) | 239 static void enterSafePointAfterPushRegisters(SafePointBarrier* barrier, Thre adState* state, intptr_t* stackEnd) |
| 219 { | 240 { |
| 220 barrier->doEnterSafePoint(state, stackEnd); | 241 barrier->doEnterSafePoint(state, stackEnd); |
| 221 } | 242 } |
| 222 | 243 |
| 223 volatile int m_canResume; | 244 volatile int m_canResume; |
| 224 volatile int m_unparkedThreadCount; | 245 volatile int m_unparkedThreadCount; |
| 225 Mutex m_mutex; | 246 Mutex m_mutex; |
| 226 ThreadCondition m_parked; | 247 ThreadCondition m_parked; |
| 227 ThreadCondition m_resume; | 248 ThreadCondition m_resume; |
| (...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 694 if (isConsistentForGC()) { | 715 if (isConsistentForGC()) { |
| 695 HeapStats scannedStats; | 716 HeapStats scannedStats; |
| 696 scannedStats.clear(); | 717 scannedStats.clear(); |
| 697 for (int i = 0; i < NumberOfHeaps; i++) | 718 for (int i = 0; i < NumberOfHeaps; i++) |
| 698 m_heaps[i]->getScannedStats(scannedStats); | 719 m_heaps[i]->getScannedStats(scannedStats); |
| 699 ASSERT(scannedStats == stats); | 720 ASSERT(scannedStats == stats); |
| 700 } | 721 } |
| 701 #endif | 722 #endif |
| 702 } | 723 } |
| 703 | 724 |
| 704 void ThreadState::stopThreads() | 725 bool ThreadState::stopThreads() |
| 705 { | 726 { |
| 706 s_safePointBarrier->parkOthers(); | 727 return s_safePointBarrier->parkOthers(); |
| 707 } | 728 } |
| 708 | 729 |
| 709 void ThreadState::resumeThreads() | 730 void ThreadState::resumeThreads() |
| 710 { | 731 { |
| 711 s_safePointBarrier->resumeOthers(); | 732 s_safePointBarrier->resumeOthers(); |
| 712 } | 733 } |
| 713 | 734 |
| 714 void ThreadState::safePoint(StackState stackState) | 735 void ThreadState::safePoint(StackState stackState) |
| 715 { | 736 { |
| 716 checkThread(); | 737 checkThread(); |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 841 state->safePoint(HeapPointersOnStack); | 862 state->safePoint(HeapPointersOnStack); |
| 842 } | 863 } |
| 843 | 864 |
| 844 ThreadState::AttachedThreadStateSet& ThreadState::attachedThreads() | 865 ThreadState::AttachedThreadStateSet& ThreadState::attachedThreads() |
| 845 { | 866 { |
| 846 DEFINE_STATIC_LOCAL(AttachedThreadStateSet, threads, ()); | 867 DEFINE_STATIC_LOCAL(AttachedThreadStateSet, threads, ()); |
| 847 return threads; | 868 return threads; |
| 848 } | 869 } |
| 849 | 870 |
| 850 } | 871 } |
| OLD | NEW |