1 /* 2 * Copyright 2011 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <stdlib.h> 18 19 #include "gtest/gtest.h" 20 #include "sfntly/port/lock.h" 21 #include "test/platform_thread.h" 22 23 namespace sfntly { 24 25 // Basic test to make sure that Acquire()/Unlock()/Try() don't crash 26 27 class BasicLockTestThread : public PlatformThread::Delegate { 28 public: 29 BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {} 30 31 virtual void ThreadMain() { 32 for (int i = 0; i < 10; i++) { 33 lock_->Acquire(); 34 acquired_++; 35 lock_->Unlock(); 36 } 37 for (int i = 0; i < 10; i++) { 38 lock_->Acquire(); 39 acquired_++; 40 PlatformThread::Sleep(rand() % 20); 41 lock_->Unlock(); 42 } 43 for (int i = 0; i < 10; i++) { 44 if (lock_->Try()) { 45 acquired_++; 46 PlatformThread::Sleep(rand() % 20); 47 lock_->Unlock(); 48 } 49 } 50 } 51 52 int acquired() const { return acquired_; } 53 54 private: 55 Lock* lock_; 56 int acquired_; 57 58 NO_COPY_AND_ASSIGN(BasicLockTestThread); 59 }; 60 61 bool BasicLockTest() { 62 Lock lock; 63 BasicLockTestThread thread(&lock); 64 PlatformThreadHandle handle = kNullThreadHandle; 65 66 EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); 67 68 int acquired = 0; 69 for (int i = 0; i < 5; i++) { 70 lock.Acquire(); 71 acquired++; 72 lock.Unlock(); 73 } 74 for (int i = 0; i < 10; i++) { 75 lock.Acquire(); 76 acquired++; 77 PlatformThread::Sleep(rand() % 20); 78 lock.Unlock(); 79 } 80 for (int i = 0; i < 10; i++) { 81 if (lock.Try()) { 82 acquired++; 83 PlatformThread::Sleep(rand() % 20); 84 lock.Unlock(); 85 } 86 } 87 for (int i = 0; i < 5; i++) { 88 lock.Acquire(); 89 acquired++; 90 PlatformThread::Sleep(rand() % 20); 91 lock.Unlock(); 92 } 93 94 PlatformThread::Join(handle); 95 96 EXPECT_GE(acquired, 20); 97 EXPECT_GE(thread.acquired(), 20); 98 99 return true; 100 } 101 102 // Test that Try() works as expected ------------------------------------------- 103 104 class TryLockTestThread : public PlatformThread::Delegate { 105 public: 106 TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {} 107 108 virtual void ThreadMain() { 109 got_lock_ = lock_->Try(); 110 if (got_lock_) 111 lock_->Unlock(); 112 } 113 114 bool got_lock() const { return got_lock_; } 115 116 private: 117 Lock* lock_; 118 bool got_lock_; 119 120 NO_COPY_AND_ASSIGN(TryLockTestThread); 121 }; 122 123 bool TryLockTest() { 124 Lock lock; 125 126 EXPECT_TRUE(lock.Try()); 127 // We now have the lock.... 128 129 // This thread will not be able to get the lock. 130 { 131 TryLockTestThread thread(&lock); 132 PlatformThreadHandle handle = kNullThreadHandle; 133 134 EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); 135 136 PlatformThread::Join(handle); 137 138 EXPECT_FALSE(thread.got_lock()); 139 } 140 141 lock.Unlock(); 142 143 // This thread will.... 144 { 145 TryLockTestThread thread(&lock); 146 PlatformThreadHandle handle = kNullThreadHandle; 147 148 EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); 149 150 PlatformThread::Join(handle); 151 152 EXPECT_TRUE(thread.got_lock()); 153 // But it released it.... 154 EXPECT_TRUE(lock.Try()); 155 } 156 157 lock.Unlock(); 158 return true; 159 } 160 161 // Tests that locks actually exclude ------------------------------------------- 162 163 class MutexLockTestThread : public PlatformThread::Delegate { 164 public: 165 MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {} 166 167 // Static helper which can also be called from the main thread. 168 static void DoStuff(Lock* lock, int* value) { 169 for (int i = 0; i < 40; i++) { 170 lock->Acquire(); 171 int v = *value; 172 PlatformThread::Sleep(rand() % 10); 173 *value = v + 1; 174 lock->Unlock(); 175 } 176 } 177 178 virtual void ThreadMain() { 179 DoStuff(lock_, value_); 180 } 181 182 private: 183 Lock* lock_; 184 int* value_; 185 186 NO_COPY_AND_ASSIGN(MutexLockTestThread); 187 }; 188 189 bool MutexTwoThreads() { 190 Lock lock; 191 int value = 0; 192 193 MutexLockTestThread thread(&lock, &value); 194 PlatformThreadHandle handle = kNullThreadHandle; 195 196 EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); 197 198 MutexLockTestThread::DoStuff(&lock, &value); 199 200 PlatformThread::Join(handle); 201 202 EXPECT_EQ(2 * 40, value); 203 return true; 204 } 205 206 bool MutexFourThreads() { 207 Lock lock; 208 int value = 0; 209 210 MutexLockTestThread thread1(&lock, &value); 211 MutexLockTestThread thread2(&lock, &value); 212 MutexLockTestThread thread3(&lock, &value); 213 PlatformThreadHandle handle1 = kNullThreadHandle; 214 PlatformThreadHandle handle2 = kNullThreadHandle; 215 PlatformThreadHandle handle3 = kNullThreadHandle; 216 217 EXPECT_TRUE(PlatformThread::Create(&thread1, &handle1)); 218 EXPECT_TRUE(PlatformThread::Create(&thread2, &handle2)); 219 EXPECT_TRUE(PlatformThread::Create(&thread3, &handle3)); 220 221 MutexLockTestThread::DoStuff(&lock, &value); 222 223 PlatformThread::Join(handle1); 224 PlatformThread::Join(handle2); 225 PlatformThread::Join(handle3); 226 227 EXPECT_EQ(4 * 40, value); 228 return true; 229 } 230 231 } // namespace sfntly 232 233 TEST(LockTest, Basic) { 234 ASSERT_TRUE(sfntly::BasicLockTest()); 235 } 236 237 TEST(LockTest, TryLock) { 238 ASSERT_TRUE(sfntly::TryLockTest()); 239 } 240 241 TEST(LockTest, Mutex) { 242 ASSERT_TRUE(sfntly::MutexTwoThreads()); 243 ASSERT_TRUE(sfntly::MutexFourThreads()); 244 } 245