Bug 1578501 [wpt PR 18803] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / image / CopyOnWrite.h
blobaf134d581a097cedbb0b83334b412fba19c49004
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/. */
6 /**
7 * CopyOnWrite<T> allows code to safely read from a data structure without
8 * worrying that reentrant code will modify it.
9 */
11 #ifndef mozilla_image_CopyOnWrite_h
12 #define mozilla_image_CopyOnWrite_h
14 #include "mozilla/RefPtr.h"
15 #include "MainThreadUtils.h"
16 #include "nsISupportsImpl.h"
18 namespace mozilla {
19 namespace image {
21 ///////////////////////////////////////////////////////////////////////////////
22 // Implementation Details
23 ///////////////////////////////////////////////////////////////////////////////
25 namespace detail {
27 template <typename T>
28 class CopyOnWriteValue final {
29 public:
30 NS_INLINE_DECL_REFCOUNTING(CopyOnWriteValue)
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) {}
43 T* get() { return mValue.get(); }
44 const T* get() const { return mValue.get(); }
46 bool HasReaders() const { return mReaders > 0; }
47 bool HasWriter() const { return mWriter; }
48 bool HasUsers() const { return HasReaders() || HasWriter(); }
50 void LockForReading() {
51 MOZ_ASSERT(!HasWriter());
52 mReaders++;
54 void UnlockForReading() {
55 MOZ_ASSERT(HasReaders());
56 mReaders--;
59 struct MOZ_STACK_CLASS AutoReadLock {
60 explicit AutoReadLock(CopyOnWriteValue* aValue) : mValue(aValue) {
61 mValue->LockForReading();
63 ~AutoReadLock() { mValue->UnlockForReading(); }
64 CopyOnWriteValue<T>* mValue;
67 void LockForWriting() {
68 MOZ_ASSERT(!HasUsers());
69 mWriter = true;
71 void UnlockForWriting() {
72 MOZ_ASSERT(HasWriter());
73 mWriter = false;
76 struct MOZ_STACK_CLASS AutoWriteLock {
77 explicit AutoWriteLock(CopyOnWriteValue* aValue) : mValue(aValue) {
78 mValue->LockForWriting();
80 ~AutoWriteLock() { mValue->UnlockForWriting(); }
81 CopyOnWriteValue<T>* mValue;
84 private:
85 CopyOnWriteValue(const CopyOnWriteValue&) = delete;
86 CopyOnWriteValue(CopyOnWriteValue&&) = delete;
88 ~CopyOnWriteValue() {}
90 RefPtr<T> mValue;
91 uint64_t mReaders = 0;
92 bool mWriter = false;
95 } // namespace detail
97 ///////////////////////////////////////////////////////////////////////////////
98 // Public API
99 ///////////////////////////////////////////////////////////////////////////////
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.
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.
111 * This is a main-thread-only data structure.
113 * To work with CopyOnWrite<T>, a type T needs to be reference counted and to
114 * support copy construction.
116 template <typename T>
117 class CopyOnWrite final {
118 typedef detail::CopyOnWriteValue<T> CopyOnWriteValue;
120 public:
121 explicit CopyOnWrite(T* aValue) : mValue(new CopyOnWriteValue(aValue)) {}
123 explicit CopyOnWrite(already_AddRefed<T>& aValue)
124 : mValue(new CopyOnWriteValue(aValue)) {}
126 explicit CopyOnWrite(already_AddRefed<T>&& aValue)
127 : mValue(new CopyOnWriteValue(aValue)) {}
129 explicit CopyOnWrite(const RefPtr<T>& aValue)
130 : mValue(new CopyOnWriteValue(aValue)) {}
132 explicit CopyOnWrite(RefPtr<T>&& aValue)
133 : mValue(new CopyOnWriteValue(aValue)) {}
135 /// @return true if it's safe to read at this time.
136 bool CanRead() const { return !mValue->HasWriter(); }
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.
143 * @return whatever value @aReader returns, or nothing if @aReader is a void
144 * function.
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());
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());
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.
164 * @return whatever value @aReader or @aOnError returns (their return types
165 * must be consistent), or nothing if the provided functions are void.
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());
172 if (!CanRead()) {
173 return aOnError();
176 return Read(aReader);
179 /// @return true if it's safe to write at this time.
180 bool CanWrite() const { return !mValue->HasWriter(); }
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.
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().
192 * @return whatever value @aWriter returns, or nothing if @aWriter is a void
193 * function.
195 template <typename WriteFunc>
196 auto Write(WriteFunc aWriter) -> decltype(aWriter(static_cast<T*>(nullptr))) {
197 MOZ_ASSERT(NS_IsMainThread());
198 MOZ_ASSERT(CanWrite());
200 // If there are readers, we need to copy first.
201 if (mValue->HasReaders()) {
202 mValue = new CopyOnWriteValue(new T(*mValue->get()));
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());
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.
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().
222 * @return whatever value @aWriter or @aOnError returns (their return types
223 * must be consistent), or nothing if the provided functions are void.
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());
230 if (!CanWrite()) {
231 return aOnError();
234 return Write(aWriter);
237 private:
238 CopyOnWrite(const CopyOnWrite&) = delete;
239 CopyOnWrite(CopyOnWrite&&) = delete;
241 RefPtr<CopyOnWriteValue> mValue;
244 } // namespace image
245 } // namespace mozilla
247 #endif // mozilla_image_CopyOnWrite_h