CopyOnWrite.h 7.9 KB

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