forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			143 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			143 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* 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/. */
 | 
						|
 | 
						|
// A class for values only accessible from a single designated thread.
 | 
						|
 | 
						|
#ifndef mozilla_ThreadBound_h
 | 
						|
#define mozilla_ThreadBound_h
 | 
						|
 | 
						|
#include "mozilla/Atomics.h"
 | 
						|
#include "prthread.h"
 | 
						|
 | 
						|
#include <type_traits>
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
template <typename T>
 | 
						|
class ThreadBound;
 | 
						|
 | 
						|
namespace detail {
 | 
						|
 | 
						|
template <bool Condition, typename T>
 | 
						|
struct AddConstIf {
 | 
						|
  using type = T;
 | 
						|
};
 | 
						|
 | 
						|
template <typename T>
 | 
						|
struct AddConstIf<true, T> {
 | 
						|
  using type = typename std::add_const<T>::type;
 | 
						|
};
 | 
						|
 | 
						|
}  // namespace detail
 | 
						|
 | 
						|
// A ThreadBound<T> is a T that can only be accessed by a specific
 | 
						|
// thread. To enforce this rule, the inner T is only accessible
 | 
						|
// through a non-copyable, immovable accessor object.
 | 
						|
// Given a ThreadBound<T> threadBoundData, it can be accessed like so:
 | 
						|
//
 | 
						|
//   auto innerData = threadBoundData.Access();
 | 
						|
//   innerData->DoStuff();
 | 
						|
//
 | 
						|
// Trying to access a ThreadBound<T> from a different thread will
 | 
						|
// trigger a MOZ_DIAGNOSTIC_ASSERT.
 | 
						|
//   The encapsulated T is constructed during the construction of the
 | 
						|
// enclosing ThreadBound<T> by forwarding all of the latter's
 | 
						|
// constructor parameters to the former. A newly constructed
 | 
						|
// ThreadBound<T> is bound to the thread it's constructed in. It's
 | 
						|
// possible to rebind the data to some otherThread by calling
 | 
						|
//
 | 
						|
//   threadBoundData.Transfer(otherThread);
 | 
						|
//
 | 
						|
// on the thread that threadBoundData is currently bound to, as long
 | 
						|
// as it's not currently being accessed. (Trying to rebind from
 | 
						|
// another thread or while an accessor exists will trigger an
 | 
						|
// assertion.)
 | 
						|
//
 | 
						|
// Note: A ThreadBound<T> may be destructed from any thread, not just
 | 
						|
// its designated thread at the time the destructor is invoked.
 | 
						|
template <typename T>
 | 
						|
class ThreadBound final {
 | 
						|
 public:
 | 
						|
  template <typename... Args>
 | 
						|
  explicit ThreadBound(Args&&... aArgs)
 | 
						|
      : mData(std::forward<Args>(aArgs)...),
 | 
						|
        mThread(PR_GetCurrentThread()),
 | 
						|
        mAccessCount(0) {}
 | 
						|
 | 
						|
  ~ThreadBound() { AssertIsNotCurrentlyAccessed(); }
 | 
						|
 | 
						|
  void Transfer(const PRThread* const aDest) {
 | 
						|
    AssertIsCorrectThread();
 | 
						|
    AssertIsNotCurrentlyAccessed();
 | 
						|
    mThread = aDest;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  T mData;
 | 
						|
 | 
						|
  // This member is (potentially) accessed by multiple threads and is
 | 
						|
  // thus the first point of synchronization between them.
 | 
						|
  Atomic<const PRThread*, ReleaseAcquire> mThread;
 | 
						|
 | 
						|
  // In order to support nested accesses (e.g. from different stack
 | 
						|
  // frames) it's necessary to maintain a counter of the existing
 | 
						|
  // accessor. Since it's possible to access a const ThreadBound, the
 | 
						|
  // counter is mutable. It's atomic because accessing it synchronizes
 | 
						|
  // access to mData (see comment in Accessor's constructor).
 | 
						|
  using AccessCountType = Atomic<int, ReleaseAcquire>;
 | 
						|
  mutable AccessCountType mAccessCount;
 | 
						|
 | 
						|
 public:
 | 
						|
  template <bool IsConst>
 | 
						|
  class MOZ_STACK_CLASS Accessor final {
 | 
						|
    using DataType = typename detail::AddConstIf<IsConst, T>::type;
 | 
						|
 | 
						|
   public:
 | 
						|
    explicit Accessor(
 | 
						|
        typename detail::AddConstIf<IsConst, ThreadBound>::type& aThreadBound)
 | 
						|
        : mData(aThreadBound.mData), mAccessCount(aThreadBound.mAccessCount) {
 | 
						|
      aThreadBound.AssertIsCorrectThread();
 | 
						|
 | 
						|
      // This load/store serves as a memory fence that guards mData
 | 
						|
      // against accesses that would trip the thread assertion.
 | 
						|
      // (Otherwise one of the loads in the caller's instruction
 | 
						|
      // stream might be scheduled before the assertion.)
 | 
						|
      ++mAccessCount;
 | 
						|
    }
 | 
						|
 | 
						|
    Accessor(const Accessor&) = delete;
 | 
						|
    Accessor(Accessor&&) = delete;
 | 
						|
    Accessor& operator=(const Accessor&) = delete;
 | 
						|
    Accessor& operator=(Accessor&&) = delete;
 | 
						|
 | 
						|
    ~Accessor() { --mAccessCount; }
 | 
						|
 | 
						|
    DataType* operator->() { return &mData; }
 | 
						|
 | 
						|
   private:
 | 
						|
    DataType& mData;
 | 
						|
    AccessCountType& mAccessCount;
 | 
						|
  };
 | 
						|
 | 
						|
  auto Access() { return Accessor<false>{*this}; }
 | 
						|
 | 
						|
  auto Access() const { return Accessor<true>{*this}; }
 | 
						|
 | 
						|
 private:
 | 
						|
  bool IsCorrectThread() const { return mThread == PR_GetCurrentThread(); }
 | 
						|
 | 
						|
  bool IsNotCurrentlyAccessed() const { return mAccessCount == 0; }
 | 
						|
 | 
						|
#define MOZ_DEFINE_THREAD_BOUND_ASSERT(predicate) \
 | 
						|
  void Assert##predicate() const { MOZ_DIAGNOSTIC_ASSERT(predicate()); }
 | 
						|
 | 
						|
  MOZ_DEFINE_THREAD_BOUND_ASSERT(IsCorrectThread)
 | 
						|
  MOZ_DEFINE_THREAD_BOUND_ASSERT(IsNotCurrentlyAccessed)
 | 
						|
 | 
						|
#undef MOZ_DEFINE_THREAD_BOUND_ASSERT
 | 
						|
};
 | 
						|
 | 
						|
}  // namespace mozilla
 | 
						|
 | 
						|
#endif  // mozilla_ThreadBound_h
 |