From 01404084aa9c4656bc3607c70dbf5ce204a706f9 Mon Sep 17 00:00:00 2001 From: danilchap Date: Mon, 24 Oct 2016 06:07:25 -0700 Subject: [PATCH] Add tests and fix thread annotations BUG=None NOTRY=true Review-Url: https://codereview.webrtc.org/2435293002 Cr-Commit-Position: refs/heads/master@{#14742} --- webrtc/BUILD.gn | 1 + webrtc/base/thread_annotations.h | 9 +- webrtc/base/thread_annotations_unittest.cc | 151 +++++++++++++++++++++ 3 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 webrtc/base/thread_annotations_unittest.cc diff --git a/webrtc/BUILD.gn b/webrtc/BUILD.gn index 41928075ea..c8b74f0269 100644 --- a/webrtc/BUILD.gn +++ b/webrtc/BUILD.gn @@ -405,6 +405,7 @@ if (rtc_include_tests) { "base/task_queue_unittest.cc", "base/task_unittest.cc", "base/testclient_unittest.cc", + "base/thread_annotations_unittest.cc", "base/thread_checker_unittest.cc", "base/thread_unittest.cc", "base/timestampaligner_unittest.cc", diff --git a/webrtc/base/thread_annotations.h b/webrtc/base/thread_annotations.h index 612242d611..84250304c8 100644 --- a/webrtc/base/thread_annotations.h +++ b/webrtc/base/thread_annotations.h @@ -31,7 +31,7 @@ // indicates a shared variable should be guarded (by any lock). GUARDED_VAR // is primarily used when the client cannot express the name of the lock. #define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) -#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded) +#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded_var) // Document if the memory location pointed to by a pointer should be guarded // by a lock when dereferencing the pointer. Similar to GUARDED_VAR, @@ -41,8 +41,8 @@ // q, which is guarded by mu1, points to a shared memory location that is // guarded by mu2, q should be annotated as follows: // int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2); -#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded_by(x)) -#define PT_GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded) +#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) +#define PT_GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_var) // Document the acquisition order between locks that can be held // simultaneously by a thread. For any two locks that need to be annotated @@ -65,7 +65,8 @@ // Document the locks acquired in the body of the function. These locks // cannot be held when calling this function (as google3's Mutex locks are // non-reentrant). -#define LOCKS_EXCLUDED(x) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(x)) +#define LOCKS_EXCLUDED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) // Document the lock the annotated function returns without acquiring it. #define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) diff --git a/webrtc/base/thread_annotations_unittest.cc b/webrtc/base/thread_annotations_unittest.cc new file mode 100644 index 0000000000..69938eff86 --- /dev/null +++ b/webrtc/base/thread_annotations_unittest.cc @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/base/thread_annotations.h" +#include "webrtc/test/gtest.h" + +namespace { + +class LOCKABLE Lock { + public: + void EnterWrite() const EXCLUSIVE_LOCK_FUNCTION() {} + void EnterRead() const SHARED_LOCK_FUNCTION() {} + bool TryEnterWrite() const EXCLUSIVE_TRYLOCK_FUNCTION(true) { return true; } + bool TryEnterRead() const SHARED_TRYLOCK_FUNCTION(true) { return true; } + void Leave() const UNLOCK_FUNCTION() {} +}; + +class SCOPED_LOCKABLE ScopeLock { + public: + explicit ScopeLock(const Lock& lock) EXCLUSIVE_LOCK_FUNCTION(lock) {} + ~ScopeLock() UNLOCK_FUNCTION() {} +}; + +class ThreadSafe { + public: + ThreadSafe() { + pt_protected_by_lock_ = new int; + pt_protected_by_anything_ = new int; + } + + ~ThreadSafe() { + delete pt_protected_by_lock_; + delete pt_protected_by_anything_; + } + + void LockInOrder() { + anylock_.EnterWrite(); + lock_.EnterWrite(); + pt_lock_.EnterWrite(); + + pt_lock_.Leave(); + lock_.Leave(); + anylock_.Leave(); + } + + void UnprotectedFunction() LOCKS_EXCLUDED(anylock_, lock_, pt_lock_) { + // Can access unprotected Value. + unprotected_ = 15; + // Can access pointers themself, but not data they point to. + int* tmp = pt_protected_by_lock_; + pt_protected_by_lock_ = pt_protected_by_anything_; + pt_protected_by_anything_ = tmp; + } + + void ReadProtected() { + lock_.EnterRead(); + unprotected_ = protected_by_anything_; + unprotected_ = protected_by_lock_; + lock_.Leave(); + + if (pt_lock_.TryEnterRead()) { + unprotected_ = *pt_protected_by_anything_; + unprotected_ = *pt_protected_by_lock_; + pt_lock_.Leave(); + } + + anylock_.EnterRead(); + unprotected_ = protected_by_anything_; + unprotected_ = *pt_protected_by_anything_; + anylock_.Leave(); + } + + void WriteProtected() { + lock_.EnterWrite(); + protected_by_anything_ = unprotected_; + protected_by_lock_ = unprotected_; + lock_.Leave(); + + if (pt_lock_.TryEnterWrite()) { + *pt_protected_by_anything_ = unprotected_; + *pt_protected_by_lock_ = unprotected_; + pt_lock_.Leave(); + } + + anylock_.EnterWrite(); + protected_by_anything_ = unprotected_; + *pt_protected_by_anything_ = unprotected_; + anylock_.Leave(); + } + + void CallReadProtectedFunction() { + lock_.EnterRead(); + pt_lock_.EnterRead(); + ReadProtectedFunction(); + pt_lock_.Leave(); + lock_.Leave(); + } + + void CallWriteProtectedFunction() { + ScopeLock scope_lock(GetLock()); + ScopeLock pt_scope_lock(pt_lock_); + WriteProtectedFunction(); + } + + private: + void ReadProtectedFunction() SHARED_LOCKS_REQUIRED(lock_, pt_lock_) { + unprotected_ = protected_by_lock_; + unprotected_ = *pt_protected_by_lock_; + } + + void WriteProtectedFunction() EXCLUSIVE_LOCKS_REQUIRED(lock_, pt_lock_) { + int x = protected_by_lock_; + *pt_protected_by_lock_ = x; + protected_by_lock_ = unprotected_; + } + + const Lock& GetLock() LOCK_RETURNED(lock_) { return lock_; } + + Lock anylock_ ACQUIRED_BEFORE(lock_); + Lock lock_; + Lock pt_lock_ ACQUIRED_AFTER(lock_); + + int unprotected_ = 0; + + int protected_by_lock_ GUARDED_BY(lock_) = 0; + int protected_by_anything_ GUARDED_VAR = 0; + + int* pt_protected_by_lock_ PT_GUARDED_BY(pt_lock_); + int* pt_protected_by_anything_ PT_GUARDED_VAR; +}; + +} // namespace + +TEST(ThreadAnnotationsTest, Test) { + // This test ensure thread annotations doesn't break compilation. + // Thus no run-time expectations. + ThreadSafe t; + t.LockInOrder(); + t.UnprotectedFunction(); + t.ReadProtected(); + t.WriteProtected(); + t.CallReadProtectedFunction(); + t.CallWriteProtectedFunction(); +}