add LockTraits
[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  * @author: Andrei Alexandrescu (andrei.alexandrescu@fb.com)
22  */
23
24 #pragma once
25
26 #include <boost/thread.hpp>
27 #include <folly/LockTraits.h>
28 #include <folly/Preprocessor.h>
29 #include <folly/SharedMutex.h>
30 #include <folly/Traits.h>
31 #include <mutex>
32 #include <type_traits>
33
34 // Temporarily provide FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES under the legacy
35 // FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES name.  This definition will be
36 // removed shortly in an upcoming diff to make Synchronized fully utilize
37 // LockTraits.
38 #define FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES \
39   FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
40
41 namespace folly {
42
43 namespace detail {
44 enum InternalDoNotUse {};
45
46 /**
47  * Free function adaptors for std:: and boost::
48  */
49
50 /**
51  * Yields true iff T has .lock() and .unlock() member functions. This
52  * is done by simply enumerating the mutexes with this interface in
53  * std and boost.
54  */
55 template <class T>
56 struct HasLockUnlock {
57   enum { value = IsOneOf<T
58       , std::mutex
59       , std::recursive_mutex
60       , boost::mutex
61       , boost::recursive_mutex
62       , boost::shared_mutex
63 #if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
64       , std::timed_mutex
65       , std::recursive_timed_mutex
66       , boost::timed_mutex
67       , boost::recursive_timed_mutex
68 #endif
69       >::value };
70 };
71
72 /**
73  * Yields true iff T has .lock_shared() and .unlock_shared() member functions.
74  * This is done by simply enumerating the mutexes with this interface.
75  */
76 template <class T>
77 struct HasLockSharedUnlockShared {
78   enum { value = IsOneOf<T
79       , boost::shared_mutex
80       >::value };
81 };
82
83 /**
84  * Acquires a mutex for reading by calling .lock().
85  *
86  * This variant is not appropriate for shared mutexes.
87  */
88 template <class T>
89 typename std::enable_if<
90   HasLockUnlock<T>::value && !HasLockSharedUnlockShared<T>::value>::type
91 acquireRead(T& mutex) {
92   mutex.lock();
93 }
94
95 /**
96  * Acquires a mutex for reading by calling .lock_shared().
97  *
98  * This variant is not appropriate for nonshared mutexes.
99  */
100 template <class T>
101 typename std::enable_if<HasLockSharedUnlockShared<T>::value>::type
102 acquireRead(T& mutex) {
103   mutex.lock_shared();
104 }
105
106 /**
107  * Acquires a mutex for reading and writing by calling .lock().
108  */
109 template <class T>
110 typename std::enable_if<HasLockUnlock<T>::value>::type
111 acquireReadWrite(T& mutex) {
112   mutex.lock();
113 }
114
115 #if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
116 /**
117  * Acquires a mutex for reading by calling .try_lock_shared_for(). This applies
118  * to boost::shared_mutex.
119  */
120 template <class T>
121 typename std::enable_if<
122   IsOneOf<T
123       , boost::shared_mutex
124       >::value, bool>::type
125 acquireRead(T& mutex,
126             unsigned int milliseconds) {
127   return mutex.try_lock_shared_for(boost::chrono::milliseconds(milliseconds));
128 }
129
130 /**
131  * Acquires a mutex for reading and writing with timeout by calling
132  * .try_lock_for(). This applies to two of the std mutex classes as
133  * enumerated below.
134  */
135 template <class T>
136 typename std::enable_if<
137   IsOneOf<T
138       , std::timed_mutex
139       , std::recursive_timed_mutex
140       >::value, bool>::type
141 acquireReadWrite(T& mutex,
142                  unsigned int milliseconds) {
143   // work around try_lock_for bug in some gcc versions, see
144   // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54562
145   // TODO: Fixed in gcc-4.9.0.
146   return mutex.try_lock()
147       || (milliseconds > 0 &&
148           mutex.try_lock_until(std::chrono::system_clock::now() +
149                                std::chrono::milliseconds(milliseconds)));
150 }
151
152 /**
153  * Acquires a mutex for reading and writing with timeout by calling
154  * .try_lock_for(). This applies to three of the boost mutex classes as
155  * enumerated below.
156  */
157 template <class T>
158 typename std::enable_if<
159   IsOneOf<T
160       , boost::shared_mutex
161       , boost::timed_mutex
162       , boost::recursive_timed_mutex
163       >::value, bool>::type
164 acquireReadWrite(T& mutex,
165                  unsigned int milliseconds) {
166   return mutex.try_lock_for(boost::chrono::milliseconds(milliseconds));
167 }
168 #endif // FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
169
170 /**
171  * Releases a mutex previously acquired for reading by calling
172  * .unlock(). The exception is boost::shared_mutex, which has a
173  * special primitive called .unlock_shared().
174  */
175 template <class T>
176 typename std::enable_if<
177   HasLockUnlock<T>::value && !HasLockSharedUnlockShared<T>::value>::type
178 releaseRead(T& mutex) {
179   mutex.unlock();
180 }
181
182 /**
183  * Special case for boost::shared_mutex.
184  */
185 template <class T>
186 typename std::enable_if<HasLockSharedUnlockShared<T>::value>::type
187 releaseRead(T& mutex) {
188   mutex.unlock_shared();
189 }
190
191 /**
192  * Releases a mutex previously acquired for reading-writing by calling
193  * .unlock().
194  */
195 template <class T>
196 typename std::enable_if<HasLockUnlock<T>::value>::type
197 releaseReadWrite(T& mutex) {
198   mutex.unlock();
199 }
200
201 } // namespace detail
202
203 /**
204  * Synchronized<T> encapsulates an object of type T (a "datum") paired
205  * with a mutex. The only way to access the datum is while the mutex
206  * is locked, and Synchronized makes it virtually impossible to do
207  * otherwise. The code that would access the datum in unsafe ways
208  * would look odd and convoluted, thus readily alerting the human
209  * reviewer. In contrast, the code that uses Synchronized<T> correctly
210  * looks simple and intuitive.
211  *
212  * The second parameter must be a mutex type. Supported mutexes are
213  * std::mutex, std::recursive_mutex, std::timed_mutex,
214  * std::recursive_timed_mutex, boost::mutex, boost::recursive_mutex,
215  * boost::shared_mutex, boost::timed_mutex,
216  * boost::recursive_timed_mutex, and the folly/RWSpinLock.h
217  * classes.
218  *
219  * You may define Synchronized support by defining 4-6 primitives in
220  * the same namespace as the mutex class (found via ADL).  The
221  * primitives are: acquireRead, acquireReadWrite, releaseRead, and
222  * releaseReadWrite. Two optional primitives for timout operations are
223  * overloads of acquireRead and acquireReadWrite. For signatures,
224  * refer to the namespace detail below, which implements the
225  * primitives for mutexes in std and boost.
226  */
227 template <class T, class Mutex = SharedMutex>
228 struct Synchronized {
229   /**
230    * Default constructor leaves both members call their own default
231    * constructor.
232    */
233   Synchronized() = default;
234
235  private:
236   static constexpr bool nxCopyCtor{
237       std::is_nothrow_copy_constructible<T>::value};
238   static constexpr bool nxMoveCtor{
239       std::is_nothrow_move_constructible<T>::value};
240
241   /**
242    * Helper constructors to enable Synchronized for
243    * non-default constructible types T.
244    * Guards are created in actual public constructors and are alive
245    * for the time required to construct the object
246    */
247   template <typename Guard>
248   Synchronized(const Synchronized& rhs,
249                const Guard& /*guard*/) noexcept(nxCopyCtor)
250       : datum_(rhs.datum_) {}
251
252   template <typename Guard>
253   Synchronized(Synchronized&& rhs, const Guard& /*guard*/) noexcept(nxMoveCtor)
254       : datum_(std::move(rhs.datum_)) {}
255
256  public:
257   /**
258    * Copy constructor copies the data (with locking the source and
259    * all) but does NOT copy the mutex. Doing so would result in
260    * deadlocks.
261    */
262   Synchronized(const Synchronized& rhs) noexcept(nxCopyCtor)
263       : Synchronized(rhs, rhs.operator->()) {}
264
265   /**
266    * Move constructor moves the data (with locking the source and all)
267    * but does not move the mutex.
268    */
269   Synchronized(Synchronized&& rhs) noexcept(nxMoveCtor)
270       : Synchronized(std::move(rhs), rhs.operator->()) {}
271
272   /**
273    * Constructor taking a datum as argument copies it. There is no
274    * need to lock the constructing object.
275    */
276   explicit Synchronized(const T& rhs) noexcept(nxCopyCtor) : datum_(rhs) {}
277
278   /**
279    * Constructor taking a datum rvalue as argument moves it. Again,
280    * there is no need to lock the constructing object.
281    */
282   explicit Synchronized(T&& rhs) noexcept(nxMoveCtor)
283       : datum_(std::move(rhs)) {}
284
285   /**
286    * Lets you construct non-movable types in-place. Use the constexpr
287    * instance `construct_in_place` as the first argument.
288    */
289   template <typename... Args>
290   explicit Synchronized(construct_in_place_t, Args&&... args)
291       : datum_(std::forward<Args>(args)...) {}
292
293   /**
294    * The canonical assignment operator only assigns the data, NOT the
295    * mutex. It locks the two objects in ascending order of their
296    * addresses.
297    */
298   Synchronized& operator=(const Synchronized& rhs) {
299     if (this == &rhs) {
300       // Self-assignment, pass.
301     } else if (this < &rhs) {
302       auto guard1 = operator->();
303       auto guard2 = rhs.operator->();
304       datum_ = rhs.datum_;
305     } else {
306       auto guard1 = rhs.operator->();
307       auto guard2 = operator->();
308       datum_ = rhs.datum_;
309     }
310     return *this;
311   }
312
313   /**
314    * Move assignment operator, only assigns the data, NOT the
315    * mutex. It locks the two objects in ascending order of their
316    * addresses.
317    */
318   Synchronized& operator=(Synchronized&& rhs) {
319     if (this == &rhs) {
320       // Self-assignment, pass.
321     } else if (this < &rhs) {
322       auto guard1 = operator->();
323       auto guard2 = rhs.operator->();
324       datum_ = std::move(rhs.datum_);
325     } else {
326       auto guard1 = rhs.operator->();
327       auto guard2 = operator->();
328       datum_ = std::move(rhs.datum_);
329     }
330     return *this;
331   }
332
333   /**
334    * Lock object, assign datum.
335    */
336   Synchronized& operator=(const T& rhs) {
337     auto guard = operator->();
338     datum_ = rhs;
339     return *this;
340   }
341
342   /**
343    * Lock object, move-assign datum.
344    */
345   Synchronized& operator=(T&& rhs) {
346     auto guard = operator->();
347     datum_ = std::move(rhs);
348     return *this;
349   }
350
351   /**
352    * A LockedPtr lp keeps a modifiable (i.e. non-const)
353    * Synchronized<T> object locked for the duration of lp's
354    * existence. Because of this, you get to access the datum's methods
355    * directly by using lp->fun().
356    */
357   struct LockedPtr {
358     /**
359      * Found no reason to leave this hanging.
360      */
361     LockedPtr() = delete;
362
363     /**
364      * Takes a Synchronized and locks it.
365      */
366     explicit LockedPtr(Synchronized* parent) : parent_(parent) {
367       acquire();
368     }
369
370     /**
371      * Takes a Synchronized and attempts to lock it for some
372      * milliseconds. If not, the LockedPtr will be subsequently null.
373      */
374     LockedPtr(Synchronized* parent, unsigned int milliseconds) {
375       using namespace detail;
376       if (acquireReadWrite(parent->mutex_, milliseconds)) {
377         parent_ = parent;
378         return;
379       }
380       // Could not acquire the resource, pointer is null
381       parent_ = nullptr;
382     }
383
384     /**
385      * This is used ONLY inside SYNCHRONIZED_DUAL. It initializes
386      * everything properly, but does not lock the parent because it
387      * "knows" someone else will lock it. Please do not use.
388      */
389     LockedPtr(Synchronized* parent, detail::InternalDoNotUse)
390         : parent_(parent) {
391     }
392
393     /**
394      * Copy ctor adds one lock.
395      */
396     LockedPtr(const LockedPtr& rhs) : parent_(rhs.parent_) {
397       acquire();
398     }
399
400     /**
401      * Assigning from another LockedPtr results in freeing the former
402      * lock and acquiring the new one. The method works with
403      * self-assignment (does nothing).
404      */
405     LockedPtr& operator=(const LockedPtr& rhs) {
406       if (parent_ != rhs.parent_) {
407         if (parent_) parent_->mutex_.unlock();
408         parent_ = rhs.parent_;
409         acquire();
410       }
411       return *this;
412     }
413
414     /**
415      * Destructor releases.
416      */
417     ~LockedPtr() {
418       using namespace detail;
419       if (parent_) releaseReadWrite(parent_->mutex_);
420     }
421
422     /**
423      * Safe to access the data. Don't save the obtained pointer by
424      * invoking lp.operator->() by hand. Also, if the method returns a
425      * handle stored inside the datum, don't use this idiom - use
426      * SYNCHRONIZED below.
427      */
428     T* operator->() {
429       return parent_ ? &parent_->datum_ : nullptr;
430     }
431
432     /**
433      * This class temporarily unlocks a LockedPtr in a scoped
434      * manner. It is used inside of the UNSYNCHRONIZED macro.
435      */
436     struct Unsynchronizer {
437       explicit Unsynchronizer(LockedPtr* p) : parent_(p) {
438         using namespace detail;
439         releaseReadWrite(parent_->parent_->mutex_);
440       }
441       Unsynchronizer(const Unsynchronizer&) = delete;
442       Unsynchronizer& operator=(const Unsynchronizer&) = delete;
443       ~Unsynchronizer() {
444         parent_->acquire();
445       }
446       LockedPtr* operator->() const {
447         return parent_;
448       }
449     private:
450       LockedPtr* parent_;
451     };
452     friend struct Unsynchronizer;
453     Unsynchronizer typeHackDoNotUse();
454
455     template <class P1, class P2>
456     friend void lockInOrder(P1& p1, P2& p2);
457
458   private:
459     void acquire() {
460       using namespace detail;
461       if (parent_) acquireReadWrite(parent_->mutex_);
462     }
463
464     // This is the entire state of LockedPtr.
465     Synchronized* parent_;
466   };
467
468   /**
469    * ConstLockedPtr does exactly what LockedPtr does, but for const
470    * Synchronized objects. Of interest is that ConstLockedPtr only
471    * uses a read lock, which is faster but more restrictive - you only
472    * get to call const methods of the datum.
473    *
474    * Much of the code between LockedPtr and
475    * ConstLockedPtr is identical and could be factor out, but there
476    * are enough nagging little differences to not justify the trouble.
477    */
478   struct ConstLockedPtr {
479     ConstLockedPtr() = delete;
480     explicit ConstLockedPtr(const Synchronized* parent) : parent_(parent) {
481       acquire();
482     }
483     ConstLockedPtr(const Synchronized* parent, detail::InternalDoNotUse)
484         : parent_(parent) {
485     }
486     ConstLockedPtr(const ConstLockedPtr& rhs) : parent_(rhs.parent_) {
487       acquire();
488     }
489     explicit ConstLockedPtr(const LockedPtr& rhs) : parent_(rhs.parent_) {
490       acquire();
491     }
492     ConstLockedPtr(const Synchronized* parent, unsigned int milliseconds) {
493       using namespace detail;
494       if (acquireRead(
495             parent->mutex_,
496             milliseconds)) {
497         parent_ = parent;
498         return;
499       }
500       // Could not acquire the resource, pointer is null
501       parent_ = nullptr;
502     }
503
504     ConstLockedPtr& operator=(const ConstLockedPtr& rhs) {
505       if (parent_ != rhs.parent_) {
506         if (parent_) parent_->mutex_.unlock_shared();
507         parent_ = rhs.parent_;
508         acquire();
509       }
510     }
511     ~ConstLockedPtr() {
512       using namespace detail;
513       if (parent_) releaseRead(parent_->mutex_);
514     }
515
516     const T* operator->() const {
517       return parent_ ? &parent_->datum_ : nullptr;
518     }
519
520     struct Unsynchronizer {
521       explicit Unsynchronizer(ConstLockedPtr* p) : parent_(p) {
522         using namespace detail;
523         releaseRead(parent_->parent_->mutex_);
524       }
525       Unsynchronizer(const Unsynchronizer&) = delete;
526       Unsynchronizer& operator=(const Unsynchronizer&) = delete;
527       ~Unsynchronizer() {
528         using namespace detail;
529         acquireRead(parent_->parent_->mutex_);
530       }
531       ConstLockedPtr* operator->() const {
532         return parent_;
533       }
534     private:
535       ConstLockedPtr* parent_;
536     };
537     friend struct Unsynchronizer;
538     Unsynchronizer typeHackDoNotUse();
539
540     template <class P1, class P2>
541     friend void lockInOrder(P1& p1, P2& p2);
542
543   private:
544     void acquire() {
545       using namespace detail;
546       if (parent_) acquireRead(parent_->mutex_);
547     }
548
549     const Synchronized* parent_;
550   };
551
552   /**
553    * This accessor offers a LockedPtr. In turn. LockedPtr offers
554    * operator-> returning a pointer to T. The operator-> keeps
555    * expanding until it reaches a pointer, so syncobj->foo() will lock
556    * the object and call foo() against it.
557   */
558   LockedPtr operator->() {
559     return LockedPtr(this);
560   }
561
562   /**
563    * Same, for constant objects. You will be able to invoke only const
564    * methods.
565    */
566   ConstLockedPtr operator->() const {
567     return ConstLockedPtr(this);
568   }
569
570   /**
571    * Attempts to acquire for a given number of milliseconds. If
572    * acquisition is unsuccessful, the returned LockedPtr is NULL.
573    */
574   LockedPtr timedAcquire(unsigned int milliseconds) {
575     return LockedPtr(this, milliseconds);
576   }
577
578   /**
579    * As above, for a constant object.
580    */
581   ConstLockedPtr timedAcquire(unsigned int milliseconds) const {
582     return ConstLockedPtr(this, milliseconds);
583   }
584
585   /**
586    * Used by SYNCHRONIZED_DUAL.
587    */
588   LockedPtr internalDoNotUse() {
589     return LockedPtr(this, detail::InternalDoNotUse());
590   }
591
592   /**
593    * ditto
594    */
595   ConstLockedPtr internalDoNotUse() const {
596     return ConstLockedPtr(this, detail::InternalDoNotUse());
597   }
598
599   /**
600    * Sometimes, although you have a mutable object, you only want to
601    * call a const method against it. The most efficient way to achieve
602    * that is by using a read lock. You get to do so by using
603    * obj.asConst()->method() instead of obj->method().
604    */
605   const Synchronized& asConst() const {
606     return *this;
607   }
608
609   /**
610    * Swaps with another Synchronized. Protected against
611    * self-swap. Only data is swapped. Locks are acquired in increasing
612    * address order.
613    */
614   void swap(Synchronized& rhs) {
615     if (this == &rhs) {
616       return;
617     }
618     if (this > &rhs) {
619       return rhs.swap(*this);
620     }
621     auto guard1 = operator->();
622     auto guard2 = rhs.operator->();
623
624     using std::swap;
625     swap(datum_, rhs.datum_);
626   }
627
628   /**
629    * Swap with another datum. Recommended because it keeps the mutex
630    * held only briefly.
631    */
632   void swap(T& rhs) {
633     LockedPtr guard = operator->();
634
635     using std::swap;
636     swap(datum_, rhs);
637   }
638
639   /**
640    * Copies datum to a given target.
641    */
642   void copy(T* target) const {
643     ConstLockedPtr guard = operator->();
644     *target = datum_;
645   }
646
647   /**
648    * Returns a fresh copy of the datum.
649    */
650   T copy() const {
651     ConstLockedPtr guard = operator->();
652     return datum_;
653   }
654
655 private:
656   T datum_;
657   mutable Mutex mutex_;
658 };
659
660 // Non-member swap primitive
661 template <class T, class M>
662 void swap(Synchronized<T, M>& lhs, Synchronized<T, M>& rhs) {
663   lhs.swap(rhs);
664 }
665
666 /**
667  * SYNCHRONIZED is the main facility that makes Synchronized<T>
668  * helpful. It is a pseudo-statement that introduces a scope where the
669  * object is locked. Inside that scope you get to access the unadorned
670  * datum.
671  *
672  * Example:
673  *
674  * Synchronized<vector<int>> svector;
675  * ...
676  * SYNCHRONIZED (svector) { ... use svector as a vector<int> ... }
677  * or
678  * SYNCHRONIZED (v, svector) { ... use v as a vector<int> ... }
679  *
680  * Refer to folly/docs/Synchronized.md for a detailed explanation and more
681  * examples.
682  */
683 #define SYNCHRONIZED(...)                                             \
684   FOLLY_PUSH_WARNING                                                  \
685   FOLLY_GCC_DISABLE_WARNING(shadow)                                   \
686   if (bool SYNCHRONIZED_state = false) {                              \
687   } else                                                              \
688     for (auto SYNCHRONIZED_lockedPtr =                                \
689              (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).operator->(); \
690          !SYNCHRONIZED_state;                                         \
691          SYNCHRONIZED_state = true)                                   \
692       for (auto& FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) =                \
693                *SYNCHRONIZED_lockedPtr.operator->();                  \
694            !SYNCHRONIZED_state;                                       \
695            SYNCHRONIZED_state = true)                                 \
696   FOLLY_POP_WARNING
697
698 #define TIMED_SYNCHRONIZED(timeout, ...)                                       \
699   if (bool SYNCHRONIZED_state = false) {                                       \
700   } else                                                                       \
701     for (auto SYNCHRONIZED_lockedPtr =                                         \
702              (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).timedAcquire(timeout); \
703          !SYNCHRONIZED_state;                                                  \
704          SYNCHRONIZED_state = true)                                            \
705       for (auto FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) =                          \
706                SYNCHRONIZED_lockedPtr.operator->();                            \
707            !SYNCHRONIZED_state;                                                \
708            SYNCHRONIZED_state = true)
709
710 /**
711  * Similar to SYNCHRONIZED, but only uses a read lock.
712  */
713 #define SYNCHRONIZED_CONST(...)            \
714   SYNCHRONIZED(                            \
715       FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)), \
716       (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).asConst())
717
718 /**
719  * Similar to TIMED_SYNCHRONIZED, but only uses a read lock.
720  */
721 #define TIMED_SYNCHRONIZED_CONST(timeout, ...) \
722   TIMED_SYNCHRONIZED(                          \
723       timeout,                                 \
724       FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)),     \
725       (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).asConst())
726
727 /**
728  * Temporarily disables synchronization inside a SYNCHRONIZED block.
729  */
730 #define UNSYNCHRONIZED(name)                                    \
731   for (decltype(SYNCHRONIZED_lockedPtr.typeHackDoNotUse())      \
732          SYNCHRONIZED_state3(&SYNCHRONIZED_lockedPtr);          \
733        !SYNCHRONIZED_state; SYNCHRONIZED_state = true)          \
734     for (auto& name = *SYNCHRONIZED_state3.operator->();        \
735          !SYNCHRONIZED_state; SYNCHRONIZED_state = true)
736
737 /**
738  * Locks two objects in increasing order of their addresses.
739  */
740 template <class P1, class P2>
741 void lockInOrder(P1& p1, P2& p2) {
742   if (static_cast<const void*>(p1.operator->()) >
743       static_cast<const void*>(p2.operator->())) {
744     p2.acquire();
745     p1.acquire();
746   } else {
747     p1.acquire();
748     p2.acquire();
749   }
750 }
751
752 /**
753  * Synchronizes two Synchronized objects (they may encapsulate
754  * different data). Synchronization is done in increasing address of
755  * object order, so there is no deadlock risk.
756  */
757 #define SYNCHRONIZED_DUAL(n1, e1, n2, e2)                       \
758   if (bool SYNCHRONIZED_state = false) {} else                  \
759     for (auto SYNCHRONIZED_lp1 = (e1).internalDoNotUse();       \
760          !SYNCHRONIZED_state; SYNCHRONIZED_state = true)        \
761       for (auto& n1 = *SYNCHRONIZED_lp1.operator->();           \
762            !SYNCHRONIZED_state;  SYNCHRONIZED_state = true)     \
763         for (auto SYNCHRONIZED_lp2 = (e2).internalDoNotUse();   \
764              !SYNCHRONIZED_state;  SYNCHRONIZED_state = true)   \
765           for (auto& n2 = *SYNCHRONIZED_lp2.operator->();       \
766                !SYNCHRONIZED_state; SYNCHRONIZED_state = true)  \
767             if ((::folly::lockInOrder(                          \
768                    SYNCHRONIZED_lp1, SYNCHRONIZED_lp2),         \
769                  false)) {}                                     \
770             else
771
772 } /* namespace folly */