You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
380 lines
12 KiB
380 lines
12 KiB
/*
|
|
* Copyright (C) 2014 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 <memory>
|
|
#include <string>
|
|
|
|
#include "base/atomic.h"
|
|
#include "barrier.h"
|
|
#include "base/time_utils.h"
|
|
#include "class_linker-inl.h"
|
|
#include "common_runtime_test.h"
|
|
#include "handle_scope-inl.h"
|
|
#include "mirror/class-inl.h"
|
|
#include "mirror/string-inl.h" // Strings are easiest to allocate
|
|
#include "object_lock.h"
|
|
#include "scoped_thread_state_change-inl.h"
|
|
#include "thread_pool.h"
|
|
|
|
namespace art {
|
|
|
|
class MonitorTest : public CommonRuntimeTest {
|
|
protected:
|
|
void SetUpRuntimeOptions(RuntimeOptions *options) override {
|
|
// Use a smaller heap
|
|
SetUpRuntimeOptionsForFillHeap(options);
|
|
|
|
options->push_back(std::make_pair("-Xint", nullptr));
|
|
}
|
|
public:
|
|
std::unique_ptr<Monitor> monitor_;
|
|
Handle<mirror::String> object_;
|
|
Handle<mirror::String> second_object_;
|
|
Handle<mirror::String> watchdog_object_;
|
|
// One exception test is for waiting on another Thread's lock. This is used to race-free &
|
|
// loop-free pass
|
|
Thread* thread_;
|
|
std::unique_ptr<Barrier> barrier_;
|
|
std::unique_ptr<Barrier> complete_barrier_;
|
|
bool completed_;
|
|
};
|
|
|
|
// Check that an exception can be thrown correctly.
|
|
// This test is potentially racy, but the timeout is long enough that it should work.
|
|
|
|
class CreateTask : public Task {
|
|
public:
|
|
CreateTask(MonitorTest* monitor_test, uint64_t initial_sleep, int64_t millis, bool expected) :
|
|
monitor_test_(monitor_test), initial_sleep_(initial_sleep), millis_(millis),
|
|
expected_(expected) {}
|
|
|
|
void Run(Thread* self) override {
|
|
{
|
|
ScopedObjectAccess soa(self);
|
|
|
|
monitor_test_->thread_ = self; // Pass the Thread.
|
|
monitor_test_->object_.Get()->MonitorEnter(self); // Lock the object. This should transition
|
|
LockWord lock_after = monitor_test_->object_.Get()->GetLockWord(false); // it to thinLocked.
|
|
LockWord::LockState new_state = lock_after.GetState();
|
|
|
|
// Cannot use ASSERT only, as analysis thinks we'll keep holding the mutex.
|
|
if (LockWord::LockState::kThinLocked != new_state) {
|
|
monitor_test_->object_.Get()->MonitorExit(self); // To appease analysis.
|
|
ASSERT_EQ(LockWord::LockState::kThinLocked, new_state); // To fail the test.
|
|
return;
|
|
}
|
|
|
|
// Force a fat lock by running identity hashcode to fill up lock word.
|
|
monitor_test_->object_.Get()->IdentityHashCode();
|
|
LockWord lock_after2 = monitor_test_->object_.Get()->GetLockWord(false);
|
|
LockWord::LockState new_state2 = lock_after2.GetState();
|
|
|
|
// Cannot use ASSERT only, as analysis thinks we'll keep holding the mutex.
|
|
if (LockWord::LockState::kFatLocked != new_state2) {
|
|
monitor_test_->object_.Get()->MonitorExit(self); // To appease analysis.
|
|
ASSERT_EQ(LockWord::LockState::kFatLocked, new_state2); // To fail the test.
|
|
return;
|
|
}
|
|
} // Need to drop the mutator lock to use the barrier.
|
|
|
|
monitor_test_->barrier_->Wait(self); // Let the other thread know we're done.
|
|
|
|
{
|
|
ScopedObjectAccess soa(self);
|
|
|
|
// Give the other task a chance to do its thing.
|
|
NanoSleep(initial_sleep_ * 1000 * 1000);
|
|
|
|
// Now try to Wait on the Monitor.
|
|
Monitor::Wait(self, monitor_test_->object_.Get(), millis_, 0, true,
|
|
ThreadState::kTimedWaiting);
|
|
|
|
// Check the exception status against what we expect.
|
|
EXPECT_EQ(expected_, self->IsExceptionPending());
|
|
if (expected_) {
|
|
self->ClearException();
|
|
}
|
|
}
|
|
|
|
monitor_test_->complete_barrier_->Wait(self); // Wait for test completion.
|
|
|
|
{
|
|
ScopedObjectAccess soa(self);
|
|
monitor_test_->object_.Get()->MonitorExit(self); // Release the object. Appeases analysis.
|
|
}
|
|
}
|
|
|
|
void Finalize() override {
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
MonitorTest* monitor_test_;
|
|
uint64_t initial_sleep_;
|
|
int64_t millis_;
|
|
bool expected_;
|
|
};
|
|
|
|
|
|
class UseTask : public Task {
|
|
public:
|
|
UseTask(MonitorTest* monitor_test, uint64_t initial_sleep, int64_t millis, bool expected) :
|
|
monitor_test_(monitor_test), initial_sleep_(initial_sleep), millis_(millis),
|
|
expected_(expected) {}
|
|
|
|
void Run(Thread* self) override {
|
|
monitor_test_->barrier_->Wait(self); // Wait for the other thread to set up the monitor.
|
|
|
|
{
|
|
ScopedObjectAccess soa(self);
|
|
|
|
// Give the other task a chance to do its thing.
|
|
NanoSleep(initial_sleep_ * 1000 * 1000);
|
|
|
|
Monitor::Wait(self, monitor_test_->object_.Get(), millis_, 0, true,
|
|
ThreadState::kTimedWaiting);
|
|
|
|
// Check the exception status against what we expect.
|
|
EXPECT_EQ(expected_, self->IsExceptionPending());
|
|
if (expected_) {
|
|
self->ClearException();
|
|
}
|
|
}
|
|
|
|
monitor_test_->complete_barrier_->Wait(self); // Wait for test completion.
|
|
}
|
|
|
|
void Finalize() override {
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
MonitorTest* monitor_test_;
|
|
uint64_t initial_sleep_;
|
|
int64_t millis_;
|
|
bool expected_;
|
|
};
|
|
|
|
class InterruptTask : public Task {
|
|
public:
|
|
InterruptTask(MonitorTest* monitor_test, uint64_t initial_sleep, uint64_t millis) :
|
|
monitor_test_(monitor_test), initial_sleep_(initial_sleep), millis_(millis) {}
|
|
|
|
void Run(Thread* self) override {
|
|
monitor_test_->barrier_->Wait(self); // Wait for the other thread to set up the monitor.
|
|
|
|
{
|
|
ScopedObjectAccess soa(self);
|
|
|
|
// Give the other task a chance to do its thing.
|
|
NanoSleep(initial_sleep_ * 1000 * 1000);
|
|
|
|
// Interrupt the other thread.
|
|
monitor_test_->thread_->Interrupt(self);
|
|
|
|
// Give it some more time to get to the exception code.
|
|
NanoSleep(millis_ * 1000 * 1000);
|
|
|
|
// Now try to Wait.
|
|
Monitor::Wait(self, monitor_test_->object_.Get(), 10, 0, true,
|
|
ThreadState::kTimedWaiting);
|
|
|
|
// No check here, as depending on scheduling we may or may not fail.
|
|
if (self->IsExceptionPending()) {
|
|
self->ClearException();
|
|
}
|
|
}
|
|
|
|
monitor_test_->complete_barrier_->Wait(self); // Wait for test completion.
|
|
}
|
|
|
|
void Finalize() override {
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
MonitorTest* monitor_test_;
|
|
uint64_t initial_sleep_;
|
|
uint64_t millis_;
|
|
};
|
|
|
|
class WatchdogTask : public Task {
|
|
public:
|
|
explicit WatchdogTask(MonitorTest* monitor_test) : monitor_test_(monitor_test) {}
|
|
|
|
void Run(Thread* self) override {
|
|
ScopedObjectAccess soa(self);
|
|
|
|
monitor_test_->watchdog_object_.Get()->MonitorEnter(self); // Lock the object.
|
|
|
|
monitor_test_->watchdog_object_.Get()->Wait(self, 30 * 1000, 0); // Wait for 30s, or being
|
|
// woken up.
|
|
|
|
monitor_test_->watchdog_object_.Get()->MonitorExit(self); // Release the lock.
|
|
|
|
if (!monitor_test_->completed_) {
|
|
LOG(FATAL) << "Watchdog timeout!";
|
|
}
|
|
}
|
|
|
|
void Finalize() override {
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
MonitorTest* monitor_test_;
|
|
};
|
|
|
|
static void CommonWaitSetup(MonitorTest* test, ClassLinker* class_linker, uint64_t create_sleep,
|
|
int64_t c_millis, bool c_expected, bool interrupt, uint64_t use_sleep,
|
|
int64_t u_millis, bool u_expected, const char* pool_name) {
|
|
Thread* const self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
// First create the object we lock. String is easiest.
|
|
StackHandleScope<3> hs(soa.Self());
|
|
test->object_ = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "hello, world!"));
|
|
test->watchdog_object_ = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self,
|
|
"hello, world!"));
|
|
|
|
// Create the barrier used to synchronize.
|
|
test->barrier_ = std::make_unique<Barrier>(2);
|
|
test->complete_barrier_ = std::make_unique<Barrier>(3);
|
|
test->completed_ = false;
|
|
|
|
// Our job: Fill the heap, then try Wait.
|
|
{
|
|
VariableSizedHandleScope vhs(soa.Self());
|
|
test->FillHeap(soa.Self(), class_linker, &vhs);
|
|
|
|
// Now release everything.
|
|
}
|
|
|
|
// Need to drop the mutator lock to allow barriers.
|
|
ScopedThreadSuspension sts(soa.Self(), kNative);
|
|
ThreadPool thread_pool(pool_name, 3);
|
|
thread_pool.AddTask(self, new CreateTask(test, create_sleep, c_millis, c_expected));
|
|
if (interrupt) {
|
|
thread_pool.AddTask(self, new InterruptTask(test, use_sleep, static_cast<uint64_t>(u_millis)));
|
|
} else {
|
|
thread_pool.AddTask(self, new UseTask(test, use_sleep, u_millis, u_expected));
|
|
}
|
|
thread_pool.AddTask(self, new WatchdogTask(test));
|
|
thread_pool.StartWorkers(self);
|
|
|
|
// Wait on completion barrier.
|
|
test->complete_barrier_->Wait(self);
|
|
test->completed_ = true;
|
|
|
|
// Wake the watchdog.
|
|
{
|
|
ScopedObjectAccess soa2(self);
|
|
test->watchdog_object_.Get()->MonitorEnter(self); // Lock the object.
|
|
test->watchdog_object_.Get()->NotifyAll(self); // Wake up waiting parties.
|
|
test->watchdog_object_.Get()->MonitorExit(self); // Release the lock.
|
|
}
|
|
|
|
thread_pool.StopWorkers(self);
|
|
}
|
|
|
|
|
|
// First test: throwing an exception when trying to wait in Monitor with another thread.
|
|
TEST_F(MonitorTest, CheckExceptionsWait1) {
|
|
// Make the CreateTask wait 10ms, the UseTask wait 10ms.
|
|
// => The use task will get the lock first and get to self == owner check.
|
|
// This will lead to OOM and monitor error messages in the log.
|
|
ScopedLogSeverity sls(LogSeverity::FATAL);
|
|
CommonWaitSetup(this, class_linker_, 10, 50, false, false, 2, 50, true,
|
|
"Monitor test thread pool 1");
|
|
}
|
|
|
|
// Second test: throwing an exception for invalid wait time.
|
|
TEST_F(MonitorTest, CheckExceptionsWait2) {
|
|
// Make the CreateTask wait 0ms, the UseTask wait 10ms.
|
|
// => The create task will get the lock first and get to ms >= 0
|
|
// This will lead to OOM and monitor error messages in the log.
|
|
ScopedLogSeverity sls(LogSeverity::FATAL);
|
|
CommonWaitSetup(this, class_linker_, 0, -1, true, false, 10, 50, true,
|
|
"Monitor test thread pool 2");
|
|
}
|
|
|
|
// Third test: throwing an interrupted-exception.
|
|
TEST_F(MonitorTest, CheckExceptionsWait3) {
|
|
// Make the CreateTask wait 0ms, then Wait for a long time. Make the InterruptTask wait 10ms,
|
|
// after which it will interrupt the create task and then wait another 10ms.
|
|
// => The create task will get to the interrupted-exception throw.
|
|
// This will lead to OOM and monitor error messages in the log.
|
|
ScopedLogSeverity sls(LogSeverity::FATAL);
|
|
CommonWaitSetup(this, class_linker_, 0, 500, true, true, 10, 50, true,
|
|
"Monitor test thread pool 3");
|
|
}
|
|
|
|
class TryLockTask : public Task {
|
|
public:
|
|
explicit TryLockTask(Handle<mirror::Object> obj) : obj_(obj) {}
|
|
|
|
void Run(Thread* self) override {
|
|
ScopedObjectAccess soa(self);
|
|
// Lock is held by other thread, try lock should fail.
|
|
ObjectTryLock<mirror::Object> lock(self, obj_);
|
|
EXPECT_FALSE(lock.Acquired());
|
|
}
|
|
|
|
void Finalize() override {
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
Handle<mirror::Object> obj_;
|
|
};
|
|
|
|
// Test trylock in deadlock scenarios.
|
|
TEST_F(MonitorTest, TestTryLock) {
|
|
ScopedLogSeverity sls(LogSeverity::FATAL);
|
|
|
|
Thread* const self = Thread::Current();
|
|
ThreadPool thread_pool("the pool", 2);
|
|
ScopedObjectAccess soa(self);
|
|
StackHandleScope<1> hs(self);
|
|
Handle<mirror::Object> obj1(
|
|
hs.NewHandle<mirror::Object>(mirror::String::AllocFromModifiedUtf8(self, "hello, world!")));
|
|
{
|
|
ObjectLock<mirror::Object> lock1(self, obj1);
|
|
{
|
|
ObjectTryLock<mirror::Object> trylock(self, obj1);
|
|
EXPECT_TRUE(trylock.Acquired());
|
|
}
|
|
// Test failure case.
|
|
thread_pool.AddTask(self, new TryLockTask(obj1));
|
|
thread_pool.StartWorkers(self);
|
|
ScopedThreadSuspension sts(self, kSuspended);
|
|
thread_pool.Wait(Thread::Current(), /*do_work=*/false, /*may_hold_locks=*/false);
|
|
}
|
|
// Test that the trylock actually locks the object.
|
|
{
|
|
ObjectTryLock<mirror::Object> trylock(self, obj1);
|
|
EXPECT_TRUE(trylock.Acquired());
|
|
obj1->Notify(self);
|
|
// Since we hold the lock there should be no monitor state exeception.
|
|
self->AssertNoPendingException();
|
|
}
|
|
thread_pool.StopWorkers(self);
|
|
}
|
|
|
|
|
|
} // namespace art
|