From afb14c2cc6095d515aa0754fb6e1e1c44db091b6 Mon Sep 17 00:00:00 2001
From: "ivankr@chromium.org"
 <ivankr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Tue, 6 Nov 2012 10:38:59 +0000
Subject: [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
---
 rlz/lib/recursive_lock.cc          |  40 +++++++
 rlz/lib/recursive_lock.h           |  34 ++++++
 rlz/lib/recursive_lock_unittest.cc | 234 +++++++++++++++++++++++++++++++++++++
 rlz/rlz.gyp                        |  14 +++
 4 files changed, 322 insertions(+)
 create mode 100644 rlz/lib/recursive_lock.cc
 create mode 100644 rlz/lib/recursive_lock.h
 create mode 100644 rlz/lib/recursive_lock_unittest.cc

(limited to 'rlz')

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
diff --git a/rlz/rlz.gyp b/rlz/rlz.gyp
index 123eaa0..cdd66dc 100644
--- a/rlz/rlz.gyp
+++ b/rlz/rlz.gyp
@@ -42,6 +42,8 @@
         'lib/lib_values.cc',
         'lib/machine_id.cc',
         'lib/machine_id.h',
+        'lib/recursive_lock.cc',
+        'lib/recursive_lock.h',
         'lib/rlz_enums.h',
         'lib/rlz_lib.cc',
         'lib/rlz_lib.h',
@@ -92,6 +94,12 @@
             ],
           },
         }],
+        ['chromeos==0', {
+          'sources!': [
+            'lib/recursive_lock.cc',
+            'lib/recursive_lock.h',
+          ]
+        }],
       ],
     },
     {
@@ -110,6 +118,7 @@
         'lib/financial_ping_test.cc',
         'lib/lib_values_unittest.cc',
         'lib/machine_id_unittest.cc',
+        'lib/recursive_lock_unittest.cc',
         'lib/rlz_lib_test.cc',
         'lib/string_utils_unittest.cc',
         'test/rlz_test_helpers.cc',
@@ -122,6 +131,11 @@
           'dependencies': [
             '../net/net.gyp:net_test_support',
           ],
+        }],
+        ['chromeos==0', {
+          'sources!': [
+            'lib/recursive_lock_unittest.cc',
+          ],
         }]
       ],
     },
-- 
cgit v1.1