/src/mozilla-central/image/CopyOnWrite.h
Line | Count | Source (jump to first uncovered line) |
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 | | { |
30 | | public: |
31 | | NS_INLINE_DECL_REFCOUNTING(CopyOnWriteValue) |
32 | | |
33 | | explicit CopyOnWriteValue(T* aValue) |
34 | | : mValue(aValue) |
35 | | , mReaders(0) |
36 | | , mWriter(false) |
37 | 0 | { |
38 | 0 | } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::CopyOnWriteValue(mozilla::image::ObserverTable*) Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::CopyOnWriteValue(Value*) |
39 | | explicit CopyOnWriteValue(already_AddRefed<T>& aValue) |
40 | | : mValue(aValue) |
41 | | , mReaders(0) |
42 | | , mWriter(false) |
43 | | { |
44 | | } |
45 | | explicit CopyOnWriteValue(already_AddRefed<T>&& aValue) |
46 | | : mValue(aValue) |
47 | | , mReaders(0) |
48 | | , mWriter(false) |
49 | | { |
50 | | } |
51 | | explicit CopyOnWriteValue(const RefPtr<T>& aValue) |
52 | | : mValue(aValue) |
53 | | , mReaders(0) |
54 | | , mWriter(false) |
55 | | { |
56 | | } |
57 | | explicit CopyOnWriteValue(RefPtr<T>&& aValue) |
58 | | : mValue(aValue) |
59 | | , mReaders(0) |
60 | | , mWriter(false) |
61 | | { |
62 | | } |
63 | | |
64 | 0 | T* get() { return mValue.get(); } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::get() Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::get() |
65 | | const T* get() const { return mValue.get(); } |
66 | | |
67 | 0 | bool HasReaders() const { return mReaders > 0; } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::HasReaders() const Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::HasReaders() const |
68 | 0 | bool HasWriter() const { return mWriter; } |
69 | | bool HasUsers() const { return HasReaders() || HasWriter(); } |
70 | | |
71 | 0 | void LockForReading() { MOZ_ASSERT(!HasWriter()); mReaders++; } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::LockForReading() Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::LockForReading() |
72 | 0 | void UnlockForReading() { MOZ_ASSERT(HasReaders()); mReaders--; } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::UnlockForReading() Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::UnlockForReading() |
73 | | |
74 | | struct MOZ_STACK_CLASS AutoReadLock |
75 | | { |
76 | | explicit AutoReadLock(CopyOnWriteValue* aValue) |
77 | | : mValue(aValue) |
78 | 0 | { |
79 | 0 | mValue->LockForReading(); |
80 | 0 | } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::AutoReadLock::AutoReadLock(mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>*) Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::AutoReadLock::AutoReadLock(mozilla::image::detail::CopyOnWriteValue<Value>*) |
81 | 0 | ~AutoReadLock() { mValue->UnlockForReading(); } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::AutoReadLock::~AutoReadLock() Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::AutoReadLock::~AutoReadLock() |
82 | | CopyOnWriteValue<T>* mValue; |
83 | | }; |
84 | | |
85 | 0 | void LockForWriting() { MOZ_ASSERT(!HasUsers()); mWriter = true; } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::LockForWriting() Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::LockForWriting() |
86 | 0 | void UnlockForWriting() { MOZ_ASSERT(HasWriter()); mWriter = false; } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::UnlockForWriting() Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::UnlockForWriting() |
87 | | |
88 | | struct MOZ_STACK_CLASS AutoWriteLock |
89 | | { |
90 | | explicit AutoWriteLock(CopyOnWriteValue* aValue) |
91 | | : mValue(aValue) |
92 | 0 | { |
93 | 0 | mValue->LockForWriting(); |
94 | 0 | } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::AutoWriteLock::AutoWriteLock(mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>*) Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::AutoWriteLock::AutoWriteLock(mozilla::image::detail::CopyOnWriteValue<Value>*) |
95 | 0 | ~AutoWriteLock() { mValue->UnlockForWriting(); } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::AutoWriteLock::~AutoWriteLock() Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::AutoWriteLock::~AutoWriteLock() |
96 | | CopyOnWriteValue<T>* mValue; |
97 | | }; |
98 | | |
99 | | private: |
100 | | CopyOnWriteValue(const CopyOnWriteValue&) = delete; |
101 | | CopyOnWriteValue(CopyOnWriteValue&&) = delete; |
102 | | |
103 | 0 | ~CopyOnWriteValue() { } Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::~CopyOnWriteValue() Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::~CopyOnWriteValue() |
104 | | |
105 | | RefPtr<T> mValue; |
106 | | uint64_t mReaders = 0; |
107 | | bool mWriter = false; |
108 | | }; |
109 | | |
110 | | } // namespace detail |
111 | | |
112 | | |
113 | | /////////////////////////////////////////////////////////////////////////////// |
114 | | // Public API |
115 | | /////////////////////////////////////////////////////////////////////////////// |
116 | | |
117 | | /** |
118 | | * CopyOnWrite<T> allows code to safely read from a data structure without |
119 | | * worrying that reentrant code will modify it. If reentrant code would modify |
120 | | * the data structure while other code is reading from it, a copy is made so |
121 | | * that readers can continue to use the old version. |
122 | | * |
123 | | * Note that it's legal to nest a writer inside any number of readers, but |
124 | | * nothing can be nested inside a writer. This is because it's assumed that the |
125 | | * state of the contained data structure may not be consistent during the write. |
126 | | * |
127 | | * This is a main-thread-only data structure. |
128 | | * |
129 | | * To work with CopyOnWrite<T>, a type T needs to be reference counted and to |
130 | | * support copy construction. |
131 | | */ |
132 | | template <typename T> |
133 | | class CopyOnWrite final |
134 | | { |
135 | | typedef detail::CopyOnWriteValue<T> CopyOnWriteValue; |
136 | | |
137 | | public: |
138 | | explicit CopyOnWrite(T* aValue) |
139 | | : mValue(new CopyOnWriteValue(aValue)) |
140 | 0 | { } Unexecuted instantiation: mozilla::image::CopyOnWrite<mozilla::image::ObserverTable>::CopyOnWrite(mozilla::image::ObserverTable*) Unexecuted instantiation: mozilla::image::CopyOnWrite<Value>::CopyOnWrite(Value*) |
141 | | |
142 | | explicit CopyOnWrite(already_AddRefed<T>& aValue) |
143 | | : mValue(new CopyOnWriteValue(aValue)) |
144 | | { } |
145 | | |
146 | | explicit CopyOnWrite(already_AddRefed<T>&& aValue) |
147 | | : mValue(new CopyOnWriteValue(aValue)) |
148 | | { } |
149 | | |
150 | | explicit CopyOnWrite(const RefPtr<T>& aValue) |
151 | | : mValue(new CopyOnWriteValue(aValue)) |
152 | | { } |
153 | | |
154 | | explicit CopyOnWrite(RefPtr<T>&& aValue) |
155 | | : mValue(new CopyOnWriteValue(aValue)) |
156 | | { } |
157 | | |
158 | | /// @return true if it's safe to read at this time. |
159 | 0 | bool CanRead() const { return !mValue->HasWriter(); } |
160 | | |
161 | | /** |
162 | | * Read from the contained data structure using the function @aReader. |
163 | | * @aReader will be passed a pointer of type |const T*|. It's not legal to |
164 | | * call this while a writer is active. |
165 | | * |
166 | | * @return whatever value @aReader returns, or nothing if @aReader is a void |
167 | | * function. |
168 | | */ |
169 | | template <typename ReadFunc> |
170 | | auto Read(ReadFunc aReader) const |
171 | | -> decltype(aReader(static_cast<const T*>(nullptr))) |
172 | 0 | { |
173 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
174 | 0 | MOZ_ASSERT(CanRead()); |
175 | 0 |
|
176 | 0 | // Run the provided function while holding a read lock. |
177 | 0 | RefPtr<CopyOnWriteValue> cowValue = mValue; |
178 | 0 | typename CopyOnWriteValue::AutoReadLock lock(cowValue); |
179 | 0 | return aReader(cowValue->get()); |
180 | 0 | } Unexecuted instantiation: Unified_cpp_image1.cpp:_ZNK7mozilla5image11CopyOnWriteINS0_13ObserverTableEE4ReadIZNS0_15ProgressTracker18SyncNotifyProgressEjRKNS_3gfx12IntRectTypedINS6_12UnknownUnitsEEEE3$_1EEDTclfp_scPKS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image1.cpp:_ZNK7mozilla5image11CopyOnWriteINS0_13ObserverTableEE4ReadIZNKS0_15ProgressTracker13ObserverCountEvE3$_4EEDTclfp_scPKS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image1.cpp:_ZNK7mozilla5image11CopyOnWriteINS0_13ObserverTableEE4ReadIZNS0_15ProgressTracker14OnUnlockedDrawEvE3$_5EEDTclfp_scPKS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image1.cpp:_ZNK7mozilla5image11CopyOnWriteINS0_13ObserverTableEE4ReadIZNS0_15ProgressTracker9OnDiscardEvE3$_6EEDTclfp_scPKS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image1.cpp:_ZNK7mozilla5image11CopyOnWriteINS0_13ObserverTableEE4ReadIZNS0_15ProgressTracker16OnImageAvailableEvE3$_7EEDTclfp_scPKS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZN26ImageCopyOnWrite_Read_Test8TestBodyEvE4$_29EEDTclfp_scPKS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZN35ImageCopyOnWrite_RecursiveRead_Test8TestBodyEvE4$_30EEDTclfp_scPKS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZZN35ImageCopyOnWrite_RecursiveRead_Test8TestBodyEvENK4$_30clEPKS2_EUlS8_E_EEDTclfp_scS8_LDnEEET_ Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvE4$_32EEDTclfp_scPKS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_ENKUlPS2_E_clES9_EUlS8_E_EEDTclfp_scS8_LDnEEET_ |
181 | | |
182 | | /** |
183 | | * Read from the contained data structure using the function @aReader. |
184 | | * @aReader will be passed a pointer of type |const T*|. If it's currently not |
185 | | * possible to read because a writer is currently active, @aOnError will be |
186 | | * called instead. |
187 | | * |
188 | | * @return whatever value @aReader or @aOnError returns (their return types |
189 | | * must be consistent), or nothing if the provided functions are void. |
190 | | */ |
191 | | template <typename ReadFunc, typename ErrorFunc> |
192 | | auto Read(ReadFunc aReader, ErrorFunc aOnError) const |
193 | | -> decltype(aReader(static_cast<const T*>(nullptr))) |
194 | 0 | { |
195 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
196 | 0 |
|
197 | 0 | if (!CanRead()) { |
198 | 0 | return aOnError(); |
199 | 0 | } |
200 | 0 | |
201 | 0 | return Read(aReader); |
202 | 0 | } Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZZN35ImageCopyOnWrite_RecursiveRead_Test8TestBodyEvENK4$_30clEPKS2_EUlS8_E_ZZNS5_8TestBodyEvENKS6_clES8_EUlvE_EEDTclfp_scS8_LDnEEET_T0_ Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_ENKUlPS2_E_clES9_EUlS8_E_ZZZNS5_8TestBodyEvENKS6_clES8_ENKSA_clES9_EUlvE_EEDTclfp_scS8_LDnEEET_T0_ |
203 | | |
204 | | /// @return true if it's safe to write at this time. |
205 | 0 | bool CanWrite() const { return !mValue->HasWriter(); } |
206 | | |
207 | | /** |
208 | | * Write to the contained data structure using the function @aWriter. |
209 | | * @aWriter will be passed a pointer of type |T*|. It's not legal to call this |
210 | | * while another writer is active. |
211 | | * |
212 | | * If readers are currently active, they will be able to continue reading from |
213 | | * a copy of the old version of the data structure. The copy will be destroyed |
214 | | * when all its readers finish. Later readers and writers will see the |
215 | | * version of the data structure produced by the most recent call to Write(). |
216 | | * |
217 | | * @return whatever value @aWriter returns, or nothing if @aWriter is a void |
218 | | * function. |
219 | | */ |
220 | | template <typename WriteFunc> |
221 | | auto Write(WriteFunc aWriter) |
222 | | -> decltype(aWriter(static_cast<T*>(nullptr))) |
223 | 0 | { |
224 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
225 | 0 | MOZ_ASSERT(CanWrite()); |
226 | 0 |
|
227 | 0 | // If there are readers, we need to copy first. |
228 | 0 | if (mValue->HasReaders()) { |
229 | 0 | mValue = new CopyOnWriteValue(new T(*mValue->get())); |
230 | 0 | } |
231 | 0 |
|
232 | 0 | // Run the provided function while holding a write lock. |
233 | 0 | RefPtr<CopyOnWriteValue> cowValue = mValue; |
234 | 0 | typename CopyOnWriteValue::AutoWriteLock lock(cowValue); |
235 | 0 | return aWriter(cowValue->get()); |
236 | 0 | } Unexecuted instantiation: Unified_cpp_image1.cpp:_ZN7mozilla5image11CopyOnWriteINS0_13ObserverTableEE5WriteIZNS0_15ProgressTracker11AddObserverEPNS0_17IProgressObserverEE3$_2EEDTclfp_scPS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image1.cpp:_ZN7mozilla5image11CopyOnWriteINS0_13ObserverTableEE5WriteIZNS0_15ProgressTracker14RemoveObserverEPNS0_17IProgressObserverEE3$_3EEDTclfp_scPS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZN7mozilla5image11CopyOnWriteI5ValueE5WriteIZN27ImageCopyOnWrite_Write_Test8TestBodyEvE4$_31EEDTclfp_scPS2_LDnEEET_ Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZN7mozilla5image11CopyOnWriteI5ValueE5WriteIZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_EUlPS2_E_EEDTclfp_scS9_LDnEEET_ Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZN7mozilla5image11CopyOnWriteI5ValueE5WriteIZZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_ENKUlPS2_E_clES9_EUlS9_E_EEDTclfp_scS9_LDnEEET_ |
237 | | |
238 | | /** |
239 | | * Write to the contained data structure using the function @aWriter. |
240 | | * @aWriter will be passed a pointer of type |T*|. If it's currently not |
241 | | * possible to write because a writer is currently active, @aOnError will be |
242 | | * called instead. |
243 | | * |
244 | | * If readers are currently active, they will be able to continue reading from |
245 | | * a copy of the old version of the data structure. The copy will be destroyed |
246 | | * when all its readers finish. Later readers and writers will see the |
247 | | * version of the data structure produced by the most recent call to Write(). |
248 | | * |
249 | | * @return whatever value @aWriter or @aOnError returns (their return types |
250 | | * must be consistent), or nothing if the provided functions are void. |
251 | | */ |
252 | | template <typename WriteFunc, typename ErrorFunc> |
253 | | auto Write(WriteFunc aWriter, ErrorFunc aOnError) |
254 | | -> decltype(aWriter(static_cast<T*>(nullptr))) |
255 | 0 | { |
256 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
257 | 0 |
|
258 | 0 | if (!CanWrite()) { |
259 | 0 | return aOnError(); |
260 | 0 | } |
261 | 0 | |
262 | 0 | return Write(aWriter); |
263 | 0 | } Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZN7mozilla5image11CopyOnWriteI5ValueE5WriteIZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_EUlPS2_E_ZZNS5_8TestBodyEvENKS6_clES8_EUlvE_EEDTclfp_scS9_LDnEEET_T0_ Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZN7mozilla5image11CopyOnWriteI5ValueE5WriteIZZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_ENKUlPS2_E_clES9_EUlS9_E_ZZZNS5_8TestBodyEvENKS6_clES8_ENKSA_clES9_EUlvE0_EEDTclfp_scS9_LDnEEET_T0_ |
264 | | |
265 | | private: |
266 | | CopyOnWrite(const CopyOnWrite&) = delete; |
267 | | CopyOnWrite(CopyOnWrite&&) = delete; |
268 | | |
269 | | RefPtr<CopyOnWriteValue> mValue; |
270 | | }; |
271 | | |
272 | | } // namespace image |
273 | | } // namespace mozilla |
274 | | |
275 | | #endif // mozilla_image_CopyOnWrite_h |