| /* |
| * 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()); |
| } |