/* * Copyright 2011 Google Inc. All Rights Reserved. * * 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 <stdlib.h> #include "gtest/gtest.h" #include "sfntly/port/lock.h" #include "test/platform_thread.h" namespace sfntly { // Basic test to make sure that Acquire()/Unlock()/Try() don't crash class BasicLockTestThread : public PlatformThread::Delegate { public: BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {} virtual void ThreadMain() { for (int i = 0; i < 10; i++) { lock_->Acquire(); acquired_++; lock_->Unlock(); } for (int i = 0; i < 10; i++) { lock_->Acquire(); acquired_++; PlatformThread::Sleep(rand() % 20); lock_->Unlock(); } for (int i = 0; i < 10; i++) { if (lock_->Try()) { acquired_++; PlatformThread::Sleep(rand() % 20); lock_->Unlock(); } } } int acquired() const { return acquired_; } private: Lock* lock_; int acquired_; NO_COPY_AND_ASSIGN(BasicLockTestThread); }; bool BasicLockTest() { Lock lock; BasicLockTestThread thread(&lock); PlatformThreadHandle handle = kNullThreadHandle; EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); int acquired = 0; for (int i = 0; i < 5; i++) { lock.Acquire(); acquired++; lock.Unlock(); } for (int i = 0; i < 10; i++) { lock.Acquire(); acquired++; PlatformThread::Sleep(rand() % 20); lock.Unlock(); } for (int i = 0; i < 10; i++) { if (lock.Try()) { acquired++; PlatformThread::Sleep(rand() % 20); lock.Unlock(); } } for (int i = 0; i < 5; i++) { lock.Acquire(); acquired++; PlatformThread::Sleep(rand() % 20); lock.Unlock(); } PlatformThread::Join(handle); EXPECT_GE(acquired, 20); EXPECT_GE(thread.acquired(), 20); return true; } // Test that Try() works as expected ------------------------------------------- class TryLockTestThread : public PlatformThread::Delegate { public: TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {} virtual void ThreadMain() { got_lock_ = lock_->Try(); if (got_lock_) lock_->Unlock(); } bool got_lock() const { return got_lock_; } private: Lock* lock_; bool got_lock_; NO_COPY_AND_ASSIGN(TryLockTestThread); }; bool TryLockTest() { Lock lock; EXPECT_TRUE(lock.Try()); // We now have the lock.... // This thread will not be able to get the lock. { TryLockTestThread thread(&lock); PlatformThreadHandle handle = kNullThreadHandle; EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); PlatformThread::Join(handle); EXPECT_FALSE(thread.got_lock()); } lock.Unlock(); // This thread will.... { TryLockTestThread thread(&lock); PlatformThreadHandle handle = kNullThreadHandle; EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); PlatformThread::Join(handle); EXPECT_TRUE(thread.got_lock()); // But it released it.... EXPECT_TRUE(lock.Try()); } lock.Unlock(); return true; } // Tests that locks actually exclude ------------------------------------------- class MutexLockTestThread : public PlatformThread::Delegate { public: MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {} // Static helper which can also be called from the main thread. static void DoStuff(Lock* lock, int* value) { for (int i = 0; i < 40; i++) { lock->Acquire(); int v = *value; PlatformThread::Sleep(rand() % 10); *value = v + 1; lock->Unlock(); } } virtual void ThreadMain() { DoStuff(lock_, value_); } private: Lock* lock_; int* value_; NO_COPY_AND_ASSIGN(MutexLockTestThread); }; bool MutexTwoThreads() { Lock lock; int value = 0; MutexLockTestThread thread(&lock, &value); PlatformThreadHandle handle = kNullThreadHandle; EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); MutexLockTestThread::DoStuff(&lock, &value); PlatformThread::Join(handle); EXPECT_EQ(2 * 40, value); return true; } bool MutexFourThreads() { Lock lock; int value = 0; MutexLockTestThread thread1(&lock, &value); MutexLockTestThread thread2(&lock, &value); MutexLockTestThread thread3(&lock, &value); PlatformThreadHandle handle1 = kNullThreadHandle; PlatformThreadHandle handle2 = kNullThreadHandle; PlatformThreadHandle handle3 = kNullThreadHandle; EXPECT_TRUE(PlatformThread::Create(&thread1, &handle1)); EXPECT_TRUE(PlatformThread::Create(&thread2, &handle2)); EXPECT_TRUE(PlatformThread::Create(&thread3, &handle3)); MutexLockTestThread::DoStuff(&lock, &value); PlatformThread::Join(handle1); PlatformThread::Join(handle2); PlatformThread::Join(handle3); EXPECT_EQ(4 * 40, value); return true; } } // namespace sfntly TEST(LockTest, Basic) { ASSERT_TRUE(sfntly::BasicLockTest()); } TEST(LockTest, TryLock) { ASSERT_TRUE(sfntly::TryLockTest()); } TEST(LockTest, Mutex) { ASSERT_TRUE(sfntly::MutexTwoThreads()); ASSERT_TRUE(sfntly::MutexFourThreads()); }