Home | History | Annotate | Download | only in wtf
      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:
     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 
     69         bool isShared() const
     70         {
     71             return !m_refCounter.hasOneRef() || (m_threadSafeRefCounter && !m_threadSafeRefCounter->hasOneRef());
     72         }
     73 
     74     private:
     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 
     87         ~CrossThreadRefCounted()
     88         {
     89             if (!m_threadSafeRefCounter)
     90                 delete m_data;
     91         }
     92 
     93         void threadSafeDeref();
     94 
     95 #ifndef NDEBUG
     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>
    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>
    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>
    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>
    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>
    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