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 |