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