proxygen
folly::pushmi::entangled< T, Dual > Struct Template Reference

#include <entangle.h>

Public Member Functions

bool tryLockBoth ()
 
void lockBoth ()
 
void unlockBoth ()
 
 entangled ()=delete
 
 entangled (const entangled &)=delete
 
entangledoperator= (const entangled &)=delete
 
entangledoperator= (entangled &&)=delete
 
 entangled (T t)
 
 entangled (entangled &&other)
 
 ~entangled ()
 
Dual * lockPointerToDual ()
 
void unlockPointerToDual ()
 

Public Attributes

std::atomic< int > stateMachine
 
T t
 
entangled< Dual, T > * dual
 

Static Public Attributes

static const int kUnlocked = 0
 
static const int kLocked = 1
 
static const int kLockedAndLossAcknowledged = 2
 

Detailed Description

template<class T, class Dual>
struct folly::pushmi::entangled< T, Dual >

Definition at line 71 of file entangle.h.

Constructor & Destructor Documentation

template<class T, class Dual>
folly::pushmi::entangled< T, Dual >::entangled ( )
delete
template<class T, class Dual>
folly::pushmi::entangled< T, Dual >::entangled ( const entangled< T, Dual > &  )
delete
template<class T, class Dual>
folly::pushmi::entangled< T, Dual >::entangled ( T  t)
inlineexplicit

Definition at line 168 of file entangle.h.

169  : stateMachine(kUnlocked), t(std::move(t)), dual(nullptr) {}
static const int kUnlocked
Definition: entangle.h:81
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
std::atomic< int > stateMachine
Definition: entangle.h:74
entangled< Dual, T > * dual
Definition: entangle.h:79
template<class T, class Dual>
folly::pushmi::entangled< T, Dual >::entangled ( entangled< T, Dual > &&  other)
inline

Definition at line 170 of file entangle.h.

References folly::pushmi::entangled< T, Dual >::dual, and folly::pushmi::entangled< T, Dual >::unlockBoth().

171  : stateMachine((other.lockBoth(), kLocked)),
172  t(std::move(other.t)),
173  dual(std::move(other.dual)) {
174  // Note that, above, we initialized stateMachine to the locked state; the
175  // address of this object hasn't escaped yet, and won't (until we unlock
176  // the dual), so it doesn't *really* matter, but it's conceptually helpful
177  // to maintain that invariant.
178 
179  // Update our dual's data.
180  if (dual != nullptr) {
181  dual->dual = this;
182  }
183 
184  // Update other's data.
185  other.dual = nullptr;
186  // unlock other so that its destructor can complete
187  other.stateMachine.store(kUnlocked);
188 
189  // We locked on other, but will unlock on *this. The locking protocol
190  // ensured that no accesses to other will occur after lock() returns, and
191  // since then we updated dual's dual to be us.
192  unlockBoth();
193  }
static const int kLocked
Definition: entangle.h:82
static const int kUnlocked
Definition: entangle.h:81
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
std::atomic< int > stateMachine
Definition: entangle.h:74
entangled< Dual, T > * dual
Definition: entangle.h:79
template<class T, class Dual>
folly::pushmi::entangled< T, Dual >::~entangled ( )
inline

Definition at line 195 of file entangle.h.

References folly::pushmi::entangled< T, Dual >::dual, folly::pushmi::entangled< T, Dual >::lockBoth(), and folly::pushmi::entangled< T, Dual >::unlockBoth().

195  {
196  lockBoth();
197  if (dual != nullptr) {
198  dual->dual = nullptr;
199  }
200  unlockBoth();
201  }
entangled< Dual, T > * dual
Definition: entangle.h:79

Member Function Documentation

template<class T, class Dual>
void folly::pushmi::entangled< T, Dual >::lockBoth ( )
inline

Definition at line 139 of file entangle.h.

References folly::pushmi::entangled< T, Dual >::tryLockBoth().

Referenced by folly::pushmi::locked_entangled_pair< T, Dual >::locked_entangled_pair(), folly::pushmi::entangled< T, Dual >::lockPointerToDual(), and folly::pushmi::entangled< T, Dual >::~entangled().

139  {
140  while (!tryLockBoth()) {
141  // Spin. But, note that all the unbounded spinning in tryLockBoth can be
142  // straightforwardly futex-ified. There's a potentialy starvation issue
143  // here, but note that it can be dealt with by adding a "priority" bit to
144  // the state machine (i.e. if my priority bit is set, the thread for whom
145  // I'm the local member of the pair gets to win the race, rather than
146  // using address-ordering).
147  }
148  }
template<class T, class Dual>
Dual* folly::pushmi::entangled< T, Dual >::lockPointerToDual ( )
inline

Definition at line 204 of file entangle.h.

References folly::pushmi::entangled< T, Dual >::lockBoth(), and folly::pushmi::entangled< T, Dual >::t.

204  {
205  lockBoth();
206  return !!dual ? std::addressof(dual->t) : nullptr;
207  }
#define nullptr
Definition: http_parser.c:41
entangled< Dual, T > * dual
Definition: entangle.h:79
template<class T, class Dual>
entangled& folly::pushmi::entangled< T, Dual >::operator= ( entangled< T, Dual > &&  )
delete
template<class T, class Dual>
bool folly::pushmi::entangled< T, Dual >::tryLockBoth ( )
inline

