blob: 03ec6f84b78b3e7cc3ec970e33971bd839fe4cce [file] [log] [blame]
Ian Rogers693ff612013-02-01 10:56:12 -08001/*
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#ifndef ART_SRC_BASE_MUTEX_INL_H_
18#define ART_SRC_BASE_MUTEX_INL_H_
19
20#include "mutex.h"
21
22#include "cutils/atomic-inline.h"
23#include "runtime.h"
24#include "thread.h"
25
26namespace art {
27
28#define CHECK_MUTEX_CALL(call, args) CHECK_PTHREAD_CALL(call, args, name_)
29
30#if ART_USE_FUTEXES
31#include "linux/futex.h"
32#include "sys/syscall.h"
33#ifndef SYS_futex
34#define SYS_futex __NR_futex
35#endif
36static inline int futex(volatile int *uaddr, int op, int val, const struct timespec *timeout, volatile int *uaddr2, int val3) {
37 return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
38}
39#endif // ART_USE_FUTEXES
40
41class ScopedContentionRecorder {
42 public:
43 ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid) :
44 mutex_(mutex), blocked_tid_(blocked_tid), owner_tid_(owner_tid),
45 start_milli_time_(MilliTime()) {
46 }
47
48 ~ScopedContentionRecorder() {
49 uint64_t end_milli_time = MilliTime();
50 mutex_->RecordContention(blocked_tid_, owner_tid_, end_milli_time - start_milli_time_);
51 }
52
53 private:
54 BaseMutex* const mutex_;
55 uint64_t blocked_tid_;
56 uint64_t owner_tid_;
57 const uint64_t start_milli_time_;
58};
59
60static inline uint64_t SafeGetTid(const Thread* self) {
61 if (self != NULL) {
62 return static_cast<uint64_t>(self->GetTid());
63 } else {
64 return static_cast<uint64_t>(GetTid());
65 }
66}
67
68static inline void CheckUnattachedThread(LockLevel level) NO_THREAD_SAFETY_ANALYSIS {
69 // The check below enumerates the cases where we expect not to be able to sanity check locks
70 // on a thread. Lock checking is disabled to avoid deadlock when checking shutdown lock.
71 // TODO: tighten this check.
72 if (kDebugLocking) {
73 Runtime* runtime = Runtime::Current();
74 CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDown() ||
75 level == kDefaultMutexLevel || level == kRuntimeShutdownLock ||
76 level == kThreadListLock || level == kLoggingLock || level == kAbortLock);
77 }
78}
79
80inline void BaseMutex::RegisterAsUnlocked(Thread* self) {
81 if (UNLIKELY(self == NULL)) {
82 CheckUnattachedThread(level_);
83 return;
84 }
85 if (level_ != kMonitorLock) {
86 if (kDebugLocking && !gAborting) {
87 CHECK(self->GetHeldMutex(level_) == this) << "Unlocking on unacquired mutex: " << name_;
88 }
89 self->SetHeldMutex(level_, NULL);
90 }
91}
92
93inline void ReaderWriterMutex::SharedLock(Thread* self) {
94 DCHECK(self == NULL || self == Thread::Current());
95#if ART_USE_FUTEXES
96 bool done = false;
97 do {
98 int32_t cur_state = state_;
99 if (cur_state >= 0) {
100 // Add as an extra reader.
101 done = android_atomic_acquire_cas(cur_state, cur_state + 1, &state_) == 0;
102 } else {
103 // Owner holds it exclusively, hang up.
104 ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self));
105 android_atomic_inc(&num_pending_readers_);
106 if (futex(&state_, FUTEX_WAIT, cur_state, NULL, NULL, 0) != 0) {
107 if (errno != EAGAIN) {
108 PLOG(FATAL) << "futex wait failed for " << name_;
109 }
110 }
111 android_atomic_dec(&num_pending_readers_);
112 }
113 } while(!done);
114#else
115 CHECK_MUTEX_CALL(pthread_rwlock_rdlock, (&rwlock_));
116#endif
117 RegisterAsLocked(self);
118 AssertSharedHeld(self);
119}
120
121inline void ReaderWriterMutex::SharedUnlock(Thread* self) {
122 DCHECK(self == NULL || self == Thread::Current());
123 AssertSharedHeld(self);
124 RegisterAsUnlocked(self);
125#if ART_USE_FUTEXES
126 bool done = false;
127 do {
128 int32_t cur_state = state_;
129 if (LIKELY(cur_state > 0)) {
130 // Reduce state by 1.
131 done = android_atomic_release_cas(cur_state, cur_state - 1, &state_) == 0;
132 if (done && (cur_state - 1) == 0) { // cas may fail due to noise?
133 if (num_pending_writers_ > 0 || num_pending_readers_ > 0) {
134 // Wake any exclusive waiters as there are now no readers.
135 futex(&state_, FUTEX_WAKE, -1, NULL, NULL, 0);
136 }
137 }
138 } else {
139 LOG(FATAL) << "Unexpected state_:" << cur_state << " for " << name_;
140 }
141 } while(!done);
142#else
143 CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_));
144#endif
145}
146
147} // namespace art
148
149#endif // ART_SRC_BASE_MUTEX_INL_H_