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