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