summaryrefslogtreecommitdiffstats
path: root/xpcom/threads/DataMutex.h
blob: 44f0a3576243359006a427eb601179aeb70100cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* -*- 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 <utility>
#include "mozilla/Mutex.h"
#include "mozilla/StaticMutex.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, typename MutexType>
class DataMutexBase {
 public:
  template <typename V>
  class MOZ_STACK_CLASS AutoLockBase {
   public:
    V* operator->() const& { return &ref(); }
    V* operator->() const&& = delete;

    V& operator*() const& { return ref(); }
    V& operator*() const&& = delete;

    // 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 V*() const& { return &ref(); }

    // Like RefPtr, don't allow implicit conversion of temporary to raw pointer.
    operator V*() const&& = delete;

    V& ref() const& {
      MOZ_ASSERT(mOwner);
      return mOwner->mValue;
    }
    V& ref() const&& = delete;

    AutoLockBase(AutoLockBase&& aOther) : mOwner(aOther.mOwner) {
      aOther.mOwner = nullptr;
    }

    ~AutoLockBase() {
      if (mOwner) {
        mOwner->mMutex.Unlock();
        mOwner = nullptr;
      }
    }

   private:
    friend class DataMutexBase;

    AutoLockBase(const AutoLockBase& aOther) = delete;

    explicit AutoLockBase(DataMutexBase<T, MutexType>* aDataMutex)
        : mOwner(aDataMutex) {
      MOZ_ASSERT(!!mOwner);
      mOwner->mMutex.Lock();
    }

    DataMutexBase<T, MutexType>* mOwner;
  };

  using AutoLock = AutoLockBase<T>;
  using ConstAutoLock = AutoLockBase<const T>;

  explicit DataMutexBase(const char* aName) : mMutex(aName) {}

  DataMutexBase(T&& aValue, const char* aName)
      : mMutex(aName), mValue(std::move(aValue)) {}

  AutoLock Lock() { return AutoLock(this); }
  ConstAutoLock ConstLock() { return ConstAutoLock(this); }

  const MutexType& Mutex() const { return mMutex; }

 private:
  MutexType mMutex;
  T mValue;
};

// Craft a version of StaticMutex that takes a const char* in its ctor.
// We need this so it works interchangeably with Mutex which requires a const
// char* aName in its ctor.
class StaticMutexNameless : public StaticMutex {
 public:
  explicit StaticMutexNameless(const char* aName) : StaticMutex() {}

 private:
  // Disallow copy construction, `=`, `new`, and `delete` like BaseStaticMutex.
#ifdef DEBUG
  StaticMutexNameless(StaticMutexNameless& aOther);
#endif  // DEBUG
  StaticMutexNameless& operator=(StaticMutexNameless* aRhs);
  static void* operator new(size_t) noexcept(true);
  static void operator delete(void*);
};

template <typename T>
using DataMutex = DataMutexBase<T, Mutex>;
template <typename T>
using StaticDataMutex = DataMutexBase<T, StaticMutexNameless>;

}  // namespace mozilla

#endif  // DataMutex_h__