summaryrefslogtreecommitdiffstats
path: root/image/CopyOnWrite.h
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /image/CopyOnWrite.h
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'image/CopyOnWrite.h')
-rw-r--r--image/CopyOnWrite.h250
1 files changed, 250 insertions, 0 deletions
diff --git a/image/CopyOnWrite.h b/image/CopyOnWrite.h
new file mode 100644
index 000000000..21db5c3bc
--- /dev/null
+++ b/image/CopyOnWrite.h
@@ -0,0 +1,250 @@
+/* -*- 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