Add tests and fix thread annotations
BUG=None NOTRY=true Review-Url: https://codereview.webrtc.org/2435293002 Cr-Commit-Position: refs/heads/master@{#14742}
This commit is contained in:
parent
b60d1962d8
commit
01404084aa
@ -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",
|
||||
|
||||
@ -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))
|
||||
|
||||
151
webrtc/base/thread_annotations_unittest.cc
Normal file
151
webrtc/base/thread_annotations_unittest.cc
Normal file
@ -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();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user