Adding upgradable locks to Synchronized
[folly.git] / folly / Synchronized.h
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * This module implements a Synchronized abstraction useful in
19  * mutex-based concurrency.
20  *
21  * The Synchronized<T, Mutex> class is the primary public API exposed by this
22  * module.  See folly/docs/Synchronized.md for a more complete explanation of
23  * this class and its benefits.
24  */
25
26 #pragma once
27
28 #include <folly/Likely.h>
29 #include <folly/LockTraits.h>
30 #include <folly/Preprocessor.h>
31 #include <folly/SharedMutex.h>
32 #include <folly/Traits.h>
33 #include <glog/logging.h>
34 #include <mutex>
35 #include <type_traits>
36
37 namespace folly {
38
39 template <class LockedType, class Mutex, class LockPolicy>
40 class LockedPtrBase;
41 template <class LockedType, class LockPolicy>
42 class LockedPtr;
43 template <class LockedType, class LockPolicy = LockPolicyExclusive>
44 class LockedGuardPtr;
45
46 /**
47  * Public version of LockInterfaceDispatcher that contains the MutexLevel enum
48  * for the passed in mutex type
49  *
50  * This is decoupled from MutexLevelValueImpl in LockTraits.h because this
51  * ensures that a heterogenous mutex with a different API can be used.  For
52  * example - if a mutex does not have a lock_shared() method but the
53  * LockTraits specialization for it supports a static non member
54  * lock_shared(Mutex&) it can be used as a shared mutex and will provide
55  * rlock() and wlock() functions.
56  */
57 template <class Mutex>
58 using MutexLevelValue = detail::MutexLevelValueImpl<
59     true,
60     LockTraits<Mutex>::is_shared,
61     LockTraits<Mutex>::is_upgrade>;
62
63 /**
64  * SynchronizedBase is a helper parent class for Synchronized<T>.
65  *
66  * It provides wlock() and rlock() methods for shared mutex types,
67  * or lock() methods for purely exclusive mutex types.
68  */
69 template <class Subclass, detail::MutexLevel level>
70 class SynchronizedBase;
71
72 /**
73  * SynchronizedBase specialization for shared mutex types.
74  *
75  * This class provides wlock() and rlock() methods for acquiring the lock and
76  * accessing the data.
77  */
78 template <class Subclass>
79 class SynchronizedBase<Subclass, detail::MutexLevel::SHARED> {
80  public:
81   using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>;
82   using ConstWLockedPtr =
83       ::folly::LockedPtr<const Subclass, LockPolicyExclusive>;
84   using ConstLockedPtr = ::folly::LockedPtr<const Subclass, LockPolicyShared>;
85
86   /**
87    * Acquire an exclusive lock, and return a LockedPtr that can be used to
88    * safely access the datum.
89    *
90    * LockedPtr offers operator -> and * to provide access to the datum.
91    * The lock will be released when the LockedPtr is destroyed.
92    */
93   LockedPtr wlock() {
94     return LockedPtr(static_cast<Subclass*>(this));
95   }
96   ConstWLockedPtr wlock() const {
97     return ConstWLockedPtr(static_cast<const Subclass*>(this));
98   }
99
100   /**
101    * Acquire a read lock, and return a ConstLockedPtr that can be used to
102    * safely access the datum.
103    */
104   ConstLockedPtr rlock() const {
105     return ConstLockedPtr(static_cast<const Subclass*>(this));
106   }
107
108   /**
109    * Attempts to acquire the lock, or fails if the timeout elapses first.
110    * If acquisition is unsuccessful, the returned LockedPtr will be null.
111    *
112    * (Use LockedPtr::isNull() to check for validity.)
113    */
114   template <class Rep, class Period>
115   LockedPtr wlock(const std::chrono::duration<Rep, Period>& timeout) {
116     return LockedPtr(static_cast<Subclass*>(this), timeout);
117   }
118   template <class Rep, class Period>
119   ConstWLockedPtr wlock(
120       const std::chrono::duration<Rep, Period>& timeout) const {
121     return ConstWLockedPtr(static_cast<const Subclass*>(this), timeout);
122   }
123
124   /**
125    * Attempts to acquire the lock, or fails if the timeout elapses first.
126    * If acquisition is unsuccessful, the returned LockedPtr will be null.
127    *
128    * (Use LockedPtr::isNull() to check for validity.)
129    */
130   template <class Rep, class Period>
131   ConstLockedPtr rlock(
132       const std::chrono::duration<Rep, Period>& timeout) const {
133     return ConstLockedPtr(static_cast<const Subclass*>(this), timeout);
134   }
135
136   /*
137    * Note: C++ 17 adds guaranteed copy elision.  (http://wg21.link/P0135)
138    * Once compilers support this, it would be nice to add wguard() and rguard()
139    * methods that return LockedGuardPtr objects.
140    */
141
142   /**
143    * Invoke a function while holding the lock exclusively.
144    *
145    * A reference to the datum will be passed into the function as its only
146    * argument.
147    *
148    * This can be used with a lambda argument for easily defining small critical
149    * sections in the code.  For example:
150    *
151    *   auto value = obj.withWLock([](auto& data) {
152    *     data.doStuff();
153    *     return data.getValue();
154    *   });
155    */
156   template <class Function>
157   auto withWLock(Function&& function) {
158     LockedGuardPtr<Subclass, LockPolicyExclusive> guardPtr(
159         static_cast<Subclass*>(this));
160     return function(*guardPtr);
161   }
162   template <class Function>
163   auto withWLock(Function&& function) const {
164     LockedGuardPtr<const Subclass, LockPolicyExclusive> guardPtr(
165         static_cast<const Subclass*>(this));
166     return function(*guardPtr);
167   }
168
169   /**
170    * Invoke a function while holding the lock exclusively.
171    *
172    * This is similar to withWLock(), but the function will be passed a
173    * LockedPtr rather than a reference to the data itself.
174    *
175    * This allows scopedUnlock() to be called on the LockedPtr argument if
176    * desired.
177    */
178   template <class Function>
179   auto withWLockPtr(Function&& function) {
180     return function(wlock());
181   }
182   template <class Function>
183   auto withWLockPtr(Function&& function) const {
184     return function(wlock());
185   }
186
187   /**
188    * Invoke a function while holding an the lock in shared mode.
189    *
190    * A const reference to the datum will be passed into the function as its
191    * only argument.
192    */
193   template <class Function>
194   auto withRLock(Function&& function) const {
195     LockedGuardPtr<const Subclass, LockPolicyShared> guardPtr(
196         static_cast<const Subclass*>(this));
197     return function(*guardPtr);
198   }
199
200   template <class Function>
201   auto withRLockPtr(Function&& function) const {
202     return function(rlock());
203   }
204 };
205
206 /**
207  * SynchronizedBase specialization for upgrade mutex types.
208  *
209  * This class provides all the functionality provided by the SynchronizedBase
210  * specialization for shared mutexes and a ulock() method that returns an
211  * upgradable lock RAII proxy
212  */
213 template <class Subclass>
214 class SynchronizedBase<Subclass, detail::MutexLevel::UPGRADE>
215     : public SynchronizedBase<Subclass, detail::MutexLevel::SHARED> {
216  public:
217   using UpgradeLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyUpgrade>;
218   using ConstUpgradeLockedPtr =
219       ::folly::LockedPtr<const Subclass, LockPolicyUpgrade>;
220   using UpgradeLockedGuardPtr =
221       ::folly::LockedGuardPtr<Subclass, LockPolicyUpgrade>;
222   using ConstUpgradeLockedGuardPtr =
223       ::folly::LockedGuardPtr<const Subclass, LockPolicyUpgrade>;
224
225   /**
226    * Acquire an upgrade lock and return a LockedPtr that can be used to safely
227    * access the datum
228    *
229    * And the const version
230    */
231   UpgradeLockedPtr ulock() {
232     return UpgradeLockedPtr(static_cast<Subclass*>(this));
233   }
234   ConstUpgradeLockedPtr ulock() const {
235     return ConstUpgradeLockedPtr(static_cast<const Subclass*>(this));
236   }
237
238   /**
239    * Acquire an upgrade lock and return a LockedPtr that can be used to safely
240    * access the datum
241    *
242    * And the const version
243    */
244   template <class Rep, class Period>
245   UpgradeLockedPtr ulock(const std::chrono::duration<Rep, Period>& timeout) {
246     return UpgradeLockedPtr(static_cast<Subclass*>(this), timeout);
247   }
248   template <class Rep, class Period>
249   UpgradeLockedPtr ulock(
250       const std::chrono::duration<Rep, Period>& timeout) const {
251     return ConstUpgradeLockedPtr(static_cast<const Subclass*>(this), timeout);
252   }
253
254   /**
255    * Invoke a function while holding the lock.
256    *
257    * A reference to the datum will be passed into the function as its only
258    * argument.
259    *
260    * This can be used with a lambda argument for easily defining small critical
261    * sections in the code.  For example:
262    *
263    *   auto value = obj.withULock([](auto& data) {
264    *     data.doStuff();
265    *     return data.getValue();
266    *   });
267    *
268    * This is probably not the function you want.  If the intent is to read the
269    * data object and determine whether you should upgrade to a write lock then
270    * the withULockPtr() method should be called instead, since it gives access
271    * to the LockedPtr proxy (which can be upgraded via the
272    * moveFromUpgradeToWrite() method)
273    */
274   template <class Function>
275   auto withULock(Function&& function) const {
276     ConstUpgradeLockedGuardPtr guardPtr(static_cast<const Subclass*>(this));
277     return function(*guardPtr);
278   }
279
280   /**
281    * Invoke a function while holding the lock exclusively.
282    *
283    * This is similar to withULock(), but the function will be passed a
284    * LockedPtr rather than a reference to the data itself.
285    *
286    * This allows scopedUnlock() and getUniqueLock() to be called on the
287    * LockedPtr argument.
288    *
289    * This also allows you to upgrade the LockedPtr proxy to a write state so
290    * that changes can be made to the underlying data
291    */
292   template <class Function>
293   auto withULockPtr(Function&& function) {
294     return function(ulock());
295   }
296   template <class Function>
297   auto withULockPtr(Function&& function) const {
298     return function(ulock());
299   }
300 };
301
302 /**
303  * SynchronizedBase specialization for non-shared mutex types.
304  *
305  * This class provides lock() methods for acquiring the lock and accessing the
306  * data.
307  */
308 template <class Subclass>
309 class SynchronizedBase<Subclass, detail::MutexLevel::UNIQUE> {
310  public:
311   using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>;
312   using ConstLockedPtr =
313       ::folly::LockedPtr<const Subclass, LockPolicyExclusive>;
314
315   /**
316    * Acquire a lock, and return a LockedPtr that can be used to safely access
317    * the datum.
318    */
319   LockedPtr lock() {
320     return LockedPtr(static_cast<Subclass*>(this));
321   }
322
323   /**
324    * Acquire a lock, and return a ConstLockedPtr that can be used to safely
325    * access the datum.
326    */
327   ConstLockedPtr lock() const {
328     return ConstLockedPtr(static_cast<const Subclass*>(this));
329   }
330
331   /**
332    * Attempts to acquire the lock, or fails if the timeout elapses first.
333    * If acquisition is unsuccessful, the returned LockedPtr will be null.
334    */
335   template <class Rep, class Period>
336   LockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) {
337     return LockedPtr(static_cast<Subclass*>(this), timeout);
338   }
339
340   /**
341    * Attempts to acquire the lock, or fails if the timeout elapses first.
342    * If acquisition is unsuccessful, the returned LockedPtr will be null.
343    */
344   template <class Rep, class Period>
345   ConstLockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) const {
346     return ConstLockedPtr(static_cast<const Subclass*>(this), timeout);
347   }
348
349   /*
350    * Note: C++ 17 adds guaranteed copy elision.  (http://wg21.link/P0135)
351    * Once compilers support this, it would be nice to add guard() methods that
352    * return LockedGuardPtr objects.
353    */
354
355   /**
356    * Invoke a function while holding the lock.
357    *
358    * A reference to the datum will be passed into the function as its only
359    * argument.
360    *
361    * This can be used with a lambda argument for easily defining small critical
362    * sections in the code.  For example:
363    *
364    *   auto value = obj.withLock([](auto& data) {
365    *     data.doStuff();
366    *     return data.getValue();
367    *   });
368    */
369   template <class Function>
370   auto withLock(Function&& function) {
371     LockedGuardPtr<Subclass, LockPolicyExclusive> guardPtr(
372         static_cast<Subclass*>(this));
373     return function(*guardPtr);
374   }
375   template <class Function>
376   auto withLock(Function&& function) const {
377     LockedGuardPtr<const Subclass, LockPolicyExclusive> guardPtr(
378         static_cast<const Subclass*>(this));
379     return function(*guardPtr);
380   }
381
382   /**
383    * Invoke a function while holding the lock exclusively.
384    *
385    * This is similar to withWLock(), but the function will be passed a
386    * LockedPtr rather than a reference to the data itself.
387    *
388    * This allows scopedUnlock() and getUniqueLock() to be called on the
389    * LockedPtr argument.
390    */
391   template <class Function>
392   auto withLockPtr(Function&& function) {
393     return function(lock());
394   }
395   template <class Function>
396   auto withLockPtr(Function&& function) const {
397     return function(lock());
398   }
399 };
400
401 /**
402  * Synchronized<T> encapsulates an object of type T (a "datum") paired
403  * with a mutex. The only way to access the datum is while the mutex
404  * is locked, and Synchronized makes it virtually impossible to do
405  * otherwise. The code that would access the datum in unsafe ways
406  * would look odd and convoluted, thus readily alerting the human
407  * reviewer. In contrast, the code that uses Synchronized<T> correctly
408  * looks simple and intuitive.
409  *
410  * The second parameter must be a mutex type.  Any mutex type supported by
411  * LockTraits<Mutex> can be used.  By default any class with lock() and
412  * unlock() methods will work automatically.  LockTraits can be specialized to
413  * teach Synchronized how to use other custom mutex types.  See the
414  * documentation in LockTraits.h for additional details.
415  *
416  * Supported mutexes that work by default include std::mutex,
417  * std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex,
418  * folly::SharedMutex, folly::RWSpinLock, and folly::SpinLock.
419  * Include LockTraitsBoost.h to get additional LockTraits specializations to
420  * support the following boost mutex types: boost::mutex,
421  * boost::recursive_mutex, boost::shared_mutex, boost::timed_mutex, and
422  * boost::recursive_timed_mutex.
423  */
424 template <class T, class Mutex = SharedMutex>
425 struct Synchronized : public SynchronizedBase<
426                           Synchronized<T, Mutex>,
427                           MutexLevelValue<Mutex>::value> {
428  private:
429   using Base =
430       SynchronizedBase<Synchronized<T, Mutex>, MutexLevelValue<Mutex>::value>;
431   static constexpr bool nxCopyCtor{
432       std::is_nothrow_copy_constructible<T>::value};
433   static constexpr bool nxMoveCtor{
434       std::is_nothrow_move_constructible<T>::value};
435
436  public:
437   using LockedPtr = typename Base::LockedPtr;
438   using ConstLockedPtr = typename Base::ConstLockedPtr;
439   using DataType = T;
440   using MutexType = Mutex;
441
442   /**
443    * Default constructor leaves both members call their own default
444    * constructor.
445    */
446   Synchronized() = default;
447
448   /**
449    * Copy constructor copies the data (with locking the source and
450    * all) but does NOT copy the mutex. Doing so would result in
451    * deadlocks.
452    *
453    * Note that the copy constructor may throw because it acquires a lock in
454    * the contextualRLock() method
455    */
456   Synchronized(const Synchronized& rhs) /* may throw */
457       : Synchronized(rhs, rhs.contextualRLock()) {}
458
459   /**
460    * Move constructor moves the data (with locking the source and all)
461    * but does not move the mutex.
462    *
463    * Note that the move constructor may throw because it acquires a lock.
464    * Since the move constructor is not declared noexcept, when objects of this
465    * class are used as elements in a vector or a similar container.  The
466    * elements might not be moved around when resizing.  They might be copied
467    * instead.  You have been warned.
468    */
469   Synchronized(Synchronized&& rhs) /* may throw */
470       : Synchronized(std::move(rhs), rhs.contextualLock()) {}
471
472   /**
473    * Constructor taking a datum as argument copies it. There is no
474    * need to lock the constructing object.
475    */
476   explicit Synchronized(const T& rhs) noexcept(nxCopyCtor) : datum_(rhs) {}
477
478   /**
479    * Constructor taking a datum rvalue as argument moves it. Again,
480    * there is no need to lock the constructing object.
481    */
482   explicit Synchronized(T&& rhs) noexcept(nxMoveCtor)
483       : datum_(std::move(rhs)) {}
484
485   /**
486    * Lets you construct non-movable types in-place. Use the constexpr
487    * instance `construct_in_place` as the first argument.
488    */
489   template <typename... Args>
490   explicit Synchronized(construct_in_place_t, Args&&... args)
491       : datum_(std::forward<Args>(args)...) {}
492
493   /**
494    * The canonical assignment operator only assigns the data, NOT the
495    * mutex. It locks the two objects in ascending order of their
496    * addresses.
497    */
498   Synchronized& operator=(const Synchronized& rhs) {
499     if (this == &rhs) {
500       // Self-assignment, pass.
501     } else if (this < &rhs) {
502       auto guard1 = operator->();
503       auto guard2 = rhs.operator->();
504       datum_ = rhs.datum_;
505     } else {
506       auto guard1 = rhs.operator->();
507       auto guard2 = operator->();
508       datum_ = rhs.datum_;
509     }
510     return *this;
511   }
512
513   /**
514    * Move assignment operator, only assigns the data, NOT the
515    * mutex. It locks the two objects in ascending order of their
516    * addresses.
517    */
518   Synchronized& operator=(Synchronized&& rhs) {
519     if (this == &rhs) {
520       // Self-assignment, pass.
521     } else if (this < &rhs) {
522       auto guard1 = operator->();
523       auto guard2 = rhs.operator->();
524       datum_ = std::move(rhs.datum_);
525     } else {
526       auto guard1 = rhs.operator->();
527       auto guard2 = operator->();
528       datum_ = std::move(rhs.datum_);
529     }
530     return *this;
531   }
532
533   /**
534    * Lock object, assign datum.
535    */
536   Synchronized& operator=(const T& rhs) {
537     auto guard = operator->();
538     datum_ = rhs;
539     return *this;
540   }
541
542   /**
543    * Lock object, move-assign datum.
544    */
545   Synchronized& operator=(T&& rhs) {
546     auto guard = operator->();
547     datum_ = std::move(rhs);
548     return *this;
549   }
550
551   /**
552    * Acquire an appropriate lock based on the context.
553    *
554    * If the mutex is a shared mutex, and the Synchronized instance is const,
555    * this acquires a shared lock.  Otherwise this acquires an exclusive lock.
556    *
557    * In general, prefer using the explicit rlock() and wlock() methods
558    * for read-write locks, and lock() for purely exclusive locks.
559    *
560    * contextualLock() is primarily intended for use in other template functions
561    * that do not necessarily know the lock type.
562    */
563   LockedPtr contextualLock() {
564     return LockedPtr(this);
565   }
566   ConstLockedPtr contextualLock() const {
567     return ConstLockedPtr(this);
568   }
569   template <class Rep, class Period>
570   LockedPtr contextualLock(const std::chrono::duration<Rep, Period>& timeout) {
571     return LockedPtr(this, timeout);
572   }
573   template <class Rep, class Period>
574   ConstLockedPtr contextualLock(
575       const std::chrono::duration<Rep, Period>& timeout) const {
576     return ConstLockedPtr(this, timeout);
577   }
578   /**
579    * contextualRLock() acquires a read lock if the mutex type is shared,
580    * or a regular exclusive lock for non-shared mutex types.
581    *
582    * contextualRLock() when you know that you prefer a read lock (if
583    * available), even if the Synchronized<T> object itself is non-const.
584    */
585   ConstLockedPtr contextualRLock() const {
586     return ConstLockedPtr(this);
587   }
588   template <class Rep, class Period>
589   ConstLockedPtr contextualRLock(
590       const std::chrono::duration<Rep, Period>& timeout) const {
591     return ConstLockedPtr(this, timeout);
592   }
593
594   /**
595    * This accessor offers a LockedPtr. In turn, LockedPtr offers
596    * operator-> returning a pointer to T. The operator-> keeps
597    * expanding until it reaches a pointer, so syncobj->foo() will lock
598    * the object and call foo() against it.
599    *
600    * NOTE: This API is planned to be deprecated in an upcoming diff.
601    * Prefer using lock(), wlock(), or rlock() instead.
602    */
603   LockedPtr operator->() {
604     return LockedPtr(this);
605   }
606
607   /**
608    * Obtain a ConstLockedPtr.
609    *
610    * NOTE: This API is planned to be deprecated in an upcoming diff.
611    * Prefer using lock(), wlock(), or rlock() instead.
612    */
613   ConstLockedPtr operator->() const {
614     return ConstLockedPtr(this);
615   }
616
617   /**
618    * Attempts to acquire for a given number of milliseconds. If
619    * acquisition is unsuccessful, the returned LockedPtr is NULL.
620    *
621    * NOTE: This API is deprecated.  Use lock(), wlock(), or rlock() instead.
622    * In the future it will be marked with a deprecation attribute to emit
623    * build-time warnings, and then it will be removed entirely.
624    */
625   LockedPtr timedAcquire(unsigned int milliseconds) {
626     return LockedPtr(this, std::chrono::milliseconds(milliseconds));
627   }
628
629   /**
630    * Attempts to acquire for a given number of milliseconds. If
631    * acquisition is unsuccessful, the returned ConstLockedPtr is NULL.
632    *
633    * NOTE: This API is deprecated.  Use lock(), wlock(), or rlock() instead.
634    * In the future it will be marked with a deprecation attribute to emit
635    * build-time warnings, and then it will be removed entirely.
636    */
637   ConstLockedPtr timedAcquire(unsigned int milliseconds) const {
638     return ConstLockedPtr(this, std::chrono::milliseconds(milliseconds));
639   }
640
641   /**
642    * Sometimes, although you have a mutable object, you only want to
643    * call a const method against it. The most efficient way to achieve
644    * that is by using a read lock. You get to do so by using
645    * obj.asConst()->method() instead of obj->method().
646    *
647    * NOTE: This API is planned to be deprecated in an upcoming diff.
648    * Use rlock() instead.
649    */
650   const Synchronized& asConst() const {
651     return *this;
652   }
653
654   /**
655    * Swaps with another Synchronized. Protected against
656    * self-swap. Only data is swapped. Locks are acquired in increasing
657    * address order.
658    */
659   void swap(Synchronized& rhs) {
660     if (this == &rhs) {
661       return;
662     }
663     if (this > &rhs) {
664       return rhs.swap(*this);
665     }
666     auto guard1 = operator->();
667     auto guard2 = rhs.operator->();
668
669     using std::swap;
670     swap(datum_, rhs.datum_);
671   }
672
673   /**
674    * Swap with another datum. Recommended because it keeps the mutex
675    * held only briefly.
676    */
677   void swap(T& rhs) {
678     LockedPtr guard(this);
679
680     using std::swap;
681     swap(datum_, rhs);
682   }
683
684   /**
685    * Copies datum to a given target.
686    */
687   void copy(T* target) const {
688     ConstLockedPtr guard(this);
689     *target = datum_;
690   }
691
692   /**
693    * Returns a fresh copy of the datum.
694    */
695   T copy() const {
696     ConstLockedPtr guard(this);
697     return datum_;
698   }
699
700  private:
701   template <class LockedType, class MutexType, class LockPolicy>
702   friend class folly::LockedPtrBase;
703   template <class LockedType, class LockPolicy>
704   friend class folly::LockedPtr;
705   template <class LockedType, class LockPolicy>
706   friend class folly::LockedGuardPtr;
707
708   /**
709    * Helper constructors to enable Synchronized for
710    * non-default constructible types T.
711    * Guards are created in actual public constructors and are alive
712    * for the time required to construct the object
713    */
714   Synchronized(
715       const Synchronized& rhs,
716       const ConstLockedPtr& /*guard*/) noexcept(nxCopyCtor)
717       : datum_(rhs.datum_) {}
718
719   Synchronized(Synchronized&& rhs, const LockedPtr& /*guard*/) noexcept(
720       nxMoveCtor)
721       : datum_(std::move(rhs.datum_)) {}
722
723   // Synchronized data members
724   T datum_;
725   mutable Mutex mutex_;
726 };
727
728 template <class SynchronizedType, class LockPolicy>
729 class ScopedUnlocker;
730
731 namespace detail {
732 /*
733  * A helper alias that resolves to "const T" if the template parameter
734  * is a const Synchronized<T>, or "T" if the parameter is not const.
735  */
736 template <class SynchronizedType>
737 using SynchronizedDataType = typename std::conditional<
738     std::is_const<SynchronizedType>::value,
739     typename SynchronizedType::DataType const,
740     typename SynchronizedType::DataType>::type;
741 /*
742  * A helper alias that resolves to a ConstLockedPtr if the template parameter
743  * is a const Synchronized<T>, or a LockedPtr if the parameter is not const.
744  */
745 template <class SynchronizedType>
746 using LockedPtrType = typename std::conditional<
747     std::is_const<SynchronizedType>::value,
748     typename SynchronizedType::ConstLockedPtr,
749     typename SynchronizedType::LockedPtr>::type;
750 } // detail
751
752 /**
753  * A helper base class for implementing LockedPtr.
754  *
755  * The main reason for having this as a separate class is so we can specialize
756  * it for std::mutex, so we can expose a std::unique_lock to the caller
757  * when std::mutex is being used.  This allows callers to use a
758  * std::condition_variable with the mutex from a Synchronized<T, std::mutex>.
759  *
760  * We don't use std::unique_lock with other Mutex types since it makes the
761  * LockedPtr class slightly larger, and it makes the logic to support
762  * ScopedUnlocker slightly more complicated.  std::mutex is the only one that
763  * really seems to benefit from the unique_lock.  std::condition_variable
764  * itself only supports std::unique_lock<std::mutex>, so there doesn't seem to
765  * be any real benefit to exposing the unique_lock with other mutex types.
766  *
767  * Note that the SynchronizedType template parameter may or may not be const
768  * qualified.
769  */
770 template <class SynchronizedType, class Mutex, class LockPolicy>
771 class LockedPtrBase {
772  public:
773   using MutexType = Mutex;
774   friend class folly::ScopedUnlocker<SynchronizedType, LockPolicy>;
775
776   /**
777    * Destructor releases.
778    */
779   ~LockedPtrBase() {
780     if (parent_) {
781       LockPolicy::unlock(parent_->mutex_);
782     }
783   }
784
785   /**
786    * Unlock the synchronized data.
787    *
788    * The LockedPtr can no longer be dereferenced after unlock() has been
789    * called.  isValid() will return false on an unlocked LockedPtr.
790    *
791    * unlock() can only be called on a LockedPtr that is valid.
792    */
793   void unlock() {
794     DCHECK(parent_ != nullptr);
795     LockPolicy::unlock(parent_->mutex_);
796     parent_ = nullptr;
797   }
798
799  protected:
800   LockedPtrBase() {}
801   explicit LockedPtrBase(SynchronizedType* parent) : parent_(parent) {
802     LockPolicy::lock(parent_->mutex_);
803   }
804   template <class Rep, class Period>
805   LockedPtrBase(
806       SynchronizedType* parent,
807       const std::chrono::duration<Rep, Period>& timeout) {
808     if (LockPolicy::try_lock_for(parent->mutex_, timeout)) {
809       this->parent_ = parent;
810     }
811   }
812   LockedPtrBase(LockedPtrBase&& rhs) noexcept : parent_(rhs.parent_) {
813     rhs.parent_ = nullptr;
814   }
815   LockedPtrBase& operator=(LockedPtrBase&& rhs) noexcept {
816     if (parent_) {
817       LockPolicy::unlock(parent_->mutex_);
818     }
819
820     parent_ = rhs.parent_;
821     rhs.parent_ = nullptr;
822     return *this;
823   }
824
825   using UnlockerData = SynchronizedType*;
826
827   /**
828    * Get a pointer to the Synchronized object from the UnlockerData.
829    *
830    * In the generic case UnlockerData is just the Synchronized pointer,
831    * so we return it as is.  (This function is more interesting in the
832    * std::mutex specialization below.)
833    */
834   static SynchronizedType* getSynchronized(UnlockerData data) {
835     return data;
836   }
837
838   UnlockerData releaseLock() {
839     DCHECK(parent_ != nullptr);
840     auto current = parent_;
841     parent_ = nullptr;
842     LockPolicy::unlock(current->mutex_);
843     return current;
844   }
845   void reacquireLock(UnlockerData&& data) {
846     DCHECK(parent_ == nullptr);
847     parent_ = data;
848     LockPolicy::lock(parent_->mutex_);
849   }
850
851   SynchronizedType* parent_ = nullptr;
852 };
853
854 /**
855  * LockedPtrBase specialization for use with std::mutex.
856  *
857  * When std::mutex is used we use a std::unique_lock to hold the mutex.
858  * This makes it possible to use std::condition_variable with a
859  * Synchronized<T, std::mutex>.
860  */
861 template <class SynchronizedType, class LockPolicy>
862 class LockedPtrBase<SynchronizedType, std::mutex, LockPolicy> {
863  public:
864   using MutexType = std::mutex;
865   friend class folly::ScopedUnlocker<SynchronizedType, LockPolicy>;
866
867   /**
868    * Destructor releases.
869    */
870   ~LockedPtrBase() {
871     // The std::unique_lock will automatically release the lock when it is
872     // destroyed, so we don't need to do anything extra here.
873   }
874
875   LockedPtrBase(LockedPtrBase&& rhs) noexcept
876       : lock_(std::move(rhs.lock_)), parent_(rhs.parent_) {
877     rhs.parent_ = nullptr;
878   }
879   LockedPtrBase& operator=(LockedPtrBase&& rhs) noexcept {
880     lock_ = std::move(rhs.lock_);
881     parent_ = rhs.parent_;
882     rhs.parent_ = nullptr;
883     return *this;
884   }
885
886   /**
887    * Get a reference to the std::unique_lock.
888    *
889    * This is provided so that callers can use Synchronized<T, std::mutex>
890    * with a std::condition_variable.
891    *
892    * While this API could be used to bypass the normal Synchronized APIs and
893    * manually interact with the underlying unique_lock, this is strongly
894    * discouraged.
895    */
896   std::unique_lock<std::mutex>& getUniqueLock() {
897     return lock_;
898   }
899
900   /**
901    * Unlock the synchronized data.
902    *
903    * The LockedPtr can no longer be dereferenced after unlock() has been
904    * called.  isValid() will return false on an unlocked LockedPtr.
905    *
906    * unlock() can only be called on a LockedPtr that is valid.
907    */
908   void unlock() {
909     DCHECK(parent_ != nullptr);
910     lock_.unlock();
911     parent_ = nullptr;
912   }
913
914  protected:
915   LockedPtrBase() {}
916   explicit LockedPtrBase(SynchronizedType* parent)
917       : lock_(parent->mutex_), parent_(parent) {}
918
919   using UnlockerData =
920       std::pair<std::unique_lock<std::mutex>, SynchronizedType*>;
921
922   static SynchronizedType* getSynchronized(const UnlockerData& data) {
923     return data.second;
924   }
925
926   UnlockerData releaseLock() {
927     DCHECK(parent_ != nullptr);
928     UnlockerData data(std::move(lock_), parent_);
929     parent_ = nullptr;
930     data.first.unlock();
931     return data;
932   }
933   void reacquireLock(UnlockerData&& data) {
934     lock_ = std::move(data.first);
935     lock_.lock();
936     parent_ = data.second;
937   }
938
939   // The specialization for std::mutex does have to store slightly more
940   // state than the default implementation.
941   std::unique_lock<std::mutex> lock_;
942   SynchronizedType* parent_ = nullptr;
943 };
944
945 /**
946  * This class temporarily unlocks a LockedPtr in a scoped manner.
947  */
948 template <class SynchronizedType, class LockPolicy>
949 class ScopedUnlocker {
950  public:
951   explicit ScopedUnlocker(LockedPtr<SynchronizedType, LockPolicy>* p)
952       : ptr_(p), data_(ptr_->releaseLock()) {}
953   ScopedUnlocker(const ScopedUnlocker&) = delete;
954   ScopedUnlocker& operator=(const ScopedUnlocker&) = delete;
955   ScopedUnlocker(ScopedUnlocker&& other) noexcept
956       : ptr_(other.ptr_), data_(std::move(other.data_)) {
957     other.ptr_ = nullptr;
958   }
959   ScopedUnlocker& operator=(ScopedUnlocker&& other) = delete;
960
961   ~ScopedUnlocker() {
962     if (ptr_) {
963       ptr_->reacquireLock(std::move(data_));
964     }
965   }
966
967   /**
968    * Return a pointer to the Synchronized object used by this ScopedUnlocker.
969    */
970   SynchronizedType* getSynchronized() const {
971     return LockedPtr<SynchronizedType, LockPolicy>::getSynchronized(data_);
972   }
973
974  private:
975   using Data = typename LockedPtr<SynchronizedType, LockPolicy>::UnlockerData;
976   LockedPtr<SynchronizedType, LockPolicy>* ptr_{nullptr};
977   Data data_;
978 };
979
980 /**
981  * A LockedPtr keeps a Synchronized<T> object locked for the duration of
982  * LockedPtr's existence.
983  *
984  * It provides access the datum's members directly by using operator->() and
985  * operator*().
986  *
987  * The LockPolicy parameter controls whether or not the lock is acquired in
988  * exclusive or shared mode.
989  */
990 template <class SynchronizedType, class LockPolicy>
991 class LockedPtr : public LockedPtrBase<
992                       SynchronizedType,
993                       typename SynchronizedType::MutexType,
994                       LockPolicy> {
995  private:
996   using Base = LockedPtrBase<
997       SynchronizedType,
998       typename SynchronizedType::MutexType,
999       LockPolicy>;
1000   using UnlockerData = typename Base::UnlockerData;
1001   // CDataType is the DataType with the appropriate const-qualification
1002   using CDataType = detail::SynchronizedDataType<SynchronizedType>;
1003
1004  public:
1005   using DataType = typename SynchronizedType::DataType;
1006   using MutexType = typename SynchronizedType::MutexType;
1007   using Synchronized = typename std::remove_const<SynchronizedType>::type;
1008   friend class ScopedUnlocker<SynchronizedType, LockPolicy>;
1009
1010   /**
1011    * Creates an uninitialized LockedPtr.
1012    *
1013    * Dereferencing an uninitialized LockedPtr is not allowed.
1014    */
1015   LockedPtr() {}
1016
1017   /**
1018    * Takes a Synchronized<T> and locks it.
1019    */
1020   explicit LockedPtr(SynchronizedType* parent) : Base(parent) {}
1021
1022   /**
1023    * Takes a Synchronized<T> and attempts to lock it, within the specified
1024    * timeout.
1025    *
1026    * Blocks until the lock is acquired or until the specified timeout expires.
1027    * If the timeout expired without acquiring the lock, the LockedPtr will be
1028    * null, and LockedPtr::isNull() will return true.
1029    */
1030   template <class Rep, class Period>
1031   LockedPtr(
1032       SynchronizedType* parent,
1033       const std::chrono::duration<Rep, Period>& timeout)
1034       : Base(parent, timeout) {}
1035
1036   /**
1037    * Move constructor.
1038    */
1039   LockedPtr(LockedPtr&& rhs) noexcept = default;
1040
1041   /**
1042    * Move assignment operator.
1043    */
1044   LockedPtr& operator=(LockedPtr&& rhs) noexcept = default;
1045
1046   /*
1047    * Copy constructor and assignment operator are deleted.
1048    */
1049   LockedPtr(const LockedPtr& rhs) = delete;
1050   LockedPtr& operator=(const LockedPtr& rhs) = delete;
1051
1052   /**
1053    * Destructor releases.
1054    */
1055   ~LockedPtr() {}
1056
1057   /**
1058    * Check if this LockedPtr is uninitialized, or points to valid locked data.
1059    *
1060    * This method can be used to check if a timed-acquire operation succeeded.
1061    * If an acquire operation times out it will result in a null LockedPtr.
1062    *
1063    * A LockedPtr is always either null, or holds a lock to valid data.
1064    * Methods such as scopedUnlock() reset the LockedPtr to null for the
1065    * duration of the unlock.
1066    */
1067   bool isNull() const {
1068     return this->parent_ == nullptr;
1069   }
1070
1071   /**
1072    * Explicit boolean conversion.
1073    *
1074    * Returns !isNull()
1075    */
1076   explicit operator bool() const {
1077     return this->parent_ != nullptr;
1078   }
1079
1080   /**
1081    * Access the locked data.
1082    *
1083    * This method should only be used if the LockedPtr is valid.
1084    */
1085   CDataType* operator->() const {
1086     return &this->parent_->datum_;
1087   }
1088
1089   /**
1090    * Access the locked data.
1091    *
1092    * This method should only be used if the LockedPtr is valid.
1093    */
1094   CDataType& operator*() const {
1095     return this->parent_->datum_;
1096   }
1097
1098   /**
1099    * Temporarily unlock the LockedPtr, and reset it to null.
1100    *
1101    * Returns an helper object that will re-lock and restore the LockedPtr when
1102    * the helper is destroyed.  The LockedPtr may not be dereferenced for as
1103    * long as this helper object exists.
1104    */
1105   ScopedUnlocker<SynchronizedType, LockPolicy> scopedUnlock() {
1106     return ScopedUnlocker<SynchronizedType, LockPolicy>(this);
1107   }
1108
1109   /***************************************************************************
1110    * Upgradable lock methods.
1111    * These are disabled via SFINAE when the mutex is not upgradable
1112    **************************************************************************/
1113   /**
1114    * Move the locked ptr from an upgrade state to an exclusive state.  The
1115    * current lock is left in a null state.
1116    */
1117   template <
1118       typename SyncType = SynchronizedType,
1119       typename = typename std::enable_if<
1120           LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
1121   LockedPtr<SynchronizedType, LockPolicyFromUpgradeToExclusive>
1122   moveFromUpgradeToWrite() {
1123     auto* parent_to_pass_on = this->parent_;
1124     this->parent_ = nullptr;
1125     return LockedPtr<SynchronizedType, LockPolicyFromUpgradeToExclusive>(
1126         parent_to_pass_on);
1127   }
1128
1129   /**
1130    * Move the locked ptr from an exclusive state to an upgrade state.  The
1131    * current lock is left in a null state.
1132    */
1133   template <
1134       typename SyncType = SynchronizedType,
1135       typename = typename std::enable_if<
1136           LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
1137   LockedPtr<SynchronizedType, LockPolicyFromExclusiveToUpgrade>
1138   moveFromWriteToUpgrade() {
1139     auto* parent_to_pass_on = this->parent_;
1140     this->parent_ = nullptr;
1141     return LockedPtr<SynchronizedType, LockPolicyFromExclusiveToUpgrade>(
1142         parent_to_pass_on);
1143   }
1144
1145   /**
1146    * Move the locked ptr from an upgrade state to a shared state.  The
1147    * current lock is left in a null state.
1148    */
1149   template <
1150       typename SyncType = SynchronizedType,
1151       typename = typename std::enable_if<
1152           LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
1153   LockedPtr<SynchronizedType, LockPolicyFromUpgradeToShared>
1154   moveFromUpgradeToShared() {
1155     auto* parent_to_pass_on = this->parent_;
1156     this->parent_ = nullptr;
1157     return LockedPtr<SynchronizedType, LockPolicyFromUpgradeToShared>(
1158         parent_to_pass_on);
1159   }
1160
1161   /**
1162    * Move the locked ptr from an exclusive state to a shared state.  The
1163    * current lock is left in a null state.
1164    */
1165   template <
1166       typename SyncType = SynchronizedType,
1167       typename = typename std::enable_if<
1168           LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
1169   LockedPtr<SynchronizedType, LockPolicyFromExclusiveToShared>
1170   moveFromWriteToShared() {
1171     auto* parent_to_pass_on = this->parent_;
1172     this->parent_ = nullptr;
1173     return LockedPtr<SynchronizedType, LockPolicyFromExclusiveToShared>(
1174         parent_to_pass_on);
1175   }
1176 };
1177
1178 /**
1179  * LockedGuardPtr is a simplified version of LockedPtr.
1180  *
1181  * It is non-movable, and supports fewer features than LockedPtr.  However, it
1182  * is ever-so-slightly more performant than LockedPtr.  (The destructor can
1183  * unconditionally release the lock, without requiring a conditional branch.)
1184  *
1185  * The relationship between LockedGuardPtr and LockedPtr is similar to that
1186  * between std::lock_guard and std::unique_lock.
1187  */
1188 template <class SynchronizedType, class LockPolicy>
1189 class LockedGuardPtr {
1190  private:
1191   // CDataType is the DataType with the appropriate const-qualification
1192   using CDataType = detail::SynchronizedDataType<SynchronizedType>;
1193
1194  public:
1195   using DataType = typename SynchronizedType::DataType;
1196   using MutexType = typename SynchronizedType::MutexType;
1197   using Synchronized = typename std::remove_const<SynchronizedType>::type;
1198
1199   LockedGuardPtr() = delete;
1200
1201   /**
1202    * Takes a Synchronized<T> and locks it.
1203    */
1204   explicit LockedGuardPtr(SynchronizedType* parent) : parent_(parent) {
1205     LockPolicy::lock(parent_->mutex_);
1206   }
1207
1208   /**
1209    * Destructor releases.
1210    */
1211   ~LockedGuardPtr() {
1212     LockPolicy::unlock(parent_->mutex_);
1213   }
1214
1215   /**
1216    * Access the locked data.
1217    */
1218   CDataType* operator->() const {
1219     return &parent_->datum_;
1220   }
1221
1222   /**
1223    * Access the locked data.
1224    */
1225   CDataType& operator*() const {
1226     return parent_->datum_;
1227   }
1228
1229  private:
1230   // This is the entire state of LockedGuardPtr.
1231   SynchronizedType* const parent_{nullptr};
1232 };
1233
1234 /**
1235  * Acquire locks for multiple Synchronized<T> objects, in a deadlock-safe
1236  * manner.
1237  *
1238  * The locks are acquired in order from lowest address to highest address.
1239  * (Note that this is not necessarily the same algorithm used by std::lock().)
1240  *
1241  * For parameters that are const and support shared locks, a read lock is
1242  * acquired.  Otherwise an exclusive lock is acquired.
1243  *
1244  * TODO: Extend acquireLocked() with variadic template versions that
1245  * allow for more than 2 Synchronized arguments.  (I haven't given too much
1246  * thought about how to implement this.  It seems like it would be rather
1247  * complicated, but I think it should be possible.)
1248  */
1249 template <class Sync1, class Sync2>
1250 std::tuple<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>>
1251 acquireLocked(Sync1& l1, Sync2& l2) {
1252   if (static_cast<const void*>(&l1) < static_cast<const void*>(&l2)) {
1253     auto p1 = l1.contextualLock();
1254     auto p2 = l2.contextualLock();
1255     return std::make_tuple(std::move(p1), std::move(p2));
1256   } else {
1257     auto p2 = l2.contextualLock();
1258     auto p1 = l1.contextualLock();
1259     return std::make_tuple(std::move(p1), std::move(p2));
1260   }
1261 }
1262
1263 /**
1264  * A version of acquireLocked() that returns a std::pair rather than a
1265  * std::tuple, which is easier to use in many places.
1266  */
1267 template <class Sync1, class Sync2>
1268 std::pair<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>>
1269 acquireLockedPair(Sync1& l1, Sync2& l2) {
1270   auto lockedPtrs = acquireLocked(l1, l2);
1271   return {std::move(std::get<0>(lockedPtrs)),
1272           std::move(std::get<1>(lockedPtrs))};
1273 }
1274
1275 /************************************************************************
1276  * NOTE: All APIs below this line will be deprecated in upcoming diffs.
1277  ************************************************************************/
1278
1279 // Non-member swap primitive
1280 template <class T, class M>
1281 void swap(Synchronized<T, M>& lhs, Synchronized<T, M>& rhs) {
1282   lhs.swap(rhs);
1283 }
1284
1285 /**
1286  * SYNCHRONIZED is the main facility that makes Synchronized<T>
1287  * helpful. It is a pseudo-statement that introduces a scope where the
1288  * object is locked. Inside that scope you get to access the unadorned
1289  * datum.
1290  *
1291  * Example:
1292  *
1293  * Synchronized<vector<int>> svector;
1294  * ...
1295  * SYNCHRONIZED (svector) { ... use svector as a vector<int> ... }
1296  * or
1297  * SYNCHRONIZED (v, svector) { ... use v as a vector<int> ... }
1298  *
1299  * Refer to folly/docs/Synchronized.md for a detailed explanation and more
1300  * examples.
1301  */
1302 #define SYNCHRONIZED(...)                                             \
1303   FOLLY_PUSH_WARNING                                                  \
1304   FOLLY_GCC_DISABLE_WARNING(shadow)                                   \
1305   if (bool SYNCHRONIZED_state = false) {                              \
1306   } else                                                              \
1307     for (auto SYNCHRONIZED_lockedPtr =                                \
1308              (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).operator->(); \
1309          !SYNCHRONIZED_state;                                         \
1310          SYNCHRONIZED_state = true)                                   \
1311       for (auto& FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) =                \
1312                *SYNCHRONIZED_lockedPtr.operator->();                  \
1313            !SYNCHRONIZED_state;                                       \
1314            SYNCHRONIZED_state = true)                                 \
1315   FOLLY_POP_WARNING
1316
1317 #define TIMED_SYNCHRONIZED(timeout, ...)                                       \
1318   if (bool SYNCHRONIZED_state = false) {                                       \
1319   } else                                                                       \
1320     for (auto SYNCHRONIZED_lockedPtr =                                         \
1321              (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).timedAcquire(timeout); \
1322          !SYNCHRONIZED_state;                                                  \
1323          SYNCHRONIZED_state = true)                                            \
1324       for (auto FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) =                          \
1325                (!SYNCHRONIZED_lockedPtr                                        \
1326                     ? nullptr                                                  \
1327                     : SYNCHRONIZED_lockedPtr.operator->());                    \
1328            !SYNCHRONIZED_state;                                                \
1329            SYNCHRONIZED_state = true)
1330
1331 /**
1332  * Similar to SYNCHRONIZED, but only uses a read lock.
1333  */
1334 #define SYNCHRONIZED_CONST(...)            \
1335   SYNCHRONIZED(                            \
1336       FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)), \
1337       (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).asConst())
1338
1339 /**
1340  * Similar to TIMED_SYNCHRONIZED, but only uses a read lock.
1341  */
1342 #define TIMED_SYNCHRONIZED_CONST(timeout, ...) \
1343   TIMED_SYNCHRONIZED(                          \
1344       timeout,                                 \
1345       FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)),     \
1346       (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).asConst())
1347
1348 /**
1349  * Temporarily disables synchronization inside a SYNCHRONIZED block.
1350  *
1351  * Note: This macro is deprecated, and kind of broken.  The input parameter
1352  * does not control what it unlocks--it always unlocks the lock acquired by the
1353  * most recent SYNCHRONIZED scope.  If you have two nested SYNCHRONIZED blocks,
1354  * UNSYNCHRONIZED always unlocks the inner-most, even if you pass in the
1355  * variable name used in the outer SYNCHRONIZED block.
1356  *
1357  * This macro will be removed soon in a subsequent diff.
1358  */
1359 #define UNSYNCHRONIZED(name)                                             \
1360   for (auto SYNCHRONIZED_state3 = SYNCHRONIZED_lockedPtr.scopedUnlock(); \
1361        !SYNCHRONIZED_state;                                              \
1362        SYNCHRONIZED_state = true)                                        \
1363     for (auto& name = *SYNCHRONIZED_state3.getSynchronized();            \
1364          !SYNCHRONIZED_state;                                            \
1365          SYNCHRONIZED_state = true)
1366
1367 /**
1368  * Synchronizes two Synchronized objects (they may encapsulate
1369  * different data). Synchronization is done in increasing address of
1370  * object order, so there is no deadlock risk.
1371  */
1372 #define SYNCHRONIZED_DUAL(n1, e1, n2, e2)                               \
1373   if (bool SYNCHRONIZED_state = false) {                                \
1374   } else                                                                \
1375     for (auto SYNCHRONIZED_ptrs = acquireLockedPair(e1, e2);            \
1376          !SYNCHRONIZED_state;                                           \
1377          SYNCHRONIZED_state = true)                                     \
1378       for (auto& n1 = *SYNCHRONIZED_ptrs.first; !SYNCHRONIZED_state;    \
1379            SYNCHRONIZED_state = true)                                   \
1380         for (auto& n2 = *SYNCHRONIZED_ptrs.second; !SYNCHRONIZED_state; \
1381              SYNCHRONIZED_state = true)
1382
1383 } /* namespace folly */