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