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/. */
7 * CopyOnWrite<T> allows code to safely read from a data structure without
8 * worrying that reentrant code will modify it.
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"
21 ///////////////////////////////////////////////////////////////////////////////
22 // Implementation Details
23 ///////////////////////////////////////////////////////////////////////////////
28 class CopyOnWriteValue final
{
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());
54 void UnlockForReading() {
55 MOZ_ASSERT(HasReaders());
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());
71 void UnlockForWriting() {
72 MOZ_ASSERT(HasWriter());
76 struct MOZ_STACK_CLASS AutoWriteLock
{
77 explicit AutoWriteLock(CopyOnWriteValue
* aValue
) : mValue(aValue
) {
78 mValue
->LockForWriting();
80 ~AutoWriteLock() { mValue
->UnlockForWriting(); }
81 CopyOnWriteValue
<T
>* mValue
;
85 CopyOnWriteValue(const CopyOnWriteValue
&) = delete;
86 CopyOnWriteValue(CopyOnWriteValue
&&) = delete;
88 ~CopyOnWriteValue() {}
91 uint64_t mReaders
= 0;
97 ///////////////////////////////////////////////////////////////////////////////
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
;
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
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
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());
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
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
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());
234 return Write(aWriter
);
238 CopyOnWrite(const CopyOnWrite
&) = delete;
239 CopyOnWrite(CopyOnWrite
&&) = delete;
241 RefPtr
<CopyOnWriteValue
> mValue
;
245 } // namespace mozilla
247 #endif // mozilla_image_CopyOnWrite_h