1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
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/Function.h>
29 #include <folly/Likely.h>
30 #include <folly/Preprocessor.h>
31 #include <folly/SharedMutex.h>
32 #include <folly/Traits.h>
33 #include <folly/Utility.h>
34 #include <folly/container/Foreach.h>
35 #include <folly/functional/ApplyTuple.h>
36 #include <folly/synchronization/Lock.h>
37 
38 #include <glog/logging.h>
39 
40 #include <array>
41 #include <mutex>
42 #include <tuple>
43 #include <type_traits>
44 #include <utility>
45 
46 namespace folly {
47 
48 namespace detail {
49 
50 template <typename, typename Mutex>
51 FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsUnique = false;
52 template <typename Mutex>
53 FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsUnique<
54     decltype(void(std::declval<Mutex&>().lock())),
55     Mutex> = true;
56 
57 template <typename, typename Mutex>
58 FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsShared = false;
59 template <typename Mutex>
60 FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsShared<
61     decltype(void(std::declval<Mutex&>().lock_shared())),
62     Mutex> = true;
63 
64 template <typename, typename Mutex>
65 FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsUpgrade = false;
66 template <typename Mutex>
67 FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsUpgrade<
68     decltype(void(std::declval<Mutex&>().lock_upgrade())),
69     Mutex> = true;
70 
71 /**
72  * An enum to describe the "level" of a mutex.  The supported levels are
73  *  Unique - a normal mutex that supports only exclusive locking
74  *  Shared - a shared mutex which has shared locking and unlocking functions;
75  *  Upgrade - a mutex that has all the methods of the two above along with
76  *            support for upgradable locking
77  */
78 enum class SynchronizedMutexLevel { Unknown, Unique, Shared, Upgrade };
79 
80 template <typename Mutex>
81 FOLLY_INLINE_VARIABLE constexpr SynchronizedMutexLevel kSynchronizedMutexLevel =
82     kSynchronizedMutexIsUpgrade<void, Mutex>  ? SynchronizedMutexLevel::Upgrade
83     : kSynchronizedMutexIsShared<void, Mutex> ? SynchronizedMutexLevel::Shared
84     : kSynchronizedMutexIsUnique<void, Mutex> ? SynchronizedMutexLevel::Unique
85                                               : SynchronizedMutexLevel::Unknown;
86 
87 enum class SynchronizedMutexMethod { Lock, TryLock };
88 
89 template <SynchronizedMutexLevel Level, SynchronizedMutexMethod Method>
90 struct SynchronizedLockPolicy {
91   static constexpr SynchronizedMutexLevel level = Level;
92   static constexpr SynchronizedMutexMethod method = Method;
93 };
94 using SynchronizedLockPolicyExclusive = SynchronizedLockPolicy<
95     SynchronizedMutexLevel::Unique,
96     SynchronizedMutexMethod::Lock>;
97 using SynchronizedLockPolicyTryExclusive = SynchronizedLockPolicy<
98     SynchronizedMutexLevel::Unique,
99     SynchronizedMutexMethod::TryLock>;
100 using SynchronizedLockPolicyShared = SynchronizedLockPolicy<
101     SynchronizedMutexLevel::Shared,
102     SynchronizedMutexMethod::Lock>;
103 using SynchronizedLockPolicyTryShared = SynchronizedLockPolicy<
104     SynchronizedMutexLevel::Shared,
105     SynchronizedMutexMethod::TryLock>;
106 using SynchronizedLockPolicyUpgrade = SynchronizedLockPolicy<
107     SynchronizedMutexLevel::Upgrade,
108     SynchronizedMutexMethod::Lock>;
109 using SynchronizedLockPolicyTryUpgrade = SynchronizedLockPolicy<
110     SynchronizedMutexLevel::Upgrade,
111     SynchronizedMutexMethod::TryLock>;
112 
113 template <SynchronizedMutexLevel>
114 struct SynchronizedLockType_ {};
115 template <>
116 struct SynchronizedLockType_<SynchronizedMutexLevel::Unique> {
117   template <typename Mutex>
118   using apply = std::unique_lock<Mutex>;
119 };
120 template <>
121 struct SynchronizedLockType_<SynchronizedMutexLevel::Shared> {
122   template <typename Mutex>
123   using apply = std::shared_lock<Mutex>;
124 };
125 template <>
126 struct SynchronizedLockType_<SynchronizedMutexLevel::Upgrade> {
127   template <typename Mutex>
128   using apply = upgrade_lock<Mutex>;
129 };
130 template <SynchronizedMutexLevel Level, typename MutexType>
131 using SynchronizedLockType =
132     typename SynchronizedLockType_<Level>::template apply<MutexType>;
133 
134 } // namespace detail
135 
136 /**
137  * SynchronizedBase is a helper parent class for Synchronized<T>.
138  *
139  * It provides wlock() and rlock() methods for shared mutex types,
140  * or lock() methods for purely exclusive mutex types.
141  */
142 template <class Subclass, detail::SynchronizedMutexLevel level>
143 class SynchronizedBase;
144 
145 template <class LockedType, class Mutex, class LockPolicy>
146 class LockedPtrBase;
147 template <class LockedType, class LockPolicy>
148 class LockedPtr;
149 
150 /**
151  * SynchronizedBase specialization for shared mutex types.
152  *
153  * This class provides wlock() and rlock() methods for acquiring the lock and
154  * accessing the data.
155  */
156 template <class Subclass>
157 class SynchronizedBase<Subclass, detail::SynchronizedMutexLevel::Shared> {
158  private:
159   template <typename T, typename P>
160   using LockedPtr_ = ::folly::LockedPtr<T, P>;
161 
162  public:
163   using LockPolicyExclusive = detail::SynchronizedLockPolicyExclusive;
164   using LockPolicyShared = detail::SynchronizedLockPolicyShared;
165   using LockPolicyTryExclusive = detail::SynchronizedLockPolicyTryExclusive;
166   using LockPolicyTryShared = detail::SynchronizedLockPolicyTryShared;
167 
168   using WLockedPtr = LockedPtr_<Subclass, LockPolicyExclusive>;
169   using ConstWLockedPtr = LockedPtr_<const Subclass, LockPolicyExclusive>;
170 
171   using RLockedPtr = LockedPtr_<Subclass, LockPolicyShared>;
172   using ConstRLockedPtr = LockedPtr_<const Subclass, LockPolicyShared>;
173 
174   using TryWLockedPtr = LockedPtr_<Subclass, LockPolicyTryExclusive>;
175   using ConstTryWLockedPtr = LockedPtr_<const Subclass, LockPolicyTryExclusive>;
176 
177   using TryRLockedPtr = LockedPtr_<Subclass, LockPolicyTryShared>;
178   using ConstTryRLockedPtr = LockedPtr_<const Subclass, LockPolicyTryShared>;
179 
180   // These aliases are deprecated.
181   // TODO: Codemod them away.
182   using LockedPtr = WLockedPtr;
183   using ConstLockedPtr = ConstRLockedPtr;
184 
185   /**
186    * Acquire an exclusive lock, and return a LockedPtr that can be used to
187    * safely access the datum.
188    *
189    * LockedPtr offers operator -> and * to provide access to the datum.
190    * The lock will be released when the LockedPtr is destroyed.
191    */
192   LockedPtr wlock() { return LockedPtr(static_cast<Subclass*>(this)); }
193   ConstWLockedPtr wlock() const {
194     return ConstWLockedPtr(static_cast<const Subclass*>(this));
195   }
196 
197   /**
198    * Attempts to acquire the lock in exclusive mode.  If acquisition is
199    * unsuccessful, the returned LockedPtr will be null.
200    *
201    * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
202    * validity.)
203    */
204   TryWLockedPtr tryWLock() {
205     return TryWLockedPtr{static_cast<Subclass*>(this)};
206   }
207   ConstTryWLockedPtr tryWLock() const {
208     return ConstTryWLockedPtr{static_cast<const Subclass*>(this)};
209   }
210 
211   /**
212    * Acquire a read lock. The returned LockedPtr will have force const
213    * access to the data unless the lock is acquired in non-const
214    * context and asNonConstUnsafe() is used.
215    */
216   RLockedPtr rlock() { return RLockedPtr(static_cast<Subclass*>(this)); }
217   ConstLockedPtr rlock() const {
218     return ConstLockedPtr(static_cast<const Subclass*>(this));
219   }
220 
221   /**
222    * Attempts to acquire the lock in shared mode.  If acquisition is
223    * unsuccessful, the returned LockedPtr will be null.
224    *
225    * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
226    * validity.)
227    */
228   TryRLockedPtr tryRLock() {
229     return TryRLockedPtr{static_cast<Subclass*>(this)};
230   }
231   ConstTryRLockedPtr tryRLock() const {
232     return ConstTryRLockedPtr{static_cast<const Subclass*>(this)};
233   }
234 
235   /**
236    * Attempts to acquire the lock, or fails if the timeout elapses first.
237    * If acquisition is unsuccessful, the returned LockedPtr will be null.
238    *
239    * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
240    * validity.)
241    */
242   template <class Rep, class Period>
243   LockedPtr wlock(const std::chrono::duration<Rep, Period>& timeout) {
244     return LockedPtr(static_cast<Subclass*>(this), timeout);
245   }
246   template <class Rep, class Period>
247   LockedPtr wlock(const std::chrono::duration<Rep, Period>& timeout) const {
248     return LockedPtr(static_cast<const Subclass*>(this), timeout);
249   }
250 
251   /**
252    * Attempts to acquire the lock, or fails if the timeout elapses first.
253    * If acquisition is unsuccessful, the returned LockedPtr will be null.
254    *
255    * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
256    * validity.)
257    */
258   template <class Rep, class Period>
259   RLockedPtr rlock(const std::chrono::duration<Rep, Period>& timeout) {
260     return RLockedPtr(static_cast<Subclass*>(this), timeout);
261   }
262   template <class Rep, class Period>
263   ConstRLockedPtr rlock(
264       const std::chrono::duration<Rep, Period>& timeout) const {
265     return ConstRLockedPtr(static_cast<const Subclass*>(this), timeout);
266   }
267 
268   /**
269    * Invoke a function while holding the lock exclusively.
270    *
271    * A reference to the datum will be passed into the function as its only
272    * argument.
273    *
274    * This can be used with a lambda argument for easily defining small critical
275    * sections in the code.  For example:
276    *
277    *   auto value = obj.withWLock([](auto& data) {
278    *     data.doStuff();
279    *     return data.getValue();
280    *   });
281    */
282   template <class Function>
283   auto withWLock(Function&& function) {
284     return function(*wlock());
285   }
286   template <class Function>
287   auto withWLock(Function&& function) const {
288     return function(*wlock());
289   }
290 
291   /**
292    * Invoke a function while holding the lock exclusively.
293    *
294    * This is similar to withWLock(), but the function will be passed a
295    * LockedPtr rather than a reference to the data itself.
296    *
297    * This allows scopedUnlock() to be called on the LockedPtr argument if
298    * desired.
299    */
300   template <class Function>
301   auto withWLockPtr(Function&& function) {
302     return function(wlock());
303   }
304   template <class Function>
305   auto withWLockPtr(Function&& function) const {
306     return function(wlock());
307   }
308 
309   /**
310    * Invoke a function while holding an the lock in shared mode.
311    *
312    * A const reference to the datum will be passed into the function as its
313    * only argument.
314    */
315   template <class Function>
316   auto withRLock(Function&& function) const {
317     return function(*rlock());
318   }
319 
320   template <class Function>
321   auto withRLockPtr(Function&& function) {
322     return function(rlock());
323   }
324 
325   template <class Function>
326   auto withRLockPtr(Function&& function) const {
327     return function(rlock());
328   }
329 };
330 
331 /**
332  * SynchronizedBase specialization for upgrade mutex types.
333  *
334  * This class provides all the functionality provided by the SynchronizedBase
335  * specialization for shared mutexes and a ulock() method that returns an
336  * upgrade lock RAII proxy
337  */
338 template <class Subclass>
339 class SynchronizedBase<Subclass, detail::SynchronizedMutexLevel::Upgrade>
340     : public SynchronizedBase<
341           Subclass,
342           detail::SynchronizedMutexLevel::Shared> {
343  private:
344   template <typename T, typename P>
345   using LockedPtr_ = ::folly::LockedPtr<T, P>;
346 
347  public:
348   using LockPolicyUpgrade = detail::SynchronizedLockPolicyUpgrade;
349   using LockPolicyTryUpgrade = detail::SynchronizedLockPolicyTryUpgrade;
350 
351   using UpgradeLockedPtr = LockedPtr_<Subclass, LockPolicyUpgrade>;
352   using ConstUpgradeLockedPtr = LockedPtr_<const Subclass, LockPolicyUpgrade>;
353 
354   using TryUpgradeLockedPtr = LockedPtr_<Subclass, LockPolicyTryUpgrade>;
355   using ConstTryUpgradeLockedPtr =
356       LockedPtr_<const Subclass, LockPolicyTryUpgrade>;
357 
358   /**
359    * Acquire an upgrade lock. The returned LockedPtr will have force
360    * const access to the data unless the lock is acquired in non-const
361    * context and asNonConstUnsafe() is used.
362    */
363   UpgradeLockedPtr ulock() {
364     return UpgradeLockedPtr(static_cast<Subclass*>(this));
365   }
366   ConstUpgradeLockedPtr ulock() const {
367     return ConstUpgradeLockedPtr(static_cast<const Subclass*>(this));
368   }
369 
370   /**
371    * Attempts to acquire the lock in upgrade mode.  If acquisition is
372    * unsuccessful, the returned LockedPtr will be null.
373    *
374    * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
375    * validity.)
376    */
377   TryUpgradeLockedPtr tryULock() {
378     return TryUpgradeLockedPtr{static_cast<Subclass*>(this)};
379   }
380 
381   /**
382    * Acquire an upgrade lock and return a LockedPtr that can be used to safely
383    * access the datum
384    *
385    * And the const version
386    */
387   template <class Rep, class Period>
388   UpgradeLockedPtr ulock(const std::chrono::duration<Rep, Period>& timeout) {
389     return UpgradeLockedPtr(static_cast<Subclass*>(this), timeout);
390   }
391 
392   /**
393    * Invoke a function while holding the lock.
394    *
395    * A reference to the datum will be passed into the function as its only
396    * argument.
397    *
398    * This can be used with a lambda argument for easily defining small critical
399    * sections in the code.  For example:
400    *
401    *   auto value = obj.withULock([](auto& data) {
402    *     data.doStuff();
403    *     return data.getValue();
404    *   });
405    *
406    * This is probably not the function you want.  If the intent is to read the
407    * data object and determine whether you should upgrade to a write lock then
408    * the withULockPtr() method should be called instead, since it gives access
409    * to the LockedPtr proxy (which can be upgraded via the
410    * moveFromUpgradeToWrite() method)
411    */
412   template <class Function>
413   auto withULock(Function&& function) {
414     return function(*ulock());
415   }
416   template <class Function>
417   auto withULock(Function&& function) const {
418     return function(*ulock());
419   }
420 
421   /**
422    * Invoke a function while holding the lock exclusively.
423    *
424    * This is similar to withULock(), but the function will be passed a
425    * LockedPtr rather than a reference to the data itself.
426    *
427    * This allows scopedUnlock() and as_lock() to be called on the
428    * LockedPtr argument.
429    *
430    * This also allows you to upgrade the LockedPtr proxy to a write state so
431    * that changes can be made to the underlying data
432    */
433   template <class Function>
434   auto withULockPtr(Function&& function) {
435     return function(ulock());
436   }
437   template <class Function>
438   auto withULockPtr(Function&& function) const {
439     return function(ulock());
440   }
441 };
442 
443 /**
444  * SynchronizedBase specialization for non-shared mutex types.
445  *
446  * This class provides lock() methods for acquiring the lock and accessing the
447  * data.
448  */
449 template <class Subclass>
450 class SynchronizedBase<Subclass, detail::SynchronizedMutexLevel::Unique> {
451  private:
452   template <typename T, typename P>
453   using LockedPtr_ = ::folly::LockedPtr<T, P>;
454 
455  public:
456   using LockPolicyExclusive = detail::SynchronizedLockPolicyExclusive;
457   using LockPolicyTryExclusive = detail::SynchronizedLockPolicyTryExclusive;
458 
459   using LockedPtr = LockedPtr_<Subclass, LockPolicyExclusive>;
460   using ConstLockedPtr = LockedPtr_<const Subclass, LockPolicyExclusive>;
461 
462   using TryLockedPtr = LockedPtr_<Subclass, LockPolicyTryExclusive>;
463   using ConstTryLockedPtr = LockedPtr_<const Subclass, LockPolicyTryExclusive>;
464 
465   /**
466    * Acquire a lock, and return a LockedPtr that can be used to safely access
467    * the datum.
468    */
469   LockedPtr lock() { return LockedPtr(static_cast<Subclass*>(this)); }
470 
471   /**
472    * Acquire a lock, and return a ConstLockedPtr that can be used to safely
473    * access the datum.
474    */
475   ConstLockedPtr lock() const {
476     return ConstLockedPtr(static_cast<const Subclass*>(this));
477   }
478 
479   /**
480    * Attempts to acquire the lock in exclusive mode.  If acquisition is
481    * unsuccessful, the returned LockedPtr will be null.
482    *
483    * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
484    * validity.)
485    */
486   TryLockedPtr tryLock() { return TryLockedPtr{static_cast<Subclass*>(this)}; }
487   ConstTryLockedPtr tryLock() const {
488     return ConstTryLockedPtr{static_cast<const Subclass*>(this)};
489   }
490 
491   /**
492    * Attempts to acquire the lock, or fails if the timeout elapses first.
493    * If acquisition is unsuccessful, the returned LockedPtr will be null.
494    */
495   template <class Rep, class Period>
496   LockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) {
497     return LockedPtr(static_cast<Subclass*>(this), timeout);
498   }
499 
500   /**
501    * Attempts to acquire the lock, or fails if the timeout elapses first.
502    * If acquisition is unsuccessful, the returned LockedPtr will be null.
503    */
504   template <class Rep, class Period>
505   ConstLockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) const {
506     return ConstLockedPtr(static_cast<const Subclass*>(this), timeout);
507   }
508 
509   /**
510    * Invoke a function while holding the lock.
511    *
512    * A reference to the datum will be passed into the function as its only
513    * argument.
514    *
515    * This can be used with a lambda argument for easily defining small critical
516    * sections in the code.  For example:
517    *
518    *   auto value = obj.withLock([](auto& data) {
519    *     data.doStuff();
520    *     return data.getValue();
521    *   });
522    */
523   template <class Function>
524   auto withLock(Function&& function) {
525     return function(*lock());
526   }
527   template <class Function>
528   auto withLock(Function&& function) const {
529     return function(*lock());
530   }
531 
532   /**
533    * Invoke a function while holding the lock exclusively.
534    *
535    * This is similar to withWLock(), but the function will be passed a
536    * LockedPtr rather than a reference to the data itself.
537    *
538    * This allows scopedUnlock() and as_lock() to be called on the
539    * LockedPtr argument.
540    */
541   template <class Function>
542   auto withLockPtr(Function&& function) {
543     return function(lock());
544   }
545   template <class Function>
546   auto withLockPtr(Function&& function) const {
547     return function(lock());
548   }
549 };
550 
551 /**
552  * Synchronized<T> encapsulates an object of type T (a "datum") paired
553  * with a mutex. The only way to access the datum is while the mutex
554  * is locked, and Synchronized makes it virtually impossible to do
555  * otherwise. The code that would access the datum in unsafe ways
556  * would look odd and convoluted, thus readily alerting the human
557  * reviewer. In contrast, the code that uses Synchronized<T> correctly
558  * looks simple and intuitive.
559  *
560  * The second parameter must be a mutex type matching the lockable
561  * family of concepts in the standard.
562  */
563 template <class T, class Mutex = SharedMutex>
564 struct Synchronized : public SynchronizedBase<
565                           Synchronized<T, Mutex>,
566                           detail::kSynchronizedMutexLevel<Mutex>> {
567  private:
568   using Base = SynchronizedBase<
569       Synchronized<T, Mutex>,
570       detail::kSynchronizedMutexLevel<Mutex>>;
571   static constexpr bool nxCopyCtor{
572       std::is_nothrow_copy_constructible<T>::value};
573   static constexpr bool nxMoveCtor{
574       std::is_nothrow_move_constructible<T>::value};
575 
576   // used to disable copy construction and assignment
577   class NonImplementedType;
578 
579  public:
580   using LockedPtr = typename Base::LockedPtr;
581   using ConstLockedPtr = typename Base::ConstLockedPtr;
582   using DataType = T;
583   using MutexType = Mutex;
584 
585   /**
586    * Default constructor leaves both members call their own default
587    * constructor.
588    */
589   Synchronized() = default;
590 
591  public:
592   /**
593    * Copy constructor; deprecated
594    *
595    * Enabled only when the data type is copy-constructible.
596    *
597    * Takes a shared-or-exclusive lock on the source mutex while performing the
598    * copy-construction of the destination data from the source data. No lock is
599    * taken on the destination mutex.
600    *
601    * May throw even when the data type is is nothrow-copy-constructible because
602    * acquiring a lock may throw.
603    */
604   /* implicit */ Synchronized(typename std::conditional<
605                               std::is_copy_constructible<T>::value,
606                               const Synchronized&,
607                               NonImplementedType>::type rhs) /* may throw */
608       : Synchronized(rhs.copy()) {}
609 
610   /**
611    * Move constructor; deprecated
612    *
613    * Move-constructs from the source data without locking either the source or
614    * the destination mutex.
615    *
616    * Semantically, assumes that the source object is a true rvalue and therefore
617    * that no synchronization is required for accessing it.
618    */
619   Synchronized(Synchronized&& rhs) noexcept(nxMoveCtor)
620       : Synchronized(std::move(rhs.datum_)) {}
621 
622   /**
623    * Constructor taking a datum as argument copies it. There is no
624    * need to lock the constructing object.
625    */
626   explicit Synchronized(const T& rhs) noexcept(nxCopyCtor) : datum_(rhs) {}
627 
628   /**
629    * Constructor taking a datum rvalue as argument moves it. Again,
630    * there is no need to lock the constructing object.
631    */
632   explicit Synchronized(T&& rhs) noexcept(nxMoveCtor)
633       : datum_(std::move(rhs)) {}
634 
635   /**
636    * Lets you construct non-movable types in-place. Use the constexpr
637    * instance `in_place` as the first argument.
638    */
639   template <typename... Args>
640   explicit Synchronized(in_place_t, Args&&... args)
641       : datum_(std::forward<Args>(args)...) {}
642 
643   /**
644    * Lets you construct the synchronized object and also pass construction
645    * parameters to the underlying mutex if desired
646    */
647   template <typename... DatumArgs, typename... MutexArgs>
648   Synchronized(
649       std::piecewise_construct_t,
650       std::tuple<DatumArgs...> datumArgs,
651       std::tuple<MutexArgs...> mutexArgs)
652       : Synchronized{
653             std::piecewise_construct,
654             std::move(datumArgs),
655             std::move(mutexArgs),
656             std::make_index_sequence<sizeof...(DatumArgs)>{},
657             std::make_index_sequence<sizeof...(MutexArgs)>{}} {}
658 
659   /**
660    * Copy assignment operator; deprecated
661    *
662    * Enabled only when the data type is copy-constructible and move-assignable.
663    *
664    * Move-assigns from a copy of the source data.
665    *
666    * Takes a shared-or-exclusive lock on the source mutex while copying the
667    * source data to a temporary. Takes an exclusive lock on the destination
668    * mutex while move-assigning from the temporary.
669    *
670    * This technique consts an extra temporary but avoids the need to take locks
671    * on both mutexes together.
672    */
673   Synchronized& operator=(typename std::conditional<
674                           std::is_copy_constructible<T>::value &&
675                               std::is_move_assignable<T>::value,
676                           const Synchronized&,
677                           NonImplementedType>::type rhs) {
678     return *this = rhs.copy();
679   }
680 
681   /**
682    * Move assignment operator; deprecated
683    *
684    * Takes an exclusive lock on the destination mutex while move-assigning the
685    * destination data from the source data. The source mutex is not locked or
686    * otherwise accessed.
687    *
688    * Semantically, assumes that the source object is a true rvalue and therefore
689    * that no synchronization is required for accessing it.
690    */
691   Synchronized& operator=(Synchronized&& rhs) {
692     return *this = std::move(rhs.datum_);
693   }
694 
695   /**
696    * Lock object, assign datum.
697    */
698   Synchronized& operator=(const T& rhs) {
699     if (&datum_ != &rhs) {
700       auto guard = LockedPtr{this};
701       datum_ = rhs;
702     }
703     return *this;
704   }
705 
706   /**
707    * Lock object, move-assign datum.
708    */
709   Synchronized& operator=(T&& rhs) {
710     if (&datum_ != &rhs) {
711       auto guard = LockedPtr{this};
712       datum_ = std::move(rhs);
713     }
714     return *this;
715   }
716 
717   /**
718    * Acquire an appropriate lock based on the context.
719    *
720    * If the mutex is a shared mutex, and the Synchronized instance is const,
721    * this acquires a shared lock.  Otherwise this acquires an exclusive lock.
722    *
723    * In general, prefer using the explicit rlock() and wlock() methods
724    * for read-write locks, and lock() for purely exclusive locks.
725    *
726    * contextualLock() is primarily intended for use in other template functions
727    * that do not necessarily know the lock type.
728    */
729   LockedPtr contextualLock() { return LockedPtr(this); }
730   ConstLockedPtr contextualLock() const { return ConstLockedPtr(this); }
731   template <class Rep, class Period>
732   LockedPtr contextualLock(const std::chrono::duration<Rep, Period>& timeout) {
733     return LockedPtr(this, timeout);
734   }
735   template <class Rep, class Period>
736   ConstLockedPtr contextualLock(
737       const std::chrono::duration<Rep, Period>& timeout) const {
738     return ConstLockedPtr(this, timeout);
739   }
740   /**
741    * contextualRLock() acquires a read lock if the mutex type is shared,
742    * or a regular exclusive lock for non-shared mutex types.
743    *
744    * contextualRLock() when you know that you prefer a read lock (if
745    * available), even if the Synchronized<T> object itself is non-const.
746    */
747   ConstLockedPtr contextualRLock() const { return ConstLockedPtr(this); }
748   template <class Rep, class Period>
749   ConstLockedPtr contextualRLock(
750       const std::chrono::duration<Rep, Period>& timeout) const {
751     return ConstLockedPtr(this, timeout);
752   }
753 
754   /**
755    * This accessor offers a LockedPtr. In turn, LockedPtr offers
756    * operator-> returning a pointer to T. The operator-> keeps
757    * expanding until it reaches a pointer, so syncobj->foo() will lock
758    * the object and call foo() against it.
759    *
760    * NOTE: This API is planned to be deprecated in an upcoming diff.
761    * Prefer using lock(), wlock(), or rlock() instead.
762    */
763   [[deprecated("use explicit lock(), wlock(), or rlock() instead")]] LockedPtr
764   operator->() {
765     return LockedPtr(this);
766   }
767 
768   /**
769    * Obtain a ConstLockedPtr.
770    *
771    * NOTE: This API is planned to be deprecated in an upcoming diff.
772    * Prefer using lock(), wlock(), or rlock() instead.
773    */
774   [[deprecated(
775       "use explicit lock(), wlock(), or rlock() instead")]] ConstLockedPtr
776   operator->() const {
777     return ConstLockedPtr(this);
778   }
779 
780   /**
781    * Attempts to acquire for a given number of milliseconds. If
782    * acquisition is unsuccessful, the returned LockedPtr is nullptr.
783    *
784    * NOTE: This API is deprecated.  Use lock(), wlock(), or rlock() instead.
785    * In the future it will be marked with a deprecation attribute to emit
786    * build-time warnings, and then it will be removed entirely.
787    */
788   LockedPtr timedAcquire(unsigned int milliseconds) {
789     return LockedPtr(this, std::chrono::milliseconds(milliseconds));
790   }
791 
792   /**
793    * Attempts to acquire for a given number of milliseconds. If
794    * acquisition is unsuccessful, the returned ConstLockedPtr is nullptr.
795    *
796    * NOTE: This API is deprecated.  Use lock(), wlock(), or rlock() instead.
797    * In the future it will be marked with a deprecation attribute to emit
798    * build-time warnings, and then it will be removed entirely.
799    */
800   ConstLockedPtr timedAcquire(unsigned int milliseconds) const {
801     return ConstLockedPtr(this, std::chrono::milliseconds(milliseconds));
802   }
803 
804   /**
805    * Swaps with another Synchronized. Protected against
806    * self-swap. Only data is swapped. Locks are acquired in increasing
807    * address order.
808    */
809   void swap(Synchronized& rhs) {
810     if (this == &rhs) {
811       return;
812     }
813     if (this > &rhs) {
814       return rhs.swap(*this);
815     }
816     auto guard1 = LockedPtr{this};
817     auto guard2 = LockedPtr{&rhs};
818 
819     using std::swap;
820     swap(datum_, rhs.datum_);
821   }
822 
823   /**
824    * Swap with another datum. Recommended because it keeps the mutex
825    * held only briefly.
826    */
827   void swap(T& rhs) {
828     LockedPtr guard(this);
829 
830     using std::swap;
831     swap(datum_, rhs);
832   }
833 
834   /**
835    * Assign another datum and return the original value. Recommended
836    * because it keeps the mutex held only briefly.
837    */
838   T exchange(T&& rhs) {
839     swap(rhs);
840     return std::move(rhs);
841   }
842 
843   /**
844    * Copies datum to a given target.
845    */
846   void copyInto(T& target) const {
847     ConstLockedPtr guard(this);
848     target = datum_;
849   }
850 
851   /**
852    * Returns a fresh copy of the datum.
853    */
854   T copy() const {
855     ConstLockedPtr guard(this);
856     return datum_;
857   }
858 
859   /**
860    * Returns a reference to the datum without acquiring a lock.
861    *
862    * Provided as a backdoor for call-sites where it is known safe to be used.
863    * For example, when it is known that only one thread has access to the
864    * Synchronized instance.
865    *
866    * To be used with care - this method explicitly overrides the normal safety
867    * guarantees provided by the rest of the Synchronized API.
868    */
869   T& unsafeGetUnlocked() { return datum_; }
870   const T& unsafeGetUnlocked() const { return datum_; }
871 
872  private:
873   template <class LockedType, class MutexType, class LockPolicy>
874   friend class folly::LockedPtrBase;
875   template <class LockedType, class LockPolicy>
876   friend class folly::LockedPtr;
877 
878   /**
879    * Helper constructors to enable Synchronized for
880    * non-default constructible types T.
881    * Guards are created in actual public constructors and are alive
882    * for the time required to construct the object
883    */
884   Synchronized(
885       const Synchronized& rhs,
886       const ConstLockedPtr& /*guard*/) noexcept(nxCopyCtor)
887       : datum_(rhs.datum_) {}
888 
889   Synchronized(Synchronized&& rhs, const LockedPtr& /*guard*/) noexcept(
890       nxMoveCtor)
891       : datum_(std::move(rhs.datum_)) {}
892 
893   template <
894       typename... DatumArgs,
895       typename... MutexArgs,
896       std::size_t... IndicesOne,
897       std::size_t... IndicesTwo>
898   Synchronized(
899       std::piecewise_construct_t,
900       std::tuple<DatumArgs...> datumArgs,
901       std::tuple<MutexArgs...> mutexArgs,
902       std::index_sequence<IndicesOne...>,
903       std::index_sequence<IndicesTwo...>)
904       : datum_{std::get<IndicesOne>(std::move(datumArgs))...},
905         mutex_{std::get<IndicesTwo>(std::move(mutexArgs))...} {}
906 
907   // simulacrum of data members - keep data members in sync!
908   // LockedPtr needs offsetof() which is specified only for standard-layout
909   // types which Synchronized is not so we define a simulacrum for offsetof
910   struct Simulacrum {
911     aligned_storage_for_t<DataType> datum_;
912     aligned_storage_for_t<MutexType> mutex_;
913   };
914 
915   // data members - keep simulacrum of data members in sync!
916   T datum_;
917   mutable Mutex mutex_;
918 };
919 
920 template <class SynchronizedType, class LockPolicy>
921 class ScopedUnlocker;
922 
923 namespace detail {
924 /*
925  * A helper alias that resolves to "const T" if the template parameter
926  * is a const Synchronized<T>, or "T" if the parameter is not const.
927  */
928 template <class SynchronizedType, bool AllowsConcurrentAccess>
929 using SynchronizedDataType = typename std::conditional<
930     AllowsConcurrentAccess || std::is_const<SynchronizedType>::value,
931     typename SynchronizedType::DataType const,
932     typename SynchronizedType::DataType>::type;
933 /*
934  * A helper alias that resolves to a ConstLockedPtr if the template parameter
935  * is a const Synchronized<T>, or a LockedPtr if the parameter is not const.
936  */
937 template <class SynchronizedType>
938 using LockedPtrType = typename std::conditional<
939     std::is_const<SynchronizedType>::value,
940     typename SynchronizedType::ConstLockedPtr,
941     typename SynchronizedType::LockedPtr>::type;
942 
943 template <
944     typename Synchronized,
945     typename LockFunc,
946     typename TryLockFunc,
947     typename... Args>
948 class SynchronizedLocker {
949  public:
950   using LockedPtr = invoke_result_t<LockFunc&, Synchronized&, const Args&...>;
951 
952   template <typename LockFuncType, typename TryLockFuncType, typename... As>
953   SynchronizedLocker(
954       Synchronized& sync,
955       LockFuncType&& lockFunc,
956       TryLockFuncType tryLockFunc,
957       As&&... as)
958       : synchronized{sync},
959         lockFunc_{std::forward<LockFuncType>(lockFunc)},
960         tryLockFunc_{std::forward<TryLockFuncType>(tryLockFunc)},
961         args_{std::forward<As>(as)...} {}
962 
963   auto lock() const {
964     auto args = std::tuple<const Args&...>{args_};
965     return apply(lockFunc_, std::tuple_cat(std::tie(synchronized), args));
966   }
967   auto tryLock() const { return tryLockFunc_(synchronized); }
968 
969  private:
970   Synchronized& synchronized;
971   LockFunc lockFunc_;
972   TryLockFunc tryLockFunc_;
973   std::tuple<Args...> args_;
974 };
975 
976 template <
977     typename Synchronized,
978     typename LockFunc,
979     typename TryLockFunc,
980     typename... Args>
981 auto makeSynchronizedLocker(
982     Synchronized& synchronized,
983     LockFunc&& lockFunc,
984     TryLockFunc&& tryLockFunc,
985     Args&&... args) {
986   using LockFuncType = std::decay_t<LockFunc>;
987   using TryLockFuncType = std::decay_t<TryLockFunc>;
988   return SynchronizedLocker<
989       Synchronized,
990       LockFuncType,
991       TryLockFuncType,
992       std::decay_t<Args>...>{
993       synchronized,
994       std::forward<LockFunc>(lockFunc),
995       std::forward<TryLockFunc>(tryLockFunc),
996       std::forward<Args>(args)...};
997 }
998 
999 /**
1000  * Acquire locks for multiple Synchronized<T> objects, in a deadlock-safe
1001  * manner.
1002  *
1003  * The function uses the "smart and polite" algorithm from this link
1004  * http://howardhinnant.github.io/dining_philosophers.html#Polite
1005  *
1006  * The gist of the algorithm is that it locks a mutex, then tries to lock the
1007  * other mutexes in a non-blocking manner.  If all the locks succeed, we are
1008  * done, if not, we release the locks we have held, yield to allow other
1009  * threads to continue and then block on the mutex that we failed to acquire.
1010  *
1011  * This allows dynamically yielding ownership of all the mutexes but one, so
1012  * that other threads can continue doing work and locking the other mutexes.
1013  * See the benchmarks in folly/test/SynchronizedBenchmark.cpp for more.
1014  */
1015 template <typename... SynchronizedLocker>
1016 auto lock(SynchronizedLocker... lockersIn)
1017     -> std::tuple<typename SynchronizedLocker::LockedPtr...> {
1018   // capture the list of lockers as a tuple
1019   auto lockers = std::forward_as_tuple(lockersIn...);
1020 
1021   // make a list of null LockedPtr instances that we will return to the caller
1022   auto lockedPtrs = std::tuple<typename SynchronizedLocker::LockedPtr...>{};
1023 
1024   // start by locking the first thing in the list
1025   std::get<0>(lockedPtrs) = std::get<0>(lockers).lock();
1026   auto indexLocked = 0;
1027 
1028   while (true) {
1029     auto couldLockAll = true;
1030 
1031     for_each(lockers, [&](auto& locker, auto index) {
1032       // if we should try_lock on the current locker then do so
1033       if (index != indexLocked) {
1034         auto lockedPtr = locker.tryLock();
1035 
1036         // if we were unable to lock this mutex,
1037         //
1038         // 1. release all the locks,
1039         // 2. yield control to another thread to be nice
1040         // 3. block on the mutex we failed to lock, acquire the lock
1041         // 4. break out and set the index of the current mutex to indicate
1042         //    which mutex we have locked
1043         if (!lockedPtr) {
1044           // writing lockedPtrs = decltype(lockedPtrs){} does not compile on
1045           // gcc, I believe this is a bug D7676798
1046           lockedPtrs = std::tuple<typename SynchronizedLocker::LockedPtr...>{};
1047 
1048           std::this_thread::yield();
1049           fetch(lockedPtrs, index) = locker.lock();
1050           indexLocked = index;
1051           couldLockAll = false;
1052 
1053           return loop_break;
1054         }
1055 
1056         // else store the locked mutex in the list we return
1057         fetch(lockedPtrs, index) = std::move(lockedPtr);
1058       }
1059 
1060       return loop_continue;
1061     });
1062 
1063     if (couldLockAll) {
1064       return lockedPtrs;
1065     }
1066   }
1067 }
1068 
1069 template <typename Synchronized, typename... Args>
1070 auto wlock(Synchronized& synchronized, Args&&... args) {
1071   return detail::makeSynchronizedLocker(
1072       synchronized,
1073       [](auto& s, auto&&... a) {
1074         return s.wlock(std::forward<decltype(a)>(a)...);
1075       },
1076       [](auto& s) { return s.tryWLock(); },
1077       std::forward<Args>(args)...);
1078 }
1079 template <typename Synchronized, typename... Args>
1080 auto rlock(Synchronized& synchronized, Args&&... args) {
1081   return detail::makeSynchronizedLocker(
1082       synchronized,
1083       [](auto& s, auto&&... a) {
1084         return s.rlock(std::forward<decltype(a)>(a)...);
1085       },
1086       [](auto& s) { return s.tryRLock(); },
1087       std::forward<Args>(args)...);
1088 }
1089 template <typename Synchronized, typename... Args>
1090 auto ulock(Synchronized& synchronized, Args&&... args) {
1091   return detail::makeSynchronizedLocker(
1092       synchronized,
1093       [](auto& s, auto&&... a) {
1094         return s.ulock(std::forward<decltype(a)>(a)...);
1095       },
1096       [](auto& s) { return s.tryULock(); },
1097       std::forward<Args>(args)...);
1098 }
1099 template <typename Synchronized, typename... Args>
1100 auto lock(Synchronized& synchronized, Args&&... args) {
1101   return detail::makeSynchronizedLocker(
1102       synchronized,
1103       [](auto& s, auto&&... a) {
1104         return s.lock(std::forward<decltype(a)>(a)...);
1105       },
1106       [](auto& s) { return s.tryLock(); },
1107       std::forward<Args>(args)...);
1108 }
1109 
1110 } // namespace detail
1111 
1112 /**
1113  * This class temporarily unlocks a LockedPtr in a scoped manner.
1114  */
1115 template <class SynchronizedType, class LockPolicy>
1116 class ScopedUnlocker {
1117  public:
1118   explicit ScopedUnlocker(LockedPtr<SynchronizedType, LockPolicy>* p) noexcept
1119       : ptr_(p), parent_(p->parent()) {
1120     ptr_->releaseLock();
1121   }
1122   ScopedUnlocker(const ScopedUnlocker&) = delete;
1123   ScopedUnlocker& operator=(const ScopedUnlocker&) = delete;
1124   ScopedUnlocker(ScopedUnlocker&& other) noexcept
1125       : ptr_(std::exchange(other.ptr_, nullptr)),
1126         parent_(std::exchange(other.parent_, nullptr)) {}
1127   ScopedUnlocker& operator=(ScopedUnlocker&& other) = delete;
1128 
1129   ~ScopedUnlocker() noexcept(false) {
1130     if (ptr_) {
1131       ptr_->reacquireLock(parent_);
1132     }
1133   }
1134 
1135  private:
1136   LockedPtr<SynchronizedType, LockPolicy>* ptr_{nullptr};
1137   SynchronizedType* parent_{nullptr};
1138 };
1139 
1140 /**
1141  * A LockedPtr keeps a Synchronized<T> object locked for the duration of
1142  * LockedPtr's existence.
1143  *
1144  * It provides access the datum's members directly by using operator->() and
1145  * operator*().
1146  *
1147  * The LockPolicy parameter controls whether or not the lock is acquired in
1148  * exclusive or shared mode.
1149  */
1150 template <class SynchronizedType, class LockPolicy>
1151 class LockedPtr {
1152  private:
1153   constexpr static bool AllowsConcurrentAccess =
1154       LockPolicy::level != detail::SynchronizedMutexLevel::Unique;
1155 
1156   using CDataType = // the DataType with the appropriate const-qualification
1157       detail::SynchronizedDataType<SynchronizedType, AllowsConcurrentAccess>;
1158 
1159   template <typename LockPolicyOther>
1160   using EnableIfSameLevel =
1161       std::enable_if_t<LockPolicy::level == LockPolicyOther::level>;
1162 
1163   template <typename, typename>
1164   friend class LockedPtr;
1165 
1166   friend class ScopedUnlocker<SynchronizedType, LockPolicy>;
1167 
1168  public:
1169   using DataType = typename SynchronizedType::DataType;
1170   using MutexType = typename SynchronizedType::MutexType;
1171   using Synchronized = typename std::remove_const<SynchronizedType>::type;
1172   using LockType = detail::SynchronizedLockType<LockPolicy::level, MutexType>;
1173 
1174   /**
1175    * Creates an uninitialized LockedPtr.
1176    *
1177    * Dereferencing an uninitialized LockedPtr is not allowed.
1178    */
1179   LockedPtr() = default;
1180 
1181   /**
1182    * Takes a Synchronized<T> and locks it.
1183    */
1184   explicit LockedPtr(SynchronizedType* parent)
1185       : lock_{!parent ? LockType{} : doLock(parent->mutex_)} {}
1186 
1187   /**
1188    * Takes a Synchronized<T> and attempts to lock it, within the specified
1189    * timeout.
1190    *
1191    * Blocks until the lock is acquired or until the specified timeout expires.
1192    * If the timeout expired without acquiring the lock, the LockedPtr will be
1193    * null, and LockedPtr::isNull() will return true.
1194    */
1195   template <class Rep, class Period>
1196   LockedPtr(
1197       SynchronizedType* parent,
1198       const std::chrono::duration<Rep, Period>& timeout)
1199       : lock_{parent ? LockType{parent->mutex_, timeout} : LockType{}} {}
1200 
1201   /**
1202    * Move constructor.
1203    */
1204   LockedPtr(LockedPtr&& rhs) noexcept = default;
1205   template <
1206       typename LockPolicyType,
1207       EnableIfSameLevel<LockPolicyType>* = nullptr>
1208   explicit LockedPtr(
1209       LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept
1210       : lock_{std::move(other.lock_)} {}
1211 
1212   /**
1213    * Move assignment operator.
1214    */
1215   LockedPtr& operator=(LockedPtr&& rhs) noexcept = default;
1216   template <
1217       typename LockPolicyType,
1218       EnableIfSameLevel<LockPolicyType>* = nullptr>
1219   LockedPtr& operator=(
1220       LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept {
1221     lock_ = std::move(other.lock_);
1222     return *this;
1223   }
1224 
1225   /*
1226    * Copy constructor and assignment operator are deleted.
1227    */
1228   LockedPtr(const LockedPtr& rhs) = delete;
1229   LockedPtr& operator=(const LockedPtr& rhs) = delete;
1230 
1231   /**
1232    * Destructor releases.
1233    */
1234   ~LockedPtr() = default;
1235 
1236   /**
1237    * Access the underlying lock object.
1238    */
1239   LockType& as_lock() noexcept { return lock_; }
1240   LockType const& as_lock() const noexcept { return lock_; }
1241 
1242   /**
1243    * Check if this LockedPtr is uninitialized, or points to valid locked data.
1244    *
1245    * This method can be used to check if a timed-acquire operation succeeded.
1246    * If an acquire operation times out it will result in a null LockedPtr.
1247    *
1248    * A LockedPtr is always either null, or holds a lock to valid data.
1249    * Methods such as scopedUnlock() reset the LockedPtr to null for the
1250    * duration of the unlock.
1251    */
1252   bool isNull() const { return !lock_.owns_lock(); }
1253 
1254   /**
1255    * Explicit boolean conversion.
1256    *
1257    * Returns !isNull()
1258    */
1259   explicit operator bool() const { return lock_.owns_lock(); }
1260 
1261   /**
1262    * Access the locked data.
1263    *
1264    * This method should only be used if the LockedPtr is valid.
1265    */
1266   CDataType* operator->() const { return std::addressof(parent()->datum_); }
1267 
1268   /**
1269    * Access the locked data.
1270    *
1271    * This method should only be used if the LockedPtr is valid.
1272    */
1273   CDataType& operator*() const { return parent()->datum_; }
1274 
1275   void unlock() noexcept { lock_ = {}; }
1276 
1277   /**
1278    * Locks that allow concurrent access (shared, upgrade) force const
1279    * access with the standard accessors even if the Synchronized
1280    * object is non-const.
1281    *
1282    * In some cases non-const access can be needed, for example:
1283    *
1284    *   - Under an upgrade lock, to get references that will be mutated
1285    *     after upgrading to a write lock.
1286    *
1287    *   - Under an read lock, if some mutating operations on the data
1288    *     are thread safe (e.g. mutating the value in an associative
1289    *     container with reference stability).
1290    *
1291    * asNonConstUnsafe() returns a non-const reference to the data if
1292    * the parent Synchronized object was non-const at the point of lock
1293    * acquisition.
1294    */
1295   template <typename = void>
1296   DataType& asNonConstUnsafe() const {
1297     static_assert(
1298         AllowsConcurrentAccess && !std::is_const<SynchronizedType>::value,
1299         "asNonConstUnsafe() is only available on non-exclusive locks"
1300         " acquired in a non-const context");
1301 
1302     return parent()->datum_;
1303   }
1304 
1305   /**
1306    * Temporarily unlock the LockedPtr, and reset it to null.
1307    *
1308    * Returns an helper object that will re-lock and restore the LockedPtr when
1309    * the helper is destroyed.  The LockedPtr may not be dereferenced for as
1310    * long as this helper object exists.
1311    */
1312   ScopedUnlocker<SynchronizedType, LockPolicy> scopedUnlock() {
1313     return ScopedUnlocker<SynchronizedType, LockPolicy>(this);
1314   }
1315 
1316   /***************************************************************************
1317    * Upgrade lock methods.
1318    * These are disabled via SFINAE when the mutex is not an upgrade mutex.
1319    **************************************************************************/
1320   /**
1321    * Move the locked ptr from an upgrade state to an exclusive state.  The
1322    * current lock is left in a null state.
1323    */
1324   template <
1325       typename SyncType = SynchronizedType,
1326       decltype(void(std::declval<typename SyncType::MutexType&>()
1327                         .lock_upgrade()))* = nullptr>
1328   LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyExclusive>
1329   moveFromUpgradeToWrite() {
1330     static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
1331     return transition_to_unique_lock(lock_);
1332   }
1333 
1334   /**
1335    * Move the locked ptr from an exclusive state to an upgrade state.  The
1336    * current lock is left in a null state.
1337    */
1338   template <
1339       typename SyncType = SynchronizedType,
1340       decltype(void(std::declval<typename SyncType::MutexType&>()
1341                         .lock_upgrade()))* = nullptr>
1342   LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyUpgrade>
1343   moveFromWriteToUpgrade() {
1344     static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
1345     return transition_to_upgrade_lock(lock_);
1346   }
1347 
1348   /**
1349    * Move the locked ptr from an upgrade state to a shared state.  The
1350    * current lock is left in a null state.
1351    */
1352   template <
1353       typename SyncType = SynchronizedType,
1354       decltype(void(std::declval<typename SyncType::MutexType&>()
1355                         .lock_upgrade()))* = nullptr>
1356   LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyShared>
1357   moveFromUpgradeToRead() {
1358     static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
1359     return transition_to_shared_lock(lock_);
1360   }
1361 
1362   /**
1363    * Move the locked ptr from an exclusive state to a shared state.  The
1364    * current lock is left in a null state.
1365    */
1366   template <
1367       typename SyncType = SynchronizedType,
1368       decltype(void(std::declval<typename SyncType::MutexType&>()
1369                         .lock_shared()))* = nullptr>
1370   LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyShared>
1371   moveFromWriteToRead() {
1372     static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
1373     return transition_to_shared_lock(lock_);
1374   }
1375 
1376  private:
1377   /* implicit */ LockedPtr(LockType lock) noexcept : lock_{std::move(lock)} {}
1378 
1379   template <typename LP>
1380   static constexpr bool is_try =
1381       LP::method == detail::SynchronizedMutexMethod::TryLock;
1382 
1383   template <
1384       typename MT,
1385       typename LT = LockType,
1386       typename LP = LockPolicy,
1387       std::enable_if_t<is_try<LP>, int> = 0>
1388   FOLLY_ERASE static LT doLock(MT& mutex) {
1389     return LT{mutex, std::try_to_lock};
1390   }
1391   template <
1392       typename MT,
1393       typename LT = LockType,
1394       typename LP = LockPolicy,
1395       std::enable_if_t<!is_try<LP>, int> = 0>
1396   FOLLY_ERASE static LT doLock(MT& mutex) {
1397     return LT{mutex};
1398   }
1399 
1400   void releaseLock() noexcept {
1401     DCHECK(lock_.owns_lock());
1402     lock_ = {};
1403   }
1404   void reacquireLock(SynchronizedType* parent) {
1405     DCHECK(parent);
1406     DCHECK(!lock_.owns_lock());
1407     lock_ = doLock(parent->mutex_);
1408   }
1409 
1410   SynchronizedType* parent() const {
1411     using simulacrum = typename SynchronizedType::Simulacrum;
1412     static_assert(sizeof(simulacrum) == sizeof(SynchronizedType), "mismatch");
1413     static_assert(alignof(simulacrum) == alignof(SynchronizedType), "mismatch");
1414     constexpr auto off = offsetof(simulacrum, mutex_);
1415     const auto raw = reinterpret_cast<char*>(lock_.mutex());
1416     return reinterpret_cast<SynchronizedType*>(raw - (raw ? off : 0));
1417   }
1418 
1419   LockType lock_;
1420 };
1421 
1422 /**
1423  * Helper functions that should be passed to either a lock() or synchronized()
1424  * invocation, these return implementation defined structs that will be used
1425  * to lock the synchronized instance appropriately.
1426  *
1427  *    lock(wlock(one), rlock(two), wlock(three));
1428  *    synchronized([](auto one, two) { ... }, wlock(one), rlock(two));
1429  *
1430  * For example in the above rlock() produces an implementation defined read
1431  * locking helper instance and wlock() a write locking helper
1432  *
1433  * Subsequent arguments passed to these locking helpers, after the first, will
1434  * be passed by const-ref to the corresponding function on the synchronized
1435  * instance.  This means that if the function accepts these parameters by
1436  * value, they will be copied.  Note that it is not necessary that the primary
1437  * locking function will be invoked at all (for eg.  the implementation might
1438  * just invoke the try*Lock() method)
1439  *
1440  *    // Try to acquire the lock for one second
1441  *    synchronized([](auto) { ... }, wlock(one, 1s));
1442  *
1443  *    // The timed lock acquire might never actually be called, if it is not
1444  *    // needed by the underlying deadlock avoiding algorithm
1445  *    synchronized([](auto, auto) { ... }, rlock(one), wlock(two, 1s));
1446  *
1447  * Note that the arguments passed to to *lock() calls will be passed by
1448  * const-ref to the function invocation, as the implementation might use them
1449  * many times
1450  */
1451 template <typename D, typename M, typename... Args>
1452 auto wlock(Synchronized<D, M>& synchronized, Args&&... args) {
1453   return detail::wlock(synchronized, std::forward<Args>(args)...);
1454 }
1455 template <typename D, typename M, typename... Args>
1456 auto wlock(const Synchronized<D, M>& synchronized, Args&&... args) {
1457   return detail::wlock(synchronized, std::forward<Args>(args)...);
1458 }
1459 template <typename Data, typename Mutex, typename... Args>
1460 auto rlock(const Synchronized<Data, Mutex>& synchronized, Args&&... args) {
1461   return detail::rlock(synchronized, std::forward<Args>(args)...);
1462 }
1463 template <typename D, typename M, typename... Args>
1464 auto ulock(Synchronized<D, M>& synchronized, Args&&... args) {
1465   return detail::ulock(synchronized, std::forward<Args>(args)...);
1466 }
1467 template <typename D, typename M, typename... Args>
1468 auto lock(Synchronized<D, M>& synchronized, Args&&... args) {
1469   return detail::lock(synchronized, std::forward<Args>(args)...);
1470 }
1471 template <typename D, typename M, typename... Args>
1472 auto lock(const Synchronized<D, M>& synchronized, Args&&... args) {
1473   return detail::lock(synchronized, std::forward<Args>(args)...);
1474 }
1475 
1476 /**
1477  * Acquire locks for multiple Synchronized<> objects, in a deadlock-safe
1478  * manner.
1479  *
1480  * Wrap the synchronized instances with the appropriate locking strategy by
1481  * using one of the four strategies - folly::lock (exclusive acquire for
1482  * exclusive only mutexes), folly::rlock (shared acquire for shareable
1483  * mutexes), folly::wlock (exclusive acquire for shareable mutexes) or
1484  * folly::ulock (upgrade acquire for upgrade mutexes) (see above)
1485  *
1486  * The locks will be acquired and the passed callable will be invoked with the
1487  * LockedPtr instances in the order that they were passed to the function
1488  */
1489 template <typename Func, typename... SynchronizedLockers>
1490 decltype(auto) synchronized(Func&& func, SynchronizedLockers&&... lockers) {
1491   return apply(
1492       std::forward<Func>(func),
1493       lock(std::forward<SynchronizedLockers>(lockers)...));
1494 }
1495 
1496 /**
1497  * Acquire locks on many lockables or synchronized instances in such a way
1498  * that the sequence of calls within the function does not cause deadlocks.
1499  *
1500  * This can often result in a performance boost as compared to simply
1501  * acquiring your locks in an ordered manner.  Even for very simple cases.
1502  * The algorithm tried to adjust to contention by blocking on the mutex it
1503  * thinks is the best fit, leaving all other mutexes open to be locked by
1504  * other threads.  See the benchmarks in folly/test/SynchronizedBenchmark.cpp
1505  * for more
1506  *
1507  * This works differently as compared to the locking algorithm in libstdc++
1508  * and is the recommended way to acquire mutexes in a generic order safe
1509  * manner.  Performance benchmarks show that this does better than the one in
1510  * libstdc++ even for the simple cases
1511  *
1512  * Usage is the same as std::lock() for arbitrary lockables
1513  *
1514  *    folly::lock(one, two, three);
1515  *
1516  * To make it work with folly::Synchronized you have to specify how you want
1517  * the locks to be acquired, use the folly::wlock(), folly::rlock(),
1518  * folly::ulock() and folly::lock() helpers defined below
1519  *
1520  *    auto [one, two] = lock(folly::wlock(a), folly::rlock(b));
1521  *
1522  * Note that you can/must avoid the folly:: namespace prefix on the lock()
1523  * function if you use the helpers, ADL lookup is done to find the lock function
1524  *
1525  * This will execute the deadlock avoidance algorithm and acquire a write lock
1526  * for a and a read lock for b
1527  */
1528 template <typename LockableOne, typename LockableTwo, typename... Lockables>
1529 void lock(LockableOne& one, LockableTwo& two, Lockables&... lockables) {
1530   auto locker = [](auto& lockable) {
1531     using Lockable = std::remove_reference_t<decltype(lockable)>;
1532     return detail::makeSynchronizedLocker(
1533         lockable,
1534         [](auto& l) { return std::unique_lock<Lockable>{l}; },
1535         [](auto& l) {
1536           auto lock = std::unique_lock<Lockable>{l, std::defer_lock};
1537           lock.try_lock();
1538           return lock;
1539         });
1540   };
1541   auto locks = lock(locker(one), locker(two), locker(lockables)...);
1542 
1543   // release ownership of the locks from the RAII lock wrapper returned by the
1544   // function above
1545   for_each(locks, [&](auto& lock) { lock.release(); });
1546 }
1547 
1548 /**
1549  * Acquire locks for multiple Synchronized<T> objects, in a deadlock-safe
1550  * manner.
1551  *
1552  * The locks are acquired in order from lowest address to highest address.
1553  * (Note that this is not necessarily the same algorithm used by std::lock().)
1554  * For parameters that are const and support shared locks, a read lock is
1555  * acquired.  Otherwise an exclusive lock is acquired.
1556  *
1557  * use lock() with folly::wlock(), folly::rlock() and folly::ulock() for
1558  * arbitrary locking without causing a deadlock (as much as possible), with the
1559  * same effects as std::lock()
1560  */
1561 template <class Sync1, class Sync2>
1562 std::tuple<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>>
1563 acquireLocked(Sync1& l1, Sync2& l2) {
1564   if (static_cast<const void*>(&l1) < static_cast<const void*>(&l2)) {
1565     auto p1 = l1.contextualLock();
1566     auto p2 = l2.contextualLock();
1567     return std::make_tuple(std::move(p1), std::move(p2));
1568   } else {
1569     auto p2 = l2.contextualLock();
1570     auto p1 = l1.contextualLock();
1571     return std::make_tuple(std::move(p1), std::move(p2));
1572   }
1573 }
1574 
1575 /**
1576  * A version of acquireLocked() that returns a std::pair rather than a
1577  * std::tuple, which is easier to use in many places.
1578  */
1579 template <class Sync1, class Sync2>
1580 std::pair<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>>
1581 acquireLockedPair(Sync1& l1, Sync2& l2) {
1582   auto lockedPtrs = acquireLocked(l1, l2);
1583   return {
1584       std::move(std::get<0>(lockedPtrs)), std::move(std::get<1>(lockedPtrs))};
1585 }
1586 
1587 /************************************************************************
1588  * NOTE: All APIs below this line will be deprecated in upcoming diffs.
1589  ************************************************************************/
1590 
1591 // Non-member swap primitive
1592 template <class T, class M>
1593 void swap(Synchronized<T, M>& lhs, Synchronized<T, M>& rhs) {
1594   lhs.swap(rhs);
1595 }
1596 
1597 /**
1598  * Disambiguate the name var by concatenating the line number of the original
1599  * point of expansion. This avoids shadowing warnings for nested
1600  * SYNCHRONIZEDs. The name is consistent if used multiple times within
1601  * another macro.
1602  * Only for internal use.
1603  */
1604 #define SYNCHRONIZED_VAR(var) FB_CONCATENATE(SYNCHRONIZED_##var##_, __LINE__)
1605 
1606 namespace detail {
1607 struct [[deprecated(
1608     "use explicit lock(), wlock(), or rlock() instead")]] SYNCHRONIZED_macro_is_deprecated{};
1609 }
1610 
1611 /**
1612  * NOTE: This API is deprecated.  Use lock(), wlock(), rlock() or the withLock
1613  * functions instead.  In the future it will be marked with a deprecation
1614  * attribute to emit build-time warnings, and then it will be removed entirely.
1615  *
1616  * SYNCHRONIZED is the main facility that makes Synchronized<T>
1617  * helpful. It is a pseudo-statement that introduces a scope where the
1618  * object is locked. Inside that scope you get to access the unadorned
1619  * datum.
1620  *
1621  * Example:
1622  *
1623  * Synchronized<vector<int>> svector;
1624  * ...
1625  * SYNCHRONIZED (svector) { ... use svector as a vector<int> ... }
1626  * or
1627  * SYNCHRONIZED (v, svector) { ... use v as a vector<int> ... }
1628  *
1629  * Refer to folly/docs/Synchronized.md for a detailed explanation and more
1630  * examples.
1631  */
1632 #define SYNCHRONIZED(...)                                                 \
1633   FOLLY_PUSH_WARNING                                                      \
1634   FOLLY_GNU_DISABLE_WARNING("-Wshadow")                                   \
1635   FOLLY_MSVC_DISABLE_WARNING(4189) /* initialized but unreferenced */     \
1636   FOLLY_MSVC_DISABLE_WARNING(4456) /* declaration hides local */          \
1637   FOLLY_MSVC_DISABLE_WARNING(4457) /* declaration hides parameter */      \
1638   FOLLY_MSVC_DISABLE_WARNING(4458) /* declaration hides member */         \
1639   FOLLY_MSVC_DISABLE_WARNING(4459) /* declaration hides global */         \
1640   FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS                                   \
1641   if (bool SYNCHRONIZED_VAR(state) = false) {                             \
1642     (void)::folly::detail::SYNCHRONIZED_macro_is_deprecated{};            \
1643   } else                                                                  \
1644     for (auto SYNCHRONIZED_VAR(lockedPtr) =                               \
1645              (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).contextualLock(); \
1646          !SYNCHRONIZED_VAR(state);                                        \
1647          SYNCHRONIZED_VAR(state) = true)                                  \
1648       for (auto& FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) =                    \
1649                *SYNCHRONIZED_VAR(lockedPtr).operator->();                 \
1650            !SYNCHRONIZED_VAR(state);                                      \
1651            SYNCHRONIZED_VAR(state) = true)                                \
1652     FOLLY_POP_WARNING
1653 
1654 /**
1655  * NOTE: This API is deprecated.  Use lock(), wlock(), rlock() or the withLock
1656  * functions instead.  In the future it will be marked with a deprecation
1657  * attribute to emit build-time warnings, and then it will be removed entirely.
1658  *
1659  * Similar to SYNCHRONIZED, but only uses a read lock.
1660  */
1661 #define SYNCHRONIZED_CONST(...)            \
1662   SYNCHRONIZED(                            \
1663       FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)), \
1664       as_const(FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))))
1665 
1666 /**
1667  * NOTE: This API is deprecated.  Use lock(), wlock(), rlock() or the withLock
1668  * functions instead.  In the future it will be marked with a deprecation
1669  * attribute to emit build-time warnings, and then it will be removed entirely.
1670  *
1671  * Synchronizes two Synchronized objects (they may encapsulate
1672  * different data). Synchronization is done in increasing address of
1673  * object order, so there is no deadlock risk.
1674  */
1675 #define SYNCHRONIZED_DUAL(n1, e1, n2, e2)                                      \
1676   if (bool SYNCHRONIZED_VAR(state) = false) {                                  \
1677     (void)::folly::detail::SYNCHRONIZED_macro_is_deprecated{};                 \
1678   } else                                                                       \
1679     for (auto SYNCHRONIZED_VAR(ptrs) = acquireLockedPair(e1, e2);              \
1680          !SYNCHRONIZED_VAR(state);                                             \
1681          SYNCHRONIZED_VAR(state) = true)                                       \
1682       for (auto& n1 = *SYNCHRONIZED_VAR(ptrs).first; !SYNCHRONIZED_VAR(state); \
1683            SYNCHRONIZED_VAR(state) = true)                                     \
1684         for (auto& n2 = *SYNCHRONIZED_VAR(ptrs).second;                        \
1685              !SYNCHRONIZED_VAR(state);                                         \
1686              SYNCHRONIZED_VAR(state) = true)
1687 
1688 } /* namespace folly */
1689