1/*
2 * Copyright (C) 2007, 2008, 2010, 2013, 2014 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include <atomic>
29#include <wtf/FastMalloc.h>
30#include <wtf/MainThread.h>
31#include <wtf/Noncopyable.h>
32
33namespace WTF {
34
35#if defined(NDEBUG) && !ENABLE(SECURITY_ASSERTIONS)
36#define CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE 0
37#else
38#define CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE 1
39#endif
40
41class ThreadSafeRefCountedBase {
42 WTF_MAKE_NONCOPYABLE(ThreadSafeRefCountedBase);
43 WTF_MAKE_FAST_ALLOCATED;
44public:
45 ThreadSafeRefCountedBase() = default;
46
47#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
48 ~ThreadSafeRefCountedBase()
49 {
50 // When this ThreadSafeRefCounted object is a part of another object, derefBase() is never called on this object.
51 m_deletionHasBegun = true;
52 }
53#endif
54
55 void ref() const
56 {
57#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
58 ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun);
59#endif
60 ++m_refCount;
61 }
62
63 bool hasOneRef() const
64 {
65#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
66 ASSERT(!m_deletionHasBegun);
67#endif
68 return refCount() == 1;
69 }
70
71 unsigned refCount() const
72 {
73 return m_refCount;
74 }
75
76protected:
77 // Returns whether the pointer should be freed or not.
78 bool derefBase() const
79 {
80 ASSERT(m_refCount);
81
82#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
83 ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun);
84#endif
85
86 if (UNLIKELY(!--m_refCount)) {
87 // Setting m_refCount to 1 here prevents double delete within the destructor but not from another thread
88 // since such a thread could have ref'ed this object long after it had been deleted. See webkit.org/b/201576.
89 m_refCount = 1;
90#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
91 m_deletionHasBegun = true;
92#endif
93 return true;
94 }
95
96 return false;
97 }
98
99private:
100 mutable std::atomic<unsigned> m_refCount { 1 };
101
102#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
103 mutable std::atomic<bool> m_deletionHasBegun { false };
104#endif
105};
106
107enum class DestructionThread { Any, Main, MainRunLoop };
108
109template<class T, DestructionThread destructionThread = DestructionThread::Any> class ThreadSafeRefCounted : public ThreadSafeRefCountedBase {
110public:
111 void deref() const
112 {
113 if (!derefBase())
114 return;
115
116 auto deleteThis = [this] {
117 delete static_cast<const T*>(this);
118 };
119 switch (destructionThread) {
120 case DestructionThread::Any:
121 break;
122 case DestructionThread::Main:
123 if (!isMainThread()) {
124 callOnMainThread(WTFMove(deleteThis));
125 return;
126 }
127 break;
128 case DestructionThread::MainRunLoop:
129 if (!isMainRunLoop()) {
130 callOnMainRunLoop(WTFMove(deleteThis));
131 return;
132 }
133 break;
134 }
135 deleteThis();
136 }
137
138protected:
139 ThreadSafeRefCounted() = default;
140};
141
142} // namespace WTF
143
144using WTF::ThreadSafeRefCounted;
145