tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CopyOnWrite.h (8265B)


      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 public:
     30  NS_INLINE_DECL_REFCOUNTING(CopyOnWriteValue)
     31 
     32  explicit CopyOnWriteValue(T* aValue)
     33      : mValue(aValue), mReaders(0), mWriter(false) {}
     34  explicit CopyOnWriteValue(already_AddRefed<T>& aValue)
     35      : mValue(aValue), mReaders(0), mWriter(false) {}
     36  explicit CopyOnWriteValue(already_AddRefed<T>&& aValue)
     37      : mValue(aValue), mReaders(0), mWriter(false) {}
     38  explicit CopyOnWriteValue(const RefPtr<T>& aValue)
     39      : mValue(aValue), mReaders(0), mWriter(false) {}
     40  explicit CopyOnWriteValue(RefPtr<T>&& aValue)
     41      : mValue(aValue), mReaders(0), mWriter(false) {}
     42 
     43  T* get() { return mValue.get(); }
     44  const T* get() const { return mValue.get(); }
     45 
     46  bool HasReaders() const { return mReaders > 0; }
     47  bool HasWriter() const { return mWriter; }
     48  bool HasUsers() const { return HasReaders() || HasWriter(); }
     49 
     50  void LockForReading() {
     51    MOZ_ASSERT(!HasWriter());
     52    mReaders++;
     53  }
     54  void UnlockForReading() {
     55    MOZ_ASSERT(HasReaders());
     56    mReaders--;
     57  }
     58 
     59  struct MOZ_STACK_CLASS AutoReadLock {
     60    explicit AutoReadLock(CopyOnWriteValue* aValue) : mValue(aValue) {
     61      mValue->LockForReading();
     62    }
     63    ~AutoReadLock() { mValue->UnlockForReading(); }
     64    CopyOnWriteValue<T>* mValue;
     65  };
     66 
     67  void LockForWriting() {
     68    MOZ_ASSERT(!HasUsers());
     69    mWriter = true;
     70  }
     71  void UnlockForWriting() {
     72    MOZ_ASSERT(HasWriter());
     73    mWriter = false;
     74  }
     75 
     76  struct MOZ_STACK_CLASS AutoWriteLock {
     77    explicit AutoWriteLock(CopyOnWriteValue* aValue) : mValue(aValue) {
     78      mValue->LockForWriting();
     79    }
     80    ~AutoWriteLock() { mValue->UnlockForWriting(); }
     81    CopyOnWriteValue<T>* mValue;
     82  };
     83 
     84 private:
     85  CopyOnWriteValue(const CopyOnWriteValue&) = delete;
     86  CopyOnWriteValue(CopyOnWriteValue&&) = delete;
     87 
     88  ~CopyOnWriteValue() {}
     89 
     90  RefPtr<T> mValue;
     91  uint64_t mReaders = 0;
     92  bool mWriter = false;
     93 };
     94 
     95 }  // namespace detail
     96 
     97 ///////////////////////////////////////////////////////////////////////////////
     98 // Public API
     99 ///////////////////////////////////////////////////////////////////////////////
    100 
    101 /**
    102 * CopyOnWrite<T> allows code to safely read from a data structure without
    103 * worrying that reentrant code will modify it. If reentrant code would modify
    104 * the data structure while other code is reading from it, a copy is made so
    105 * that readers can continue to use the old version.
    106 *
    107 * Note that it's legal to nest a writer inside any number of readers, but
    108 * nothing can be nested inside a writer. This is because it's assumed that the
    109 * state of the contained data structure may not be consistent during the write.
    110 *
    111 * This is a main-thread-only data structure.
    112 *
    113 * To work with CopyOnWrite<T>, a type T needs to be reference counted and to
    114 * support copy construction.
    115 */
    116 template <typename T>
    117 class CopyOnWrite final {
    118  typedef detail::CopyOnWriteValue<T> CopyOnWriteValue;
    119 
    120 public:
    121  explicit CopyOnWrite(T* aValue) : mValue(new CopyOnWriteValue(aValue)) {}
    122 
    123  explicit CopyOnWrite(already_AddRefed<T>& aValue)
    124      : mValue(new CopyOnWriteValue(aValue)) {}
    125 
    126  explicit CopyOnWrite(already_AddRefed<T>&& aValue)
    127      : mValue(new CopyOnWriteValue(aValue)) {}
    128 
    129  explicit CopyOnWrite(const RefPtr<T>& aValue)
    130      : mValue(new CopyOnWriteValue(aValue)) {}
    131 
    132  explicit CopyOnWrite(RefPtr<T>&& aValue)
    133      : mValue(new CopyOnWriteValue(aValue)) {}
    134 
    135  /// @return true if it's safe to read at this time.
    136  bool CanRead() const { return !mValue->HasWriter(); }
    137 
    138  /**
    139   * Read from the contained data structure using the function @aReader.
    140   * @aReader will be passed a pointer of type |const T*|. It's not legal to
    141   * call this while a writer is active.
    142   *
    143   * @return whatever value @aReader returns, or nothing if @aReader is a void
    144   *         function.
    145   */
    146  template <typename ReadFunc>
    147  auto Read(ReadFunc aReader) const
    148      -> decltype(aReader(static_cast<const T*>(nullptr))) {
    149    MOZ_ASSERT(NS_IsMainThread());
    150    MOZ_ASSERT(CanRead());
    151 
    152    // Run the provided function while holding a read lock.
    153    RefPtr<CopyOnWriteValue> cowValue = mValue;
    154    typename CopyOnWriteValue::AutoReadLock lock(cowValue);
    155    return aReader(cowValue->get());
    156  }
    157 
    158  /**
    159   * Read from the contained data structure using the function @aReader.
    160   * @aReader will be passed a pointer of type |const T*|. If it's currently not
    161   * possible to read because a writer is currently active, @aOnError will be
    162   * called instead.
    163   *
    164   * @return whatever value @aReader or @aOnError returns (their return types
    165   *         must be consistent), or nothing if the provided functions are void.
    166   */
    167  template <typename ReadFunc, typename ErrorFunc>
    168  auto Read(ReadFunc aReader, ErrorFunc aOnError) const
    169      -> decltype(aReader(static_cast<const T*>(nullptr))) {
    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  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  auto Write(WriteFunc aWriter) -> decltype(aWriter(static_cast<T*>(nullptr))) {
    197    MOZ_ASSERT(NS_IsMainThread());
    198    MOZ_ASSERT(CanWrite());
    199 
    200    // If there are readers, we need to copy first.
    201    if (mValue->HasReaders()) {
    202      mValue = new CopyOnWriteValue(new T(*mValue->get()));
    203    }
    204 
    205    // Run the provided function while holding a write lock.
    206    RefPtr<CopyOnWriteValue> cowValue = mValue;
    207    typename CopyOnWriteValue::AutoWriteLock lock(cowValue);
    208    return aWriter(cowValue->get());
    209  }
    210 
    211  /**
    212   * Write to the contained data structure using the function @aWriter.
    213   * @aWriter will be passed a pointer of type |T*|. If it's currently not
    214   * possible to write because a writer is currently active, @aOnError will be
    215   * called instead.
    216   *
    217   * If readers are currently active, they will be able to continue reading from
    218   * a copy of the old version of the data structure. The copy will be destroyed
    219   * when all its readers finish.  Later readers and writers will see the
    220   * version of the data structure produced by the most recent call to Write().
    221   *
    222   * @return whatever value @aWriter or @aOnError returns (their return types
    223   *         must be consistent), or nothing if the provided functions are void.
    224   */
    225  template <typename WriteFunc, typename ErrorFunc>
    226  auto Write(WriteFunc aWriter, ErrorFunc aOnError)
    227      -> decltype(aWriter(static_cast<T*>(nullptr))) {
    228    MOZ_ASSERT(NS_IsMainThread());
    229 
    230    if (!CanWrite()) {
    231      return aOnError();
    232    }
    233 
    234    return Write(aWriter);
    235  }
    236 
    237 private:
    238  CopyOnWrite(const CopyOnWrite&) = delete;
    239  CopyOnWrite(CopyOnWrite&&) = delete;
    240 
    241  RefPtr<CopyOnWriteValue> mValue;
    242 };
    243 
    244 }  // namespace image
    245 }  // namespace mozilla
    246 
    247 #endif  // mozilla_image_CopyOnWrite_h