Definition at line 92 of file entangle.h.

References folly::pushmi::entangled< T, Dual >::kLockedAndLossAcknowledged, folly::pushmi::entangled< T, Dual >::kUnlocked, and folly::pushmi::entangled< T, Dual >::stateMachine.

Referenced by folly::pushmi::entangled< T, Dual >::lockBoth().

92  {
93  // Try to acquire the local lock. We have to start locally, since local
94  // addresses are the only ones we know are safe at first. The rule is, you
95  // have to hold *both* locks to write any of either entangled object's
96  // metadata, but need only one to read it.
97  int expected = kUnlocked;
98  if (!stateMachine.compare_exchange_weak(
99  expected,
100  kLocked,
101  std::memory_order_seq_cst,
102  std::memory_order_relaxed)) {
103  return false;
104  }
105  // Having *either* object local-locked protects the data in both objects.
106  // Once we hold our lock, no control data can change, in either object.
107  if (dual == nullptr) {
108  return true;
109  }
110  expected = kUnlocked;
111  if (dual->stateMachine.compare_exchange_strong(
112  expected, kLocked, std::memory_order_seq_cst)) {
113  return true;
114  }
115  // We got here, and so hit the race; we're deadlocked if we stick to
116  // locking. Revert to address-ordering. Note that address-ordering would
117  // not be safe on its own, because of the lifetime issues involved; the
118  // addresses here are only stable *because* we know both sides are locked,
119  // and because of the invariant that you must hold both locks to modify
120  // either piece of data.
121  if ((uintptr_t)this < (uintptr_t)dual) {
122  // I get to win the race. I'll acquire the locks, but have to make sure
123  // my memory stays valid until the other thread acknowledges its loss.
124  while (stateMachine.load(std::memory_order_relaxed) !=
126  // Spin.
127  }
128  stateMachine.store(kLocked, std::memory_order_relaxed);
129  return true;
130  } else {
131  // I lose the race, but have to coordinate with the winning thread, so
132  // that it knows that I'm not about to try to touch it's data
133  dual->stateMachine.store(
134  kLockedAndLossAcknowledged, std::memory_order_relaxed);
135  return false;
136  }
137  }
static const int kLocked
Definition: entangle.h:82
static const int kUnlocked
Definition: entangle.h:81
std::atomic< int > stateMachine
Definition: entangle.h:74
entangled< Dual, T > * dual
Definition: entangle.h:79
static const int kLockedAndLossAcknowledged
Definition: entangle.h:83
template<class T, class Dual>
void folly::pushmi::entangled< T, Dual >::unlockBoth ( )
inline

Definition at line 150 of file entangle.h.

References folly::pushmi::entangled< T, Dual >::entangled(), folly::pushmi::entangled< T, Dual >::operator=(), and folly::pushmi::entangled< T, Dual >::stateMachine.

Referenced by folly::pushmi::entangled< T, Dual >::entangled(), folly::pushmi::entangled< T, Dual >::unlockPointerToDual(), folly::pushmi::entangled< T, Dual >::~entangled(), and folly::pushmi::locked_entangled_pair< T, Dual >::~locked_entangled_pair().

150  {
151  // Note that unlocking locally and then remotely is the right order. There
152  // are no concurrent accesses to this object (as an API constraint -- lock
153  // and unlock are not thread safe!), and no other thread will touch the
154  // other object so long as its locked. Going in the other order could let
155  // another thread incorrectly think we're going down the deadlock-avoidance
156  // path in tryLock().
157  stateMachine.store(kUnlocked, std::memory_order_release);
158  if (dual != nullptr) {
159  dual->stateMachine.store(kUnlocked, std::memory_order_release);
160  }
161  }
static const int kUnlocked
Definition: entangle.h:81
std::atomic< int > stateMachine
Definition: entangle.h:74
entangled< Dual, T > * dual
Definition: entangle.h:79
template<class T, class Dual>
void folly::pushmi::entangled< T, Dual >::unlockPointerToDual ( )
inline

Definition at line 209 of file entangle.h.

References folly::pushmi::entangled< T, Dual >::unlockBoth().

209  {
210  unlockBoth();
211  }

Member Data Documentation

template<class T, class Dual>
const int folly::pushmi::entangled< T, Dual >::kLocked = 1
static

Definition at line 82 of file entangle.h.

template<class T, class Dual>
const int folly::pushmi::entangled< T, Dual >::kLockedAndLossAcknowledged = 2
static

Definition at line 83 of file entangle.h.

Referenced by folly::pushmi::entangled< T, Dual >::tryLockBoth().

template<class T, class Dual>
const int folly::pushmi::entangled< T, Dual >::kUnlocked = 0
static

Definition at line 81 of file entangle.h.

Referenced by folly::pushmi::entangled< T, Dual >::tryLockBoth().

template<class T, class Dual>
std::atomic<int> folly::pushmi::entangled< T, Dual >::stateMachine

The documentation for this struct was generated from the following file: