#ifndef threading_ProtectedData_h
#define threading_ProtectedData_h
#include "threading/Thread.h"
namespace JS {
class Zone;
}
namespace js {
#if defined(DEBUG) && !defined(ANDROID)
# define JS_HAS_PROTECTED_DATA_CHECKS
#endif
#define DECLARE_ONE_BOOL_OPERATOR(OP, T) \
template <typename U> \
bool operator OP(const U& other) const { \
return ref() OP static_cast<T>(other); \
}
#define DECLARE_BOOL_OPERATORS(T) \
DECLARE_ONE_BOOL_OPERATOR(==, T) \
DECLARE_ONE_BOOL_OPERATOR(!=, T) \
DECLARE_ONE_BOOL_OPERATOR(<=, T) \
DECLARE_ONE_BOOL_OPERATOR(>=, T) \
DECLARE_ONE_BOOL_OPERATOR(<, T) \
DECLARE_ONE_BOOL_OPERATOR(>, T)
class MOZ_RAII AutoNoteSingleThreadedRegion {
public:
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
static mozilla::Atomic<size_t, mozilla::SequentiallyConsistent,
mozilla::recordreplay::Behavior::DontPreserve>
count;
AutoNoteSingleThreadedRegion() { count++; }
~AutoNoteSingleThreadedRegion() { count--; }
#else
AutoNoteSingleThreadedRegion() {}
#endif
};
template <typename Check, typename T>
class ProtectedData {
typedef ProtectedData<Check, T> ThisType;
public:
template <typename... Args>
explicit ProtectedData(const Check& check, Args&&... args)
: value(std::forward<Args>(args)...)
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
,
check(check)
#endif
{
}
DECLARE_BOOL_OPERATORS(T)
operator const T&() const { return ref(); }
const T& operator->() const { return ref(); }
template <typename U>
ThisType& operator=(const U& p) {
this->ref() = p;
return *this;
}
template <typename U>
ThisType& operator=(U&& p) {
this->ref() = std::move(p);
return *this;
}
template <typename U>
T& operator+=(const U& rhs) {
return ref() += rhs;
}
template <typename U>
T& operator-=(const U& rhs) {
return ref() -= rhs;
}
template <typename U>
T& operator*=(const U& rhs) {
return ref() *= rhs;
}
template <typename U>
T& operator/=(const U& rhs) {
return ref() /= rhs;
}
template <typename U>
T& operator&=(const U& rhs) {
return ref() &= rhs;
}
template <typename U>
T& operator|=(const U& rhs) {
return ref() |= rhs;
}
T& operator++() { return ++ref(); }
T& operator--() { return --ref(); }
T operator++(int) { return ref()++; }
T operator--(int) { return ref()--; }
T& ref() {
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
if (!AutoNoteSingleThreadedRegion::count) {
check.check();
}
#endif
return value;
}
const T& ref() const {
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
if (!AutoNoteSingleThreadedRegion::count) {
check.check();
}
#endif
return value;
}
T& refNoCheck() { return value; }
const T& refNoCheck() const { return value; }
static size_t offsetOfValue() { return offsetof(ThisType, value); }
private:
T value;
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
Check check;
#endif
};
template <typename Check, typename T>
class ProtectedDataNoCheckArgs : public ProtectedData<Check, T> {
using Base = ProtectedData<Check, T>;
public:
template <typename... Args>
explicit ProtectedDataNoCheckArgs(Args&&... args)
: ProtectedData<Check, T>(Check(), std::forward<Args>(args)...) {}
using Base::operator=;
};
template <typename Check, typename T>
class ProtectedDataZoneArg : public ProtectedData<Check, T> {
using Base = ProtectedData<Check, T>;
public:
template <typename... Args>
explicit ProtectedDataZoneArg(JS::Zone* zone, Args&&... args)
: ProtectedData<Check, T>(Check(zone), std::forward<Args>(args)...) {}
using Base::operator=;
};
class CheckUnprotected {
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
public:
inline void check() const {}
#endif
};
template <typename T>
using UnprotectedData = ProtectedDataNoCheckArgs<CheckUnprotected, T>;
class CheckThreadLocal {
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
Thread::Id id;
public:
CheckThreadLocal() : id(ThisThread::GetId()) {}
void check() const;
#endif
};
template <typename T>
using ThreadData = ProtectedDataNoCheckArgs<CheckThreadLocal, T>;
enum class AllowedHelperThread { None, GCTask, IonCompile, GCTaskOrIonCompile };
template <AllowedHelperThread Helper>
class CheckMainThread {
public:
void check() const;
};
template <typename T>
using MainThreadData =
ProtectedDataNoCheckArgs<CheckMainThread<AllowedHelperThread::None>, T>;
template <typename T>
using MainThreadOrGCTaskData =
ProtectedDataNoCheckArgs<CheckMainThread<AllowedHelperThread::GCTask>, T>;
template <typename T>
using MainThreadOrIonCompileData =
ProtectedDataNoCheckArgs<CheckMainThread<AllowedHelperThread::IonCompile>,
T>;
template <AllowedHelperThread Helper>
class CheckZone {
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
protected:
JS::Zone* zone;
public:
explicit CheckZone(JS::Zone* zone) : zone(zone) {}
void check() const;
#else
public:
explicit CheckZone(JS::Zone* zone) {}
#endif
};
template <typename T>
using ZoneData = ProtectedDataZoneArg<CheckZone<AllowedHelperThread::None>, T>;
template <typename T>
using ZoneOrGCTaskData =
ProtectedDataZoneArg<CheckZone<AllowedHelperThread::GCTask>, T>;
template <typename T>
using ZoneOrIonCompileData =
ProtectedDataZoneArg<CheckZone<AllowedHelperThread::IonCompile>, T>;
template <typename T>
using ZoneOrGCTaskOrIonCompileData =
ProtectedDataZoneArg<CheckZone<AllowedHelperThread::GCTaskOrIonCompile>, T>;
enum class GlobalLock { GCLock, ScriptDataLock, HelperThreadLock };
template <GlobalLock Lock, AllowedHelperThread Helper>
class CheckGlobalLock {
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
public:
void check() const;
#endif
};
template <typename T>
using GCLockData = ProtectedDataNoCheckArgs<
CheckGlobalLock<GlobalLock::GCLock, AllowedHelperThread::None>, T>;
template <typename T>
using ScriptDataLockData = ProtectedDataNoCheckArgs<
CheckGlobalLock<GlobalLock::ScriptDataLock, AllowedHelperThread::None>, T>;
template <typename T>
using HelperThreadLockData = ProtectedDataNoCheckArgs<
CheckGlobalLock<GlobalLock::HelperThreadLock, AllowedHelperThread::None>,
T>;
template <typename Check, typename T>
class ProtectedDataWriteOnce {
typedef ProtectedDataWriteOnce<Check, T> ThisType;
public:
template <typename... Args>
explicit ProtectedDataWriteOnce(Args&&... args)
: value(std::forward<Args>(args)...)
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
,
nwrites(0)
#endif
{
}
DECLARE_BOOL_OPERATORS(T)
operator const T&() const { return ref(); }
const T& operator->() const { return ref(); }
template <typename U>
ThisType& operator=(const U& p) {
if (ref() != p) {
this->writeRef() = p;
}
return *this;
}
const T& ref() const { return value; }
T& writeRef() {
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
if (!AutoNoteSingleThreadedRegion::count) {
check.check();
}
MOZ_ASSERT(++nwrites <= 2);
#endif
return value;
}
private:
T value;
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
Check check;
size_t nwrites;
#endif
};
template <typename T>
using WriteOnceData = ProtectedDataWriteOnce<CheckUnprotected, T>;
template <AllowedHelperThread Helper>
class CheckArenaListAccess : public CheckZone<AllowedHelperThread::None> {
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
public:
explicit CheckArenaListAccess(JS::Zone* zone)
: CheckZone<AllowedHelperThread::None>(zone) {}
void check() const;
#else
public:
explicit CheckArenaListAccess(JS::Zone* zone)
: CheckZone<AllowedHelperThread::None>(zone) {}
#endif
};
template <typename T>
using ArenaListData =
ProtectedDataZoneArg<CheckArenaListAccess<AllowedHelperThread::GCTask>, T>;
#undef DECLARE_ASSIGNMENT_OPERATOR
#undef DECLARE_ONE_BOOL_OPERATOR
#undef DECLARE_BOOL_OPERATORS
}
#endif