blob: 0ef5fb42e9671afb87e31328686e99e496823a04 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "monitor.h"
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include "mutex.h"
#include "object.h"
#include "thread.h"
namespace art {
/*
* Every Object has a monitor associated with it, but not every Object is
* actually locked. Even the ones that are locked do not need a
* full-fledged monitor until a) there is actual contention or b) wait()
* is called on the Object.
*
* For Android, we have implemented a scheme similar to the one described
* in Bacon et al.'s "Thin locks: featherweight synchronization for Java"
* (ACM 1998). Things are even easier for us, though, because we have
* a full 32 bits to work with.
*
* The two states of an Object's lock are referred to as "thin" and
* "fat". A lock may transition from the "thin" state to the "fat"
* state and this transition is referred to as inflation. Once a lock
* has been inflated it remains in the "fat" state indefinitely.
*
* The lock value itself is stored in Object.lock. The LSB of the
* lock encodes its state. When cleared, the lock is in the "thin"
* state and its bits are formatted as follows:
*
* [31 ---- 19] [18 ---- 3] [2 ---- 1] [0]
* lock count thread id hash state 0
*
* When set, the lock is in the "fat" state and its bits are formatted
* as follows:
*
* [31 ---- 3] [2 ---- 1] [0]
* pointer hash state 1
*
* For an in-depth description of the mechanics of thin-vs-fat locking,
* read the paper referred to above.
*
* Monitors provide:
* - mutually exclusive access to resources
* - a way for multiple threads to wait for notification
*
* In effect, they fill the role of both mutexes and condition variables.
*
* Only one thread can own the monitor at any time. There may be several
* threads waiting on it (the wait call unlocks it). One or more waiting
* threads may be getting interrupted or notified at any given time.
*
* TODO: the various members of monitor are not SMP-safe.
*/
/*
* Monitor accessor. Extracts a monitor structure pointer from a fat
* lock. Performs no error checking.
*/
#define LW_MONITOR(x) \
((Monitor*)((x) & ~((LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT) | LW_SHAPE_MASK)))
/*
* Lock recursion count field. Contains a count of the number of times
* a lock has been recursively acquired.
*/
#define LW_LOCK_COUNT_MASK 0x1fff
#define LW_LOCK_COUNT_SHIFT 19
#define LW_LOCK_COUNT(x) (((x) >> LW_LOCK_COUNT_SHIFT) & LW_LOCK_COUNT_MASK)
Monitor::Monitor(Object* obj)
: owner_(NULL),
lock_count_(0),
obj_(obj),
wait_set_(NULL),
lock_("a monitor lock"),
next_(NULL),
owner_filename_(NULL),
owner_line_number_(0) {
}
Monitor::~Monitor() {
DCHECK(obj_ != NULL);
DCHECK_EQ(LW_SHAPE(*obj_->GetRawLockWordAddress()), LW_SHAPE_FAT);
#ifndef NDEBUG
/* This lock is associated with an object
* that's being swept. The only possible way
* anyone could be holding this lock would be
* if some JNI code locked but didn't unlock
* the object, in which case we've got some bad
* native code somewhere.
*/
DCHECK(lock_.TryLock());
lock_.Unlock();
#endif
}
/*
* Links a thread into a monitor's wait set. The monitor lock must be
* held by the caller of this routine.
*/
void Monitor::AppendToWaitSet(Thread* thread) {
DCHECK(owner_ == Thread::Current());
DCHECK(thread != NULL);
DCHECK(thread->wait_next_ == NULL) << thread->wait_next_;
if (wait_set_ == NULL) {
wait_set_ = thread;
return;
}
// push_back.
Thread* t = wait_set_;
while (t->wait_next_ != NULL) {
t = t->wait_next_;
}
t->wait_next_ = thread;
}
/*
* Unlinks a thread from a monitor's wait set. The monitor lock must
* be held by the caller of this routine.
*/
void Monitor::RemoveFromWaitSet(Thread *thread) {
DCHECK(owner_ == Thread::Current());
DCHECK(thread != NULL);
if (wait_set_ == NULL) {
return;
}
if (wait_set_ == thread) {
wait_set_ = thread->wait_next_;
thread->wait_next_ = NULL;
return;
}
Thread* t = wait_set_;
while (t->wait_next_ != NULL) {
if (t->wait_next_ == thread) {
t->wait_next_ = thread->wait_next_;
thread->wait_next_ = NULL;
return;
}
t = t->wait_next_;
}
}
// Global list of all monitors. Used for cleanup.
static Monitor* gMonitorList = NULL;
void Monitor::FreeMonitorList() {
Monitor* m = gMonitorList;
while (m != NULL) {
Monitor* next = m->next_;
delete m;
m = next;
}
}
/*
* Frees monitor objects belonging to unmarked objects.
*/
static void SweepMonitorList(Monitor** mon, bool (isUnmarkedObject)(void*)) {
UNIMPLEMENTED(FATAL);
#if 0
Monitor handle;
Monitor *curr;
assert(mon != NULL);
assert(isUnmarkedObject != NULL);
Monitor* prev = &handle;
prev->next = curr = *mon;
while (curr != NULL) {
Object* obj = curr->obj;
if ((*isUnmarkedObject)(obj) != 0) {
prev->next = curr->next;
delete curr;
curr = prev->next;
} else {
prev = curr;
curr = curr->next;
}
}
*mon = handle.next;
#endif
}
void Monitor::SweepMonitorList(bool (isUnmarkedObject)(void*)) {
::art::SweepMonitorList(&gMonitorList, isUnmarkedObject);
}
/*
static char *logWriteInt(char *dst, int value) {
*dst++ = EVENT_TYPE_INT;
set4LE((uint8_t *)dst, value);
return dst + 4;
}
static char *logWriteString(char *dst, const char *value, size_t len) {
*dst++ = EVENT_TYPE_STRING;
len = len < 32 ? len : 32;
set4LE((uint8_t *)dst, len);
dst += 4;
memcpy(dst, value, len);
return dst + len;
}
#define EVENT_LOG_TAG_dvm_lock_sample 20003
static void logContentionEvent(Thread *self, uint32_t waitMs, uint32_t samplePercent,
const char *ownerFileName, uint32_t ownerLineNumber)
{
const StackSaveArea *saveArea;
const Method *meth;
uint32_t relativePc;
char eventBuffer[174];
const char *fileName;
char procName[33];
char *cp;
size_t len;
int fd;
saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
meth = saveArea->method;
cp = eventBuffer;
// Emit the event list length, 1 byte.
*cp++ = 9;
// Emit the process name, <= 37 bytes.
fd = open("/proc/self/cmdline", O_RDONLY);
memset(procName, 0, sizeof(procName));
read(fd, procName, sizeof(procName) - 1);
close(fd);
len = strlen(procName);
cp = logWriteString(cp, procName, len);
// Emit the sensitive thread ("main thread") status, 5 bytes.
bool isSensitive = false;
if (gDvm.isSensitiveThreadHook != NULL) {
isSensitive = gDvm.isSensitiveThreadHook();
}
cp = logWriteInt(cp, isSensitive);
// Emit self thread name string, <= 37 bytes.
std::string selfName = dvmGetThreadName(self);
cp = logWriteString(cp, selfName.c_str(), selfName.size());
// Emit the wait time, 5 bytes.
cp = logWriteInt(cp, waitMs);
// Emit the source code file name, <= 37 bytes.
fileName = dvmGetMethodSourceFile(meth);
if (fileName == NULL) fileName = "";
cp = logWriteString(cp, fileName, strlen(fileName));
// Emit the source code line number, 5 bytes.
relativePc = saveArea->xtra.currentPc - saveArea->method->insns;
cp = logWriteInt(cp, dvmLineNumFromPC(meth, relativePc));
// Emit the lock owner source code file name, <= 37 bytes.
if (ownerFileName == NULL) {
ownerFileName = "";
} else if (strcmp(fileName, ownerFileName) == 0) {
// Common case, so save on log space.
ownerFileName = "-";
}
cp = logWriteString(cp, ownerFileName, strlen(ownerFileName));
// Emit the source code line number, 5 bytes.
cp = logWriteInt(cp, ownerLineNumber);
// Emit the sample percentage, 5 bytes.
cp = logWriteInt(cp, samplePercent);
assert((size_t)(cp - eventBuffer) <= sizeof(eventBuffer));
android_btWriteLog(EVENT_LOG_TAG_dvm_lock_sample,
EVENT_TYPE_LIST,
eventBuffer,
(size_t)(cp - eventBuffer));
}
*/
void Monitor::Lock(Thread* self) {
// uint32_t waitThreshold, samplePercent;
// uint64_t waitStart, waitEnd, waitMs;
if (owner_ == self) {
lock_count_++;
return;
}
if (!lock_.TryLock()) {
{
ScopedThreadStateChange tsc(self, Thread::kBlocked);
// waitThreshold = gDvm.lockProfThreshold;
// if (waitThreshold) {
// waitStart = dvmGetRelativeTimeUsec();
// }
// const char* currentOwnerFileName = mon->ownerFileName;
// uint32_t currentOwnerLineNumber = mon->ownerLineNumber;
lock_.Lock();
// if (waitThreshold) {
// waitEnd = dvmGetRelativeTimeUsec();
// }
}
// if (waitThreshold) {
// waitMs = (waitEnd - waitStart) / 1000;
// if (waitMs >= waitThreshold) {
// samplePercent = 100;
// } else {
// samplePercent = 100 * waitMs / waitThreshold;
// }
// if (samplePercent != 0 && ((uint32_t)rand() % 100 < samplePercent)) {
// logContentionEvent(self, waitMs, samplePercent, currentOwnerFileName, currentOwnerLineNumber);
// }
// }
}
owner_ = self;
DCHECK_EQ(lock_count_, 0);
// When debugging, save the current monitor holder for future
// acquisition failures to use in sampled logging.
// if (gDvm.lockProfThreshold > 0) {
// const StackSaveArea *saveArea;
// const Method *meth;
// mon->ownerLineNumber = 0;
// if (self->interpSave.curFrame == NULL) {
// mon->ownerFileName = "no_frame";
// } else if ((saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame)) == NULL) {
// mon->ownerFileName = "no_save_area";
// } else if ((meth = saveArea->method) == NULL) {
// mon->ownerFileName = "no_method";
// } else {
// uint32_t relativePc = saveArea->xtra.currentPc - saveArea->method->insns;
// mon->ownerFileName = (char*) dvmGetMethodSourceFile(meth);
// if (mon->ownerFileName == NULL) {
// mon->ownerFileName = "no_method_file";
// } else {
// mon->ownerLineNumber = dvmLineNumFromPC(meth, relativePc);
// }
// }
// }
}
void ThrowIllegalMonitorStateException(const char* msg) {
Thread::Current()->ThrowNewException("Ljava/lang/IllegalMonitorStateException;", "%s", msg);
}
bool Monitor::Unlock(Thread* self) {
DCHECK(self != NULL);
if (owner_ == self) {
// We own the monitor, so nobody else can be in here.
if (lock_count_ == 0) {
owner_ = NULL;
owner_filename_ = "unlocked";
owner_line_number_ = 0;
lock_.Unlock();
} else {
--lock_count_;
}
} else {
// We don't own this, so we're not allowed to unlock it.
// The JNI spec says that we should throw IllegalMonitorStateException
// in this case.
ThrowIllegalMonitorStateException("unlock of unowned monitor");
return false;
}
return true;
}
/*
* Converts the given relative waiting time into an absolute time.
*/
void ToAbsoluteTime(int64_t ms, int32_t ns, struct timespec *ts) {
int64_t endSec;
#ifdef HAVE_TIMEDWAIT_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, ts);
#else
{
struct timeval tv;
gettimeofday(&tv, NULL);
ts->tv_sec = tv.tv_sec;
ts->tv_nsec = tv.tv_usec * 1000;
}
#endif
endSec = ts->tv_sec + ms / 1000;
if (endSec >= 0x7fffffff) {
LOG(INFO) << "Note: end time exceeds epoch";
endSec = 0x7ffffffe;
}
ts->tv_sec = endSec;
ts->tv_nsec = (ts->tv_nsec + (ms % 1000) * 1000000) + ns;
// Catch rollover.
if (ts->tv_nsec >= 1000000000L) {
ts->tv_sec++;
ts->tv_nsec -= 1000000000L;
}
}
int dvmRelativeCondWait(pthread_cond_t* cond, pthread_mutex_t* mutex, int64_t ms, int32_t ns) {
struct timespec ts;
ToAbsoluteTime(ms, ns, &ts);
#if defined(HAVE_TIMEDWAIT_MONOTONIC)
int rc = pthread_cond_timedwait_monotonic(cond, mutex, &ts);
#else
int rc = pthread_cond_timedwait(cond, mutex, &ts);
#endif
DCHECK(rc == 0 || rc == ETIMEDOUT);
return rc;
}
/*
* Wait on a monitor until timeout, interrupt, or notification. Used for
* Object.wait() and (somewhat indirectly) Thread.sleep() and Thread.join().
*
* If another thread calls Thread.interrupt(), we throw InterruptedException
* and return immediately if one of the following are true:
* - blocked in wait(), wait(long), or wait(long, int) methods of Object
* - blocked in join(), join(long), or join(long, int) methods of Thread
* - blocked in sleep(long), or sleep(long, int) methods of Thread
* Otherwise, we set the "interrupted" flag.
*
* Checks to make sure that "ns" is in the range 0-999999
* (i.e. fractions of a millisecond) and throws the appropriate
* exception if it isn't.
*
* The spec allows "spurious wakeups", and recommends that all code using
* Object.wait() do so in a loop. This appears to derive from concerns
* about pthread_cond_wait() on multiprocessor systems. Some commentary
* on the web casts doubt on whether these can/should occur.
*
* Since we're allowed to wake up "early", we clamp extremely long durations
* to return at the end of the 32-bit time epoch.
*/
void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, bool interruptShouldThrow) {
DCHECK(self != NULL);
// Make sure that we hold the lock.
if (owner_ != self) {
ThrowIllegalMonitorStateException("object not locked by thread before wait()");
return;
}
// Enforce the timeout range.
if (ms < 0 || ns < 0 || ns > 999999) {
Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
"timeout arguments out of range: ms=%lld ns=%d", ms, ns);
return;
}
// Compute absolute wakeup time, if necessary.
struct timespec ts;
bool timed = false;
if (ms != 0 || ns != 0) {
ToAbsoluteTime(ms, ns, &ts);
timed = true;
}
/*
* Add ourselves to the set of threads waiting on this monitor, and
* release our hold. We need to let it go even if we're a few levels
* deep in a recursive lock, and we need to restore that later.
*
* We append to the wait set ahead of clearing the count and owner
* fields so the subroutine can check that the calling thread owns
* the monitor. Aside from that, the order of member updates is
* not order sensitive as we hold the pthread mutex.
*/
AppendToWaitSet(self);
int prevLockCount = lock_count_;
lock_count_ = 0;
owner_ = NULL;
const char* savedFileName = owner_filename_;
owner_filename_ = NULL;
uint32_t savedLineNumber = owner_line_number_;
owner_line_number_ = 0;
/*
* Update thread status. If the GC wakes up, it'll ignore us, knowing
* that we won't touch any references in this state, and we'll check
* our suspend mode before we transition out.
*/
if (timed) {
self->SetState(Thread::kTimedWaiting);
} else {
self->SetState(Thread::kWaiting);
}
self->wait_mutex_->Lock();
/*
* Set wait_monitor_ to the monitor object we will be waiting on.
* When wait_monitor_ is non-NULL a notifying or interrupting thread
* must signal the thread's wait_cond_ to wake it up.
*/
DCHECK(self->wait_monitor_ == NULL);
self->wait_monitor_ = this;
/*
* Handle the case where the thread was interrupted before we called
* wait().
*/
bool wasInterrupted = false;
if (self->interrupted_) {
wasInterrupted = true;
self->wait_monitor_ = NULL;
self->wait_mutex_->Unlock();
goto done;
}
/*
* Release the monitor lock and wait for a notification or
* a timeout to occur.
*/
lock_.Unlock();
if (!timed) {
self->wait_cond_->Wait(*self->wait_mutex_);
} else {
self->wait_cond_->TimedWait(*self->wait_mutex_, ts);
}
if (self->interrupted_) {
wasInterrupted = true;
}
self->interrupted_ = false;
self->wait_monitor_ = NULL;
self->wait_mutex_->Unlock();
// Reacquire the monitor lock.
Lock(self);
done:
/*
* We remove our thread from wait set after restoring the count
* and owner fields so the subroutine can check that the calling
* thread owns the monitor. Aside from that, the order of member
* updates is not order sensitive as we hold the pthread mutex.
*/
owner_ = self;
lock_count_ = prevLockCount;
owner_filename_ = savedFileName;
owner_line_number_ = savedLineNumber;
RemoveFromWaitSet(self);
/* set self->status back to Thread::kRunnable, and self-suspend if needed */
self->SetState(Thread::kRunnable);
if (wasInterrupted) {
/*
* We were interrupted while waiting, or somebody interrupted an
* un-interruptible thread earlier and we're bailing out immediately.
*
* The doc sayeth: "The interrupted status of the current thread is
* cleared when this exception is thrown."
*/
self->interrupted_ = false;
if (interruptShouldThrow) {
Thread::Current()->ThrowNewException("Ljava/lang/InterruptedException;", "%s", "");
}
}
}
void Monitor::Notify(Thread* self) {
DCHECK(self != NULL);
// Make sure that we hold the lock.
if (owner_ != self) {
ThrowIllegalMonitorStateException("object not locked by thread before notify()");
return;
}
// Signal the first waiting thread in the wait set.
while (wait_set_ != NULL) {
Thread* thread = wait_set_;
wait_set_ = thread->wait_next_;
thread->wait_next_ = NULL;
// Check to see if the thread is still waiting.
MutexLock mu(*thread->wait_mutex_);
if (thread->wait_monitor_ != NULL) {
thread->wait_cond_->Signal();
return;
}
}
}
void Monitor::NotifyAll(Thread* self) {
DCHECK(self != NULL);
// Make sure that we hold the lock.
if (owner_ != self) {
ThrowIllegalMonitorStateException("object not locked by thread before notifyAll()");
return;
}
// Signal all threads in the wait set.
while (wait_set_ != NULL) {
Thread* thread = wait_set_;
wait_set_ = thread->wait_next_;
thread->wait_next_ = NULL;
thread->Notify();
}
}
/*
* Changes the shape of a monitor from thin to fat, preserving the
* internal lock state. The calling thread must own the lock.
*/
void Monitor::Inflate(Thread* self, Object* obj) {
DCHECK(self != NULL);
DCHECK(obj != NULL);
DCHECK_EQ(LW_SHAPE(*obj->GetRawLockWordAddress()), LW_SHAPE_THIN);
DCHECK_EQ(LW_LOCK_OWNER(*obj->GetRawLockWordAddress()), static_cast<int32_t>(self->thin_lock_id_));
// Allocate and acquire a new monitor.
Monitor* m = new Monitor(obj);
// Replace the head of the list with the new monitor.
do {
m->next_ = gMonitorList;
} while (android_atomic_release_cas((int32_t)m->next_, (int32_t)m, (int32_t*)(void*)&gMonitorList) != 0);
m->Lock(self);
// Propagate the lock state.
uint32_t thin = *obj->GetRawLockWordAddress();
m->lock_count_ = LW_LOCK_COUNT(thin);
thin &= LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT;
thin |= reinterpret_cast<uint32_t>(m) | LW_SHAPE_FAT;
// Publish the updated lock word.
android_atomic_release_store(thin, obj->GetRawLockWordAddress());
}
void Monitor::MonitorEnter(Thread* self, Object* obj) {
volatile int32_t* thinp = obj->GetRawLockWordAddress();
struct timespec tm;
long sleepDelayNs;
long minSleepDelayNs = 1000000; /* 1 millisecond */
long maxSleepDelayNs = 1000000000; /* 1 second */
uint32_t thin, newThin, threadId;
assert(self != NULL);
assert(obj != NULL);
threadId = self->thin_lock_id_;
retry:
thin = *thinp;
if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
/*
* The lock is a thin lock. The owner field is used to
* determine the acquire method, ordered by cost.
*/
if (LW_LOCK_OWNER(thin) == threadId) {
/*
* The calling thread owns the lock. Increment the
* value of the recursion count field.
*/
*thinp += 1 << LW_LOCK_COUNT_SHIFT;
if (LW_LOCK_COUNT(*thinp) == LW_LOCK_COUNT_MASK) {
/*
* The reacquisition limit has been reached. Inflate
* the lock so the next acquire will not overflow the
* recursion count field.
*/
Inflate(self, obj);
}
} else if (LW_LOCK_OWNER(thin) == 0) {
/*
* The lock is unowned. Install the thread id of the
* calling thread into the owner field. This is the
* common case. In performance critical code the JIT
* will have tried this before calling out to the VM.
*/
newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
if (android_atomic_acquire_cas(thin, newThin, thinp) != 0) {
// The acquire failed. Try again.
goto retry;
}
} else {
LOG(INFO) << StringPrintf("(%d) spin on lock %p: %#x (%#x) %#x", threadId, thinp, 0, *thinp, thin);
// The lock is owned by another thread. Notify the VM that we are about to wait.
Thread::State oldStatus = self->SetState(Thread::kBlocked);
// Spin until the thin lock is released or inflated.
sleepDelayNs = 0;
for (;;) {
thin = *thinp;
// Check the shape of the lock word. Another thread
// may have inflated the lock while we were waiting.
if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
if (LW_LOCK_OWNER(thin) == 0) {
// The lock has been released. Install the thread id of the
// calling thread into the owner field.
newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
if (android_atomic_acquire_cas(thin, newThin, thinp) == 0) {
// The acquire succeed. Break out of the loop and proceed to inflate the lock.
break;
}
} else {
// The lock has not been released. Yield so the owning thread can run.
if (sleepDelayNs == 0) {
sched_yield();
sleepDelayNs = minSleepDelayNs;
} else {
tm.tv_sec = 0;
tm.tv_nsec = sleepDelayNs;
nanosleep(&tm, NULL);
// Prepare the next delay value. Wrap to avoid once a second polls for eternity.
if (sleepDelayNs < maxSleepDelayNs / 2) {
sleepDelayNs *= 2;
} else {
sleepDelayNs = minSleepDelayNs;
}
}
}
} else {
// The thin lock was inflated by another thread. Let the VM know we are no longer
// waiting and try again.
LOG(INFO) << "(" << threadId << ") lock " << (void*) thinp << " surprise-fattened";
self->SetState(oldStatus);
goto retry;
}
}
LOG(INFO) << StringPrintf("(%d) spin on lock done %p: %#x (%#x) %#x", threadId, thinp, 0, *thinp, thin);
// We have acquired the thin lock. Let the VM know that we are no longer waiting.
self->SetState(oldStatus);
// Fatten the lock.
Inflate(self, obj);
LOG(INFO) << StringPrintf("(%d) lock %p fattened", threadId, thinp);
}
} else {
// The lock is a fat lock.
DCHECK(LW_MONITOR(*thinp) != NULL);
LW_MONITOR(*thinp)->Lock(self);
}
}
bool Monitor::MonitorExit(Thread* self, Object* obj) {
volatile int32_t* thinp = obj->GetRawLockWordAddress();
DCHECK(self != NULL);
DCHECK_EQ(self->GetState(), Thread::kRunnable);
DCHECK(obj != NULL);
/*
* Cache the lock word as its value can change while we are
* examining its state.
*/
uint32_t thin = *thinp;
if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
/*
* The lock is thin. We must ensure that the lock is owned
* by the given thread before unlocking it.
*/
if (LW_LOCK_OWNER(thin) == self->thin_lock_id_) {
/*
* We are the lock owner. It is safe to update the lock
* without CAS as lock ownership guards the lock itself.
*/
if (LW_LOCK_COUNT(thin) == 0) {
/*
* The lock was not recursively acquired, the common
* case. Unlock by clearing all bits except for the
* hash state.
*/
thin &= (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT);
android_atomic_release_store(thin, thinp);
} else {
/*
* The object was recursively acquired. Decrement the
* lock recursion count field.
*/
*thinp -= 1 << LW_LOCK_COUNT_SHIFT;
}
} else {
/*
* We do not own the lock. The JVM spec requires that we
* throw an exception in this case.
*/
ThrowIllegalMonitorStateException("unlock of unowned monitor");
return false;
}
} else {
/*
* The lock is fat. We must check to see if Unlock has
* raised any exceptions before continuing.
*/
DCHECK(LW_MONITOR(*thinp) != NULL);
if (!LW_MONITOR(*thinp)->Unlock(self)) {
// An exception has been raised. Do not fall through.
return false;
}
}
return true;
}
/*
* Object.wait(). Also called for class init.
*/
void Monitor::Wait(Thread* self, Object *obj, int64_t ms, int32_t ns, bool interruptShouldThrow) {
volatile int32_t* thinp = obj->GetRawLockWordAddress();
// If the lock is still thin, we need to fatten it.
uint32_t thin = *thinp;
if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
// Make sure that 'self' holds the lock.
if (LW_LOCK_OWNER(thin) != self->thin_lock_id_) {
ThrowIllegalMonitorStateException("object not locked by thread before wait()");
return;
}
/* This thread holds the lock. We need to fatten the lock
* so 'self' can block on it. Don't update the object lock
* field yet, because 'self' needs to acquire the lock before
* any other thread gets a chance.
*/
Inflate(self, obj);
LOG(INFO) << StringPrintf("(%d) lock %p fattened by wait()", self->thin_lock_id_, thinp);
}
LW_MONITOR(*thinp)->Wait(self, ms, ns, interruptShouldThrow);
}
void Monitor::Notify(Thread* self, Object *obj) {
uint32_t thin = *obj->GetRawLockWordAddress();
// If the lock is still thin, there aren't any waiters;
// waiting on an object forces lock fattening.
if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
// Make sure that 'self' holds the lock.
if (LW_LOCK_OWNER(thin) != self->thin_lock_id_) {
ThrowIllegalMonitorStateException("object not locked by thread before notify()");
return;
}
// no-op; there are no waiters to notify.
} else {
// It's a fat lock.
LW_MONITOR(thin)->Notify(self);
}
}
void Monitor::NotifyAll(Thread* self, Object *obj) {
uint32_t thin = *obj->GetRawLockWordAddress();
// If the lock is still thin, there aren't any waiters;
// waiting on an object forces lock fattening.
if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
// Make sure that 'self' holds the lock.
if (LW_LOCK_OWNER(thin) != self->thin_lock_id_) {
ThrowIllegalMonitorStateException("object not locked by thread before notifyAll()");
return;
}
// no-op; there are no waiters to notify.
} else {
// It's a fat lock.
LW_MONITOR(thin)->NotifyAll(self);
}
}
uint32_t Monitor::GetLockOwner(uint32_t raw_lock_word) {
if (LW_SHAPE(raw_lock_word) == LW_SHAPE_THIN) {
return LW_LOCK_OWNER(raw_lock_word);
} else {
Thread* owner = LW_MONITOR(raw_lock_word)->owner_;
return owner ? owner->GetThinLockId() : 0;
}
}
} // namespace art