Bug 1783299 - Add support for threadsafe mirrored StaticPref strings. r=KrisWright

Prior to this patch, one would need to manually instantiate a copy of a
string from a preference on the main thread in order to access it in a
threadsafe manner on another thread.

This patch adds support for a `DataMutexString` threadsafe type for
mirror: always type StaticPrefs, and works similarly to the existing
atomic types.

Differential Revision: https://phabricator.services.mozilla.com/D153829
This commit is contained in:
Andrew Osmond 2022-08-16 01:00:21 +00:00
Родитель 7b7c37a440
Коммит e8f9ed23ec
8 изменённых файлов: 190 добавлений и 32 удалений

Просмотреть файл

@ -132,6 +132,18 @@ using ipc::FileDescriptor;
#endif // DEBUG #endif // DEBUG
// Forward declarations.
namespace mozilla::StaticPrefs {
static void InitAll();
static void StartObservingAlwaysPrefs();
static void InitOncePrefs();
static void InitStaticPrefsFromShared();
static void RegisterOncePrefs(SharedPrefMapBuilder& aBuilder);
static void ShutdownAlwaysPrefs();
} // namespace mozilla::StaticPrefs
//=========================================================================== //===========================================================================
// Low-level types and operations // Low-level types and operations
//=========================================================================== //===========================================================================
@ -1129,6 +1141,10 @@ class MOZ_STACK_CLASS PrefWrapper : public PrefWrapperBase {
return NS_OK; return NS_OK;
} }
nsresult GetValue(PrefValueKind aKind, nsACString* aResult) const {
return GetValue(aKind, *aResult);
}
// Returns false if this pref doesn't have a user value worth saving. // Returns false if this pref doesn't have a user value worth saving.
bool UserValueToStringForSaving(nsCString& aStr) { bool UserValueToStringForSaving(nsCString& aStr) {
// Should we save the user value, if present? Only if it does not match the // Should we save the user value, if present? Only if it does not match the
@ -3616,6 +3632,7 @@ void Preferences::Shutdown() {
if (!sShutdown) { if (!sShutdown) {
sShutdown = true; // Don't create the singleton instance after here. sShutdown = true; // Don't create the singleton instance after here.
sPreferences = nullptr; sPreferences = nullptr;
StaticPrefs::ShutdownAlwaysPrefs();
} }
} }
@ -3697,17 +3714,6 @@ void Preferences::DeserializePreferences(char* aStr, size_t aPrefsLen) {
gContentProcessPrefsAreInited = true; gContentProcessPrefsAreInited = true;
} }
// Forward declarations.
namespace StaticPrefs {
static void InitAll();
static void StartObservingAlwaysPrefs();
static void InitOncePrefs();
static void InitStaticPrefsFromShared();
static void RegisterOncePrefs(SharedPrefMapBuilder& aBuilder);
} // namespace StaticPrefs
/* static */ /* static */
FileDescriptor Preferences::EnsureSnapshot(size_t* aSize) { FileDescriptor Preferences::EnsureSnapshot(size_t* aSize) {
MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(XRE_IsParentProcess());
@ -4529,6 +4535,9 @@ static nsCString PrefValueToString(const uint32_t* u) {
static nsCString PrefValueToString(const float* f) { static nsCString PrefValueToString(const float* f) {
return nsPrintfCString("%f", *f); return nsPrintfCString("%f", *f);
} }
static nsCString PrefValueToString(const nsACString* s) {
return nsCString(*s);
}
static nsCString PrefValueToString(const nsACString& s) { return nsCString(s); } static nsCString PrefValueToString(const nsACString& s) { return nsCString(s); }
// These preference getter wrappers allow us to look up the value for static // These preference getter wrappers allow us to look up the value for static
@ -4637,13 +4646,36 @@ struct Internals {
return result; return result;
} }
template <typename T, typename V>
static void MOZ_NEVER_INLINE AssignMirror(T& aMirror, V aValue) {
aMirror = aValue;
}
static void MOZ_NEVER_INLINE AssignMirror(DataMutexString& aMirror,
nsCString&& aValue) {
auto lock = aMirror.Lock();
lock->Assign(std::move(aValue));
}
static void MOZ_NEVER_INLINE AssignMirror(DataMutexString& aMirror,
const nsLiteralCString& aValue) {
auto lock = aMirror.Lock();
lock->Assign(aValue);
}
static void ClearMirror(DataMutexString& aMirror) {
auto lock = aMirror.Lock();
lock->Assign(nsCString());
}
template <typename T> template <typename T>
static void UpdateMirror(const char* aPref, void* aMirror) { static void UpdateMirror(const char* aPref, void* aMirror) {
StripAtomic<T> value; StripAtomic<T> value;
nsresult rv = GetPrefValue(aPref, &value, PrefValueKind::User); nsresult rv = GetPrefValue(aPref, &value, PrefValueKind::User);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
*static_cast<T*>(aMirror) = value; AssignMirror(*static_cast<T*>(aMirror),
std::forward<StripAtomic<T>>(value));
} else { } else {
// GetPrefValue() can fail if the update is caused by the pref being // GetPrefValue() can fail if the update is caused by the pref being
// deleted or if it fails to make a cast. This assertion is the only place // deleted or if it fails to make a cast. This assertion is the only place
@ -5433,6 +5465,16 @@ static MOZ_NEVER_INLINE void AddMirror(T* aMirror, const nsACString& aPref,
AddMirrorCallback(aMirror, aPref); AddMirrorCallback(aMirror, aPref);
} }
static MOZ_NEVER_INLINE void AddMirror(DataMutexString& aMirror,
const nsACString& aPref) {
auto lock = aMirror.Lock();
nsCString result(*lock);
Internals::GetPrefValue(PromiseFlatCString(aPref).get(), result,
PrefValueKind::User);
lock->Assign(std::move(result));
AddMirrorCallback(&aMirror, aPref);
}
// The InitPref_*() functions below end in a `_<type>` suffix because they are // The InitPref_*() functions below end in a `_<type>` suffix because they are
// used by the PREF macro definition in InitAll() below. // used by the PREF macro definition in InitAll() below.
@ -5511,6 +5553,15 @@ static void InitAlwaysPref(const nsCString& aName, T* aCache,
*aCache = aDefaultValue; *aCache = aDefaultValue;
} }
static void InitAlwaysPref(const nsCString& aName, DataMutexString& aCache,
const nsLiteralCString& aDefaultValue) {
// Only called in the parent process. Set/reset the pref value and the
// `always` mirror to the default value.
// `once` mirrors will be initialized lazily in InitOncePrefs().
InitPref_String(aName, aDefaultValue.get());
Internals::AssignMirror(aCache, aDefaultValue);
}
static Atomic<bool> sOncePrefRead(false); static Atomic<bool> sOncePrefRead(false);
static StaticMutex sOncePrefMutex MOZ_UNANNOTATED; static StaticMutex sOncePrefMutex MOZ_UNANNOTATED;
@ -5538,11 +5589,14 @@ void MaybeInitOncePrefs() {
#define NEVER_PREF(name, cpp_type, value) #define NEVER_PREF(name, cpp_type, value)
#define ALWAYS_PREF(name, base_id, full_id, cpp_type, default_value) \ #define ALWAYS_PREF(name, base_id, full_id, cpp_type, default_value) \
cpp_type sMirror_##full_id(default_value); cpp_type sMirror_##full_id(default_value);
#define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, default_value) \
cpp_type sMirror_##full_id("DataMutexString");
#define ONCE_PREF(name, base_id, full_id, cpp_type, default_value) \ #define ONCE_PREF(name, base_id, full_id, cpp_type, default_value) \
cpp_type sMirror_##full_id(default_value); cpp_type sMirror_##full_id(default_value);
#include "mozilla/StaticPrefListAll.h" #include "mozilla/StaticPrefListAll.h"
#undef NEVER_PREF #undef NEVER_PREF
#undef ALWAYS_PREF #undef ALWAYS_PREF
#undef ALWAYS_DATAMUTEX_PREF
#undef ONCE_PREF #undef ONCE_PREF
static void InitAll() { static void InitAll() {
@ -5559,11 +5613,14 @@ static void InitAll() {
InitPref_##cpp_type(name ""_ns, value); InitPref_##cpp_type(name ""_ns, value);
#define ALWAYS_PREF(name, base_id, full_id, cpp_type, value) \ #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value) \
InitAlwaysPref(name ""_ns, &sMirror_##full_id, value); InitAlwaysPref(name ""_ns, &sMirror_##full_id, value);
#define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value) \
InitAlwaysPref(name ""_ns, sMirror_##full_id, value);
#define ONCE_PREF(name, base_id, full_id, cpp_type, value) \ #define ONCE_PREF(name, base_id, full_id, cpp_type, value) \
InitPref_##cpp_type(name ""_ns, value); InitPref_##cpp_type(name ""_ns, value);
#include "mozilla/StaticPrefListAll.h" #include "mozilla/StaticPrefListAll.h"
#undef NEVER_PREF #undef NEVER_PREF
#undef ALWAYS_PREF #undef ALWAYS_PREF
#undef ALWAYS_DATAMUTEX_PREF
#undef ONCE_PREF #undef ONCE_PREF
} }
@ -5577,10 +5634,13 @@ static void StartObservingAlwaysPrefs() {
#define NEVER_PREF(name, cpp_type, value) #define NEVER_PREF(name, cpp_type, value)
#define ALWAYS_PREF(name, base_id, full_id, cpp_type, value) \ #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value) \
AddMirror(&sMirror_##full_id, name ""_ns, sMirror_##full_id); AddMirror(&sMirror_##full_id, name ""_ns, sMirror_##full_id);
#define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value) \
AddMirror(sMirror_##full_id, name ""_ns);
#define ONCE_PREF(name, base_id, full_id, cpp_type, value) #define ONCE_PREF(name, base_id, full_id, cpp_type, value)
#include "mozilla/StaticPrefListAll.h" #include "mozilla/StaticPrefListAll.h"
#undef NEVER_PREF #undef NEVER_PREF
#undef ALWAYS_PREF #undef ALWAYS_PREF
#undef ALWAYS_DATAMUTEX_PREF
#undef ONCE_PREF #undef ONCE_PREF
} }
@ -5596,6 +5656,7 @@ static void InitOncePrefs() {
// suggest that it should instead be `always`-mirrored. // suggest that it should instead be `always`-mirrored.
#define NEVER_PREF(name, cpp_type, value) #define NEVER_PREF(name, cpp_type, value)
#define ALWAYS_PREF(name, base_id, full_id, cpp_type, value) #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value)
#define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value)
#ifdef DEBUG #ifdef DEBUG
# define ONCE_PREF(name, base_id, full_id, cpp_type, value) \ # define ONCE_PREF(name, base_id, full_id, cpp_type, value) \
{ \ { \
@ -5624,6 +5685,23 @@ static void InitOncePrefs() {
#include "mozilla/StaticPrefListAll.h" #include "mozilla/StaticPrefListAll.h"
#undef NEVER_PREF #undef NEVER_PREF
#undef ALWAYS_PREF #undef ALWAYS_PREF
#undef ALWAYS_DATAMUTEX_PREF
#undef ONCE_PREF
}
static void ShutdownAlwaysPrefs() {
MOZ_ASSERT(NS_IsMainThread());
// We may need to do clean up for leak detection for some StaticPrefs.
#define NEVER_PREF(name, cpp_type, value)
#define ALWAYS_PREF(name, base_id, full_id, cpp_type, value)
#define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value) \
Internals::ClearMirror(sMirror_##full_id);
#define ONCE_PREF(name, base_id, full_id, cpp_type, value)
#include "mozilla/StaticPrefListAll.h"
#undef NEVER_PREF
#undef ALWAYS_PREF
#undef ALWAYS_DATAMUTEX_PREF
#undef ONCE_PREF #undef ONCE_PREF
} }
@ -5696,12 +5774,14 @@ static void RegisterOncePrefs(SharedPrefMapBuilder& aBuilder) {
// "$$$" prefix and suffix to the preference name. // "$$$" prefix and suffix to the preference name.
#define NEVER_PREF(name, cpp_type, value) #define NEVER_PREF(name, cpp_type, value)
#define ALWAYS_PREF(name, base_id, full_id, cpp_type, value) #define ALWAYS_PREF(name, base_id, full_id, cpp_type, value)
#define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, value)
#define ONCE_PREF(name, base_id, full_id, cpp_type, value) \ #define ONCE_PREF(name, base_id, full_id, cpp_type, value) \
SaveOncePrefToSharedMap(aBuilder, ONCE_PREF_NAME(name) ""_ns, \ SaveOncePrefToSharedMap(aBuilder, ONCE_PREF_NAME(name) ""_ns, \
cpp_type(sMirror_##full_id)); cpp_type(sMirror_##full_id));
#include "mozilla/StaticPrefListAll.h" #include "mozilla/StaticPrefListAll.h"
#undef NEVER_PREF #undef NEVER_PREF
#undef ALWAYS_PREF #undef ALWAYS_PREF
#undef ALWAYS_DATAMUTEX_PREF
#undef ONCE_PREF #undef ONCE_PREF
} }
@ -5744,6 +5824,20 @@ static void InitStaticPrefsFromShared() {
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed accessing " name); \ MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed accessing " name); \
StaticPrefs::sMirror_##full_id = val; \ StaticPrefs::sMirror_##full_id = val; \
} }
#define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, default_value) \
{ \
StripAtomic<cpp_type> val; \
if (!XRE_IsParentProcess() && IsString<cpp_type>::value && \
gContentProcessPrefsAreInited && sCrashOnBlocklistedPref) { \
MOZ_DIAGNOSTIC_ASSERT( \
!ShouldSanitizePreference(name, XRE_IsContentProcess()), \
"Should not access the preference '" name "' in Content Processes"); \
} \
DebugOnly<nsresult> rv = Internals::GetSharedPrefValue(name, &val); \
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed accessing " name); \
Internals::AssignMirror(StaticPrefs::sMirror_##full_id, \
std::forward<StripAtomic<cpp_type>>(val)); \
}
#define ONCE_PREF(name, base_id, full_id, cpp_type, default_value) \ #define ONCE_PREF(name, base_id, full_id, cpp_type, default_value) \
{ \ { \
cpp_type val; \ cpp_type val; \
@ -5761,6 +5855,7 @@ static void InitStaticPrefsFromShared() {
#include "mozilla/StaticPrefListAll.h" #include "mozilla/StaticPrefListAll.h"
#undef NEVER_PREF #undef NEVER_PREF
#undef ALWAYS_PREF #undef ALWAYS_PREF
#undef ALWAYS_DATAMUTEX_PREF
#undef ONCE_PREF #undef ONCE_PREF
// `once`-mirrored prefs have been set to their value in the step above and // `once`-mirrored prefs have been set to their value in the step above and

Просмотреть файл

@ -10,6 +10,8 @@
#include <type_traits> #include <type_traits>
#include "mozilla/Atomics.h" #include "mozilla/Atomics.h"
#include "mozilla/DataMutex.h"
#include "nsString.h"
namespace mozilla { namespace mozilla {
@ -19,12 +21,17 @@ class SharedPrefMapBuilder;
typedef const char* String; typedef const char* String;
using DataMutexString = StaticDataMutex<nsCString>;
template <typename T> template <typename T>
struct IsString : std::false_type {}; struct IsString : std::false_type {};
template <> template <>
struct IsString<String> : std::true_type {}; struct IsString<String> : std::true_type {};
template <>
struct IsString<DataMutexString> : std::true_type {};
typedef Atomic<bool, Relaxed> RelaxedAtomicBool; typedef Atomic<bool, Relaxed> RelaxedAtomicBool;
typedef Atomic<bool, ReleaseAcquire> ReleaseAcquireAtomicBool; typedef Atomic<bool, ReleaseAcquire> ReleaseAcquireAtomicBool;
typedef Atomic<bool, SequentiallyConsistent> SequentiallyConsistentAtomicBool; typedef Atomic<bool, SequentiallyConsistent> SequentiallyConsistentAtomicBool;
@ -58,6 +65,11 @@ struct StripAtomicImpl<std::atomic<T>> {
typedef T Type; typedef T Type;
}; };
template <>
struct StripAtomicImpl<DataMutexString> {
typedef nsCString Type;
};
template <typename T> template <typename T>
using StripAtomic = typename StripAtomicImpl<T>::Type; using StripAtomic = typename StripAtomicImpl<T>::Type;

Просмотреть файл

@ -42,10 +42,11 @@
# and JS code). # and JS code).
# #
# - `type` is one of `bool`, `int32_t`, `uint32_t`, `float`, an atomic version # - `type` is one of `bool`, `int32_t`, `uint32_t`, `float`, an atomic version
# of one of those, or `String`. Note that float prefs are stored internally # of one of those, `String` or `DataMutexString`. Note that float prefs are
# as strings. The C++ preprocessor doesn't like template syntax in a macro # stored internally as strings. The C++ preprocessor doesn't like template
# argument, so use the typedefs defined in StaticPrefsBase.h; for example, # syntax in a macro argument, so use the typedefs defined in
# use `RelaxedAtomicBool` instead of `Atomic<bool, Relaxed>`. # StaticPrefsBase.h; for example, use `RelaxedAtomicBool` instead of
# `Atomic<bool, Relaxed>`.
# #
# - `value` is the default value. Its type should be appropriate for # - `value` is the default value. Its type should be appropriate for
# <cpp-type>, otherwise the generated code will fail to compile. A complex # <cpp-type>, otherwise the generated code will fail to compile. A complex

Просмотреть файл

@ -37,6 +37,21 @@ namespace StaticPrefs {
inline StripAtomic<cpp_type> GetPrefDefault_##base_id() { \ inline StripAtomic<cpp_type> GetPrefDefault_##base_id() { \
return default_value; \ return default_value; \
} }
#define ALWAYS_DATAMUTEX_PREF(name, base_id, full_id, cpp_type, default_value) \
extern cpp_type sMirror_##full_id; \
inline cpp_type::ConstAutoLock full_id() { \
if (!XRE_IsParentProcess() && IsString<cpp_type>::value && \
sCrashOnBlocklistedPref) { \
MOZ_DIAGNOSTIC_ASSERT( \
!ShouldSanitizePreference(name, XRE_IsContentProcess()), \
"Should not access the preference '" name "' in Content Processes"); \
} \
return sMirror_##full_id.ConstLock(); \
} \
inline const char* GetPrefName_##base_id() { return name; } \
inline StripAtomic<cpp_type> GetPrefDefault_##base_id() { \
return default_value; \
}
#define ONCE_PREF(name, base_id, full_id, cpp_type, default_value) \ #define ONCE_PREF(name, base_id, full_id, cpp_type, default_value) \
extern cpp_type sMirror_##full_id; \ extern cpp_type sMirror_##full_id; \
inline cpp_type full_id() { \ inline cpp_type full_id() { \

Просмотреть файл

@ -13,6 +13,7 @@
#undef NEVER_PREF #undef NEVER_PREF
#undef ALWAYS_PREF #undef ALWAYS_PREF
#undef ALWAYS_DATAMUTEX_PREF
#undef ONCE_PREF #undef ONCE_PREF
} // namespace StaticPrefs } // namespace StaticPrefs

Просмотреть файл

@ -46,6 +46,7 @@ VALID_TYPES.update(
"SequentiallyConsistentAtomicUint32": "uint32_t", "SequentiallyConsistentAtomicUint32": "uint32_t",
"AtomicFloat": "float", "AtomicFloat": "float",
"String": None, "String": None,
"DataMutexString": None,
} }
) )
@ -81,6 +82,14 @@ ALWAYS_PREF(
{full_id}, {full_id},
{typ}, {value} {typ}, {value}
) )
""",
"always_datamutex": """\
ALWAYS_DATAMUTEX_PREF(
"{name}",
{base_id},
{full_id},
{typ}, {value}
)
""", """,
} }
@ -165,11 +174,11 @@ def check_pref_list(pref_list):
if "value" not in pref: if "value" not in pref:
error("missing `value` key for pref `{}`".format(name)) error("missing `value` key for pref `{}`".format(name))
value = pref["value"] value = pref["value"]
if typ == "String": if typ == "String" or typ == "DataMutexString":
if type(value) != str: if type(value) != str:
error( error(
"non-string `value` value `{}` for `String` pref `{}`; " "non-string `value` value `{}` for `{}` pref `{}`; "
"add double quotes".format(value, name) "add double quotes".format(value, typ, name)
) )
elif typ in VALID_BOOL_TYPES: elif typ in VALID_BOOL_TYPES:
if value not in (True, False): if value not in (True, False):
@ -179,6 +188,8 @@ def check_pref_list(pref_list):
if "mirror" not in pref: if "mirror" not in pref:
error("missing `mirror` key for pref `{}`".format(name)) error("missing `mirror` key for pref `{}`".format(name))
mirror = pref["mirror"] mirror = pref["mirror"]
if typ.startswith("DataMutex"):
mirror += "_datamutex"
if mirror not in MIRROR_TEMPLATES: if mirror not in MIRROR_TEMPLATES:
error("invalid `mirror` value `{}` for pref `{}`".format(mirror, name)) error("invalid `mirror` value `{}` for pref `{}`".format(mirror, name))
@ -261,6 +272,8 @@ def generate_code(pref_list, input_filename):
full_id += "_AtStartup" full_id += "_AtStartup"
if do_not_use_directly: if do_not_use_directly:
full_id += "_DoNotUseDirectly" full_id += "_DoNotUseDirectly"
if typ.startswith("DataMutex"):
mirror += "_datamutex"
group = mk_group(pref) group = mk_group(pref)
@ -273,6 +286,9 @@ def generate_code(pref_list, input_filename):
if typ == "String": if typ == "String":
# Quote string literals, and escape double-quote chars. # Quote string literals, and escape double-quote chars.
value = '"{}"'.format(value.replace('"', '\\"')) value = '"{}"'.format(value.replace('"', '\\"'))
elif typ == "DataMutexString":
# Quote string literals, and escape double-quote chars.
value = '"{}"_ns'.format(value.replace('"', '\\"'))
elif typ in VALID_BOOL_TYPES: elif typ in VALID_BOOL_TYPES:
# Convert Python bools to C++ bools. # Convert Python bools to C++ bools.
if value is True: if value is True:

Просмотреть файл

@ -69,6 +69,12 @@ good_input = """
mirror: always mirror: always
rust: true rust: true
# A comment.
- name: my.datamutex.string
type: DataMutexString
value: "foobar" # This string is quoted.
mirror: always
# YAML+Python interprets `10 + 10 * 20` as a string, and so it is printed # YAML+Python interprets `10 + 10 * 20` as a string, and so it is printed
# unchanged. # unchanged.
- name: my.atomic.int - name: my.atomic.int
@ -152,6 +158,13 @@ ALWAYS_PREF(
RelaxedAtomicBool, true RelaxedAtomicBool, true
) )
ALWAYS_DATAMUTEX_PREF(
"my.datamutex.string",
my_datamutex_string,
my_datamutex_string,
DataMutexString, "foobar"_ns
)
ALWAYS_PREF( ALWAYS_PREF(
"my.atomic.int", "my.atomic.int",
my_atomic_int, my_atomic_int,

Просмотреть файл

@ -38,32 +38,33 @@ namespace mozilla {
template <typename T, typename MutexType> template <typename T, typename MutexType>
class DataMutexBase { class DataMutexBase {
public: public:
class MOZ_STACK_CLASS AutoLock { template <typename V>
class MOZ_STACK_CLASS AutoLockBase {
public: public:
T* operator->() const& { return &ref(); } V* operator->() const& { return &ref(); }
T* operator->() const&& = delete; V* operator->() const&& = delete;
T& operator*() const& { return ref(); } V& operator*() const& { return ref(); }
T& operator*() const&& = delete; V& operator*() const&& = delete;
// Like RefPtr, make this act like its underlying raw pointer type // Like RefPtr, make this act like its underlying raw pointer type
// whenever it is used in a context where a raw pointer is expected. // whenever it is used in a context where a raw pointer is expected.
operator T*() const& { return &ref(); } operator V*() const& { return &ref(); }
// Like RefPtr, don't allow implicit conversion of temporary to raw pointer. // Like RefPtr, don't allow implicit conversion of temporary to raw pointer.
operator T*() const&& = delete; operator V*() const&& = delete;
T& ref() const& { V& ref() const& {
MOZ_ASSERT(mOwner); MOZ_ASSERT(mOwner);
return mOwner->mValue; return mOwner->mValue;
} }
T& ref() const&& = delete; V& ref() const&& = delete;
AutoLock(AutoLock&& aOther) : mOwner(aOther.mOwner) { AutoLockBase(AutoLockBase&& aOther) : mOwner(aOther.mOwner) {
aOther.mOwner = nullptr; aOther.mOwner = nullptr;
} }
~AutoLock() { ~AutoLockBase() {
if (mOwner) { if (mOwner) {
mOwner->mMutex.Unlock(); mOwner->mMutex.Unlock();
mOwner = nullptr; mOwner = nullptr;
@ -73,9 +74,9 @@ class DataMutexBase {
private: private:
friend class DataMutexBase; friend class DataMutexBase;
AutoLock(const AutoLock& aOther) = delete; AutoLockBase(const AutoLockBase& aOther) = delete;
explicit AutoLock(DataMutexBase<T, MutexType>* aDataMutex) explicit AutoLockBase(DataMutexBase<T, MutexType>* aDataMutex)
: mOwner(aDataMutex) { : mOwner(aDataMutex) {
MOZ_ASSERT(!!mOwner); MOZ_ASSERT(!!mOwner);
mOwner->mMutex.Lock(); mOwner->mMutex.Lock();
@ -84,12 +85,16 @@ class DataMutexBase {
DataMutexBase<T, MutexType>* mOwner; DataMutexBase<T, MutexType>* mOwner;
}; };
using AutoLock = AutoLockBase<T>;
using ConstAutoLock = AutoLockBase<const T>;
explicit DataMutexBase(const char* aName) : mMutex(aName) {} explicit DataMutexBase(const char* aName) : mMutex(aName) {}
DataMutexBase(T&& aValue, const char* aName) DataMutexBase(T&& aValue, const char* aName)
: mMutex(aName), mValue(std::move(aValue)) {} : mMutex(aName), mValue(std::move(aValue)) {}
AutoLock Lock() { return AutoLock(this); } AutoLock Lock() { return AutoLock(this); }
ConstAutoLock ConstLock() { return ConstAutoLock(this); }
const MutexType& Mutex() const { return mMutex; } const MutexType& Mutex() const { return mMutex; }