1/*
2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#pragma once
22
23#include <wtf/Assertions.h>
24#include <wtf/FastMalloc.h>
25#include <wtf/MainThread.h>
26#include <wtf/Noncopyable.h>
27
28namespace WTF {
29
30#if defined(NDEBUG) && !ENABLE(SECURITY_ASSERTIONS)
31#define CHECK_REF_COUNTED_LIFECYCLE 0
32#else
33#define CHECK_REF_COUNTED_LIFECYCLE 1
34#endif
35
36// This base class holds the non-template methods and attributes.
37// The RefCounted class inherits from it reducing the template bloat
38// generated by the compiler (technique called template hoisting).
39class RefCountedBase {
40public:
41 void ref() const
42 {
43 applyRefDerefThreadingCheck();
44
45#if CHECK_REF_COUNTED_LIFECYCLE
46 ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun);
47 ASSERT(!m_adoptionIsRequired);
48#endif
49 ++m_refCount;
50 }
51
52 bool hasOneRef() const
53 {
54#if CHECK_REF_COUNTED_LIFECYCLE
55 ASSERT(!m_deletionHasBegun);
56#endif
57 return m_refCount == 1;
58 }
59
60 unsigned refCount() const
61 {
62 return m_refCount;
63 }
64
65 void relaxAdoptionRequirement()
66 {
67#if CHECK_REF_COUNTED_LIFECYCLE
68 ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun);
69 ASSERT(m_adoptionIsRequired);
70 m_adoptionIsRequired = false;
71#endif
72 }
73
74 // Please only call this method if you really know that what you're doing is safe (e.g.
75 // locking at call sites).
76 void disableThreadingChecks()
77 {
78#if !ASSERT_DISABLED
79 m_areThreadingChecksEnabled = false;
80#endif
81 }
82
83 static void enableThreadingChecksGlobally()
84 {
85#if !ASSERT_DISABLED
86 areThreadingChecksEnabledGlobally = true;
87#endif
88 }
89
90protected:
91 RefCountedBase()
92 : m_refCount(1)
93#if !ASSERT_DISABLED
94 , m_isOwnedByMainThread(isMainThread())
95#endif
96#if CHECK_REF_COUNTED_LIFECYCLE
97 , m_deletionHasBegun(false)
98 , m_adoptionIsRequired(true)
99#endif
100 {
101 }
102
103 void applyRefDerefThreadingCheck() const
104 {
105#if !ASSERT_DISABLED
106 if (hasOneRef()) {
107 // Likely an ownership transfer across threads that may be safe.
108 m_isOwnedByMainThread = isMainThread();
109 } else if (areThreadingChecksEnabledGlobally && m_areThreadingChecksEnabled) {
110 // If you hit this assertion, it means that the RefCounted object was ref/deref'd
111 // from both the main thread and another in a way that is likely concurrent and unsafe.
112 // Derive from ThreadSafeRefCounted and make sure the destructor is safe on threads
113 // that call deref, or ref/deref from a single thread.
114 ASSERT_WITH_MESSAGE(m_isOwnedByMainThread == isMainThread(), "Unsafe to ref/deref from different threads");
115 }
116#endif
117 }
118
119 ~RefCountedBase()
120 {
121#if CHECK_REF_COUNTED_LIFECYCLE
122 ASSERT(m_deletionHasBegun);
123 ASSERT(!m_adoptionIsRequired);
124#endif
125 }
126
127 // Returns whether the pointer should be freed or not.
128 bool derefBase() const
129 {
130 applyRefDerefThreadingCheck();
131
132#if CHECK_REF_COUNTED_LIFECYCLE
133 ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun);
134 ASSERT(!m_adoptionIsRequired);
135#endif
136
137 ASSERT(m_refCount);
138 unsigned tempRefCount = m_refCount - 1;
139 if (!tempRefCount) {
140#if CHECK_REF_COUNTED_LIFECYCLE
141 m_deletionHasBegun = true;
142#endif
143 return true;
144 }
145 m_refCount = tempRefCount;
146 return false;
147 }
148
149#if CHECK_REF_COUNTED_LIFECYCLE
150 bool deletionHasBegun() const
151 {
152 return m_deletionHasBegun;
153 }
154#endif
155
156private:
157
158#if CHECK_REF_COUNTED_LIFECYCLE
159 friend void adopted(RefCountedBase*);
160#endif
161
162 mutable unsigned m_refCount;
163#if !ASSERT_DISABLED
164 mutable bool m_isOwnedByMainThread;
165 bool m_areThreadingChecksEnabled { true };
166#endif
167 WTF_EXPORT_PRIVATE static bool areThreadingChecksEnabledGlobally;
168#if CHECK_REF_COUNTED_LIFECYCLE
169 mutable bool m_deletionHasBegun;
170 mutable bool m_adoptionIsRequired;
171#endif
172};
173
174#if CHECK_REF_COUNTED_LIFECYCLE
175inline void adopted(RefCountedBase* object)
176{
177 if (!object)
178 return;
179 ASSERT_WITH_SECURITY_IMPLICATION(!object->m_deletionHasBegun);
180 object->m_adoptionIsRequired = false;
181}
182#endif
183
184template<typename T, typename Deleter = std::default_delete<T>> class RefCounted : public RefCountedBase {
185 WTF_MAKE_NONCOPYABLE(RefCounted); WTF_MAKE_FAST_ALLOCATED;
186public:
187 void deref() const
188 {
189 if (derefBase())
190 Deleter()(const_cast<T*>(static_cast<const T*>(this)));
191 }
192
193protected:
194 RefCounted() { }
195 ~RefCounted()
196 {
197 }
198};
199
200} // namespace WTF
201
202using WTF::RefCounted;
203