forked from flutter/engine
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Import synchronization utilities into FML. (flutter#5312)
- Loading branch information
1 parent
ef7a459
commit 2e6aad3
Showing
10 changed files
with
838 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright 2016 The Fuchsia 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 FLUTTER_FML_CLOSURE_H_ | ||
#define FLUTTER_FML_CLOSURE_H_ | ||
|
||
#include <functional> | ||
|
||
namespace fml { | ||
|
||
using closure = std::function<void()>; | ||
|
||
} // namespace fml | ||
|
||
#endif // FLUTTER_FML_CLOSURE_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Copyright 2016 The Fuchsia Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
// Macros for static thread-safety analysis. | ||
// | ||
// These are from http://clang.llvm.org/docs/ThreadSafetyAnalysis.html (and thus | ||
// really derive from google3's thread_annotations.h). | ||
// | ||
// TODO(vtl): We're still using the old-fashioned, deprecated annotations | ||
// ("locks" instead of "capabilities"), since the new ones don't work yet (in | ||
// particular, |TRY_ACQUIRE()| doesn't work: b/19264527). | ||
// https://github.com/domokit/mojo/issues/314 | ||
|
||
#ifndef FLUTTER_FML_SYNCHRONIZATION_THREAD_ANNOTATIONS_H_ | ||
#define FLUTTER_FML_SYNCHRONIZATION_THREAD_ANNOTATIONS_H_ | ||
|
||
// Enable thread-safety attributes only with clang. | ||
// The attributes can be safely erased when compiling with other compilers. | ||
#if defined(__clang__) | ||
#define FML_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) | ||
#else | ||
#define FML_THREAD_ANNOTATION_ATTRIBUTE__(x) | ||
#endif | ||
|
||
#define FML_GUARDED_BY(x) FML_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) | ||
|
||
#define FML_PT_GUARDED_BY(x) FML_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) | ||
|
||
#define FML_ACQUIRE(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) | ||
|
||
#define FML_RELEASE(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) | ||
|
||
#define FML_ACQUIRED_AFTER(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) | ||
|
||
#define FML_ACQUIRED_BEFORE(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) | ||
|
||
#define FML_EXCLUSIVE_LOCKS_REQUIRED(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) | ||
|
||
#define FML_SHARED_LOCKS_REQUIRED(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) | ||
|
||
#define FML_LOCKS_EXCLUDED(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) | ||
|
||
#define FML_LOCK_RETURNED(x) FML_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) | ||
|
||
#define FML_LOCKABLE FML_THREAD_ANNOTATION_ATTRIBUTE__(lockable) | ||
|
||
#define FML_SCOPED_LOCKABLE FML_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) | ||
|
||
#define FML_EXCLUSIVE_LOCK_FUNCTION(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) | ||
|
||
#define FML_SHARED_LOCK_FUNCTION(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) | ||
|
||
#define FML_ASSERT_EXCLUSIVE_LOCK(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) | ||
|
||
#define FML_ASSERT_SHARED_LOCK(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) | ||
|
||
#define FML_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) | ||
|
||
#define FML_SHARED_TRYLOCK_FUNCTION(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) | ||
|
||
#define FML_UNLOCK_FUNCTION(...) \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) | ||
|
||
#define FML_NO_THREAD_SAFETY_ANALYSIS \ | ||
FML_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) | ||
|
||
// Use this in the header to annotate a function/method as not being | ||
// thread-safe. This is equivalent to |FML_NO_THREAD_SAFETY_ANALYSIS|, but | ||
// semantically different: it declares that the caller must abide by additional | ||
// restrictions. Limitation: Unfortunately, you can't apply this to a method in | ||
// an interface (i.e., pure virtual method) and have it applied automatically to | ||
// implementations. | ||
#define FML_NOT_THREAD_SAFE FML_NO_THREAD_SAFETY_ANALYSIS | ||
|
||
#endif // FLUTTER_FML_SYNCHRONIZATION_THREAD_ANNOTATIONS_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// Copyright 2016 The Fuchsia Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
// Tests of the static thread annotation macros. These fall into two categories, | ||
// positive tests (testing that correct code compiles and works) and negative | ||
// tests (testing that incorrect code does not compile). | ||
// | ||
// Unfortunately, we don't have systematic/automated negative compilation tests. | ||
// So instead we have some cheesy macros that you can define to enable | ||
// individual compilation failures. | ||
|
||
#include "flutter/fml/synchronization/thread_annotations.h" | ||
|
||
#include <mutex> | ||
|
||
#include "flutter/fml/macros.h" | ||
#include "gtest/gtest.h" | ||
|
||
// Uncomment these to enable particular compilation failure tests. | ||
// #define NC_GUARDED_BY | ||
// TODO(vtl): |ACQUIRED_{BEFORE,AFTER}()| are currently unimplemented in clang | ||
// as of 2015-07-06 ("To be fixed in a future update."). So this actually | ||
// compiles! | ||
// #define NC_ACQUIRED_BEFORE | ||
|
||
namespace fml { | ||
namespace { | ||
|
||
// Test FML_GUARDED_BY --------------------------------------------------------- | ||
|
||
class GuardedByClass { | ||
public: | ||
GuardedByClass() : x_() {} | ||
~GuardedByClass() {} | ||
|
||
void GoodSet(int x) { | ||
mu_.lock(); | ||
x_ = x; | ||
mu_.unlock(); | ||
} | ||
|
||
#ifdef NC_GUARDED_BY | ||
void BadSet(int x) { x_ = x; } | ||
#endif | ||
|
||
private: | ||
std::mutex mu_; | ||
int x_ FML_GUARDED_BY(mu_); | ||
|
||
FML_DISALLOW_COPY_AND_ASSIGN(GuardedByClass); | ||
}; | ||
|
||
TEST(ThreadAnnotationsTest, GuardedBy) { | ||
GuardedByClass c; | ||
c.GoodSet(123); | ||
} | ||
|
||
// Test FML_ACQUIRED_BEFORE ---------------------------------------------------- | ||
|
||
class AcquiredBeforeClass2; | ||
|
||
class AcquiredBeforeClass1 { | ||
public: | ||
AcquiredBeforeClass1() {} | ||
~AcquiredBeforeClass1() {} | ||
|
||
void NoOp() { | ||
mu_.lock(); | ||
mu_.unlock(); | ||
} | ||
|
||
#ifdef NC_ACQUIRED_BEFORE | ||
void BadMethod(AcquiredBeforeClass2* c2); | ||
#endif | ||
|
||
private: | ||
friend class AcquiredBeforeClass2; | ||
|
||
std::mutex mu_; | ||
|
||
FML_DISALLOW_COPY_AND_ASSIGN(AcquiredBeforeClass1); | ||
}; | ||
|
||
class AcquiredBeforeClass2 { | ||
public: | ||
AcquiredBeforeClass2() {} | ||
~AcquiredBeforeClass2() {} | ||
|
||
void NoOp() { | ||
mu_.lock(); | ||
mu_.unlock(); | ||
} | ||
|
||
void GoodMethod(AcquiredBeforeClass1* c1) { | ||
mu_.lock(); | ||
c1->NoOp(); | ||
mu_.unlock(); | ||
} | ||
|
||
private: | ||
std::mutex mu_ FML_ACQUIRED_BEFORE(AcquiredBeforeClass1::mu_); | ||
|
||
FML_DISALLOW_COPY_AND_ASSIGN(AcquiredBeforeClass2); | ||
}; | ||
|
||
#ifdef NC_ACQUIRED_BEFORE | ||
void AcquiredBeforeClass1::BadMethod(AcquiredBeforeClass2* c2) { | ||
mu_.lock(); | ||
c2->NoOp(); | ||
mu_.unlock(); | ||
} | ||
#endif | ||
|
||
TEST(ThreadAnnotationsTest, AcquiredBefore) { | ||
AcquiredBeforeClass1 c1; | ||
AcquiredBeforeClass2 c2; | ||
c2.GoodMethod(&c1); | ||
#ifdef NC_ACQUIRED_BEFORE | ||
c1.BadMethod(&c2); | ||
#endif | ||
} | ||
|
||
// TODO(vtl): Test more things. | ||
|
||
} // namespace | ||
} // namespace fml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Copyright 2016 The Fuchsia Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
// A class for checking that the current thread is/isn't the same as an initial | ||
// thread. | ||
|
||
#ifndef FLUTTER_FML_SYNCHRONIZATION_THREAD_CHECKER_H_ | ||
#define FLUTTER_FML_SYNCHRONIZATION_THREAD_CHECKER_H_ | ||
|
||
#include "flutter/fml/build_config.h" | ||
|
||
#if defined(OS_WIN) | ||
#include <windows.h> | ||
#else | ||
#include <pthread.h> | ||
#endif | ||
|
||
#include "flutter/fml/logging.h" | ||
#include "flutter/fml/macros.h" | ||
|
||
namespace fml { | ||
|
||
// A simple class that records the identity of the thread that it was created | ||
// on, and at later points can tell if the current thread is the same as its | ||
// creation thread. This class is thread-safe. | ||
// | ||
// Note: Unlike Chromium's |base::ThreadChecker|, this is *not* Debug-only (so | ||
// #ifdef it out if you want something Debug-only). (Rationale: Having a | ||
// |CalledOnValidThread()| that lies in Release builds seems bad. Moreover, | ||
// there's a small space cost to having even an empty class. ) | ||
class ThreadChecker final { | ||
public: | ||
#if defined(OS_WIN) | ||
ThreadChecker() : self_(GetCurrentThreadId()) {} | ||
~ThreadChecker() {} | ||
|
||
bool IsCreationThreadCurrent() const { return GetCurrentThreadId() == self_; } | ||
|
||
private: | ||
const DWORD self_; | ||
|
||
#else | ||
ThreadChecker() : self_(pthread_self()) {} | ||
~ThreadChecker() {} | ||
|
||
// Returns true if the current thread is the thread this object was created | ||
// on and false otherwise. | ||
bool IsCreationThreadCurrent() const { | ||
return !!pthread_equal(pthread_self(), self_); | ||
} | ||
|
||
private: | ||
const pthread_t self_; | ||
#endif | ||
|
||
FML_DISALLOW_COPY_AND_ASSIGN(ThreadChecker); | ||
}; | ||
|
||
#ifndef NDEBUG | ||
#define FML_DECLARE_THREAD_CHECKER(c) fml::ThreadChecker c | ||
#define FML_DCHECK_CREATION_THREAD_IS_CURRENT(c) \ | ||
FML_DCHECK((c).IsCreationThreadCurrent()) | ||
#else | ||
#define FML_DECLARE_THREAD_CHECKER(c) | ||
#define FML_DCHECK_CREATION_THREAD_IS_CURRENT(c) ((void)0) | ||
#endif | ||
|
||
} // namespace fml | ||
|
||
#endif // FLUTTER_FML_SYNCHRONIZATION_THREAD_CHECKER_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright 2016 The Fuchsia 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 "flutter/fml/synchronization/thread_checker.h" | ||
|
||
#include <thread> | ||
|
||
#include "gtest/gtest.h" | ||
|
||
namespace fml { | ||
namespace { | ||
|
||
TEST(ThreadCheckerTest, SameThread) { | ||
ThreadChecker checker; | ||
EXPECT_TRUE(checker.IsCreationThreadCurrent()); | ||
} | ||
|
||
// Note: This test depends on |std::thread| being compatible with | ||
// |pthread_self()|. | ||
TEST(ThreadCheckerTest, DifferentThreads) { | ||
ThreadChecker checker1; | ||
EXPECT_TRUE(checker1.IsCreationThreadCurrent()); | ||
|
||
std::thread thread([&checker1]() { | ||
ThreadChecker checker2; | ||
EXPECT_TRUE(checker2.IsCreationThreadCurrent()); | ||
EXPECT_FALSE(checker1.IsCreationThreadCurrent()); | ||
}); | ||
thread.join(); | ||
|
||
// Note: Without synchronization, we can't look at |checker2| from the main | ||
// thread. | ||
} | ||
|
||
} // namespace | ||
} // namespace fml |
Oops, something went wrong.