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