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 39 namespace WTF { 40 41 // Used to allowing sharing data across classes and threads (like ThreadedSafeShared). 42 // 43 // Why not just use ThreadSafeShared? 44 // ThreadSafeShared 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 : public Noncopyable { 55 public: create(T * data)56 static PassRefPtr<CrossThreadRefCounted<T> > create(T* data) 57 { 58 return adoptRef(new CrossThreadRefCounted<T>(data, 0)); 59 } 60 61 // Used to make an instance that can be used on another thread. 62 PassRefPtr<CrossThreadRefCounted<T> > crossThreadCopy(); 63 64 void ref(); 65 void deref(); 66 T* release(); 67 isShared()68 bool isShared() const 69 { 70 return !m_refCounter.hasOneRef() || (m_threadSafeRefCounter && !m_threadSafeRefCounter->hasOneRef()); 71 } 72 73 private: CrossThreadRefCounted(T * data,ThreadSafeSharedBase * threadedCounter)74 CrossThreadRefCounted(T* data, ThreadSafeSharedBase* threadedCounter) 75 : m_threadSafeRefCounter(threadedCounter) 76 , m_data(data) 77 #ifndef NDEBUG 78 , m_threadId(0) 79 #endif 80 { 81 } 82 ~CrossThreadRefCounted()83 ~CrossThreadRefCounted() 84 { 85 if (!m_threadSafeRefCounter) 86 delete m_data; 87 } 88 89 void threadSafeDeref(); 90 91 #ifndef NDEBUG isOwnedByCurrentThread()92 bool isOwnedByCurrentThread() const { return !m_threadId || m_threadId == currentThread(); } 93 #endif 94 95 RefCountedBase m_refCounter; 96 ThreadSafeSharedBase* m_threadSafeRefCounter; 97 T* m_data; 98 #ifndef NDEBUG 99 ThreadIdentifier m_threadId; 100 #endif 101 }; 102 103 template<class T> ref()104 void CrossThreadRefCounted<T>::ref() 105 { 106 ASSERT(isOwnedByCurrentThread()); 107 m_refCounter.ref(); 108 #ifndef NDEBUG 109 // Store the threadId as soon as the ref count gets to 2. 110 // The class gets created with a ref count of 1 and then passed 111 // to another thread where to ref count get increased. This 112 // is a heuristic but it seems to always work and has helped 113 // find some bugs. 114 if (!m_threadId && m_refCounter.refCount() == 2) 115 m_threadId = currentThread(); 116 #endif 117 } 118 119 template<class T> deref()120 void CrossThreadRefCounted<T>::deref() 121 { 122 ASSERT(isOwnedByCurrentThread()); 123 if (m_refCounter.derefBase()) { 124 threadSafeDeref(); 125 delete this; 126 } else { 127 #ifndef NDEBUG 128 // Clear the threadId when the ref goes to 1 because it 129 // is safe to be passed to another thread at this point. 130 if (m_threadId && m_refCounter.refCount() == 1) 131 m_threadId = 0; 132 #endif 133 } 134 } 135 136 template<class T> release()137 T* CrossThreadRefCounted<T>::release() 138 { 139 ASSERT(!isShared()); 140 141 T* data = m_data; 142 m_data = 0; 143 return data; 144 } 145 146 template<class T> crossThreadCopy()147 PassRefPtr<CrossThreadRefCounted<T> > CrossThreadRefCounted<T>::crossThreadCopy() 148 { 149 ASSERT(isOwnedByCurrentThread()); 150 if (m_threadSafeRefCounter) 151 m_threadSafeRefCounter->ref(); 152 else 153 m_threadSafeRefCounter = new ThreadSafeSharedBase(2); 154 155 return adoptRef(new CrossThreadRefCounted<T>(m_data, m_threadSafeRefCounter)); 156 } 157 158 159 template<class T> threadSafeDeref()160 void CrossThreadRefCounted<T>::threadSafeDeref() 161 { 162 if (m_threadSafeRefCounter && m_threadSafeRefCounter->derefBase()) { 163 delete m_threadSafeRefCounter; 164 m_threadSafeRefCounter = 0; 165 } 166 } 167 } // namespace WTF 168 169 using WTF::CrossThreadRefCounted; 170 171 #endif // CrossThreadRefCounted_h 172