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