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