123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- /**
- * CopyOnWrite<T> allows code to safely read from a data structure without
- * worrying that reentrant code will modify it.
- */
- #ifndef mozilla_image_CopyOnWrite_h
- #define mozilla_image_CopyOnWrite_h
- #include "mozilla/RefPtr.h"
- #include "MainThreadUtils.h"
- #include "nsISupportsImpl.h"
- namespace mozilla {
- namespace image {
- ///////////////////////////////////////////////////////////////////////////////
- // Implementation Details
- ///////////////////////////////////////////////////////////////////////////////
- namespace detail {
- template <typename T>
- class CopyOnWriteValue final
- {
- public:
- NS_INLINE_DECL_REFCOUNTING(CopyOnWriteValue)
- explicit CopyOnWriteValue(T* aValue) : mValue(aValue) { }
- explicit CopyOnWriteValue(already_AddRefed<T>& aValue) : mValue(aValue) { }
- explicit CopyOnWriteValue(already_AddRefed<T>&& aValue) : mValue(aValue) { }
- explicit CopyOnWriteValue(const RefPtr<T>& aValue) : mValue(aValue) { }
- explicit CopyOnWriteValue(RefPtr<T>&& aValue) : mValue(aValue) { }
- T* get() { return mValue.get(); }
- const T* get() const { return mValue.get(); }
- bool HasReaders() const { return mReaders > 0; }
- bool HasWriter() const { return mWriter; }
- bool HasUsers() const { return HasReaders() || HasWriter(); }
- void LockForReading() { MOZ_ASSERT(!HasWriter()); mReaders++; }
- void UnlockForReading() { MOZ_ASSERT(HasReaders()); mReaders--; }
- struct MOZ_STACK_CLASS AutoReadLock
- {
- explicit AutoReadLock(CopyOnWriteValue* aValue)
- : mValue(aValue)
- {
- mValue->LockForReading();
- }
- ~AutoReadLock() { mValue->UnlockForReading(); }
- CopyOnWriteValue<T>* mValue;
- };
- void LockForWriting() { MOZ_ASSERT(!HasUsers()); mWriter = true; }
- void UnlockForWriting() { MOZ_ASSERT(HasWriter()); mWriter = false; }
- struct MOZ_STACK_CLASS AutoWriteLock
- {
- explicit AutoWriteLock(CopyOnWriteValue* aValue)
- : mValue(aValue)
- {
- mValue->LockForWriting();
- }
- ~AutoWriteLock() { mValue->UnlockForWriting(); }
- CopyOnWriteValue<T>* mValue;
- };
- private:
- CopyOnWriteValue(const CopyOnWriteValue&) = delete;
- CopyOnWriteValue(CopyOnWriteValue&&) = delete;
- ~CopyOnWriteValue() { }
- RefPtr<T> mValue;
- uint64_t mReaders = 0;
- bool mWriter = false;
- };
- } // namespace detail
- ///////////////////////////////////////////////////////////////////////////////
- // Public API
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * CopyOnWrite<T> allows code to safely read from a data structure without
- * worrying that reentrant code will modify it. If reentrant code would modify
- * the data structure while other code is reading from it, a copy is made so
- * that readers can continue to use the old version.
- *
- * Note that it's legal to nest a writer inside any number of readers, but
- * nothing can be nested inside a writer. This is because it's assumed that the
- * state of the contained data structure may not be consistent during the write.
- *
- * This is a main-thread-only data structure.
- *
- * To work with CopyOnWrite<T>, a type T needs to be reference counted and to
- * support copy construction.
- */
- template <typename T>
- class CopyOnWrite final
- {
- typedef detail::CopyOnWriteValue<T> CopyOnWriteValue;
- public:
- explicit CopyOnWrite(T* aValue)
- : mValue(new CopyOnWriteValue(aValue))
- { }
- explicit CopyOnWrite(already_AddRefed<T>& aValue)
- : mValue(new CopyOnWriteValue(aValue))
- { }
- explicit CopyOnWrite(already_AddRefed<T>&& aValue)
- : mValue(new CopyOnWriteValue(aValue))
- { }
- explicit CopyOnWrite(const RefPtr<T>& aValue)
- : mValue(new CopyOnWriteValue(aValue))
- { }
- explicit CopyOnWrite(RefPtr<T>&& aValue)
- : mValue(new CopyOnWriteValue(aValue))
- { }
- /// @return true if it's safe to read at this time.
- bool CanRead() const { return !mValue->HasWriter(); }
- /**
- * Read from the contained data structure using the function @aReader.
- * @aReader will be passed a pointer of type |const T*|. It's not legal to
- * call this while a writer is active.
- *
- * @return whatever value @aReader returns, or nothing if @aReader is a void
- * function.
- */
- template <typename ReadFunc>
- auto Read(ReadFunc aReader) const
- -> decltype(aReader(static_cast<const T*>(nullptr)))
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(CanRead());
- // Run the provided function while holding a read lock.
- RefPtr<CopyOnWriteValue> cowValue = mValue;
- typename CopyOnWriteValue::AutoReadLock lock(cowValue);
- return aReader(cowValue->get());
- }
- /**
- * Read from the contained data structure using the function @aReader.
- * @aReader will be passed a pointer of type |const T*|. If it's currently not
- * possible to read because a writer is currently active, @aOnError will be
- * called instead.
- *
- * @return whatever value @aReader or @aOnError returns (their return types
- * must be consistent), or nothing if the provided functions are void.
- */
- template <typename ReadFunc, typename ErrorFunc>
- auto Read(ReadFunc aReader, ErrorFunc aOnError) const
- -> decltype(aReader(static_cast<const T*>(nullptr)))
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (!CanRead()) {
- return aOnError();
- }
- return Read(aReader);
- }
- /// @return true if it's safe to write at this time.
- bool CanWrite() const { return !mValue->HasWriter(); }
- /**
- * Write to the contained data structure using the function @aWriter.
- * @aWriter will be passed a pointer of type |T*|. It's not legal to call this
- * while another writer is active.
- *
- * If readers are currently active, they will be able to continue reading from
- * a copy of the old version of the data structure. The copy will be destroyed
- * when all its readers finish. Later readers and writers will see the
- * version of the data structure produced by the most recent call to Write().
- *
- * @return whatever value @aWriter returns, or nothing if @aWriter is a void
- * function.
- */
- template <typename WriteFunc>
- auto Write(WriteFunc aWriter)
- -> decltype(aWriter(static_cast<T*>(nullptr)))
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(CanWrite());
- // If there are readers, we need to copy first.
- if (mValue->HasReaders()) {
- mValue = new CopyOnWriteValue(new T(*mValue->get()));
- }
- // Run the provided function while holding a write lock.
- RefPtr<CopyOnWriteValue> cowValue = mValue;
- typename CopyOnWriteValue::AutoWriteLock lock(cowValue);
- return aWriter(cowValue->get());
- }
- /**
- * Write to the contained data structure using the function @aWriter.
- * @aWriter will be passed a pointer of type |T*|. If it's currently not
- * possible to write because a writer is currently active, @aOnError will be
- * called instead.
- *
- * If readers are currently active, they will be able to continue reading from
- * a copy of the old version of the data structure. The copy will be destroyed
- * when all its readers finish. Later readers and writers will see the
- * version of the data structure produced by the most recent call to Write().
- *
- * @return whatever value @aWriter or @aOnError returns (their return types
- * must be consistent), or nothing if the provided functions are void.
- */
- template <typename WriteFunc, typename ErrorFunc>
- auto Write(WriteFunc aWriter, ErrorFunc aOnError)
- -> decltype(aWriter(static_cast<T*>(nullptr)))
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (!CanWrite()) {
- return aOnError();
- }
- return Write(aWriter);
- }
- private:
- CopyOnWrite(const CopyOnWrite&) = delete;
- CopyOnWrite(CopyOnWrite&&) = delete;
- RefPtr<CopyOnWriteValue> mValue;
- };
- } // namespace image
- } // namespace mozilla
- #endif // mozilla_image_CopyOnWrite_h
|