]>
Commit | Line | Data |
---|---|---|
ba379fdc A |
1 | /* |
2 | * Copyright (C) 2009 Google 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 are | |
6 | * met: | |
7 | * | |
8 | * * Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * * Redistributions in binary form must reproduce the above | |
11 | * copyright notice, this list of conditions and the following disclaimer | |
12 | * in the documentation and/or other materials provided with the | |
13 | * distribution. | |
14 | * * Neither the name of Google Inc. nor the names of its | |
15 | * contributors may be used to endorse or promote products derived from | |
16 | * this software without specific prior written permission. | |
17 | * | |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | ||
31 | #ifndef CrossThreadRefCounted_h | |
32 | #define CrossThreadRefCounted_h | |
33 | ||
34 | #include <wtf/Noncopyable.h> | |
35 | #include <wtf/PassRefPtr.h> | |
36 | #include <wtf/RefCounted.h> | |
37 | #include <wtf/Threading.h> | |
38 | ||
f9bf01c6 A |
39 | #include <wtf/iphone/WebCoreThread.h> |
40 | ||
ba379fdc A |
41 | namespace WTF { |
42 | ||
43 | // Used to allowing sharing data across classes and threads (like ThreadedSafeShared). | |
44 | // | |
45 | // Why not just use ThreadSafeShared? | |
46 | // ThreadSafeShared can have a significant perf impact when used in low level classes | |
47 | // (like UString) that get ref/deref'ed a lot. This class has the benefit of doing fast ref | |
48 | // counts like RefPtr whenever possible, but it has the downside that you need to copy it | |
49 | // to use it on another thread. | |
50 | // | |
51 | // Is this class threadsafe? | |
52 | // While each instance of the class is not threadsafe, the copied instance is threadsafe | |
53 | // with respect to the original and any other copies. The underlying m_data is jointly | |
54 | // owned by the original instance and all copies. | |
55 | template<class T> | |
f9bf01c6 | 56 | class CrossThreadRefCounted : public Noncopyable { |
ba379fdc A |
57 | public: |
58 | static PassRefPtr<CrossThreadRefCounted<T> > create(T* data) | |
59 | { | |
60 | return adoptRef(new CrossThreadRefCounted<T>(data, 0)); | |
61 | } | |
62 | ||
63 | // Used to make an instance that can be used on another thread. | |
64 | PassRefPtr<CrossThreadRefCounted<T> > crossThreadCopy(); | |
65 | ||
66 | void ref(); | |
67 | void deref(); | |
68 | T* release(); | |
69 | ||
70 | bool isShared() const | |
71 | { | |
72 | return !m_refCounter.hasOneRef() || (m_threadSafeRefCounter && !m_threadSafeRefCounter->hasOneRef()); | |
73 | } | |
74 | ||
ba379fdc A |
75 | private: |
76 | CrossThreadRefCounted(T* data, ThreadSafeSharedBase* threadedCounter) | |
77 | : m_threadSafeRefCounter(threadedCounter) | |
78 | , m_data(data) | |
79 | #ifndef NDEBUG | |
80 | , m_threadId(0) | |
81 | #endif | |
82 | { | |
83 | } | |
84 | ||
85 | ~CrossThreadRefCounted() | |
86 | { | |
87 | if (!m_threadSafeRefCounter) | |
88 | delete m_data; | |
89 | } | |
90 | ||
91 | void threadSafeDeref(); | |
92 | ||
f9bf01c6 A |
93 | #ifndef NDEBUG |
94 | bool isOwnedByCurrentThread() const { | |
95 | return !m_threadId || m_threadId == currentThread() || ((isMainThread() || pthread_main_np()) && WebCoreWebThreadIsLockedOrDisabled()); | |
96 | } | |
97 | #endif | |
98 | ||
ba379fdc A |
99 | RefCountedBase m_refCounter; |
100 | ThreadSafeSharedBase* m_threadSafeRefCounter; | |
101 | T* m_data; | |
102 | #ifndef NDEBUG | |
103 | ThreadIdentifier m_threadId; | |
104 | #endif | |
105 | }; | |
106 | ||
107 | template<class T> | |
108 | void CrossThreadRefCounted<T>::ref() | |
109 | { | |
f9bf01c6 A |
110 | ASSERT(isOwnedByCurrentThread()); |
111 | ||
ba379fdc A |
112 | m_refCounter.ref(); |
113 | #ifndef NDEBUG | |
114 | // Store the threadId as soon as the ref count gets to 2. | |
115 | // The class gets created with a ref count of 1 and then passed | |
116 | // to another thread where to ref count get increased. This | |
117 | // is a heuristic but it seems to always work and has helped | |
118 | // find some bugs. | |
119 | if (!m_threadId && m_refCounter.refCount() == 2) | |
120 | m_threadId = currentThread(); | |
121 | #endif | |
122 | } | |
123 | ||
124 | template<class T> | |
125 | void CrossThreadRefCounted<T>::deref() | |
126 | { | |
f9bf01c6 A |
127 | ASSERT(isOwnedByCurrentThread()); |
128 | ||
ba379fdc A |
129 | if (m_refCounter.derefBase()) { |
130 | threadSafeDeref(); | |
131 | delete this; | |
132 | } else { | |
133 | #ifndef NDEBUG | |
134 | // Clear the threadId when the ref goes to 1 because it | |
135 | // is safe to be passed to another thread at this point. | |
136 | if (m_threadId && m_refCounter.refCount() == 1) | |
137 | m_threadId = 0; | |
138 | #endif | |
139 | } | |
140 | } | |
141 | ||
142 | template<class T> | |
143 | T* CrossThreadRefCounted<T>::release() | |
144 | { | |
145 | ASSERT(!isShared()); | |
146 | ||
147 | T* data = m_data; | |
148 | m_data = 0; | |
149 | return data; | |
150 | } | |
151 | ||
152 | template<class T> | |
153 | PassRefPtr<CrossThreadRefCounted<T> > CrossThreadRefCounted<T>::crossThreadCopy() | |
154 | { | |
f9bf01c6 A |
155 | ASSERT(isOwnedByCurrentThread()); |
156 | ||
ba379fdc A |
157 | if (m_threadSafeRefCounter) |
158 | m_threadSafeRefCounter->ref(); | |
159 | else | |
160 | m_threadSafeRefCounter = new ThreadSafeSharedBase(2); | |
f9bf01c6 | 161 | |
ba379fdc A |
162 | return adoptRef(new CrossThreadRefCounted<T>(m_data, m_threadSafeRefCounter)); |
163 | } | |
164 | ||
165 | ||
166 | template<class T> | |
167 | void CrossThreadRefCounted<T>::threadSafeDeref() | |
168 | { | |
169 | if (m_threadSafeRefCounter && m_threadSafeRefCounter->derefBase()) { | |
170 | delete m_threadSafeRefCounter; | |
171 | m_threadSafeRefCounter = 0; | |
172 | } | |
173 | } | |
174 | } // namespace WTF | |
175 | ||
176 | using WTF::CrossThreadRefCounted; | |
177 | ||
178 | #endif // CrossThreadRefCounted_h |