LCOV - code coverage report
Current view: top level - image - CopyOnWrite.h (source / functions) Hit Total Coverage
Test: output.info Lines: 40 41 97.6 %
Date: 2017-07-14 16:53:18 Functions: 25 27 92.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /**
       7             :  * CopyOnWrite<T> allows code to safely read from a data structure without
       8             :  * worrying that reentrant code will modify it.
       9             :  */
      10             : 
      11             : #ifndef mozilla_image_CopyOnWrite_h
      12             : #define mozilla_image_CopyOnWrite_h
      13             : 
      14             : #include "mozilla/RefPtr.h"
      15             : #include "MainThreadUtils.h"
      16             : #include "nsISupportsImpl.h"
      17             : 
      18             : namespace mozilla {
      19             : namespace image {
      20             : 
      21             : ///////////////////////////////////////////////////////////////////////////////
      22             : // Implementation Details
      23             : ///////////////////////////////////////////////////////////////////////////////
      24             : 
      25             : namespace detail {
      26             : 
      27             : template <typename T>
      28             : class CopyOnWriteValue final
      29             : {
      30             : public:
      31        1319 :   NS_INLINE_DECL_REFCOUNTING(CopyOnWriteValue)
      32             : 
      33          41 :   explicit CopyOnWriteValue(T* aValue) : mValue(aValue) { }
      34             :   explicit CopyOnWriteValue(already_AddRefed<T>& aValue) : mValue(aValue) { }
      35             :   explicit CopyOnWriteValue(already_AddRefed<T>&& aValue) : mValue(aValue) { }
      36             :   explicit CopyOnWriteValue(const RefPtr<T>& aValue) : mValue(aValue) { }
      37             :   explicit CopyOnWriteValue(RefPtr<T>&& aValue) : mValue(aValue) { }
      38             : 
      39         638 :   T* get() { return mValue.get(); }
      40             :   const T* get() const { return mValue.get(); }
      41             : 
      42         833 :   bool HasReaders() const { return mReaders > 0; }
      43        1471 :   bool HasWriter() const { return mWriter; }
      44         195 :   bool HasUsers() const { return HasReaders() || HasWriter(); }
      45             : 
      46         443 :   void LockForReading() { MOZ_ASSERT(!HasWriter()); mReaders++; }
      47         443 :   void UnlockForReading() { MOZ_ASSERT(HasReaders()); mReaders--; }
      48             : 
      49             :   struct MOZ_STACK_CLASS AutoReadLock
      50             :   {
      51         443 :     explicit AutoReadLock(CopyOnWriteValue* aValue)
      52         443 :       : mValue(aValue)
      53             :     {
      54         443 :       mValue->LockForReading();
      55         443 :     }
      56         443 :     ~AutoReadLock() { mValue->UnlockForReading(); }
      57             :     CopyOnWriteValue<T>* mValue;
      58             :   };
      59             : 
      60         195 :   void LockForWriting() { MOZ_ASSERT(!HasUsers()); mWriter = true; }
      61         195 :   void UnlockForWriting() { MOZ_ASSERT(HasWriter()); mWriter = false; }
      62             : 
      63             :   struct MOZ_STACK_CLASS AutoWriteLock
      64             :   {
      65         195 :     explicit AutoWriteLock(CopyOnWriteValue* aValue)
      66         195 :       : mValue(aValue)
      67             :     {
      68         195 :       mValue->LockForWriting();
      69         195 :     }
      70         195 :     ~AutoWriteLock() { mValue->UnlockForWriting(); }
      71             :     CopyOnWriteValue<T>* mValue;
      72             :   };
      73             : 
      74             : private:
      75             :   CopyOnWriteValue(const CopyOnWriteValue&) = delete;
      76             :   CopyOnWriteValue(CopyOnWriteValue&&) = delete;
      77             : 
      78           1 :   ~CopyOnWriteValue() { }
      79             : 
      80             :   RefPtr<T> mValue;
      81             :   uint64_t mReaders = 0;
      82             :   bool mWriter = false;
      83             : };
      84             : 
      85             : } // namespace detail
      86             : 
      87             : 
      88             : ///////////////////////////////////////////////////////////////////////////////
      89             : // Public API
      90             : ///////////////////////////////////////////////////////////////////////////////
      91             : 
      92             : /**
      93             :  * CopyOnWrite<T> allows code to safely read from a data structure without
      94             :  * worrying that reentrant code will modify it. If reentrant code would modify
      95             :  * the data structure while other code is reading from it, a copy is made so
      96             :  * that readers can continue to use the old version.
      97             :  *
      98             :  * Note that it's legal to nest a writer inside any number of readers, but
      99             :  * nothing can be nested inside a writer. This is because it's assumed that the
     100             :  * state of the contained data structure may not be consistent during the write.
     101             :  *
     102             :  * This is a main-thread-only data structure.
     103             :  *
     104             :  * To work with CopyOnWrite<T>, a type T needs to be reference counted and to
     105             :  * support copy construction.
     106             :  */
     107             : template <typename T>
     108           1 : class CopyOnWrite final
     109             : {
     110             :   typedef detail::CopyOnWriteValue<T> CopyOnWriteValue;
     111             : 
     112             : public:
     113          41 :   explicit CopyOnWrite(T* aValue)
     114          41 :   : mValue(new CopyOnWriteValue(aValue))
     115          41 :   { }
     116             : 
     117             :   explicit CopyOnWrite(already_AddRefed<T>& aValue)
     118             :     : mValue(new CopyOnWriteValue(aValue))
     119             :   { }
     120             : 
     121             :   explicit CopyOnWrite(already_AddRefed<T>&& aValue)
     122             :     : mValue(new CopyOnWriteValue(aValue))
     123             :   { }
     124             : 
     125             :   explicit CopyOnWrite(const RefPtr<T>& aValue)
     126             :     : mValue(new CopyOnWriteValue(aValue))
     127             :   { }
     128             : 
     129             :   explicit CopyOnWrite(RefPtr<T>&& aValue)
     130             :     : mValue(new CopyOnWriteValue(aValue))
     131             :   { }
     132             : 
     133             :   /// @return true if it's safe to read at this time.
     134         443 :   bool CanRead() const { return !mValue->HasWriter(); }
     135             : 
     136             :   /**
     137             :    * Read from the contained data structure using the function @aReader.
     138             :    * @aReader will be passed a pointer of type |const T*|. It's not legal to
     139             :    * call this while a writer is active.
     140             :    *
     141             :    * @return whatever value @aReader returns, or nothing if @aReader is a void
     142             :    *         function.
     143             :    */
     144             :   template <typename ReadFunc>
     145         443 :   auto Read(ReadFunc aReader) const
     146             :     -> decltype(aReader(static_cast<const T*>(nullptr)))
     147             :   {
     148         443 :     MOZ_ASSERT(NS_IsMainThread());
     149         443 :     MOZ_ASSERT(CanRead());
     150             : 
     151             :     // Run the provided function while holding a read lock.
     152         886 :     RefPtr<CopyOnWriteValue> cowValue = mValue;
     153         886 :     typename CopyOnWriteValue::AutoReadLock lock(cowValue);
     154         886 :     return aReader(cowValue->get());
     155             :   }
     156             : 
     157             :   /**
     158             :    * Read from the contained data structure using the function @aReader.
     159             :    * @aReader will be passed a pointer of type |const T*|. If it's currently not
     160             :    * possible to read because a writer is currently active, @aOnError will be
     161             :    * called instead.
     162             :    *
     163             :    * @return whatever value @aReader or @aOnError returns (their return types
     164             :    *         must be consistent), or nothing if the provided functions are void.
     165             :    */
     166             :   template <typename ReadFunc, typename ErrorFunc>
     167             :   auto Read(ReadFunc aReader, ErrorFunc aOnError) const
     168             :     -> decltype(aReader(static_cast<const T*>(nullptr)))
     169             :   {
     170             :     MOZ_ASSERT(NS_IsMainThread());
     171             : 
     172             :     if (!CanRead()) {
     173             :       return aOnError();
     174             :     }
     175             : 
     176             :     return Read(aReader);
     177             :   }
     178             : 
     179             :   /// @return true if it's safe to write at this time.
     180         195 :   bool CanWrite() const { return !mValue->HasWriter(); }
     181             : 
     182             :   /**
     183             :    * Write to the contained data structure using the function @aWriter.
     184             :    * @aWriter will be passed a pointer of type |T*|. It's not legal to call this
     185             :    * while another writer is active.
     186             :    *
     187             :    * If readers are currently active, they will be able to continue reading from
     188             :    * a copy of the old version of the data structure. The copy will be destroyed
     189             :    * when all its readers finish.  Later readers and writers will see the
     190             :    * version of the data structure produced by the most recent call to Write().
     191             :    *
     192             :    * @return whatever value @aWriter returns, or nothing if @aWriter is a void
     193             :    *         function.
     194             :    */
     195             :   template <typename WriteFunc>
     196         195 :   auto Write(WriteFunc aWriter)
     197             :     -> decltype(aWriter(static_cast<T*>(nullptr)))
     198             :   {
     199         195 :     MOZ_ASSERT(NS_IsMainThread());
     200         195 :     MOZ_ASSERT(CanWrite());
     201             : 
     202             :     // If there are readers, we need to copy first.
     203         195 :     if (mValue->HasReaders()) {
     204           0 :       mValue = new CopyOnWriteValue(new T(*mValue->get()));
     205             :     }
     206             : 
     207             :     // Run the provided function while holding a write lock.
     208         390 :     RefPtr<CopyOnWriteValue> cowValue = mValue;
     209         390 :     typename CopyOnWriteValue::AutoWriteLock lock(cowValue);
     210         390 :     return aWriter(cowValue->get());
     211             :   }
     212             : 
     213             :   /**
     214             :    * Write to the contained data structure using the function @aWriter.
     215             :    * @aWriter will be passed a pointer of type |T*|. If it's currently not
     216             :    * possible to write because a writer is currently active, @aOnError will be
     217             :    * called instead.
     218             :    *
     219             :    * If readers are currently active, they will be able to continue reading from
     220             :    * a copy of the old version of the data structure. The copy will be destroyed
     221             :    * when all its readers finish.  Later readers and writers will see the
     222             :    * version of the data structure produced by the most recent call to Write().
     223             :    *
     224             :    * @return whatever value @aWriter or @aOnError returns (their return types
     225             :    *         must be consistent), or nothing if the provided functions are void.
     226             :    */
     227             :   template <typename WriteFunc, typename ErrorFunc>
     228             :   auto Write(WriteFunc aWriter, ErrorFunc aOnError)
     229             :     -> decltype(aWriter(static_cast<T*>(nullptr)))
     230             :   {
     231             :     MOZ_ASSERT(NS_IsMainThread());
     232             : 
     233             :     if (!CanWrite()) {
     234             :       return aOnError();
     235             :     }
     236             : 
     237             :     return Write(aWriter);
     238             :   }
     239             : 
     240             : private:
     241             :   CopyOnWrite(const CopyOnWrite&) = delete;
     242             :   CopyOnWrite(CopyOnWrite&&) = delete;
     243             : 
     244             :   RefPtr<CopyOnWriteValue> mValue;
     245             : };
     246             : 
     247             : } // namespace image
     248             : } // namespace mozilla
     249             : 
     250             : #endif // mozilla_image_CopyOnWrite_h

Generated by: LCOV version 1.13