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
15namespace fml {
16namespace internal {
17
18// See ref_counted.h for comments on the public methods.
19class 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
86inline RefCountedThreadSafeBase::RefCountedThreadSafeBase()
87 : ref_count_(1u)
88#ifndef NDEBUG
89 ,
90 adoption_required_(true),
91 destruction_started_(false)
92#endif
93{
94}
95
96inline 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