diff options
author | ivankr@chromium.org <ivankr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-06 10:38:59 +0000 |
---|---|---|
committer | ivankr@chromium.org <ivankr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-06 10:38:59 +0000 |
commit | afb14c2cc6095d515aa0754fb6e1e1c44db091b6 (patch) | |
tree | f22113e474f2b7329e3f49b8ff887104f0a29775 /rlz/lib | |
parent | bfdd5c40a2e31b52cda6b1bb782eadb286f6f2d5 (diff) | |
download | chromium_src-afb14c2cc6095d515aa0754fb6e1e1c44db091b6.zip chromium_src-afb14c2cc6095d515aa0754fb6e1e1c44db091b6.tar.gz chromium_src-afb14c2cc6095d515aa0754fb6e1e1c44db091b6.tar.bz2 |
[cros] Add RecursiveLock for CrOS implementation.
BUG=157348
Review URL: https://chromiumcodereview.appspot.com/11361057
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166186 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'rlz/lib')
-rw-r--r-- | rlz/lib/recursive_lock.cc | 40 | ||||
-rw-r--r-- | rlz/lib/recursive_lock.h | 34 | ||||
-rw-r--r-- | rlz/lib/recursive_lock_unittest.cc | 234 |
3 files changed, 308 insertions, 0 deletions
diff --git a/rlz/lib/recursive_lock.cc b/rlz/lib/recursive_lock.cc new file mode 100644 index 0000000..686cf0e --- /dev/null +++ b/rlz/lib/recursive_lock.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "rlz/lib/recursive_lock.h" + +#include "base/logging.h" + +namespace rlz_lib { + +RecursiveLock::RecursiveLock() + : owner_(), + recursion_() { +} + +RecursiveLock::~RecursiveLock() { +} + +void RecursiveLock::Acquire() { + base::subtle::Atomic32 me = base::PlatformThread::CurrentId(); + if (me != base::subtle::NoBarrier_Load(&owner_)) { + lock_.Acquire(); + DCHECK(!recursion_); + DCHECK(!owner_); + base::subtle::NoBarrier_Store(&owner_, me); + } + ++recursion_; +} + +void RecursiveLock::Release() { + DCHECK_EQ(base::subtle::NoBarrier_Load(&owner_), + base::PlatformThread::CurrentId()); + DCHECK_GT(recursion_, 0); + if (!--recursion_) { + base::subtle::NoBarrier_Store(&owner_, 0); + lock_.Release(); + } +} + +} // namespace rlz_lib diff --git a/rlz/lib/recursive_lock.h b/rlz/lib/recursive_lock.h new file mode 100644 index 0000000..43c95747 --- /dev/null +++ b/rlz/lib/recursive_lock.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RLZ_CHROMEOS_LIB_RECURSIVE_LOCK_H_ +#define RLZ_CHROMEOS_LIB_RECURSIVE_LOCK_H_ + +#include "base/atomicops.h" +#include "base/basictypes.h" +#include "base/synchronization/lock.h" + +namespace rlz_lib { + + +class RecursiveLock { + public: + RecursiveLock(); + ~RecursiveLock(); + + void Acquire(); + void Release(); + + private: + // Underlying non-recursive lock. + base::Lock lock_; + // Owner thread ID. + base::subtle::Atomic32 owner_; + // Recursion lock depth. + int recursion_; +}; + +} // namespace rlz_lib + +#endif // RLZ_CHROMEOS_LIB_RECURSIVE_LOCK_H_ diff --git a/rlz/lib/recursive_lock_unittest.cc b/rlz/lib/recursive_lock_unittest.cc new file mode 100644 index 0000000..916af7f --- /dev/null +++ b/rlz/lib/recursive_lock_unittest.cc @@ -0,0 +1,234 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "rlz/lib/recursive_lock.h" + +#include <stdlib.h> + +#include "base/compiler_specific.h" +#include "base/threading/platform_thread.h" +#include "base/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::kNullThreadHandle; +using base::PlatformThread; +using base::PlatformThreadHandle; +using base::TimeDelta; + +namespace rlz_lib { + +// Basic test to make sure that Acquire()/Release() don't crash. +class BasicLockTestThread : public PlatformThread::Delegate { + public: + BasicLockTestThread(RecursiveLock* lock) : lock_(lock), acquired_(0) {} + + virtual void ThreadMain() OVERRIDE { + for (int i = 0; i < 10; i++) { + lock_->Acquire(); + acquired_++; + lock_->Release(); + } + for (int i = 0; i < 10; i++) { + lock_->Acquire(); + acquired_++; + PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20)); + lock_->Release(); + } + } + + int acquired() const { return acquired_; } + + private: + RecursiveLock* lock_; + int acquired_; + + DISALLOW_COPY_AND_ASSIGN(BasicLockTestThread); +}; + +TEST(RecursiveLockTest, Basic) { + RecursiveLock lock; + BasicLockTestThread thread(&lock); + PlatformThreadHandle handle = kNullThreadHandle; + + ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); + + int acquired = 0; + for (int i = 0; i < 5; i++) { + lock.Acquire(); + acquired++; + lock.Release(); + } + for (int i = 0; i < 10; i++) { + lock.Acquire(); + acquired++; + PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20)); + lock.Release(); + } + for (int i = 0; i < 5; i++) { + lock.Acquire(); + acquired++; + PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20)); + lock.Release(); + } + + PlatformThread::Join(handle); + + EXPECT_EQ(acquired, 20); + EXPECT_EQ(thread.acquired(), 20); +} + +// Tests that locks are actually exclusive. +class MutexLockTestThread : public PlatformThread::Delegate { + public: + MutexLockTestThread(RecursiveLock* lock, int* value) + : lock_(lock), + value_(value) { + } + + // Static helper which can also be called from the main thread. + static void DoStuff(RecursiveLock* lock, int* value) { + for (int i = 0; i < 40; i++) { + lock->Acquire(); + int v = *value; + PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10)); + *value = v + 1; + lock->Release(); + } + } + + virtual void ThreadMain() OVERRIDE { + DoStuff(lock_, value_); + } + + private: + RecursiveLock* lock_; + int* value_; + + DISALLOW_COPY_AND_ASSIGN(MutexLockTestThread); +}; + +TEST(RecursiveLockTest, MutexTwoThreads) { + RecursiveLock lock; + int value = 0; + + MutexLockTestThread thread(&lock, &value); + PlatformThreadHandle handle = kNullThreadHandle; + + ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); + + MutexLockTestThread::DoStuff(&lock, &value); + + PlatformThread::Join(handle); + + EXPECT_EQ(2 * 40, value); +} + +TEST(RecursiveLockTest, MutexFourThreads) { + RecursiveLock 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; + + ASSERT_TRUE(PlatformThread::Create(0, &thread1, &handle1)); + ASSERT_TRUE(PlatformThread::Create(0, &thread2, &handle2)); + ASSERT_TRUE(PlatformThread::Create(0, &thread3, &handle3)); + + MutexLockTestThread::DoStuff(&lock, &value); + + PlatformThread::Join(handle1); + PlatformThread::Join(handle2); + PlatformThread::Join(handle3); + + EXPECT_EQ(4 * 40, value); +} + +// Tests that locks are recursive. +class MutexRecursiveLockTestThread : public PlatformThread::Delegate { + public: + MutexRecursiveLockTestThread(RecursiveLock* lock, int* value) + : lock_(lock), + value_(value) { + } + + // Static helper which can also be called from the main thread. + static void DoStuff(RecursiveLock* lock, int* value) { + for (int i = 0; i < 20; i++) { + // First lock. + lock->Acquire(); + int v = *value; + PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10)); + *value = v + 1; + { + // Recursive lock. + lock->Acquire(); + int v = *value; + PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10)); + *value = v + 1; + lock->Release(); + } + v = *value; + PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10)); + *value = v + 1; + lock->Release(); + } + } + + virtual void ThreadMain() OVERRIDE { + DoStuff(lock_, value_); + } + + private: + RecursiveLock* lock_; + int* value_; + + DISALLOW_COPY_AND_ASSIGN(MutexRecursiveLockTestThread); +}; + + +TEST(RecursiveLockTest, MutexTwoThreadsRecursive) { + RecursiveLock lock; + int value = 0; + + MutexRecursiveLockTestThread thread(&lock, &value); + PlatformThreadHandle handle = kNullThreadHandle; + + ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); + + MutexRecursiveLockTestThread::DoStuff(&lock, &value); + + PlatformThread::Join(handle); + + EXPECT_EQ(2 * 60, value); +} + +TEST(RecursiveLockTest, MutexFourThreadsRecursive) { + RecursiveLock lock; + int value = 0; + + MutexRecursiveLockTestThread thread1(&lock, &value); + MutexRecursiveLockTestThread thread2(&lock, &value); + MutexRecursiveLockTestThread thread3(&lock, &value); + PlatformThreadHandle handle1 = kNullThreadHandle; + PlatformThreadHandle handle2 = kNullThreadHandle; + PlatformThreadHandle handle3 = kNullThreadHandle; + + ASSERT_TRUE(PlatformThread::Create(0, &thread1, &handle1)); + ASSERT_TRUE(PlatformThread::Create(0, &thread2, &handle2)); + ASSERT_TRUE(PlatformThread::Create(0, &thread3, &handle3)); + + MutexRecursiveLockTestThread::DoStuff(&lock, &value); + + PlatformThread::Join(handle1); + PlatformThread::Join(handle2); + PlatformThread::Join(handle3); + + EXPECT_EQ(4 * 60, value); +} + +} // namespace rlz_lib |