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