improve Synchronized LockedPtr class, and add new lock() APIs
[folly.git] / folly / Synchronized.h
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * This module implements a Synchronized abstraction useful in
19  * mutex-based concurrency.
20  *
21  * The Synchronized<T, Mutex> class is the primary public API exposed by this
22  * module.  See folly/docs/Synchronized.md for a more complete explanation of
23  * this class and its benefits.
24  */
25
26 #pragma once
27
28 #include <folly/LockTraits.h>
29 #include <folly/Preprocessor.h>
30 #include <folly/SharedMutex.h>
31 #include <folly/Traits.h>
32 #include <glog/logging.h>
33 #include <mutex>
34 #include <type_traits>
35
36 namespace folly {
37
38 template <class LockedType, class Mutex, class LockPolicy>
39 class LockedPtrBase;
40 template <class LockedType, class LockPolicy>
41 class LockedPtr;
42
43 /**
44  * SynchronizedBase is a helper parent class for Synchronized<T>.
45  *
46  * It provides wlock() and rlock() methods for shared mutex types,
47  * or lock() methods for purely exclusive mutex types.
48  */
49 template <class Subclass, bool is_shared>
50 class SynchronizedBase;
51
52 /**
53  * SynchronizedBase specialization for shared mutex types.
54  *
55  * This class provides wlock() and rlock() methods for acquiring the lock and
56  * accessing the data.
57  */
58 template <class Subclass>
59 class SynchronizedBase<Subclass, true> {
60  public:
61   using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>;
62   using ConstWLockedPtr =
63       ::folly::LockedPtr<const Subclass, LockPolicyExclusive>;
64   using ConstLockedPtr = ::folly::LockedPtr<const Subclass, LockPolicyShared>;
65
66   /**
67    * Acquire an exclusive lock, and return a LockedPtr that can be used to
68    * safely access the datum.
69    *
70    * LockedPtr offers operator -> and * to provide access to the datum.
71    * The lock will be released when the LockedPtr is destroyed.
72    */
73   LockedPtr wlock() {
74     return LockedPtr(static_cast<Subclass*>(this));
75   }
76   ConstWLockedPtr wlock() const {
77     return ConstWLockedPtr(static_cast<const Subclass*>(this));
78   }
79
80   /**
81    * Acquire a read lock, and return a ConstLockedPtr that can be used to
82    * safely access the datum.
83    */
84   ConstLockedPtr rlock() const {
85     return ConstLockedPtr(static_cast<const Subclass*>(this));
86   }
87
88   /**
89    * Attempts to acquire the lock, or fails if the timeout elapses first.
90    * If acquisition is unsuccessful, the returned LockedPtr will be null.
91    *
92    * (Use LockedPtr::isNull() to check for validity.)
93    */
94   template <class Rep, class Period>
95   LockedPtr wlock(const std::chrono::duration<Rep, Period>& timeout) {
96     return LockedPtr(static_cast<Subclass*>(this), timeout);
97   }
98   template <class Rep, class Period>
99   ConstWLockedPtr wlock(
100       const std::chrono::duration<Rep, Period>& timeout) const {
101     return ConstWLockedPtr(static_cast<const Subclass*>(this), timeout);
102   }
103
104   /**
105    * Attempts to acquire the lock, or fails if the timeout elapses first.
106    * If acquisition is unsuccessful, the returned LockedPtr will be null.
107    *
108    * (Use LockedPtr::isNull() to check for validity.)
109    */
110   template <class Rep, class Period>
111   ConstLockedPtr rlock(
112       const std::chrono::duration<Rep, Period>& timeout) const {
113     return ConstLockedPtr(static_cast<const Subclass*>(this), timeout);
114   }
115 };
116
117 /**
118  * SynchronizedBase specialization for non-shared mutex types.
119  *
120  * This class provides lock() methods for acquiring the lock and accessing the
121  * data.
122  */
123 template <class Subclass>
124 class SynchronizedBase<Subclass, false> {
125  public:
126   using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>;
127   using ConstLockedPtr =
128       ::folly::LockedPtr<const Subclass, LockPolicyExclusive>;
129
130   /**
131    * Acquire a lock, and return a LockedPtr that can be used to safely access
132    * the datum.
133    */
134   LockedPtr lock() {
135     return LockedPtr(static_cast<Subclass*>(this));
136   }
137
138   /**
139    * Acquire a lock, and return a ConstLockedPtr that can be used to safely
140    * access the datum.
141    */
142   ConstLockedPtr lock() const {
143     return ConstLockedPtr(static_cast<const Subclass*>(this));
144   }
145
146   /**
147    * Attempts to acquire the lock, or fails if the timeout elapses first.
148    * If acquisition is unsuccessful, the returned LockedPtr will be null.
149    */
150   template <class Rep, class Period>
151   LockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) {
152     return LockedPtr(static_cast<Subclass*>(this), timeout);
153   }
154
155   /**
156    * Attempts to acquire the lock, or fails if the timeout elapses first.
157    * If acquisition is unsuccessful, the returned LockedPtr will be null.
158    */
159   template <class Rep, class Period>
160   ConstLockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) const {
161     return ConstLockedPtr(static_cast<const Subclass*>(this), timeout);
162   }
163 };
164
165 /**
166  * Synchronized<T> encapsulates an object of type T (a "datum") paired
167  * with a mutex. The only way to access the datum is while the mutex
168  * is locked, and Synchronized makes it virtually impossible to do
169  * otherwise. The code that would access the datum in unsafe ways
170  * would look odd and convoluted, thus readily alerting the human
171  * reviewer. In contrast, the code that uses Synchronized<T> correctly
172  * looks simple and intuitive.
173  *
174  * The second parameter must be a mutex type.  Any mutex type supported by
175  * LockTraits<Mutex> can be used.  By default any class with lock() and
176  * unlock() methods will work automatically.  LockTraits can be specialized to
177  * teach Synchronized how to use other custom mutex types.  See the
178  * documentation in LockTraits.h for additional details.
179  *
180  * Supported mutexes that work by default include std::mutex,
181  * std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex,
182  * folly::SharedMutex, folly::RWSpinLock, and folly::SpinLock.
183  * Include LockTraitsBoost.h to get additional LockTraits specializations to
184  * support the following boost mutex types: boost::mutex,
185  * boost::recursive_mutex, boost::shared_mutex, boost::timed_mutex, and
186  * boost::recursive_timed_mutex.
187  */
188 template <class T, class Mutex = SharedMutex>
189 struct Synchronized : public SynchronizedBase<
190                           Synchronized<T, Mutex>,
191                           LockTraits<Mutex>::is_shared> {
192  private:
193   using Base =
194       SynchronizedBase<Synchronized<T, Mutex>, LockTraits<Mutex>::is_shared>;
195   static constexpr bool nxCopyCtor{
196       std::is_nothrow_copy_constructible<T>::value};
197   static constexpr bool nxMoveCtor{
198       std::is_nothrow_move_constructible<T>::value};
199
200  public:
201   using LockedPtr = typename Base::LockedPtr;
202   using ConstLockedPtr = typename Base::ConstLockedPtr;
203   using DataType = T;
204   using MutexType = Mutex;
205
206   /**
207    * Default constructor leaves both members call their own default
208    * constructor.
209    */
210   Synchronized() = default;
211
212   /**
213    * Copy constructor copies the data (with locking the source and
214    * all) but does NOT copy the mutex. Doing so would result in
215    * deadlocks.
216    */
217   Synchronized(const Synchronized& rhs) noexcept(nxCopyCtor)
218       : Synchronized(rhs, rhs.contextualRLock()) {}
219
220   /**
221    * Move constructor moves the data (with locking the source and all)
222    * but does not move the mutex.
223    */
224   Synchronized(Synchronized&& rhs) noexcept(nxMoveCtor)
225       : Synchronized(std::move(rhs), rhs.contextualLock()) {}
226
227   /**
228    * Constructor taking a datum as argument copies it. There is no
229    * need to lock the constructing object.
230    */
231   explicit Synchronized(const T& rhs) noexcept(nxCopyCtor) : datum_(rhs) {}
232
233   /**
234    * Constructor taking a datum rvalue as argument moves it. Again,
235    * there is no need to lock the constructing object.
236    */
237   explicit Synchronized(T&& rhs) noexcept(nxMoveCtor)
238       : datum_(std::move(rhs)) {}
239
240   /**
241    * Lets you construct non-movable types in-place. Use the constexpr
242    * instance `construct_in_place` as the first argument.
243    */
244   template <typename... Args>
245   explicit Synchronized(construct_in_place_t, Args&&... args)
246       : datum_(std::forward<Args>(args)...) {}
247
248   /**
249    * The canonical assignment operator only assigns the data, NOT the
250    * mutex. It locks the two objects in ascending order of their
251    * addresses.
252    */
253   Synchronized& operator=(const Synchronized& rhs) {
254     if (this == &rhs) {
255       // Self-assignment, pass.
256     } else if (this < &rhs) {
257       auto guard1 = operator->();
258       auto guard2 = rhs.operator->();
259       datum_ = rhs.datum_;
260     } else {
261       auto guard1 = rhs.operator->();
262       auto guard2 = operator->();
263       datum_ = rhs.datum_;
264     }
265     return *this;
266   }
267
268   /**
269    * Move assignment operator, only assigns the data, NOT the
270    * mutex. It locks the two objects in ascending order of their
271    * addresses.
272    */
273   Synchronized& operator=(Synchronized&& rhs) {
274     if (this == &rhs) {
275       // Self-assignment, pass.
276     } else if (this < &rhs) {
277       auto guard1 = operator->();
278       auto guard2 = rhs.operator->();
279       datum_ = std::move(rhs.datum_);
280     } else {
281       auto guard1 = rhs.operator->();
282       auto guard2 = operator->();
283       datum_ = std::move(rhs.datum_);
284     }
285     return *this;
286   }
287
288   /**
289    * Lock object, assign datum.
290    */
291   Synchronized& operator=(const T& rhs) {
292     auto guard = operator->();
293     datum_ = rhs;
294     return *this;
295   }
296
297   /**
298    * Lock object, move-assign datum.
299    */
300   Synchronized& operator=(T&& rhs) {
301     auto guard = operator->();
302     datum_ = std::move(rhs);
303     return *this;
304   }
305
306   /**
307    * Acquire an appropriate lock based on the context.
308    *
309    * If the mutex is a shared mutex, and the Synchronized instance is const,
310    * this acquires a shared lock.  Otherwise this acquires an exclusive lock.
311    *
312    * In general, prefer using the explicit rlock() and wlock() methods
313    * for read-write locks, and lock() for purely exclusive locks.
314    *
315    * contextualLock() is primarily intended for use in other template functions
316    * that do not necessarily know the lock type.
317    */
318   LockedPtr contextualLock() {
319     return LockedPtr(this);
320   }
321   ConstLockedPtr contextualLock() const {
322     return ConstLockedPtr(this);
323   }
324   template <class Rep, class Period>
325   LockedPtr contextualLock(const std::chrono::duration<Rep, Period>& timeout) {
326     return LockedPtr(this, timeout);
327   }
328   template <class Rep, class Period>
329   ConstLockedPtr contextualLock(
330       const std::chrono::duration<Rep, Period>& timeout) const {
331     return ConstLockedPtr(this, timeout);
332   }
333   /**
334    * contextualRLock() acquires a read lock if the mutex type is shared,
335    * or a regular exclusive lock for non-shared mutex types.
336    *
337    * contextualRLock() when you know that you prefer a read lock (if
338    * available), even if the Synchronized<T> object itself is non-const.
339    */
340   ConstLockedPtr contextualRLock() const {
341     return ConstLockedPtr(this);
342   }
343   template <class Rep, class Period>
344   ConstLockedPtr contextualRLock(
345       const std::chrono::duration<Rep, Period>& timeout) const {
346     return ConstLockedPtr(this, timeout);
347   }
348
349   /**
350    * This accessor offers a LockedPtr. In turn, LockedPtr offers
351    * operator-> returning a pointer to T. The operator-> keeps
352    * expanding until it reaches a pointer, so syncobj->foo() will lock
353    * the object and call foo() against it.
354    *
355    * NOTE: This API is planned to be deprecated in an upcoming diff.
356    * Prefer using lock(), wlock(), or rlock() instead.
357    */
358   LockedPtr operator->() {
359     return LockedPtr(this);
360   }
361
362   /**
363    * Obtain a ConstLockedPtr.
364    *
365    * NOTE: This API is planned to be deprecated in an upcoming diff.
366    * Prefer using lock(), wlock(), or rlock() instead.
367    */
368   ConstLockedPtr operator->() const {
369     return ConstLockedPtr(this);
370   }
371
372   /**
373    * Attempts to acquire for a given number of milliseconds. If
374    * acquisition is unsuccessful, the returned LockedPtr is NULL.
375    *
376    * NOTE: This API is deprecated.  Use lock(), wlock(), or rlock() instead.
377    * In the future it will be marked with a deprecation attribute to emit
378    * build-time warnings, and then it will be removed entirely.
379    */
380   LockedPtr timedAcquire(unsigned int milliseconds) {
381     return LockedPtr(this, std::chrono::milliseconds(milliseconds));
382   }
383
384   /**
385    * Attempts to acquire for a given number of milliseconds. If
386    * acquisition is unsuccessful, the returned ConstLockedPtr is NULL.
387    *
388    * NOTE: This API is deprecated.  Use lock(), wlock(), or rlock() instead.
389    * In the future it will be marked with a deprecation attribute to emit
390    * build-time warnings, and then it will be removed entirely.
391    */
392   ConstLockedPtr timedAcquire(unsigned int milliseconds) const {
393     return ConstLockedPtr(this, std::chrono::milliseconds(milliseconds));
394   }
395
396   /**
397    * Sometimes, although you have a mutable object, you only want to
398    * call a const method against it. The most efficient way to achieve
399    * that is by using a read lock. You get to do so by using
400    * obj.asConst()->method() instead of obj->method().
401    *
402    * NOTE: This API is planned to be deprecated in an upcoming diff.
403    * Use rlock() instead.
404    */
405   const Synchronized& asConst() const {
406     return *this;
407   }
408
409   /**
410    * Swaps with another Synchronized. Protected against
411    * self-swap. Only data is swapped. Locks are acquired in increasing
412    * address order.
413    */
414   void swap(Synchronized& rhs) {
415     if (this == &rhs) {
416       return;
417     }
418     if (this > &rhs) {
419       return rhs.swap(*this);
420     }
421     auto guard1 = operator->();
422     auto guard2 = rhs.operator->();
423
424     using std::swap;
425     swap(datum_, rhs.datum_);
426   }
427
428   /**
429    * Swap with another datum. Recommended because it keeps the mutex
430    * held only briefly.
431    */
432   void swap(T& rhs) {
433     LockedPtr guard(this);
434
435     using std::swap;
436     swap(datum_, rhs);
437   }
438
439   /**
440    * Copies datum to a given target.
441    */
442   void copy(T* target) const {
443     ConstLockedPtr guard(this);
444     *target = datum_;
445   }
446
447   /**
448    * Returns a fresh copy of the datum.
449    */
450   T copy() const {
451     ConstLockedPtr guard(this);
452     return datum_;
453   }
454
455  private:
456   template <class LockedType, class MutexType, class LockPolicy>
457   friend class folly::LockedPtrBase;
458   template <class LockedType, class LockPolicy>
459   friend class folly::LockedPtr;
460
461   /**
462    * Helper constructors to enable Synchronized for
463    * non-default constructible types T.
464    * Guards are created in actual public constructors and are alive
465    * for the time required to construct the object
466    */
467   Synchronized(
468       const Synchronized& rhs,
469       const ConstLockedPtr& /*guard*/) noexcept(nxCopyCtor)
470       : datum_(rhs.datum_) {}
471
472   Synchronized(Synchronized&& rhs, const LockedPtr& /*guard*/) noexcept(
473       nxMoveCtor)
474       : datum_(std::move(rhs.datum_)) {}
475
476   // Synchronized data members
477   T datum_;
478   mutable Mutex mutex_;
479 };
480
481 template <class SynchronizedType, class LockPolicy>
482 class ScopedUnlocker;
483
484 /**
485  * A helper base class for implementing LockedPtr.
486  *
487  * The main reason for having this as a separate class is so we can specialize
488  * it for std::mutex, so we can expose a std::unique_lock to the caller
489  * when std::mutex is being used.  This allows callers to use a
490  * std::condition_variable with the mutex from a Synchronized<T, std::mutex>.
491  *
492  * We don't use std::unique_lock with other Mutex types since it makes the
493  * LockedPtr class slightly larger, and it makes the logic to support
494  * ScopedUnlocker slightly more complicated.  std::mutex is the only one that
495  * really seems to benefit from the unique_lock.  std::condition_variable
496  * itself only supports std::unique_lock<std::mutex>, so there doesn't seem to
497  * be any real benefit to exposing the unique_lock with other mutex types.
498  *
499  * Note that the SynchronizedType template parameter may or may not be const
500  * qualified.
501  */
502 template <class SynchronizedType, class Mutex, class LockPolicy>
503 class LockedPtrBase {
504  public:
505   using MutexType = Mutex;
506   friend class folly::ScopedUnlocker<SynchronizedType, LockPolicy>;
507
508   /**
509    * Destructor releases.
510    */
511   ~LockedPtrBase() {
512     if (parent_) {
513       LockPolicy::unlock(parent_->mutex_);
514     }
515   }
516
517  protected:
518   LockedPtrBase() {}
519   explicit LockedPtrBase(SynchronizedType* parent) : parent_(parent) {
520     LockPolicy::lock(parent_->mutex_);
521   }
522   template <class Rep, class Period>
523   LockedPtrBase(
524       SynchronizedType* parent,
525       const std::chrono::duration<Rep, Period>& timeout) {
526     if (LockPolicy::try_lock_for(parent->mutex_, timeout)) {
527       this->parent_ = parent;
528     }
529   }
530   LockedPtrBase(LockedPtrBase&& rhs) noexcept : parent_(rhs.parent_) {
531     rhs.parent_ = nullptr;
532   }
533   LockedPtrBase& operator=(LockedPtrBase&& rhs) noexcept {
534     if (parent_) {
535       LockPolicy::unlock(parent_->mutex_);
536     }
537
538     parent_ = rhs.parent_;
539     rhs.parent_ = nullptr;
540     return *this;
541   }
542
543   using UnlockerData = SynchronizedType*;
544
545   /**
546    * Get a pointer to the Synchronized object from the UnlockerData.
547    *
548    * In the generic case UnlockerData is just the Synchronized pointer,
549    * so we return it as is.  (This function is more interesting in the
550    * std::mutex specialization below.)
551    */
552   static SynchronizedType* getSynchronized(UnlockerData data) {
553     return data;
554   }
555
556   UnlockerData releaseLock() {
557     auto current = parent_;
558     parent_ = nullptr;
559     LockPolicy::unlock(current->mutex_);
560     return current;
561   }
562   void reacquireLock(UnlockerData&& data) {
563     DCHECK(parent_ == nullptr);
564     parent_ = data;
565     LockPolicy::lock(parent_->mutex_);
566   }
567
568   SynchronizedType* parent_ = nullptr;
569 };
570
571 /**
572  * LockedPtrBase specialization for use with std::mutex.
573  *
574  * When std::mutex is used we use a std::unique_lock to hold the mutex.
575  * This makes it possible to use std::condition_variable with a
576  * Synchronized<T, std::mutex>.
577  */
578 template <class SynchronizedType, class LockPolicy>
579 class LockedPtrBase<SynchronizedType, std::mutex, LockPolicy> {
580  public:
581   using MutexType = std::mutex;
582   friend class folly::ScopedUnlocker<SynchronizedType, LockPolicy>;
583
584   /**
585    * Destructor releases.
586    */
587   ~LockedPtrBase() {
588     // The std::unique_lock will automatically release the lock when it is
589     // destroyed, so we don't need to do anything extra here.
590   }
591
592   LockedPtrBase(LockedPtrBase&& rhs) noexcept
593       : lock_(std::move(rhs.lock_)), parent_(rhs.parent_) {
594     rhs.parent_ = nullptr;
595   }
596   LockedPtrBase& operator=(LockedPtrBase&& rhs) noexcept {
597     lock_ = std::move(rhs.lock_);
598     parent_ = rhs.parent_;
599     rhs.parent_ = nullptr;
600     return *this;
601   }
602
603   /**
604    * Get a reference to the std::unique_lock.
605    *
606    * This is provided so that callers can use Synchronized<T, std::mutex>
607    * with a std::condition_variable.
608    *
609    * While this API could be used to bypass the normal Synchronized APIs and
610    * manually interact with the underlying unique_lock, this is strongly
611    * discouraged.
612    */
613   std::unique_lock<std::mutex>& getUniqueLock() {
614     return lock_;
615   }
616
617  protected:
618   LockedPtrBase() {}
619   explicit LockedPtrBase(SynchronizedType* parent)
620       : lock_(parent->mutex_), parent_(parent) {}
621
622   using UnlockerData =
623       std::pair<std::unique_lock<std::mutex>, SynchronizedType*>;
624
625   static SynchronizedType* getSynchronized(const UnlockerData& data) {
626     return data.second;
627   }
628
629   UnlockerData releaseLock() {
630     UnlockerData data(std::move(lock_), parent_);
631     parent_ = nullptr;
632     data.first.unlock();
633     return data;
634   }
635   void reacquireLock(UnlockerData&& data) {
636     lock_ = std::move(data.first);
637     lock_.lock();
638     parent_ = data.second;
639   }
640
641   // The specialization for std::mutex does have to store slightly more
642   // state than the default implementation.
643   std::unique_lock<std::mutex> lock_;
644   SynchronizedType* parent_ = nullptr;
645 };
646
647 /**
648  * This class temporarily unlocks a LockedPtr in a scoped manner.
649  */
650 template <class SynchronizedType, class LockPolicy>
651 class ScopedUnlocker {
652  public:
653   explicit ScopedUnlocker(LockedPtr<SynchronizedType, LockPolicy>* p)
654       : ptr_(p), data_(ptr_->releaseLock()) {}
655   ScopedUnlocker(const ScopedUnlocker&) = delete;
656   ScopedUnlocker& operator=(const ScopedUnlocker&) = delete;
657   ScopedUnlocker(ScopedUnlocker&& other) noexcept
658       : ptr_(other.ptr_), data_(std::move(other.data_)) {
659     other.ptr_ = nullptr;
660   }
661   ScopedUnlocker& operator=(ScopedUnlocker&& other) = delete;
662
663   ~ScopedUnlocker() {
664     if (ptr_) {
665       ptr_->reacquireLock(std::move(data_));
666     }
667   }
668
669   /**
670    * Return a pointer to the Synchronized object used by this ScopedUnlocker.
671    */
672   SynchronizedType* getSynchronized() const {
673     return LockedPtr<SynchronizedType, LockPolicy>::getSynchronized(data_);
674   }
675
676  private:
677   using Data = typename LockedPtr<SynchronizedType, LockPolicy>::UnlockerData;
678   LockedPtr<SynchronizedType, LockPolicy>* ptr_{nullptr};
679   Data data_;
680 };
681
682 /**
683  * A LockedPtr keeps a Synchronized<T> object locked for the duration of
684  * LockedPtr's existence.
685  *
686  * It provides access the datum's members directly by using operator->() and
687  * operator*().
688  *
689  * The LockPolicy parameter controls whether or not the lock is acquired in
690  * exclusive or shared mode.
691  */
692 template <class SynchronizedType, class LockPolicy>
693 class LockedPtr : public LockedPtrBase<
694                       SynchronizedType,
695                       typename SynchronizedType::MutexType,
696                       LockPolicy> {
697  private:
698   using Base = LockedPtrBase<
699       SynchronizedType,
700       typename SynchronizedType::MutexType,
701       LockPolicy>;
702   using UnlockerData = typename Base::UnlockerData;
703   // CDataType is the DataType with the appropriate const-qualification
704   using CDataType = typename std::conditional<
705       std::is_const<SynchronizedType>::value,
706       typename SynchronizedType::DataType const,
707       typename SynchronizedType::DataType>::type;
708
709  public:
710   using DataType = typename SynchronizedType::DataType;
711   using MutexType = typename SynchronizedType::MutexType;
712   using Synchronized = typename std::remove_const<SynchronizedType>::type;
713   friend class ScopedUnlocker<SynchronizedType, LockPolicy>;
714
715   /**
716    * Creates an uninitialized LockedPtr.
717    *
718    * Dereferencing an uninitialized LockedPtr is not allowed.
719    */
720   LockedPtr() {}
721
722   /**
723    * Takes a Synchronized<T> and locks it.
724    */
725   explicit LockedPtr(SynchronizedType* parent) : Base(parent) {}
726
727   /**
728    * Takes a Synchronized<T> and attempts to lock it, within the specified
729    * timeout.
730    *
731    * Blocks until the lock is acquired or until the specified timeout expires.
732    * If the timeout expired without acquiring the lock, the LockedPtr will be
733    * null, and LockedPtr::isNull() will return true.
734    */
735   template <class Rep, class Period>
736   LockedPtr(
737       SynchronizedType* parent,
738       const std::chrono::duration<Rep, Period>& timeout)
739       : Base(parent, timeout) {}
740
741   /**
742    * Move constructor.
743    */
744   LockedPtr(LockedPtr&& rhs) noexcept = default;
745
746   /**
747    * Move assignment operator.
748    */
749   LockedPtr& operator=(LockedPtr&& rhs) noexcept = default;
750
751   /*
752    * Copy constructor and assignment operator are deleted.
753    */
754   LockedPtr(const LockedPtr& rhs) = delete;
755   LockedPtr& operator=(const LockedPtr& rhs) = delete;
756
757   /**
758    * Destructor releases.
759    */
760   ~LockedPtr() {}
761
762   /**
763    * Check if this LockedPtr is uninitialized, or points to valid locked data.
764    *
765    * This method can be used to check if a timed-acquire operation succeeded.
766    * If an acquire operation times out it will result in a null LockedPtr.
767    *
768    * A LockedPtr is always either null, or holds a lock to valid data.
769    * Methods such as scopedUnlock() reset the LockedPtr to null for the
770    * duration of the unlock.
771    */
772   bool isNull() const {
773     return this->parent_ == nullptr;
774   }
775
776   /**
777    * Explicit boolean conversion.
778    *
779    * Returns !isNull()
780    */
781   explicit operator bool() const {
782     return this->parent_ != nullptr;
783   }
784
785   /**
786    * Access the locked data.
787    *
788    * This method should only be used if the LockedPtr is valid.
789    */
790   CDataType* operator->() const {
791     return &this->parent_->datum_;
792   }
793
794   /**
795    * Access the locked data.
796    *
797    * This method should only be used if the LockedPtr is valid.
798    */
799   CDataType& operator*() const {
800     return this->parent_->datum_;
801   }
802
803   /**
804    * Temporarily unlock the LockedPtr, and reset it to null.
805    *
806    * Returns an helper object that will re-lock and restore the LockedPtr when
807    * the helper is destroyed.  The LockedPtr may not be dereferenced for as
808    * long as this helper object exists.
809    */
810   ScopedUnlocker<SynchronizedType, LockPolicy> scopedUnlock() {
811     return ScopedUnlocker<SynchronizedType, LockPolicy>(this);
812   }
813 };
814
815 namespace detail {
816 /*
817  * A helper alias to select a ConstLockedPtr if the template parameter is a
818  * const Synchronized<T>, or a LockedPtr if the parameter is not const.
819  */
820 template <class SynchronizedType>
821 using LockedPtrType = typename std::conditional<
822     std::is_const<SynchronizedType>::value,
823     typename SynchronizedType::ConstLockedPtr,
824     typename SynchronizedType::LockedPtr>::type;
825 } // detail
826
827 /**
828  * Acquire locks for multiple Synchronized<T> objects, in a deadlock-safe
829  * manner.
830  *
831  * The locks are acquired in order from lowest address to highest address.
832  * (Note that this is not necessarily the same algorithm used by std::lock().)
833  *
834  * For parameters that are const and support shared locks, a read lock is
835  * acquired.  Otherwise an exclusive lock is acquired.
836  *
837  * TODO: Extend acquireLocked() with variadic template versions that
838  * allow for more than 2 Synchronized arguments.  (I haven't given too much
839  * thought about how to implement this.  It seems like it would be rather
840  * complicated, but I think it should be possible.)
841  */
842 template <class Sync1, class Sync2>
843 std::tuple<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>>
844 acquireLocked(Sync1& l1, Sync2& l2) {
845   if (static_cast<const void*>(&l1) < static_cast<const void*>(&l2)) {
846     auto p1 = l1.contextualLock();
847     auto p2 = l2.contextualLock();
848     return std::make_tuple(std::move(p1), std::move(p2));
849   } else {
850     auto p2 = l2.contextualLock();
851     auto p1 = l1.contextualLock();
852     return std::make_tuple(std::move(p1), std::move(p2));
853   }
854 }
855
856 /**
857  * A version of acquireLocked() that returns a std::pair rather than a
858  * std::tuple, which is easier to use in many places.
859  */
860 template <class Sync1, class Sync2>
861 std::pair<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>>
862 acquireLockedPair(Sync1& l1, Sync2& l2) {
863   auto lockedPtrs = acquireLocked(l1, l2);
864   return {std::move(std::get<0>(lockedPtrs)),
865           std::move(std::get<1>(lockedPtrs))};
866 }
867
868 /************************************************************************
869  * NOTE: All APIs below this line will be deprecated in upcoming diffs.
870  ************************************************************************/
871
872 // Non-member swap primitive
873 template <class T, class M>
874 void swap(Synchronized<T, M>& lhs, Synchronized<T, M>& rhs) {
875   lhs.swap(rhs);
876 }
877
878 /**
879  * SYNCHRONIZED is the main facility that makes Synchronized<T>
880  * helpful. It is a pseudo-statement that introduces a scope where the
881  * object is locked. Inside that scope you get to access the unadorned
882  * datum.
883  *
884  * Example:
885  *
886  * Synchronized<vector<int>> svector;
887  * ...
888  * SYNCHRONIZED (svector) { ... use svector as a vector<int> ... }
889  * or
890  * SYNCHRONIZED (v, svector) { ... use v as a vector<int> ... }
891  *
892  * Refer to folly/docs/Synchronized.md for a detailed explanation and more
893  * examples.
894  */
895 #define SYNCHRONIZED(...)                                             \
896   FOLLY_PUSH_WARNING                                                  \
897   FOLLY_GCC_DISABLE_WARNING(shadow)                                   \
898   if (bool SYNCHRONIZED_state = false) {                              \
899   } else                                                              \
900     for (auto SYNCHRONIZED_lockedPtr =                                \
901              (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).operator->(); \
902          !SYNCHRONIZED_state;                                         \
903          SYNCHRONIZED_state = true)                                   \
904       for (auto& FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) =                \
905                *SYNCHRONIZED_lockedPtr.operator->();                  \
906            !SYNCHRONIZED_state;                                       \
907            SYNCHRONIZED_state = true)                                 \
908   FOLLY_POP_WARNING
909
910 #define TIMED_SYNCHRONIZED(timeout, ...)                                       \
911   if (bool SYNCHRONIZED_state = false) {                                       \
912   } else                                                                       \
913     for (auto SYNCHRONIZED_lockedPtr =                                         \
914              (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).timedAcquire(timeout); \
915          !SYNCHRONIZED_state;                                                  \
916          SYNCHRONIZED_state = true)                                            \
917       for (auto FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) =                          \
918                (!SYNCHRONIZED_lockedPtr                                        \
919                     ? nullptr                                                  \
920                     : SYNCHRONIZED_lockedPtr.operator->());                    \
921            !SYNCHRONIZED_state;                                                \
922            SYNCHRONIZED_state = true)
923
924 /**
925  * Similar to SYNCHRONIZED, but only uses a read lock.
926  */
927 #define SYNCHRONIZED_CONST(...)            \
928   SYNCHRONIZED(                            \
929       FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)), \
930       (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).asConst())
931
932 /**
933  * Similar to TIMED_SYNCHRONIZED, but only uses a read lock.
934  */
935 #define TIMED_SYNCHRONIZED_CONST(timeout, ...) \
936   TIMED_SYNCHRONIZED(                          \
937       timeout,                                 \
938       FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)),     \
939       (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).asConst())
940
941 /**
942  * Temporarily disables synchronization inside a SYNCHRONIZED block.
943  *
944  * Note: This macro is deprecated, and kind of broken.  The input parameter
945  * does not control what it unlocks--it always unlocks the lock acquired by the
946  * most recent SYNCHRONIZED scope.  If you have two nested SYNCHRONIZED blocks,
947  * UNSYNCHRONIZED always unlocks the inner-most, even if you pass in the
948  * variable name used in the outer SYNCHRONIZED block.
949  *
950  * This macro will be removed soon in a subsequent diff.
951  */
952 #define UNSYNCHRONIZED(name)                                             \
953   for (auto SYNCHRONIZED_state3 = SYNCHRONIZED_lockedPtr.scopedUnlock(); \
954        !SYNCHRONIZED_state;                                              \
955        SYNCHRONIZED_state = true)                                        \
956     for (auto& name = *SYNCHRONIZED_state3.getSynchronized();            \
957          !SYNCHRONIZED_state;                                            \
958          SYNCHRONIZED_state = true)
959
960 /**
961  * Synchronizes two Synchronized objects (they may encapsulate
962  * different data). Synchronization is done in increasing address of
963  * object order, so there is no deadlock risk.
964  */
965 #define SYNCHRONIZED_DUAL(n1, e1, n2, e2)                               \
966   if (bool SYNCHRONIZED_state = false) {                                \
967   } else                                                                \
968     for (auto SYNCHRONIZED_ptrs = acquireLockedPair(e1, e2);            \
969          !SYNCHRONIZED_state;                                           \
970          SYNCHRONIZED_state = true)                                     \
971       for (auto& n1 = *SYNCHRONIZED_ptrs.first; !SYNCHRONIZED_state;    \
972            SYNCHRONIZED_state = true)                                   \
973         for (auto& n2 = *SYNCHRONIZED_ptrs.second; !SYNCHRONIZED_state; \
974              SYNCHRONIZED_state = true)
975
976 } /* namespace folly */