Line data Source code
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 1319 : NS_INLINE_DECL_REFCOUNTING(CopyOnWriteValue)
32 :
33 41 : explicit CopyOnWriteValue(T* aValue) : mValue(aValue) { }
34 : explicit CopyOnWriteValue(already_AddRefed<T>& aValue) : mValue(aValue) { }
35 : explicit CopyOnWriteValue(already_AddRefed<T>&& aValue) : mValue(aValue) { }
36 : explicit CopyOnWriteValue(const RefPtr<T>& aValue) : mValue(aValue) { }
37 : explicit CopyOnWriteValue(RefPtr<T>&& aValue) : mValue(aValue) { }
38 :
39 638 : T* get() { return mValue.get(); }
40 : const T* get() const { return mValue.get(); }
41 :
42 833 : bool HasReaders() const { return mReaders > 0; }
43 1471 : bool HasWriter() const { return mWriter; }
44 195 : bool HasUsers() const { return HasReaders() || HasWriter(); }
45 :
46 443 : void LockForReading() { MOZ_ASSERT(!HasWriter()); mReaders++; }
47 443 : void UnlockForReading() { MOZ_ASSERT(HasReaders()); mReaders--; }
48 :
49 : struct MOZ_STACK_CLASS AutoReadLock
50 : {
51 443 : explicit AutoReadLock(CopyOnWriteValue* aValue)
52 443 : : mValue(aValue)
53 : {
54 443 : mValue->LockForReading();
55 443 : }
56 443 : ~AutoReadLock() { mValue->UnlockForReading(); }
57 : CopyOnWriteValue<T>* mValue;
58 : };
59 :
60 195 : void LockForWriting() { MOZ_ASSERT(!HasUsers()); mWriter = true; }
61 195 : void UnlockForWriting() { MOZ_ASSERT(HasWriter()); mWriter = false; }
62 :
63 : struct MOZ_STACK_CLASS AutoWriteLock
64 : {
65 195 : explicit AutoWriteLock(CopyOnWriteValue* aValue)
66 195 : : mValue(aValue)
67 : {
68 195 : mValue->LockForWriting();
69 195 : }
70 195 : ~AutoWriteLock() { mValue->UnlockForWriting(); }
71 : CopyOnWriteValue<T>* mValue;
72 : };
73 :
74 : private:
75 : CopyOnWriteValue(const CopyOnWriteValue&) = delete;
76 : CopyOnWriteValue(CopyOnWriteValue&&) = delete;
77 :
78 1 : ~CopyOnWriteValue() { }
79 :
80 : RefPtr<T> mValue;
81 : uint64_t mReaders = 0;
82 : bool mWriter = false;
83 : };
84 :
85 : } // namespace detail
86 :
87 :
88 : ///////////////////////////////////////////////////////////////////////////////
89 : // Public API
90 : ///////////////////////////////////////////////////////////////////////////////
91 :
92 : /**
93 : * CopyOnWrite<T> allows code to safely read from a data structure without
94 : * worrying that reentrant code will modify it. If reentrant code would modify
95 : * the data structure while other code is reading from it, a copy is made so
96 : * that readers can continue to use the old version.
97 : *
98 : * Note that it's legal to nest a writer inside any number of readers, but
99 : * nothing can be nested inside a writer. This is because it's assumed that the
100 : * state of the contained data structure may not be consistent during the write.
101 : *
102 : * This is a main-thread-only data structure.
103 : *
104 : * To work with CopyOnWrite<T>, a type T needs to be reference counted and to
105 : * support copy construction.
106 : */
107 : template <typename T>
108 1 : class CopyOnWrite final
109 : {
110 : typedef detail::CopyOnWriteValue<T> CopyOnWriteValue;
111 :
112 : public:
113 41 : explicit CopyOnWrite(T* aValue)
114 41 : : mValue(new CopyOnWriteValue(aValue))
115 41 : { }
116 :
117 : explicit CopyOnWrite(already_AddRefed<T>& aValue)
118 : : mValue(new CopyOnWriteValue(aValue))
119 : { }
120 :
121 : explicit CopyOnWrite(already_AddRefed<T>&& aValue)
122 : : mValue(new CopyOnWriteValue(aValue))
123 : { }
124 :
125 : explicit CopyOnWrite(const RefPtr<T>& aValue)
126 : : mValue(new CopyOnWriteValue(aValue))
127 : { }
128 :
129 : explicit CopyOnWrite(RefPtr<T>&& aValue)
130 : : mValue(new CopyOnWriteValue(aValue))
131 : { }
132 :
133 : /// @return true if it's safe to read at this time.
134 443 : bool CanRead() const { return !mValue->HasWriter(); }
135 :
136 : /**
137 : * Read from the contained data structure using the function @aReader.
138 : * @aReader will be passed a pointer of type |const T*|. It's not legal to
139 : * call this while a writer is active.
140 : *
141 : * @return whatever value @aReader returns, or nothing if @aReader is a void
142 : * function.
143 : */
144 : template <typename ReadFunc>
145 443 : auto Read(ReadFunc aReader) const
146 : -> decltype(aReader(static_cast<const T*>(nullptr)))
147 : {
148 443 : MOZ_ASSERT(NS_IsMainThread());
149 443 : MOZ_ASSERT(CanRead());
150 :
151 : // Run the provided function while holding a read lock.
152 886 : RefPtr<CopyOnWriteValue> cowValue = mValue;
153 886 : typename CopyOnWriteValue::AutoReadLock lock(cowValue);
154 886 : return aReader(cowValue->get());
155 : }
156 :
157 : /**
158 : * Read from the contained data structure using the function @aReader.
159 : * @aReader will be passed a pointer of type |const T*|. If it's currently not
160 : * possible to read because a writer is currently active, @aOnError will be
161 : * called instead.
162 : *
163 : * @return whatever value @aReader or @aOnError returns (their return types
164 : * must be consistent), or nothing if the provided functions are void.
165 : */
166 : template <typename ReadFunc, typename ErrorFunc>
167 : auto Read(ReadFunc aReader, ErrorFunc aOnError) const
168 : -> decltype(aReader(static_cast<const T*>(nullptr)))
169 : {
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.
180 195 : 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 195 : auto Write(WriteFunc aWriter)
197 : -> decltype(aWriter(static_cast<T*>(nullptr)))
198 : {
199 195 : MOZ_ASSERT(NS_IsMainThread());
200 195 : MOZ_ASSERT(CanWrite());
201 :
202 : // If there are readers, we need to copy first.
203 195 : if (mValue->HasReaders()) {
204 0 : mValue = new CopyOnWriteValue(new T(*mValue->get()));
205 : }
206 :
207 : // Run the provided function while holding a write lock.
208 390 : RefPtr<CopyOnWriteValue> cowValue = mValue;
209 390 : typename CopyOnWriteValue::AutoWriteLock lock(cowValue);
210 390 : return aWriter(cowValue->get());
211 : }
212 :
213 : /**
214 : * Write to the contained data structure using the function @aWriter.
215 : * @aWriter will be passed a pointer of type |T*|. If it's currently not
216 : * possible to write because a writer is currently active, @aOnError will be
217 : * called instead.
218 : *
219 : * If readers are currently active, they will be able to continue reading from
220 : * a copy of the old version of the data structure. The copy will be destroyed
221 : * when all its readers finish. Later readers and writers will see the
222 : * version of the data structure produced by the most recent call to Write().
223 : *
224 : * @return whatever value @aWriter or @aOnError returns (their return types
225 : * must be consistent), or nothing if the provided functions are void.
226 : */
227 : template <typename WriteFunc, typename ErrorFunc>
228 : auto Write(WriteFunc aWriter, ErrorFunc aOnError)
229 : -> decltype(aWriter(static_cast<T*>(nullptr)))
230 : {
231 : MOZ_ASSERT(NS_IsMainThread());
232 :
233 : if (!CanWrite()) {
234 : return aOnError();
235 : }
236 :
237 : return Write(aWriter);
238 : }
239 :
240 : private:
241 : CopyOnWrite(const CopyOnWrite&) = delete;
242 : CopyOnWrite(CopyOnWrite&&) = delete;
243 :
244 : RefPtr<CopyOnWriteValue> mValue;
245 : };
246 :
247 : } // namespace image
248 : } // namespace mozilla
249 :
250 : #endif // mozilla_image_CopyOnWrite_h
|