blob: 5829c54386a152bf2e7212ac9bad727413608ea2 [file] [log] [blame]
// Copyright 2011 Google Inc.
//
// 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.
//
// Author: morlovich@google.com (Maksim Orlovich)
#include "pagespeed/kernel/sharedmem/shared_mem_lock_manager_test_base.h"
#include "pagespeed/kernel/base/function.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/md5_hasher.h"
#include "pagespeed/kernel/base/mock_message_handler.h"
#include "pagespeed/kernel/base/mock_timer.h"
#include "pagespeed/kernel/base/named_lock_manager.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/sharedmem/shared_mem_lock_manager.h"
#include "pagespeed/kernel/sharedmem/shared_mem_test_base.h"
#include "pagespeed/kernel/thread/scheduler_based_abstract_lock.h"
#include "pagespeed/kernel/util/platform.h"
namespace net_instaweb {
namespace {
const char kPath[] = "shm_locks";
const char kLockA[] = "lock_a";
const char kLockB[] = "lock_b";
} // namespace
SharedMemLockManagerTestBase::SharedMemLockManagerTestBase(
SharedMemTestEnv* test_env)
: test_env_(test_env),
shmem_runtime_(test_env->CreateSharedMemRuntime()),
thread_system_(Platform::CreateThreadSystem()),
timer_(thread_system_->NewMutex(), 0),
handler_(thread_system_->NewMutex()),
scheduler_(thread_system_.get(), &timer_) {
}
void SharedMemLockManagerTestBase::SetUp() {
root_lock_manager_.reset(CreateLockManager());
EXPECT_TRUE(root_lock_manager_->Initialize());
}
void SharedMemLockManagerTestBase::TearDown() {
SharedMemLockManager::GlobalCleanup(shmem_runtime_.get(), kPath, &handler_);
}
bool SharedMemLockManagerTestBase::CreateChild(TestMethod method) {
Function* callback =
new MemberFunction0<SharedMemLockManagerTestBase>(method, this);
return test_env_->CreateChild(callback);
}
SharedMemLockManager* SharedMemLockManagerTestBase::CreateLockManager() {
return new SharedMemLockManager(shmem_runtime_.get(), kPath, &scheduler_,
&hasher_, &handler_);
}
SharedMemLockManager* SharedMemLockManagerTestBase::AttachDefault() {
SharedMemLockManager* lock_man = CreateLockManager();
if (!lock_man->Attach()) {
delete lock_man;
lock_man = NULL;
}
return lock_man;
}
void SharedMemLockManagerTestBase::TestBasic() {
scoped_ptr<SharedMemLockManager> lock_manager(AttachDefault());
ASSERT_TRUE(lock_manager.get() != NULL);
scoped_ptr<SchedulerBasedAbstractLock> lock_a(
lock_manager->CreateNamedLock(kLockA));
scoped_ptr<SchedulerBasedAbstractLock> lock_b(
lock_manager->CreateNamedLock(kLockB));
ASSERT_TRUE(lock_a.get() != NULL);
ASSERT_TRUE(lock_b.get() != NULL);
EXPECT_FALSE(lock_a->Held());
EXPECT_FALSE(lock_b->Held());
// Can lock exactly once...
EXPECT_TRUE(lock_a->TryLock());
EXPECT_TRUE(lock_b->TryLock());
EXPECT_TRUE(lock_a->Held());
EXPECT_TRUE(lock_b->Held());
EXPECT_FALSE(lock_a->TryLock());
EXPECT_FALSE(lock_b->TryLock());
EXPECT_TRUE(lock_a->Held());
EXPECT_TRUE(lock_b->Held());
// Unlocking lets one lock again
lock_b->Unlock();
EXPECT_FALSE(lock_b->Held());
EXPECT_FALSE(lock_a->TryLock());
EXPECT_TRUE(lock_b->TryLock());
// Now unlock A, and let kid confirm the state
lock_a->Unlock();
EXPECT_FALSE(lock_a->Held());
CreateChild(&SharedMemLockManagerTestBase::TestBasicChild);
test_env_->WaitForChildren();
// A should still be unlocked since child's locks should get cleaned up
// by ~NamedLock.. but not lock b, which we were holding
EXPECT_TRUE(lock_a->TryLock());
EXPECT_FALSE(lock_b->TryLock());
}
void SharedMemLockManagerTestBase::TestBasicChild() {
scoped_ptr<SharedMemLockManager> lock_manager(AttachDefault());
scoped_ptr<SchedulerBasedAbstractLock> lock_a(
lock_manager->CreateNamedLock(kLockA));
scoped_ptr<SchedulerBasedAbstractLock> lock_b(
lock_manager->CreateNamedLock(kLockB));
if (lock_a.get() == NULL || lock_b.get() == NULL) {
test_env_->ChildFailed();
}
// A should lock fine
if (!lock_a->TryLock() || !lock_a->Held()) {
test_env_->ChildFailed();
}
// B shouldn't lock fine.
if (lock_b->TryLock() || lock_b->Held()) {
test_env_->ChildFailed();
}
// Note: here we should unlock a due to destruction of A.
}
void SharedMemLockManagerTestBase::TestDestructorUnlock() {
// Standalone test for destructors cleaning up. It is covered by the
// above, but this does it single-threaded, without weird things.
scoped_ptr<SharedMemLockManager> lock_manager(AttachDefault());
ASSERT_TRUE(lock_manager.get() != NULL);
{
scoped_ptr<SchedulerBasedAbstractLock> lock_a(
lock_manager->CreateNamedLock(kLockA));
EXPECT_TRUE(lock_a->TryLock());
}
{
scoped_ptr<SchedulerBasedAbstractLock> lock_a(
lock_manager->CreateNamedLock(kLockA));
EXPECT_TRUE(lock_a->TryLock());
}
}
void SharedMemLockManagerTestBase::TestSteal() {
scoped_ptr<SharedMemLockManager> lock_manager(AttachDefault());
ASSERT_TRUE(lock_manager.get() != NULL);
scoped_ptr<SchedulerBasedAbstractLock> lock_a(
lock_manager->CreateNamedLock(kLockA));
EXPECT_TRUE(lock_a->TryLock());
EXPECT_TRUE(lock_a->Held());
CreateChild(&SharedMemLockManagerTestBase::TestStealChild);
test_env_->WaitForChildren();
}
void SharedMemLockManagerTestBase::TestStealChild() {
const int kStealTimeMs = 1000;
scoped_ptr<SharedMemLockManager> lock_manager(AttachDefault());
ASSERT_TRUE(lock_manager.get() != NULL);
scoped_ptr<SchedulerBasedAbstractLock> lock_a(
lock_manager->CreateNamedLock(kLockA));
// First, attempting to steal should fail, as 'time' hasn't moved yet.
if (lock_a->TryLockStealOld(kStealTimeMs) || lock_a->Held()) {
test_env_->ChildFailed();
}
timer_.AdvanceMs(kStealTimeMs + 1);
// Now it should succeed.
if (!lock_a->TryLockStealOld(kStealTimeMs) || !lock_a->Held()) {
test_env_->ChildFailed();
}
}
} // namespace net_instaweb