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