source: webkit/trunk/Source/WTF/wtf/Ref.h

Last change on this file was 293682, checked in by Chris Dumez, 3 years ago

static_reference_cast(const Ref<X, Y>&) causes unnecessary ref-counting churn
https://bugs.webkit.org/show_bug.cgi?id=239970

Reviewed by Geoffrey Garen.

This function was calling copyRef() to get a non-const Ref<> and then calling Ref::get() to construct a
new Ref<> of the destination type. The copyRef() would increase the ref count by 1, constructing the
new destination Ref<> would increase the ref count by 1 again and then the temporary Ref going out of
scope would decrease the ref count by one.

We now call static_reference_cast(Ref<X, Y>&&) with the result of the copyRef(), which will leak
the pointer from the Ref<> and adopt it in the destination Ref, thus avoiding unnecessary churn.

Also drop the static_reference_cast(Ref<X, Y>&) overload as it is no longer more efficient than the
static_reference_cast(const Ref<X, Y>&) one.

  • Source/WTF/wtf/Ref.h:

(WTF::static_reference_cast):

Canonical link: https://commits.webkit.org/250182@main

File size: 8.4 KB
Line 
1/*
2 * Copyright (C) 2013-2020 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include <wtf/Assertions.h>
29#include <wtf/Forward.h>
30#include <wtf/GetPtr.h>
31#include <wtf/RawPtrTraits.h>
32#include <wtf/StdLibExtras.h>
33#include <wtf/TypeCasts.h>
34
35#if ASAN_ENABLED
36extern "C" void __asan_poison_memory_region(void const volatile *addr, size_t size);
37extern "C" void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
38extern "C" int __asan_address_is_poisoned(void const volatile *addr);
39#endif
40
41namespace WTF {
42
43inline void adopted(const void*) { }
44
45template<typename T, typename PtrTraits> class Ref;
46template<typename T, typename PtrTraits = RawPtrTraits<T>> Ref<T, PtrTraits> adoptRef(T&);
47
48template<typename T, typename Traits>
49class Ref {
50public:
51 using PtrTraits = Traits;
52 static constexpr bool isRef = true;
53
54 ~Ref()
55 {
56#if ASAN_ENABLED
57 if (__asan_address_is_poisoned(this))
58 __asan_unpoison_memory_region(this, sizeof(*this));
59#endif
60 if (auto* ptr = PtrTraits::exchange(m_ptr, nullptr))
61 ptr->deref();
62 }
63
64 Ref(T& object)
65 : m_ptr(&object)
66 {
67 object.ref();
68 }
69
70 Ref(const Ref& other)
71 : m_ptr(other.ptr())
72 {
73 m_ptr->ref();
74 }
75
76 template<typename X, typename Y> Ref(const Ref<X, Y>& other)
77 : m_ptr(other.ptr())
78 {
79 m_ptr->ref();
80 }
81
82 Ref(Ref&& other)
83 : m_ptr(&other.leakRef())
84 {
85 ASSERT(m_ptr);
86 }
87
88 template<typename X, typename Y>
89 Ref(Ref<X, Y>&& other)
90 : m_ptr(&other.leakRef())
91 {
92 ASSERT(m_ptr);
93 }
94
95 Ref& operator=(T&);
96 Ref& operator=(Ref&&);
97 template<typename X, typename Y> Ref& operator=(Ref<X, Y>&&);
98
99 Ref& operator=(const Ref&);
100 template<typename X, typename Y> Ref& operator=(const Ref<X, Y>&);
101
102 template<typename X, typename Y> void swap(Ref<X, Y>&);
103
104 // Hash table deleted values, which are only constructed and never copied or destroyed.
105 Ref(HashTableDeletedValueType) : m_ptr(PtrTraits::hashTableDeletedValue()) { }
106 bool isHashTableDeletedValue() const { return PtrTraits::isHashTableDeletedValue(m_ptr); }
107
108 Ref(HashTableEmptyValueType) : m_ptr(hashTableEmptyValue()) { }
109 bool isHashTableEmptyValue() const { return m_ptr == hashTableEmptyValue(); }
110 static T* hashTableEmptyValue() { return nullptr; }
111
112 const T* ptrAllowingHashTableEmptyValue() const { ASSERT(m_ptr || isHashTableEmptyValue()); return PtrTraits::unwrap(m_ptr); }
113 T* ptrAllowingHashTableEmptyValue() { ASSERT(m_ptr || isHashTableEmptyValue()); return PtrTraits::unwrap(m_ptr); }
114
115 T* operator->() const { ASSERT(m_ptr); return PtrTraits::unwrap(m_ptr); }
116 T* ptr() const RETURNS_NONNULL { ASSERT(m_ptr); return PtrTraits::unwrap(m_ptr); }
117 T& get() const { ASSERT(m_ptr); return *PtrTraits::unwrap(m_ptr); }
118 operator T&() const { ASSERT(m_ptr); return *PtrTraits::unwrap(m_ptr); }
119 bool operator!() const { ASSERT(m_ptr); return !*m_ptr; }
120
121 template<typename X, typename Y> Ref<T, PtrTraits> replace(Ref<X, Y>&&) WARN_UNUSED_RETURN;
122
123 // The following function is deprecated.
124 Ref copyRef() && = delete;
125 Ref copyRef() const & WARN_UNUSED_RETURN { return Ref(*m_ptr); }
126
127 T& leakRef() WARN_UNUSED_RETURN
128 {
129 ASSERT(m_ptr);
130
131 T& result = *PtrTraits::exchange(m_ptr, nullptr);
132#if ASAN_ENABLED
133 __asan_poison_memory_region(this, sizeof(*this));
134#endif
135 return result;
136 }
137
138private:
139 friend Ref adoptRef<T>(T&);
140 template<typename X, typename Y> friend class Ref;
141
142 enum AdoptTag { Adopt };
143 Ref(T& object, AdoptTag)
144 : m_ptr(&object)
145 {
146 }
147
148 typename PtrTraits::StorageType m_ptr;
149};
150
151template<typename T, typename U> Ref<T, U> adoptRef(T&);
152
153template<typename T, typename U>
154inline Ref<T, U>& Ref<T, U>::operator=(T& reference)
155{
156 Ref copiedReference = reference;
157 swap(copiedReference);
158 return *this;
159}
160
161template<typename T, typename U>
162inline Ref<T, U>& Ref<T, U>::operator=(Ref&& reference)
163{
164#if ASAN_ENABLED
165 if (__asan_address_is_poisoned(this))
166 __asan_unpoison_memory_region(this, sizeof(*this));
167#endif
168 Ref movedReference = WTFMove(reference);
169 swap(movedReference);
170 return *this;
171}
172
173template<typename T, typename U>
174template<typename X, typename Y>
175inline Ref<T, U>& Ref<T, U>::operator=(Ref<X, Y>&& reference)
176{
177#if ASAN_ENABLED
178 if (__asan_address_is_poisoned(this))
179 __asan_unpoison_memory_region(this, sizeof(*this));
180#endif
181 Ref movedReference = WTFMove(reference);
182 swap(movedReference);
183 return *this;
184}
185
186template<typename T, typename U>
187inline Ref<T, U>& Ref<T, U>::operator=(const Ref& reference)
188{
189#if ASAN_ENABLED
190 if (__asan_address_is_poisoned(this))
191 __asan_unpoison_memory_region(this, sizeof(*this));
192#endif
193 Ref copiedReference = reference;
194 swap(copiedReference);
195 return *this;
196}
197
198template<typename T, typename U>
199template<typename X, typename Y>
200inline Ref<T, U>& Ref<T, U>::operator=(const Ref<X, Y>& reference)
201{
202#if ASAN_ENABLED
203 if (__asan_address_is_poisoned(this))
204 __asan_unpoison_memory_region(this, sizeof(*this));
205#endif
206 Ref copiedReference = reference;
207 swap(copiedReference);
208 return *this;
209}
210
211template<typename T, typename U>
212template<typename X, typename Y>
213inline void Ref<T, U>::swap(Ref<X, Y>& other)
214{
215 U::swap(m_ptr, other.m_ptr);
216}
217
218template<typename T, typename U, typename X, typename Y, typename = std::enable_if_t<!std::is_same<U, RawPtrTraits<T>>::value || !std::is_same<Y, RawPtrTraits<X>>::value>>
219inline void swap(Ref<T, U>& a, Ref<X, Y>& b)
220{
221 a.swap(b);
222}
223
224template<typename T, typename U>
225template<typename X, typename Y>
226inline Ref<T, U> Ref<T, U>::replace(Ref<X, Y>&& reference)
227{
228#if ASAN_ENABLED
229 if (__asan_address_is_poisoned(this))
230 __asan_unpoison_memory_region(this, sizeof(*this));
231#endif
232 auto oldReference = adoptRef(*m_ptr);
233 m_ptr = &reference.leakRef();
234 return oldReference;
235}
236
237template<typename T, typename U = RawPtrTraits<T>, typename X, typename Y>
238inline Ref<T, U> static_reference_cast(Ref<X, Y>&& reference)
239{
240 return adoptRef(static_cast<T&>(reference.leakRef()));
241}
242
243template<typename T, typename U = RawPtrTraits<T>, typename X, typename Y>
244ALWAYS_INLINE Ref<T, U> static_reference_cast(const Ref<X, Y>& reference)
245{
246 return static_reference_cast<T, U>(reference.copyRef());
247}
248
249template <typename T, typename U>
250struct GetPtrHelper<Ref<T, U>> {
251 typedef T* PtrType;
252 static T* getPtr(const Ref<T, U>& p) { return const_cast<T*>(p.ptr()); }
253};
254
255template <typename T, typename U>
256struct IsSmartPtr<Ref<T, U>> {
257 static constexpr bool value = true;
258};
259
260template<typename T, typename U>
261inline Ref<T, U> adoptRef(T& reference)
262{
263 adopted(&reference);
264 return Ref<T, U>(reference, Ref<T, U>::Adopt);
265}
266
267template<typename ExpectedType, typename ArgType, typename PtrTraits>
268inline bool is(Ref<ArgType, PtrTraits>& source)
269{
270 return is<ExpectedType>(source.get());
271}
272
273template<typename ExpectedType, typename ArgType, typename PtrTraits>
274inline bool is(const Ref<ArgType, PtrTraits>& source)
275{
276 return is<ExpectedType>(source.get());
277}
278
279} // namespace WTF
280
281using WTF::Ref;
282using WTF::adoptRef;
283using WTF::static_reference_cast;
Note: See TracBrowser for help on using the repository browser.