Bug 1426291 - Add Rust style mutex template which wraps data with the mutex that synchronizes it. r=jwwang
authorChris Pearce <cpearce@mozilla.com>
Wed, 20 Dec 2017 16:15:09 +1300
changeset 397033 4ae5f2caa47120e4247d7d09f11fcc4f77357cd8
parent 397032 50dfae23ef9583bff536d60b78aa6bbc8ba84830
child 397034 fb96226aef906b8faa4cde6749ccef3a6870e786
push id33123
push userncsoregi@mozilla.com
push dateThu, 21 Dec 2017 10:00:47 +0000
treeherdermozilla-central@06a19fbe2581 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang
bugs1426291
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1426291 - Add Rust style mutex template which wraps data with the mutex that synchronizes it. r=jwwang Rust's std::sync::Mutex has some nice properties. Its philosphy is to lock data, rather than code. It wraps data you're trying to make thread safe with a mutex, and in order to get a reference to the wrapped data you need to lock the mutex and access it through an intermediate layer. This is good, as the mutex that's protecting access to the data is explicitly associated with the data, and it's impossible to forget to take the lock before accessing the data. This patch adds a similar mutex wrapper to Media Playback code. If it works well, we can look at moving it into xpcom. MozReview-Commit-ID: 4APAic6Fh8m
dom/media/eme/DataMutex.h
dom/media/eme/moz.build
dom/media/gtest/TestDataMutex.cpp
dom/media/gtest/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/media/eme/DataMutex.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef DataMutex_h__
+#define DataMutex_h__
+
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+
+// A template to wrap a type with a mutex so that accesses to the type's
+// data are required to take the lock before accessing it. This ensures
+// that a mutex is explicitly associated with the data that it protects,
+// and makes it impossible to access the data without first taking the
+// associated mutex.
+//
+// This is based on Rust's std::sync::Mutex, which operates under the
+// strategy of locking data, rather than code.
+//
+// Examples:
+//
+//    DataMutex<uint32_t> u32DataMutex(1, "u32DataMutex");
+//    auto x = u32DataMutex.Lock();
+//    *x = 4;
+//    assert(*x, 4u);
+//
+//    DataMutex<nsTArray<uint32_t>> arrayDataMutex("arrayDataMutex");
+//    auto a = arrayDataMutex.Lock();
+//    auto& x = a.ref();
+//    x.AppendElement(1u);
+//    assert(x[0], 1u);
+//
+template<typename T>
+class DataMutex
+{
+private:
+  class MOZ_STACK_CLASS AutoLock
+  {
+  public:
+    T* operator->() const { return &ref(); }
+
+    T& operator*() const { return ref(); }
+
+    // Like RefPtr, make this act like its underlying raw pointer type
+    // whenever it is used in a context where a raw pointer is expected.
+    operator T*() const & { return &ref(); }
+
+    // Like RefPtr, don't allow implicit conversion of temporary to raw pointer.
+    operator T*() const && = delete;
+
+    T& ref() const
+    {
+      MOZ_ASSERT(mOwner);
+      return mOwner->mValue;
+    }
+
+    AutoLock(AutoLock&& aOther)
+      : mOwner(aOther.mOwner)
+    {
+      aOther.mOwner = nullptr;
+    }
+
+    ~AutoLock()
+    {
+      if (mOwner) {
+        mOwner->mMutex.Unlock();
+        mOwner = nullptr;
+      }
+    }
+
+  private:
+    friend class DataMutex;
+
+    AutoLock(const AutoLock& aOther) = delete;
+
+    explicit AutoLock(DataMutex<T>* aDataMutex)
+      : mOwner(aDataMutex)
+    {
+      MOZ_ASSERT(!!mOwner);
+      mOwner->mMutex.Lock();
+    }
+
+    DataMutex<T>* mOwner;
+  };
+
+public:
+  explicit DataMutex(const char* aName)
+    : mMutex(aName)
+  {
+  }
+
+  DataMutex(T&& aValue, const char* aName)
+    : mMutex(aName)
+    , mValue(aValue)
+  {
+  }
+
+  AutoLock Lock() { return AutoLock(this); }
+
+private:
+  Mutex mMutex;
+  T mValue;
+};
+
+} // namespace mozilla
+
+#endif // DataMutex_h__
--- a/dom/media/eme/moz.build
+++ b/dom/media/eme/moz.build
@@ -13,16 +13,17 @@ EXPORTS.mozilla.dom += [
     'MediaKeyStatusMap.h',
     'MediaKeySystemAccess.h',
     'MediaKeySystemAccessManager.h',
 ]
 
 EXPORTS.mozilla += [
     'CDMCaps.h',
     'CDMProxy.h',
+    'DataMutex.h',
     'DecryptorProxyCallback.h',
     'DetailedPromise.h',
     'EMEUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'CDMCaps.cpp',
     'DetailedPromise.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/media/gtest/TestDataMutex.cpp
@@ -0,0 +1,42 @@
+/* -*- 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/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/DataMutex.h"
+#include "nsTArray.h"
+
+using mozilla::DataMutex;
+
+struct A
+{
+  void Set(int a) { mValue = a; }
+  int mValue;
+};
+
+TEST(DataMutex, Basic)
+{
+  {
+    DataMutex<uint32_t> i(1, "1");
+    auto x = i.Lock();
+    *x = 4;
+    ASSERT_EQ(*x, 4u);
+  }
+  {
+    DataMutex<A> a({ 4 }, "StructA");
+    auto x = a.Lock();
+    ASSERT_EQ(x->mValue, 4);
+    x->Set(8);
+    ASSERT_EQ(x->mValue, 8);
+  }
+  {
+    DataMutex<nsTArray<uint32_t>> _a("array");
+    auto a = _a.Lock();
+    auto& x = a.ref();
+    ASSERT_EQ(x.Length(), 0u);
+    x.AppendElement(1u);
+    ASSERT_EQ(x.Length(), 1u);
+    ASSERT_EQ(x[0], 1u);
+  }
+}
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -9,16 +9,17 @@ UNIFIED_SOURCES += [
     'TestAudioBuffers.cpp',
     'TestAudioCompactor.cpp',
     'TestAudioMixer.cpp',
     'TestAudioPacketizer.cpp',
     'TestAudioSegment.cpp',
     'TestAudioTrackEncoder.cpp',
     'TestBlankVideoDataCreator.cpp',
     'TestCDMStorage.cpp',
+    'TestDataMutex.cpp',
     'TestGMPCrossOrigin.cpp',
     'TestGMPRemoveAndDelete.cpp',
     'TestGMPUtils.cpp',
     'TestIntervalSet.cpp',
     'TestMediaDataDecoder.cpp',
     'TestMediaEventSource.cpp',
     'TestMediaMIMETypes.cpp',
     'TestMP3Demuxer.cpp',