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:
NS_INLINE_DECL_REFCOUNTING(CopyOnWriteValue)30   NS_INLINE_DECL_REFCOUNTING(CopyOnWriteValue)
31 
32   explicit CopyOnWriteValue(T* aValue)
33       : mValue(aValue), mReaders(0), mWriter(false) {}
CopyOnWriteValue(already_AddRefed<T> & aValue)34   explicit CopyOnWriteValue(already_AddRefed<T>& aValue)
35       : mValue(aValue), mReaders(0), mWriter(false) {}
CopyOnWriteValue(already_AddRefed<T> && aValue)36   explicit CopyOnWriteValue(already_AddRefed<T>&& aValue)
37       : mValue(aValue), mReaders(0), mWriter(false) {}
CopyOnWriteValue(const RefPtr<T> & aValue)38   explicit CopyOnWriteValue(const RefPtr<T>& aValue)
39       : mValue(aValue), mReaders(0), mWriter(false) {}
CopyOnWriteValue(RefPtr<T> && aValue)40   explicit CopyOnWriteValue(RefPtr<T>&& aValue)
41       : mValue(aValue), mReaders(0), mWriter(false) {}
42 
get()43   T* get() { return mValue.get(); }
get()44   const T* get() const { return mValue.get(); }
45 
HasReaders()46   bool HasReaders() const { return mReaders > 0; }
HasWriter()47   bool HasWriter() const { return mWriter; }
HasUsers()48   bool HasUsers() const { return HasReaders() || HasWriter(); }
49 
LockForReading()50   void LockForReading() {
51     MOZ_ASSERT(!HasWriter());
52     mReaders++;
53   }
UnlockForReading()54   void UnlockForReading() {
55     MOZ_ASSERT(HasReaders());
56     mReaders--;
57   }
58 
59   struct MOZ_STACK_CLASS AutoReadLock {
AutoReadLockAutoReadLock60     explicit AutoReadLock(CopyOnWriteValue* aValue) : mValue(aValue) {
61       mValue->LockForReading();
62     }
~AutoReadLockAutoReadLock63     ~AutoReadLock() { mValue->UnlockForReading(); }
64     CopyOnWriteValue<T>* mValue;
65   };
66 
LockForWriting()67   void LockForWriting() {
68     MOZ_ASSERT(!HasUsers());
69     mWriter = true;
70   }
UnlockForWriting()71   void UnlockForWriting() {
72     MOZ_ASSERT(HasWriter());
73     mWriter = false;
74   }
75 
76   struct MOZ_STACK_CLASS AutoWriteLock {
AutoWriteLockAutoWriteLock77     explicit AutoWriteLock(CopyOnWriteValue* aValue) : mValue(aValue) {
78       mValue->LockForWriting();
79     }
~AutoWriteLockAutoWriteLock80     ~AutoWriteLock() { mValue->UnlockForWriting(); }
81     CopyOnWriteValue<T>* mValue;
82   };
83 
84  private:
85   CopyOnWriteValue(const CopyOnWriteValue&) = delete;
86   CopyOnWriteValue(CopyOnWriteValue&&) = delete;
87 
~CopyOnWriteValue()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:
CopyOnWrite(T * aValue)121   explicit CopyOnWrite(T* aValue) : mValue(new CopyOnWriteValue(aValue)) {}
122 
CopyOnWrite(already_AddRefed<T> & aValue)123   explicit CopyOnWrite(already_AddRefed<T>& aValue)
124       : mValue(new CopyOnWriteValue(aValue)) {}
125 
CopyOnWrite(already_AddRefed<T> && aValue)126   explicit CopyOnWrite(already_AddRefed<T>&& aValue)
127       : mValue(new CopyOnWriteValue(aValue)) {}
128 
CopyOnWrite(const RefPtr<T> & aValue)129   explicit CopyOnWrite(const RefPtr<T>& aValue)
130       : mValue(new CopyOnWriteValue(aValue)) {}
131 
CopyOnWrite(RefPtr<T> && aValue)132   explicit CopyOnWrite(RefPtr<T>&& aValue)
133       : mValue(new CopyOnWriteValue(aValue)) {}
134 
135   /// @return true if it's safe to read at this time.
CanRead()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.
CanWrite()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
248