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