| 1 | // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "vm/heap/safepoint.h" |
| 6 | |
| 7 | #include "vm/heap/heap.h" |
| 8 | #include "vm/thread.h" |
| 9 | #include "vm/thread_registry.h" |
| 10 | |
| 11 | namespace dart { |
| 12 | |
| 13 | DEFINE_FLAG(bool, trace_safepoint, false, "Trace Safepoint logic." ); |
| 14 | |
| 15 | SafepointOperationScope::SafepointOperationScope(Thread* T) |
| 16 | : ThreadStackResource(T) { |
| 17 | ASSERT(T != nullptr && T->isolate_group() != nullptr); |
| 18 | |
| 19 | SafepointHandler* handler = T->isolate_group()->safepoint_handler(); |
| 20 | ASSERT(handler != NULL); |
| 21 | |
| 22 | // Signal all threads to get to a safepoint and wait for them to |
| 23 | // get to a safepoint. |
| 24 | handler->SafepointThreads(T); |
| 25 | } |
| 26 | |
| 27 | SafepointOperationScope::~SafepointOperationScope() { |
| 28 | Thread* T = thread(); |
| 29 | ASSERT(T != nullptr && T->isolate_group() != nullptr); |
| 30 | |
| 31 | // Resume all threads which are blocked for the safepoint operation. |
| 32 | SafepointHandler* handler = T->isolate_group()->safepoint_handler(); |
| 33 | ASSERT(handler != NULL); |
| 34 | handler->ResumeThreads(T); |
| 35 | } |
| 36 | |
| 37 | ForceGrowthSafepointOperationScope::ForceGrowthSafepointOperationScope( |
| 38 | Thread* T) |
| 39 | : ThreadStackResource(T) { |
| 40 | ASSERT(T != NULL); |
| 41 | IsolateGroup* IG = T->isolate_group(); |
| 42 | ASSERT(IG != NULL); |
| 43 | |
| 44 | SafepointHandler* handler = IG->safepoint_handler(); |
| 45 | ASSERT(handler != NULL); |
| 46 | |
| 47 | // Signal all threads to get to a safepoint and wait for them to |
| 48 | // get to a safepoint. |
| 49 | handler->SafepointThreads(T); |
| 50 | |
| 51 | // N.B.: Change growth policy inside the safepoint to prevent racy access. |
| 52 | Heap* heap = IG->heap(); |
| 53 | current_growth_controller_state_ = heap->GrowthControlState(); |
| 54 | heap->DisableGrowthControl(); |
| 55 | } |
| 56 | |
| 57 | ForceGrowthSafepointOperationScope::~ForceGrowthSafepointOperationScope() { |
| 58 | Thread* T = thread(); |
| 59 | ASSERT(T != NULL); |
| 60 | IsolateGroup* IG = T->isolate_group(); |
| 61 | ASSERT(IG != NULL); |
| 62 | |
| 63 | // N.B.: Change growth policy inside the safepoint to prevent racy access. |
| 64 | Heap* heap = IG->heap(); |
| 65 | heap->SetGrowthControlState(current_growth_controller_state_); |
| 66 | |
| 67 | // Resume all threads which are blocked for the safepoint operation. |
| 68 | SafepointHandler* handler = IG->safepoint_handler(); |
| 69 | ASSERT(handler != NULL); |
| 70 | handler->ResumeThreads(T); |
| 71 | |
| 72 | if (current_growth_controller_state_) { |
| 73 | ASSERT(T->CanCollectGarbage()); |
| 74 | // Check if we passed the growth limit during the scope. |
| 75 | if (heap->old_space()->ReachedHardThreshold()) { |
| 76 | heap->CollectGarbage(Heap::kMarkSweep, Heap::kOldSpace); |
| 77 | } else { |
| 78 | heap->CheckStartConcurrentMarking(T, Heap::kOldSpace); |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | SafepointHandler::SafepointHandler(IsolateGroup* isolate_group) |
| 84 | : isolate_group_(isolate_group), |
| 85 | safepoint_lock_(), |
| 86 | number_threads_not_at_safepoint_(0), |
| 87 | safepoint_operation_count_(0), |
| 88 | owner_(NULL) {} |
| 89 | |
| 90 | SafepointHandler::~SafepointHandler() { |
| 91 | ASSERT(owner_ == NULL); |
| 92 | ASSERT(safepoint_operation_count_ == 0); |
| 93 | isolate_group_ = NULL; |
| 94 | } |
| 95 | |
| 96 | void SafepointHandler::SafepointThreads(Thread* T) { |
| 97 | ASSERT(T->no_safepoint_scope_depth() == 0); |
| 98 | ASSERT(T->execution_state() == Thread::kThreadInVM); |
| 99 | |
| 100 | { |
| 101 | // First grab the threads list lock for this isolate |
| 102 | // and check if a safepoint is already in progress. This |
| 103 | // ensures that two threads do not start a safepoint operation |
| 104 | // at the same time. |
| 105 | MonitorLocker sl(threads_lock()); |
| 106 | |
| 107 | // Now check to see if a safepoint operation is already in progress |
| 108 | // for this isolate, block if an operation is in progress. |
| 109 | while (SafepointInProgress()) { |
| 110 | // If we are recursively invoking a Safepoint operation then we |
| 111 | // just increment the count and return, otherwise we wait for the |
| 112 | // safepoint operation to be done. |
| 113 | if (owner_ == T) { |
| 114 | increment_safepoint_operation_count(); |
| 115 | return; |
| 116 | } |
| 117 | sl.WaitWithSafepointCheck(T); |
| 118 | } |
| 119 | |
| 120 | // Set safepoint in progress state by this thread. |
| 121 | SetSafepointInProgress(T); |
| 122 | |
| 123 | // Go over the active thread list and ensure that all threads active |
| 124 | // in the isolate reach a safepoint. |
| 125 | Thread* current = isolate_group()->thread_registry()->active_list(); |
| 126 | while (current != NULL) { |
| 127 | MonitorLocker tl(current->thread_lock()); |
| 128 | if (!current->BypassSafepoints()) { |
| 129 | if (current == T) { |
| 130 | current->SetAtSafepoint(true); |
| 131 | } else { |
| 132 | uint32_t state = current->SetSafepointRequested(true); |
| 133 | if (!Thread::IsAtSafepoint(state)) { |
| 134 | // Thread is not already at a safepoint so try to |
| 135 | // get it to a safepoint and wait for it to check in. |
| 136 | if (current->IsMutatorThread()) { |
| 137 | current->ScheduleInterruptsLocked(Thread::kVMInterrupt); |
| 138 | } |
| 139 | MonitorLocker sl(&safepoint_lock_); |
| 140 | ++number_threads_not_at_safepoint_; |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | current = current->next(); |
| 145 | } |
| 146 | } |
| 147 | // Now wait for all threads that are not already at a safepoint to check-in. |
| 148 | { |
| 149 | MonitorLocker sl(&safepoint_lock_); |
| 150 | intptr_t num_attempts = 0; |
| 151 | while (number_threads_not_at_safepoint_ > 0) { |
| 152 | Monitor::WaitResult retval = sl.Wait(1000); |
| 153 | if (retval == Monitor::kTimedOut) { |
| 154 | num_attempts += 1; |
| 155 | if (FLAG_trace_safepoint && num_attempts > 10) { |
| 156 | // We have been waiting too long, start logging this as we might |
| 157 | // have an issue where a thread is not checking in for a safepoint. |
| 158 | for (Thread* current = |
| 159 | isolate_group()->thread_registry()->active_list(); |
| 160 | current != NULL; current = current->next()) { |
| 161 | if (!current->IsAtSafepoint()) { |
| 162 | OS::PrintErr("Attempt:%" Pd |
| 163 | " waiting for thread %s to check in\n" , |
| 164 | num_attempts, current->os_thread()->name()); |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | } |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | void SafepointHandler::ResumeThreads(Thread* T) { |
| 174 | // First resume all the threads which are blocked for the safepoint |
| 175 | // operation. |
| 176 | MonitorLocker sl(threads_lock()); |
| 177 | |
| 178 | // First check if we are in a recursive safepoint operation, in that case |
| 179 | // we just decrement safepoint_operation_count and return. |
| 180 | ASSERT(SafepointInProgress()); |
| 181 | if (safepoint_operation_count() > 1) { |
| 182 | decrement_safepoint_operation_count(); |
| 183 | return; |
| 184 | } |
| 185 | Thread* current = isolate_group()->thread_registry()->active_list(); |
| 186 | while (current != NULL) { |
| 187 | MonitorLocker tl(current->thread_lock()); |
| 188 | if (!current->BypassSafepoints()) { |
| 189 | if (current == T) { |
| 190 | current->SetAtSafepoint(false); |
| 191 | } else { |
| 192 | uint32_t state = current->SetSafepointRequested(false); |
| 193 | if (Thread::IsBlockedForSafepoint(state)) { |
| 194 | tl.Notify(); |
| 195 | } |
| 196 | } |
| 197 | } |
| 198 | current = current->next(); |
| 199 | } |
| 200 | // Now reset the safepoint_in_progress_ state and notify all threads |
| 201 | // that are waiting to enter the isolate or waiting to start another |
| 202 | // safepoint operation. |
| 203 | ResetSafepointInProgress(T); |
| 204 | sl.NotifyAll(); |
| 205 | } |
| 206 | |
| 207 | void SafepointHandler::EnterSafepointUsingLock(Thread* T) { |
| 208 | MonitorLocker tl(T->thread_lock()); |
| 209 | T->SetAtSafepoint(true); |
| 210 | if (T->IsSafepointRequested()) { |
| 211 | MonitorLocker sl(&safepoint_lock_); |
| 212 | ASSERT(number_threads_not_at_safepoint_ > 0); |
| 213 | number_threads_not_at_safepoint_ -= 1; |
| 214 | sl.Notify(); |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | void SafepointHandler::ExitSafepointUsingLock(Thread* T) { |
| 219 | MonitorLocker tl(T->thread_lock()); |
| 220 | ASSERT(T->IsAtSafepoint()); |
| 221 | while (T->IsSafepointRequested()) { |
| 222 | T->SetBlockedForSafepoint(true); |
| 223 | tl.Wait(); |
| 224 | T->SetBlockedForSafepoint(false); |
| 225 | } |
| 226 | T->SetAtSafepoint(false); |
| 227 | } |
| 228 | |
| 229 | void SafepointHandler::BlockForSafepoint(Thread* T) { |
| 230 | ASSERT(!T->BypassSafepoints()); |
| 231 | MonitorLocker tl(T->thread_lock()); |
| 232 | if (T->IsSafepointRequested()) { |
| 233 | T->SetAtSafepoint(true); |
| 234 | { |
| 235 | MonitorLocker sl(&safepoint_lock_); |
| 236 | ASSERT(number_threads_not_at_safepoint_ > 0); |
| 237 | number_threads_not_at_safepoint_ -= 1; |
| 238 | sl.Notify(); |
| 239 | } |
| 240 | while (T->IsSafepointRequested()) { |
| 241 | T->SetBlockedForSafepoint(true); |
| 242 | tl.Wait(); |
| 243 | T->SetBlockedForSafepoint(false); |
| 244 | } |
| 245 | T->SetAtSafepoint(false); |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | } // namespace dart |
| 250 | |