1 | // Copyright 2013 The Flutter Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | // Internal implementation details for ref_counted.h. |
6 | |
7 | #ifndef FLUTTER_FML_MEMORY_REF_COUNTED_INTERNAL_H_ |
8 | #define FLUTTER_FML_MEMORY_REF_COUNTED_INTERNAL_H_ |
9 | |
10 | #include <atomic> |
11 | |
12 | #include "flutter/fml/logging.h" |
13 | #include "flutter/fml/macros.h" |
14 | |
15 | namespace fml { |
16 | namespace internal { |
17 | |
18 | // See ref_counted.h for comments on the public methods. |
19 | class RefCountedThreadSafeBase { |
20 | public: |
21 | void AddRef() const { |
22 | #ifndef NDEBUG |
23 | FML_DCHECK(!adoption_required_); |
24 | FML_DCHECK(!destruction_started_); |
25 | #endif |
26 | ref_count_.fetch_add(1u, std::memory_order_relaxed); |
27 | } |
28 | |
29 | bool HasOneRef() const { |
30 | return ref_count_.load(std::memory_order_acquire) == 1u; |
31 | } |
32 | |
33 | void AssertHasOneRef() const { FML_DCHECK(HasOneRef()); } |
34 | |
35 | protected: |
36 | RefCountedThreadSafeBase(); |
37 | ~RefCountedThreadSafeBase(); |
38 | |
39 | // Returns true if the object should self-delete. |
40 | bool Release() const { |
41 | #ifndef NDEBUG |
42 | FML_DCHECK(!adoption_required_); |
43 | FML_DCHECK(!destruction_started_); |
44 | #endif |
45 | FML_DCHECK(ref_count_.load(std::memory_order_acquire) != 0u); |
46 | // TODO(vtl): We could add the following: |
47 | // if (ref_count_.load(std::memory_order_relaxed) == 1u) { |
48 | // #ifndef NDEBUG |
49 | // destruction_started_= true; |
50 | // #endif |
51 | // return true; |
52 | // } |
53 | // This would be correct. On ARM (an Nexus 4), in *single-threaded* tests, |
54 | // this seems to make the destruction case marginally faster (barely |
55 | // measurable), and while the non-destruction case remains about the same |
56 | // (possibly marginally slower, but my measurements aren't good enough to |
57 | // have any confidence in that). I should try multithreaded/multicore tests. |
58 | if (ref_count_.fetch_sub(1u, std::memory_order_release) == 1u) { |
59 | std::atomic_thread_fence(std::memory_order_acquire); |
60 | #ifndef NDEBUG |
61 | destruction_started_ = true; |
62 | #endif |
63 | return true; |
64 | } |
65 | return false; |
66 | } |
67 | |
68 | #ifndef NDEBUG |
69 | void Adopt() { |
70 | FML_DCHECK(adoption_required_); |
71 | adoption_required_ = false; |
72 | } |
73 | #endif |
74 | |
75 | private: |
76 | mutable std::atomic_uint_fast32_t ref_count_; |
77 | |
78 | #ifndef NDEBUG |
79 | mutable bool adoption_required_; |
80 | mutable bool destruction_started_; |
81 | #endif |
82 | |
83 | FML_DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase); |
84 | }; |
85 | |
86 | inline RefCountedThreadSafeBase::RefCountedThreadSafeBase() |
87 | : ref_count_(1u) |
88 | #ifndef NDEBUG |
89 | , |
90 | adoption_required_(true), |
91 | destruction_started_(false) |
92 | #endif |
93 | { |
94 | } |
95 | |
96 | inline RefCountedThreadSafeBase::~RefCountedThreadSafeBase() { |
97 | #ifndef NDEBUG |
98 | FML_DCHECK(!adoption_required_); |
99 | // Should only be destroyed as a result of |Release()|. |
100 | FML_DCHECK(destruction_started_); |
101 | #endif |
102 | } |
103 | |
104 | } // namespace internal |
105 | } // namespace fml |
106 | |
107 | #endif // FLUTTER_FML_MEMORY_REF_COUNTED_INTERNAL_H_ |
108 |