1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "mutex.h" 18 19 #include <errno.h> 20 #include <sys/time.h> 21 22 #define ATRACE_TAG ATRACE_TAG_DALVIK 23 #include "cutils/trace.h" 24 25 #include "atomic.h" 26 #include "base/logging.h" 27 #include "base/time_utils.h" 28 #include "base/value_object.h" 29 #include "mutex-inl.h" 30 #include "runtime.h" 31 #include "scoped_thread_state_change.h" 32 #include "thread-inl.h" 33 34 namespace art { 35 36 Mutex* Locks::abort_lock_ = nullptr; 37 Mutex* Locks::alloc_tracker_lock_ = nullptr; 38 Mutex* Locks::allocated_monitor_ids_lock_ = nullptr; 39 Mutex* Locks::allocated_thread_ids_lock_ = nullptr; 40 ReaderWriterMutex* Locks::breakpoint_lock_ = nullptr; 41 ReaderWriterMutex* Locks::classlinker_classes_lock_ = nullptr; 42 Mutex* Locks::deoptimization_lock_ = nullptr; 43 ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr; 44 Mutex* Locks::instrument_entrypoints_lock_ = nullptr; 45 Mutex* Locks::intern_table_lock_ = nullptr; 46 Mutex* Locks::jni_libraries_lock_ = nullptr; 47 Mutex* Locks::logging_lock_ = nullptr; 48 Mutex* Locks::mem_maps_lock_ = nullptr; 49 Mutex* Locks::modify_ldt_lock_ = nullptr; 50 ReaderWriterMutex* Locks::mutator_lock_ = nullptr; 51 Mutex* Locks::profiler_lock_ = nullptr; 52 Mutex* Locks::reference_processor_lock_ = nullptr; 53 Mutex* Locks::reference_queue_cleared_references_lock_ = nullptr; 54 Mutex* Locks::reference_queue_finalizer_references_lock_ = nullptr; 55 Mutex* Locks::reference_queue_phantom_references_lock_ = nullptr; 56 Mutex* Locks::reference_queue_soft_references_lock_ = nullptr; 57 Mutex* Locks::reference_queue_weak_references_lock_ = nullptr; 58 Mutex* Locks::runtime_shutdown_lock_ = nullptr; 59 Mutex* Locks::thread_list_lock_ = nullptr; 60 ConditionVariable* Locks::thread_exit_cond_ = nullptr; 61 Mutex* Locks::thread_suspend_count_lock_ = nullptr; 62 Mutex* Locks::trace_lock_ = nullptr; 63 Mutex* Locks::unexpected_signal_lock_ = nullptr; 64 65 struct AllMutexData { 66 // A guard for all_mutexes_ that's not a mutex (Mutexes must CAS to acquire and busy wait). 67 Atomic<const BaseMutex*> all_mutexes_guard; 68 // All created mutexes guarded by all_mutexes_guard_. 69 std::set<BaseMutex*>* all_mutexes; 70 AllMutexData() : all_mutexes(nullptr) {} 71 }; 72 static struct AllMutexData gAllMutexData[kAllMutexDataSize]; 73 74 #if ART_USE_FUTEXES 75 static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, const timespec& rhs) { 76 const int32_t one_sec = 1000 * 1000 * 1000; // one second in nanoseconds. 77 result_ts->tv_sec = lhs.tv_sec - rhs.tv_sec; 78 result_ts->tv_nsec = lhs.tv_nsec - rhs.tv_nsec; 79 if (result_ts->tv_nsec < 0) { 80 result_ts->tv_sec--; 81 result_ts->tv_nsec += one_sec; 82 } else if (result_ts->tv_nsec > one_sec) { 83 result_ts->tv_sec++; 84 result_ts->tv_nsec -= one_sec; 85 } 86 return result_ts->tv_sec < 0; 87 } 88 #endif 89 90 class ScopedAllMutexesLock FINAL { 91 public: 92 explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) { 93 while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakAcquire(0, mutex)) { 94 NanoSleep(100); 95 } 96 } 97 98 ~ScopedAllMutexesLock() { 99 #if !defined(__clang__) 100 // TODO: remove this workaround target GCC/libc++/bionic bug "invalid failure memory model". 101 while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakSequentiallyConsistent(mutex_, 0)) { 102 #else 103 while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakRelease(mutex_, 0)) { 104 #endif 105 NanoSleep(100); 106 } 107 } 108 109 private: 110 const BaseMutex* const mutex_; 111 }; 112 113 // Scoped class that generates events at the beginning and end of lock contention. 114 class ScopedContentionRecorder FINAL : public ValueObject { 115 public: 116 ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid) 117 : mutex_(kLogLockContentions ? mutex : nullptr), 118 blocked_tid_(kLogLockContentions ? blocked_tid : 0), 119 owner_tid_(kLogLockContentions ? owner_tid : 0), 120 start_nano_time_(kLogLockContentions ? NanoTime() : 0) { 121 if (ATRACE_ENABLED()) { 122 std::string msg = StringPrintf("Lock contention on %s (owner tid: %" PRIu64 ")", 123 mutex->GetName(), owner_tid); 124 ATRACE_BEGIN(msg.c_str()); 125 } 126 } 127 128 ~ScopedContentionRecorder() { 129 ATRACE_END(); 130 if (kLogLockContentions) { 131 uint64_t end_nano_time = NanoTime(); 132 mutex_->RecordContention(blocked_tid_, owner_tid_, end_nano_time - start_nano_time_); 133 } 134 } 135 136 private: 137 BaseMutex* const mutex_; 138 const uint64_t blocked_tid_; 139 const uint64_t owner_tid_; 140 const uint64_t start_nano_time_; 141 }; 142 143 BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) { 144 if (kLogLockContentions) { 145 ScopedAllMutexesLock mu(this); 146 std::set<BaseMutex*>** all_mutexes_ptr = &gAllMutexData->all_mutexes; 147 if (*all_mutexes_ptr == nullptr) { 148 // We leak the global set of all mutexes to avoid ordering issues in global variable 149 // construction/destruction. 150 *all_mutexes_ptr = new std::set<BaseMutex*>(); 151 } 152 (*all_mutexes_ptr)->insert(this); 153 } 154 } 155 156 BaseMutex::~BaseMutex() { 157 if (kLogLockContentions) { 158 ScopedAllMutexesLock mu(this); 159 gAllMutexData->all_mutexes->erase(this); 160 } 161 } 162 163 void BaseMutex::DumpAll(std::ostream& os) { 164 if (kLogLockContentions) { 165 os << "Mutex logging:\n"; 166 ScopedAllMutexesLock mu(reinterpret_cast<const BaseMutex*>(-1)); 167 std::set<BaseMutex*>* all_mutexes = gAllMutexData->all_mutexes; 168 if (all_mutexes == nullptr) { 169 // No mutexes have been created yet during at startup. 170 return; 171 } 172 typedef std::set<BaseMutex*>::const_iterator It; 173 os << "(Contended)\n"; 174 for (It it = all_mutexes->begin(); it != all_mutexes->end(); ++it) { 175 BaseMutex* mutex = *it; 176 if (mutex->HasEverContended()) { 177 mutex->Dump(os); 178 os << "\n"; 179 } 180 } 181 os << "(Never contented)\n"; 182 for (It it = all_mutexes->begin(); it != all_mutexes->end(); ++it) { 183 BaseMutex* mutex = *it; 184 if (!mutex->HasEverContended()) { 185 mutex->Dump(os); 186 os << "\n"; 187 } 188 } 189 } 190 } 191 192 void BaseMutex::CheckSafeToWait(Thread* self) { 193 if (self == nullptr) { 194 CheckUnattachedThread(level_); 195 return; 196 } 197 if (kDebugLocking) { 198 CHECK(self->GetHeldMutex(level_) == this || level_ == kMonitorLock) 199 << "Waiting on unacquired mutex: " << name_; 200 bool bad_mutexes_held = false; 201 for (int i = kLockLevelCount - 1; i >= 0; --i) { 202 if (i != level_) { 203 BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i)); 204 // We expect waits to happen while holding the thread list suspend thread lock. 205 if (held_mutex != nullptr) { 206 LOG(ERROR) << "Holding \"" << held_mutex->name_ << "\" " 207 << "(level " << LockLevel(i) << ") while performing wait on " 208 << "\"" << name_ << "\" (level " << level_ << ")"; 209 bad_mutexes_held = true; 210 } 211 } 212 } 213 if (gAborting == 0) { // Avoid recursive aborts. 214 CHECK(!bad_mutexes_held); 215 } 216 } 217 } 218 219 void BaseMutex::ContentionLogData::AddToWaitTime(uint64_t value) { 220 if (kLogLockContentions) { 221 // Atomically add value to wait_time. 222 wait_time.FetchAndAddSequentiallyConsistent(value); 223 } 224 } 225 226 void BaseMutex::RecordContention(uint64_t blocked_tid, 227 uint64_t owner_tid, 228 uint64_t nano_time_blocked) { 229 if (kLogLockContentions) { 230 ContentionLogData* data = contention_log_data_; 231 ++(data->contention_count); 232 data->AddToWaitTime(nano_time_blocked); 233 ContentionLogEntry* log = data->contention_log; 234 // This code is intentionally racy as it is only used for diagnostics. 235 uint32_t slot = data->cur_content_log_entry.LoadRelaxed(); 236 if (log[slot].blocked_tid == blocked_tid && 237 log[slot].owner_tid == blocked_tid) { 238 ++log[slot].count; 239 } else { 240 uint32_t new_slot; 241 do { 242 slot = data->cur_content_log_entry.LoadRelaxed(); 243 new_slot = (slot + 1) % kContentionLogSize; 244 } while (!data->cur_content_log_entry.CompareExchangeWeakRelaxed(slot, new_slot)); 245 log[new_slot].blocked_tid = blocked_tid; 246 log[new_slot].owner_tid = owner_tid; 247 log[new_slot].count.StoreRelaxed(1); 248 } 249 } 250 } 251 252 void BaseMutex::DumpContention(std::ostream& os) const { 253 if (kLogLockContentions) { 254 const ContentionLogData* data = contention_log_data_; 255 const ContentionLogEntry* log = data->contention_log; 256 uint64_t wait_time = data->wait_time.LoadRelaxed(); 257 uint32_t contention_count = data->contention_count.LoadRelaxed(); 258 if (contention_count == 0) { 259 os << "never contended"; 260 } else { 261 os << "contended " << contention_count 262 << " total wait of contender " << PrettyDuration(wait_time) 263 << " average " << PrettyDuration(wait_time / contention_count); 264 SafeMap<uint64_t, size_t> most_common_blocker; 265 SafeMap<uint64_t, size_t> most_common_blocked; 266 for (size_t i = 0; i < kContentionLogSize; ++i) { 267 uint64_t blocked_tid = log[i].blocked_tid; 268 uint64_t owner_tid = log[i].owner_tid; 269 uint32_t count = log[i].count.LoadRelaxed(); 270 if (count > 0) { 271 auto it = most_common_blocked.find(blocked_tid); 272 if (it != most_common_blocked.end()) { 273 most_common_blocked.Overwrite(blocked_tid, it->second + count); 274 } else { 275 most_common_blocked.Put(blocked_tid, count); 276 } 277 it = most_common_blocker.find(owner_tid); 278 if (it != most_common_blocker.end()) { 279 most_common_blocker.Overwrite(owner_tid, it->second + count); 280 } else { 281 most_common_blocker.Put(owner_tid, count); 282 } 283 } 284 } 285 uint64_t max_tid = 0; 286 size_t max_tid_count = 0; 287 for (const auto& pair : most_common_blocked) { 288 if (pair.second > max_tid_count) { 289 max_tid = pair.first; 290 max_tid_count = pair.second; 291 } 292 } 293 if (max_tid != 0) { 294 os << " sample shows most blocked tid=" << max_tid; 295 } 296 max_tid = 0; 297 max_tid_count = 0; 298 for (const auto& pair : most_common_blocker) { 299 if (pair.second > max_tid_count) { 300 max_tid = pair.first; 301 max_tid_count = pair.second; 302 } 303 } 304 if (max_tid != 0) { 305 os << " sample shows tid=" << max_tid << " owning during this time"; 306 } 307 } 308 } 309 } 310 311 312 Mutex::Mutex(const char* name, LockLevel level, bool recursive) 313 : BaseMutex(name, level), recursive_(recursive), recursion_count_(0) { 314 #if ART_USE_FUTEXES 315 DCHECK_EQ(0, state_.LoadRelaxed()); 316 DCHECK_EQ(0, num_contenders_.LoadRelaxed()); 317 #else 318 CHECK_MUTEX_CALL(pthread_mutex_init, (&mutex_, nullptr)); 319 #endif 320 exclusive_owner_ = 0; 321 } 322 323 // Helper to ignore the lock requirement. 324 static bool IsShuttingDown() NO_THREAD_SAFETY_ANALYSIS { 325 Runtime* runtime = Runtime::Current(); 326 return runtime == nullptr || runtime->IsShuttingDownLocked(); 327 } 328 329 Mutex::~Mutex() { 330 bool shutting_down = IsShuttingDown(); 331 #if ART_USE_FUTEXES 332 if (state_.LoadRelaxed() != 0) { 333 LOG(shutting_down ? WARNING : FATAL) << "destroying mutex with owner: " << exclusive_owner_; 334 } else { 335 if (exclusive_owner_ != 0) { 336 LOG(shutting_down ? WARNING : FATAL) << "unexpectedly found an owner on unlocked mutex " 337 << name_; 338 } 339 if (num_contenders_.LoadSequentiallyConsistent() != 0) { 340 LOG(shutting_down ? WARNING : FATAL) << "unexpectedly found a contender on mutex " << name_; 341 } 342 } 343 #else 344 // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread 345 // may still be using locks. 346 int rc = pthread_mutex_destroy(&mutex_); 347 if (rc != 0) { 348 errno = rc; 349 // TODO: should we just not log at all if shutting down? this could be the logging mutex! 350 MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_); 351 PLOG(shutting_down ? WARNING : FATAL) << "pthread_mutex_destroy failed for " << name_; 352 } 353 #endif 354 } 355 356 void Mutex::ExclusiveLock(Thread* self) { 357 DCHECK(self == nullptr || self == Thread::Current()); 358 if (kDebugLocking && !recursive_) { 359 AssertNotHeld(self); 360 } 361 if (!recursive_ || !IsExclusiveHeld(self)) { 362 #if ART_USE_FUTEXES 363 bool done = false; 364 do { 365 int32_t cur_state = state_.LoadRelaxed(); 366 if (LIKELY(cur_state == 0)) { 367 // Change state from 0 to 1 and impose load/store ordering appropriate for lock acquisition. 368 done = state_.CompareExchangeWeakAcquire(0 /* cur_state */, 1 /* new state */); 369 } else { 370 // Failed to acquire, hang up. 371 ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); 372 num_contenders_++; 373 if (futex(state_.Address(), FUTEX_WAIT, 1, nullptr, nullptr, 0) != 0) { 374 // EAGAIN and EINTR both indicate a spurious failure, try again from the beginning. 375 // We don't use TEMP_FAILURE_RETRY so we can intentionally retry to acquire the lock. 376 if ((errno != EAGAIN) && (errno != EINTR)) { 377 PLOG(FATAL) << "futex wait failed for " << name_; 378 } 379 } 380 num_contenders_--; 381 } 382 } while (!done); 383 DCHECK_EQ(state_.LoadRelaxed(), 1); 384 #else 385 CHECK_MUTEX_CALL(pthread_mutex_lock, (&mutex_)); 386 #endif 387 DCHECK_EQ(exclusive_owner_, 0U); 388 exclusive_owner_ = SafeGetTid(self); 389 RegisterAsLocked(self); 390 } 391 recursion_count_++; 392 if (kDebugLocking) { 393 CHECK(recursion_count_ == 1 || recursive_) << "Unexpected recursion count on mutex: " 394 << name_ << " " << recursion_count_; 395 AssertHeld(self); 396 } 397 } 398 399 bool Mutex::ExclusiveTryLock(Thread* self) { 400 DCHECK(self == nullptr || self == Thread::Current()); 401 if (kDebugLocking && !recursive_) { 402 AssertNotHeld(self); 403 } 404 if (!recursive_ || !IsExclusiveHeld(self)) { 405 #if ART_USE_FUTEXES 406 bool done = false; 407 do { 408 int32_t cur_state = state_.LoadRelaxed(); 409 if (cur_state == 0) { 410 // Change state from 0 to 1 and impose load/store ordering appropriate for lock acquisition. 411 done = state_.CompareExchangeWeakAcquire(0 /* cur_state */, 1 /* new state */); 412 } else { 413 return false; 414 } 415 } while (!done); 416 DCHECK_EQ(state_.LoadRelaxed(), 1); 417 #else 418 int result = pthread_mutex_trylock(&mutex_); 419 if (result == EBUSY) { 420 return false; 421 } 422 if (result != 0) { 423 errno = result; 424 PLOG(FATAL) << "pthread_mutex_trylock failed for " << name_; 425 } 426 #endif 427 DCHECK_EQ(exclusive_owner_, 0U); 428 exclusive_owner_ = SafeGetTid(self); 429 RegisterAsLocked(self); 430 } 431 recursion_count_++; 432 if (kDebugLocking) { 433 CHECK(recursion_count_ == 1 || recursive_) << "Unexpected recursion count on mutex: " 434 << name_ << " " << recursion_count_; 435 AssertHeld(self); 436 } 437 return true; 438 } 439 440 void Mutex::ExclusiveUnlock(Thread* self) { 441 if (kIsDebugBuild && self != nullptr && self != Thread::Current()) { 442 std::string name1 = "<null>"; 443 std::string name2 = "<null>"; 444 if (self != nullptr) { 445 self->GetThreadName(name1); 446 } 447 if (Thread::Current() != nullptr) { 448 Thread::Current()->GetThreadName(name2); 449 } 450 LOG(FATAL) << GetName() << " level=" << level_ << " self=" << name1 451 << " Thread::Current()=" << name2; 452 } 453 AssertHeld(self); 454 DCHECK_NE(exclusive_owner_, 0U); 455 recursion_count_--; 456 if (!recursive_ || recursion_count_ == 0) { 457 if (kDebugLocking) { 458 CHECK(recursion_count_ == 0 || recursive_) << "Unexpected recursion count on mutex: " 459 << name_ << " " << recursion_count_; 460 } 461 RegisterAsUnlocked(self); 462 #if ART_USE_FUTEXES 463 bool done = false; 464 do { 465 int32_t cur_state = state_.LoadRelaxed(); 466 if (LIKELY(cur_state == 1)) { 467 // We're no longer the owner. 468 exclusive_owner_ = 0; 469 // Change state to 0 and impose load/store ordering appropriate for lock release. 470 // Note, the relaxed loads below musn't reorder before the CompareExchange. 471 // TODO: the ordering here is non-trivial as state is split across 3 fields, fix by placing 472 // a status bit into the state on contention. 473 done = state_.CompareExchangeWeakSequentiallyConsistent(cur_state, 0 /* new state */); 474 if (LIKELY(done)) { // Spurious fail? 475 // Wake a contender. 476 if (UNLIKELY(num_contenders_.LoadRelaxed() > 0)) { 477 futex(state_.Address(), FUTEX_WAKE, 1, nullptr, nullptr, 0); 478 } 479 } 480 } else { 481 // Logging acquires the logging lock, avoid infinite recursion in that case. 482 if (this != Locks::logging_lock_) { 483 LOG(FATAL) << "Unexpected state_ in unlock " << cur_state << " for " << name_; 484 } else { 485 LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, 486 StringPrintf("Unexpected state_ %d in unlock for %s", 487 cur_state, name_).c_str()); 488 _exit(1); 489 } 490 } 491 } while (!done); 492 #else 493 exclusive_owner_ = 0; 494 CHECK_MUTEX_CALL(pthread_mutex_unlock, (&mutex_)); 495 #endif 496 } 497 } 498 499 void Mutex::Dump(std::ostream& os) const { 500 os << (recursive_ ? "recursive " : "non-recursive ") 501 << name_ 502 << " level=" << static_cast<int>(level_) 503 << " rec=" << recursion_count_ 504 << " owner=" << GetExclusiveOwnerTid() << " "; 505 DumpContention(os); 506 } 507 508 std::ostream& operator<<(std::ostream& os, const Mutex& mu) { 509 mu.Dump(os); 510 return os; 511 } 512 513 ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level) 514 : BaseMutex(name, level) 515 #if ART_USE_FUTEXES 516 , state_(0), num_pending_readers_(0), num_pending_writers_(0) 517 #endif 518 { // NOLINT(whitespace/braces) 519 #if !ART_USE_FUTEXES 520 CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, nullptr)); 521 #endif 522 exclusive_owner_ = 0; 523 } 524 525 ReaderWriterMutex::~ReaderWriterMutex() { 526 #if ART_USE_FUTEXES 527 CHECK_EQ(state_.LoadRelaxed(), 0); 528 CHECK_EQ(exclusive_owner_, 0U); 529 CHECK_EQ(num_pending_readers_.LoadRelaxed(), 0); 530 CHECK_EQ(num_pending_writers_.LoadRelaxed(), 0); 531 #else 532 // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread 533 // may still be using locks. 534 int rc = pthread_rwlock_destroy(&rwlock_); 535 if (rc != 0) { 536 errno = rc; 537 // TODO: should we just not log at all if shutting down? this could be the logging mutex! 538 MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_); 539 Runtime* runtime = Runtime::Current(); 540 bool shutting_down = runtime == nullptr || runtime->IsShuttingDownLocked(); 541 PLOG(shutting_down ? WARNING : FATAL) << "pthread_rwlock_destroy failed for " << name_; 542 } 543 #endif 544 } 545 546 void ReaderWriterMutex::ExclusiveLock(Thread* self) { 547 DCHECK(self == nullptr || self == Thread::Current()); 548 AssertNotExclusiveHeld(self); 549 #if ART_USE_FUTEXES 550 bool done = false; 551 do { 552 int32_t cur_state = state_.LoadRelaxed(); 553 if (LIKELY(cur_state == 0)) { 554 // Change state from 0 to -1 and impose load/store ordering appropriate for lock acquisition. 555 done = state_.CompareExchangeWeakAcquire(0 /* cur_state*/, -1 /* new state */); 556 } else { 557 // Failed to acquire, hang up. 558 ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); 559 ++num_pending_writers_; 560 if (futex(state_.Address(), FUTEX_WAIT, cur_state, nullptr, nullptr, 0) != 0) { 561 // EAGAIN and EINTR both indicate a spurious failure, try again from the beginning. 562 // We don't use TEMP_FAILURE_RETRY so we can intentionally retry to acquire the lock. 563 if ((errno != EAGAIN) && (errno != EINTR)) { 564 PLOG(FATAL) << "futex wait failed for " << name_; 565 } 566 } 567 --num_pending_writers_; 568 } 569 } while (!done); 570 DCHECK_EQ(state_.LoadRelaxed(), -1); 571 #else 572 CHECK_MUTEX_CALL(pthread_rwlock_wrlock, (&rwlock_)); 573 #endif 574 DCHECK_EQ(exclusive_owner_, 0U); 575 exclusive_owner_ = SafeGetTid(self); 576 RegisterAsLocked(self); 577 AssertExclusiveHeld(self); 578 } 579 580 void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { 581 DCHECK(self == nullptr || self == Thread::Current()); 582 AssertExclusiveHeld(self); 583 RegisterAsUnlocked(self); 584 DCHECK_NE(exclusive_owner_, 0U); 585 #if ART_USE_FUTEXES 586 bool done = false; 587 do { 588 int32_t cur_state = state_.LoadRelaxed(); 589 if (LIKELY(cur_state == -1)) { 590 // We're no longer the owner. 591 exclusive_owner_ = 0; 592 // Change state from -1 to 0 and impose load/store ordering appropriate for lock release. 593 // Note, the relaxed loads below musn't reorder before the CompareExchange. 594 // TODO: the ordering here is non-trivial as state is split across 3 fields, fix by placing 595 // a status bit into the state on contention. 596 done = state_.CompareExchangeWeakSequentiallyConsistent(-1 /* cur_state*/, 0 /* new state */); 597 if (LIKELY(done)) { // Weak CAS may fail spuriously. 598 // Wake any waiters. 599 if (UNLIKELY(num_pending_readers_.LoadRelaxed() > 0 || 600 num_pending_writers_.LoadRelaxed() > 0)) { 601 futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); 602 } 603 } 604 } else { 605 LOG(FATAL) << "Unexpected state_:" << cur_state << " for " << name_; 606 } 607 } while (!done); 608 #else 609 exclusive_owner_ = 0; 610 CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_)); 611 #endif 612 } 613 614 #if HAVE_TIMED_RWLOCK 615 bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32_t ns) { 616 DCHECK(self == nullptr || self == Thread::Current()); 617 #if ART_USE_FUTEXES 618 bool done = false; 619 timespec end_abs_ts; 620 InitTimeSpec(true, CLOCK_MONOTONIC, ms, ns, &end_abs_ts); 621 do { 622 int32_t cur_state = state_.LoadRelaxed(); 623 if (cur_state == 0) { 624 // Change state from 0 to -1 and impose load/store ordering appropriate for lock acquisition. 625 done = state_.CompareExchangeWeakAcquire(0 /* cur_state */, -1 /* new state */); 626 } else { 627 // Failed to acquire, hang up. 628 timespec now_abs_ts; 629 InitTimeSpec(true, CLOCK_MONOTONIC, 0, 0, &now_abs_ts); 630 timespec rel_ts; 631 if (ComputeRelativeTimeSpec(&rel_ts, end_abs_ts, now_abs_ts)) { 632 return false; // Timed out. 633 } 634 ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); 635 ++num_pending_writers_; 636 if (futex(state_.Address(), FUTEX_WAIT, cur_state, &rel_ts, nullptr, 0) != 0) { 637 if (errno == ETIMEDOUT) { 638 --num_pending_writers_; 639 return false; // Timed out. 640 } else if ((errno != EAGAIN) && (errno != EINTR)) { 641 // EAGAIN and EINTR both indicate a spurious failure, 642 // recompute the relative time out from now and try again. 643 // We don't use TEMP_FAILURE_RETRY so we can recompute rel_ts; 644 PLOG(FATAL) << "timed futex wait failed for " << name_; 645 } 646 } 647 --num_pending_writers_; 648 } 649 } while (!done); 650 #else 651 timespec ts; 652 InitTimeSpec(true, CLOCK_REALTIME, ms, ns, &ts); 653 int result = pthread_rwlock_timedwrlock(&rwlock_, &ts); 654 if (result == ETIMEDOUT) { 655 return false; 656 } 657 if (result != 0) { 658 errno = result; 659 PLOG(FATAL) << "pthread_rwlock_timedwrlock failed for " << name_; 660 } 661 #endif 662 exclusive_owner_ = SafeGetTid(self); 663 RegisterAsLocked(self); 664 AssertSharedHeld(self); 665 return true; 666 } 667 #endif 668 669 #if ART_USE_FUTEXES 670 void ReaderWriterMutex::HandleSharedLockContention(Thread* self, int32_t cur_state) { 671 // Owner holds it exclusively, hang up. 672 ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self)); 673 ++num_pending_readers_; 674 if (futex(state_.Address(), FUTEX_WAIT, cur_state, nullptr, nullptr, 0) != 0) { 675 if (errno != EAGAIN) { 676 PLOG(FATAL) << "futex wait failed for " << name_; 677 } 678 } 679 --num_pending_readers_; 680 } 681 #endif 682 683 bool ReaderWriterMutex::SharedTryLock(Thread* self) { 684 DCHECK(self == nullptr || self == Thread::Current()); 685 #if ART_USE_FUTEXES 686 bool done = false; 687 do { 688 int32_t cur_state = state_.LoadRelaxed(); 689 if (cur_state >= 0) { 690 // Add as an extra reader and impose load/store ordering appropriate for lock acquisition. 691 done = state_.CompareExchangeWeakAcquire(cur_state, cur_state + 1); 692 } else { 693 // Owner holds it exclusively. 694 return false; 695 } 696 } while (!done); 697 #else 698 int result = pthread_rwlock_tryrdlock(&rwlock_); 699 if (result == EBUSY) { 700 return false; 701 } 702 if (result != 0) { 703 errno = result; 704 PLOG(FATAL) << "pthread_mutex_trylock failed for " << name_; 705 } 706 #endif 707 RegisterAsLocked(self); 708 AssertSharedHeld(self); 709 return true; 710 } 711 712 bool ReaderWriterMutex::IsSharedHeld(const Thread* self) const { 713 DCHECK(self == nullptr || self == Thread::Current()); 714 bool result; 715 if (UNLIKELY(self == nullptr)) { // Handle unattached threads. 716 result = IsExclusiveHeld(self); // TODO: a better best effort here. 717 } else { 718 result = (self->GetHeldMutex(level_) == this); 719 } 720 return result; 721 } 722 723 void ReaderWriterMutex::Dump(std::ostream& os) const { 724 os << name_ 725 << " level=" << static_cast<int>(level_) 726 << " owner=" << GetExclusiveOwnerTid() 727 #if ART_USE_FUTEXES 728 << " state=" << state_.LoadSequentiallyConsistent() 729 << " num_pending_writers=" << num_pending_writers_.LoadSequentiallyConsistent() 730 << " num_pending_readers=" << num_pending_readers_.LoadSequentiallyConsistent() 731 #endif 732 << " "; 733 DumpContention(os); 734 } 735 736 std::ostream& operator<<(std::ostream& os, const ReaderWriterMutex& mu) { 737 mu.Dump(os); 738 return os; 739 } 740 741 ConditionVariable::ConditionVariable(const char* name, Mutex& guard) 742 : name_(name), guard_(guard) { 743 #if ART_USE_FUTEXES 744 DCHECK_EQ(0, sequence_.LoadRelaxed()); 745 num_waiters_ = 0; 746 #else 747 pthread_condattr_t cond_attrs; 748 CHECK_MUTEX_CALL(pthread_condattr_init, (&cond_attrs)); 749 #if !defined(__APPLE__) 750 // Apple doesn't have CLOCK_MONOTONIC or pthread_condattr_setclock. 751 CHECK_MUTEX_CALL(pthread_condattr_setclock, (&cond_attrs, CLOCK_MONOTONIC)); 752 #endif 753 CHECK_MUTEX_CALL(pthread_cond_init, (&cond_, &cond_attrs)); 754 #endif 755 } 756 757 ConditionVariable::~ConditionVariable() { 758 #if ART_USE_FUTEXES 759 if (num_waiters_!= 0) { 760 Runtime* runtime = Runtime::Current(); 761 bool shutting_down = runtime == nullptr || runtime->IsShuttingDown(Thread::Current()); 762 LOG(shutting_down ? WARNING : FATAL) << "ConditionVariable::~ConditionVariable for " << name_ 763 << " called with " << num_waiters_ << " waiters."; 764 } 765 #else 766 // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread 767 // may still be using condition variables. 768 int rc = pthread_cond_destroy(&cond_); 769 if (rc != 0) { 770 errno = rc; 771 MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_); 772 Runtime* runtime = Runtime::Current(); 773 bool shutting_down = (runtime == nullptr) || runtime->IsShuttingDownLocked(); 774 PLOG(shutting_down ? WARNING : FATAL) << "pthread_cond_destroy failed for " << name_; 775 } 776 #endif 777 } 778 779 void ConditionVariable::Broadcast(Thread* self) { 780 DCHECK(self == nullptr || self == Thread::Current()); 781 // TODO: enable below, there's a race in thread creation that causes false failures currently. 782 // guard_.AssertExclusiveHeld(self); 783 DCHECK_EQ(guard_.GetExclusiveOwnerTid(), SafeGetTid(self)); 784 #if ART_USE_FUTEXES 785 if (num_waiters_ > 0) { 786 sequence_++; // Indicate the broadcast occurred. 787 bool done = false; 788 do { 789 int32_t cur_sequence = sequence_.LoadRelaxed(); 790 // Requeue waiters onto mutex. The waiter holds the contender count on the mutex high ensuring 791 // mutex unlocks will awaken the requeued waiter thread. 792 done = futex(sequence_.Address(), FUTEX_CMP_REQUEUE, 0, 793 reinterpret_cast<const timespec*>(std::numeric_limits<int32_t>::max()), 794 guard_.state_.Address(), cur_sequence) != -1; 795 if (!done) { 796 if (errno != EAGAIN) { 797 PLOG(FATAL) << "futex cmp requeue failed for " << name_; 798 } 799 } 800 } while (!done); 801 } 802 #else 803 CHECK_MUTEX_CALL(pthread_cond_broadcast, (&cond_)); 804 #endif 805 } 806 807 void ConditionVariable::Signal(Thread* self) { 808 DCHECK(self == nullptr || self == Thread::Current()); 809 guard_.AssertExclusiveHeld(self); 810 #if ART_USE_FUTEXES 811 if (num_waiters_ > 0) { 812 sequence_++; // Indicate a signal occurred. 813 // Futex wake 1 waiter who will then come and in contend on mutex. It'd be nice to requeue them 814 // to avoid this, however, requeueing can only move all waiters. 815 int num_woken = futex(sequence_.Address(), FUTEX_WAKE, 1, nullptr, nullptr, 0); 816 // Check something was woken or else we changed sequence_ before they had chance to wait. 817 CHECK((num_woken == 0) || (num_woken == 1)); 818 } 819 #else 820 CHECK_MUTEX_CALL(pthread_cond_signal, (&cond_)); 821 #endif 822 } 823 824 void ConditionVariable::Wait(Thread* self) { 825 guard_.CheckSafeToWait(self); 826 WaitHoldingLocks(self); 827 } 828 829 void ConditionVariable::WaitHoldingLocks(Thread* self) { 830 DCHECK(self == nullptr || self == Thread::Current()); 831 guard_.AssertExclusiveHeld(self); 832 unsigned int old_recursion_count = guard_.recursion_count_; 833 #if ART_USE_FUTEXES 834 num_waiters_++; 835 // Ensure the Mutex is contended so that requeued threads are awoken. 836 guard_.num_contenders_++; 837 guard_.recursion_count_ = 1; 838 int32_t cur_sequence = sequence_.LoadRelaxed(); 839 guard_.ExclusiveUnlock(self); 840 if (futex(sequence_.Address(), FUTEX_WAIT, cur_sequence, nullptr, nullptr, 0) != 0) { 841 // Futex failed, check it is an expected error. 842 // EAGAIN == EWOULDBLK, so we let the caller try again. 843 // EINTR implies a signal was sent to this thread. 844 if ((errno != EINTR) && (errno != EAGAIN)) { 845 PLOG(FATAL) << "futex wait failed for " << name_; 846 } 847 } 848 guard_.ExclusiveLock(self); 849 CHECK_GE(num_waiters_, 0); 850 num_waiters_--; 851 // We awoke and so no longer require awakes from the guard_'s unlock. 852 CHECK_GE(guard_.num_contenders_.LoadRelaxed(), 0); 853 guard_.num_contenders_--; 854 #else 855 uint64_t old_owner = guard_.exclusive_owner_; 856 guard_.exclusive_owner_ = 0; 857 guard_.recursion_count_ = 0; 858 CHECK_MUTEX_CALL(pthread_cond_wait, (&cond_, &guard_.mutex_)); 859 guard_.exclusive_owner_ = old_owner; 860 #endif 861 guard_.recursion_count_ = old_recursion_count; 862 } 863 864 bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { 865 DCHECK(self == nullptr || self == Thread::Current()); 866 bool timed_out = false; 867 guard_.AssertExclusiveHeld(self); 868 guard_.CheckSafeToWait(self); 869 unsigned int old_recursion_count = guard_.recursion_count_; 870 #if ART_USE_FUTEXES 871 timespec rel_ts; 872 InitTimeSpec(false, CLOCK_REALTIME, ms, ns, &rel_ts); 873 num_waiters_++; 874 // Ensure the Mutex is contended so that requeued threads are awoken. 875 guard_.num_contenders_++; 876 guard_.recursion_count_ = 1; 877 int32_t cur_sequence = sequence_.LoadRelaxed(); 878 guard_.ExclusiveUnlock(self); 879 if (futex(sequence_.Address(), FUTEX_WAIT, cur_sequence, &rel_ts, nullptr, 0) != 0) { 880 if (errno == ETIMEDOUT) { 881 // Timed out we're done. 882 timed_out = true; 883 } else if ((errno == EAGAIN) || (errno == EINTR)) { 884 // A signal or ConditionVariable::Signal/Broadcast has come in. 885 } else { 886 PLOG(FATAL) << "timed futex wait failed for " << name_; 887 } 888 } 889 guard_.ExclusiveLock(self); 890 CHECK_GE(num_waiters_, 0); 891 num_waiters_--; 892 // We awoke and so no longer require awakes from the guard_'s unlock. 893 CHECK_GE(guard_.num_contenders_.LoadRelaxed(), 0); 894 guard_.num_contenders_--; 895 #else 896 #if !defined(__APPLE__) 897 int clock = CLOCK_MONOTONIC; 898 #else 899 int clock = CLOCK_REALTIME; 900 #endif 901 uint64_t old_owner = guard_.exclusive_owner_; 902 guard_.exclusive_owner_ = 0; 903 guard_.recursion_count_ = 0; 904 timespec ts; 905 InitTimeSpec(true, clock, ms, ns, &ts); 906 int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &guard_.mutex_, &ts)); 907 if (rc == ETIMEDOUT) { 908 timed_out = true; 909 } else if (rc != 0) { 910 errno = rc; 911 PLOG(FATAL) << "TimedWait failed for " << name_; 912 } 913 guard_.exclusive_owner_ = old_owner; 914 #endif 915 guard_.recursion_count_ = old_recursion_count; 916 return timed_out; 917 } 918 919 void Locks::Init() { 920 if (logging_lock_ != nullptr) { 921 // Already initialized. 922 if (kRuntimeISA == kX86 || kRuntimeISA == kX86_64) { 923 DCHECK(modify_ldt_lock_ != nullptr); 924 } else { 925 DCHECK(modify_ldt_lock_ == nullptr); 926 } 927 DCHECK(abort_lock_ != nullptr); 928 DCHECK(alloc_tracker_lock_ != nullptr); 929 DCHECK(allocated_monitor_ids_lock_ != nullptr); 930 DCHECK(allocated_thread_ids_lock_ != nullptr); 931 DCHECK(breakpoint_lock_ != nullptr); 932 DCHECK(classlinker_classes_lock_ != nullptr); 933 DCHECK(deoptimization_lock_ != nullptr); 934 DCHECK(heap_bitmap_lock_ != nullptr); 935 DCHECK(intern_table_lock_ != nullptr); 936 DCHECK(jni_libraries_lock_ != nullptr); 937 DCHECK(logging_lock_ != nullptr); 938 DCHECK(mutator_lock_ != nullptr); 939 DCHECK(profiler_lock_ != nullptr); 940 DCHECK(thread_list_lock_ != nullptr); 941 DCHECK(thread_suspend_count_lock_ != nullptr); 942 DCHECK(trace_lock_ != nullptr); 943 DCHECK(unexpected_signal_lock_ != nullptr); 944 } else { 945 // Create global locks in level order from highest lock level to lowest. 946 LockLevel current_lock_level = kInstrumentEntrypointsLock; 947 DCHECK(instrument_entrypoints_lock_ == nullptr); 948 instrument_entrypoints_lock_ = new Mutex("instrument entrypoint lock", current_lock_level); 949 950 #define UPDATE_CURRENT_LOCK_LEVEL(new_level) \ 951 if (new_level >= current_lock_level) { \ 952 /* Do not use CHECKs or FATAL here, abort_lock_ is not setup yet. */ \ 953 fprintf(stderr, "New local level %d is not less than current level %d\n", \ 954 new_level, current_lock_level); \ 955 exit(1); \ 956 } \ 957 current_lock_level = new_level; 958 959 UPDATE_CURRENT_LOCK_LEVEL(kMutatorLock); 960 DCHECK(mutator_lock_ == nullptr); 961 mutator_lock_ = new ReaderWriterMutex("mutator lock", current_lock_level); 962 963 UPDATE_CURRENT_LOCK_LEVEL(kHeapBitmapLock); 964 DCHECK(heap_bitmap_lock_ == nullptr); 965 heap_bitmap_lock_ = new ReaderWriterMutex("heap bitmap lock", current_lock_level); 966 967 UPDATE_CURRENT_LOCK_LEVEL(kTraceLock); 968 DCHECK(trace_lock_ == nullptr); 969 trace_lock_ = new Mutex("trace lock", current_lock_level); 970 971 UPDATE_CURRENT_LOCK_LEVEL(kRuntimeShutdownLock); 972 DCHECK(runtime_shutdown_lock_ == nullptr); 973 runtime_shutdown_lock_ = new Mutex("runtime shutdown lock", current_lock_level); 974 975 UPDATE_CURRENT_LOCK_LEVEL(kProfilerLock); 976 DCHECK(profiler_lock_ == nullptr); 977 profiler_lock_ = new Mutex("profiler lock", current_lock_level); 978 979 UPDATE_CURRENT_LOCK_LEVEL(kDeoptimizationLock); 980 DCHECK(deoptimization_lock_ == nullptr); 981 deoptimization_lock_ = new Mutex("Deoptimization lock", current_lock_level); 982 983 UPDATE_CURRENT_LOCK_LEVEL(kAllocTrackerLock); 984 DCHECK(alloc_tracker_lock_ == nullptr); 985 alloc_tracker_lock_ = new Mutex("AllocTracker lock", current_lock_level); 986 987 UPDATE_CURRENT_LOCK_LEVEL(kThreadListLock); 988 DCHECK(thread_list_lock_ == nullptr); 989 thread_list_lock_ = new Mutex("thread list lock", current_lock_level); 990 991 UPDATE_CURRENT_LOCK_LEVEL(kJniLoadLibraryLock); 992 DCHECK(jni_libraries_lock_ == nullptr); 993 jni_libraries_lock_ = new Mutex("JNI shared libraries map lock", current_lock_level); 994 995 UPDATE_CURRENT_LOCK_LEVEL(kBreakpointLock); 996 DCHECK(breakpoint_lock_ == nullptr); 997 breakpoint_lock_ = new ReaderWriterMutex("breakpoint lock", current_lock_level); 998 999 UPDATE_CURRENT_LOCK_LEVEL(kClassLinkerClassesLock); 1000 DCHECK(classlinker_classes_lock_ == nullptr); 1001 classlinker_classes_lock_ = new ReaderWriterMutex("ClassLinker classes lock", 1002 current_lock_level); 1003 1004 UPDATE_CURRENT_LOCK_LEVEL(kMonitorPoolLock); 1005 DCHECK(allocated_monitor_ids_lock_ == nullptr); 1006 allocated_monitor_ids_lock_ = new Mutex("allocated monitor ids lock", current_lock_level); 1007 1008 UPDATE_CURRENT_LOCK_LEVEL(kAllocatedThreadIdsLock); 1009 DCHECK(allocated_thread_ids_lock_ == nullptr); 1010 allocated_thread_ids_lock_ = new Mutex("allocated thread ids lock", current_lock_level); 1011 1012 if (kRuntimeISA == kX86 || kRuntimeISA == kX86_64) { 1013 UPDATE_CURRENT_LOCK_LEVEL(kModifyLdtLock); 1014 DCHECK(modify_ldt_lock_ == nullptr); 1015 modify_ldt_lock_ = new Mutex("modify_ldt lock", current_lock_level); 1016 } 1017 1018 UPDATE_CURRENT_LOCK_LEVEL(kInternTableLock); 1019 DCHECK(intern_table_lock_ == nullptr); 1020 intern_table_lock_ = new Mutex("InternTable lock", current_lock_level); 1021 1022 UPDATE_CURRENT_LOCK_LEVEL(kReferenceProcessorLock); 1023 DCHECK(reference_processor_lock_ == nullptr); 1024 reference_processor_lock_ = new Mutex("ReferenceProcessor lock", current_lock_level); 1025 1026 UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueClearedReferencesLock); 1027 DCHECK(reference_queue_cleared_references_lock_ == nullptr); 1028 reference_queue_cleared_references_lock_ = new Mutex("ReferenceQueue cleared references lock", current_lock_level); 1029 1030 UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueWeakReferencesLock); 1031 DCHECK(reference_queue_weak_references_lock_ == nullptr); 1032 reference_queue_weak_references_lock_ = new Mutex("ReferenceQueue cleared references lock", current_lock_level); 1033 1034 UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueFinalizerReferencesLock); 1035 DCHECK(reference_queue_finalizer_references_lock_ == nullptr); 1036 reference_queue_finalizer_references_lock_ = new Mutex("ReferenceQueue finalizer references lock", current_lock_level); 1037 1038 UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueuePhantomReferencesLock); 1039 DCHECK(reference_queue_phantom_references_lock_ == nullptr); 1040 reference_queue_phantom_references_lock_ = new Mutex("ReferenceQueue phantom references lock", current_lock_level); 1041 1042 UPDATE_CURRENT_LOCK_LEVEL(kReferenceQueueSoftReferencesLock); 1043 DCHECK(reference_queue_soft_references_lock_ == nullptr); 1044 reference_queue_soft_references_lock_ = new Mutex("ReferenceQueue soft references lock", current_lock_level); 1045 1046 UPDATE_CURRENT_LOCK_LEVEL(kAbortLock); 1047 DCHECK(abort_lock_ == nullptr); 1048 abort_lock_ = new Mutex("abort lock", current_lock_level, true); 1049 1050 UPDATE_CURRENT_LOCK_LEVEL(kThreadSuspendCountLock); 1051 DCHECK(thread_suspend_count_lock_ == nullptr); 1052 thread_suspend_count_lock_ = new Mutex("thread suspend count lock", current_lock_level); 1053 1054 UPDATE_CURRENT_LOCK_LEVEL(kUnexpectedSignalLock); 1055 DCHECK(unexpected_signal_lock_ == nullptr); 1056 unexpected_signal_lock_ = new Mutex("unexpected signal lock", current_lock_level, true); 1057 1058 UPDATE_CURRENT_LOCK_LEVEL(kMemMapsLock); 1059 DCHECK(mem_maps_lock_ == nullptr); 1060 mem_maps_lock_ = new Mutex("mem maps lock", current_lock_level); 1061 1062 UPDATE_CURRENT_LOCK_LEVEL(kLoggingLock); 1063 DCHECK(logging_lock_ == nullptr); 1064 logging_lock_ = new Mutex("logging lock", current_lock_level, true); 1065 1066 #undef UPDATE_CURRENT_LOCK_LEVEL 1067 1068 InitConditions(); 1069 } 1070 } 1071 1072 void Locks::InitConditions() { 1073 thread_exit_cond_ = new ConditionVariable("thread exit condition variable", *thread_list_lock_); 1074 } 1075 1076 } // namespace art 1077