move assignment operators for folly::Synchronized
[folly.git] / folly / Synchronized.h
1 /*
2  * Copyright 2013 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  * @author: Andrei Alexandrescu (andrei.alexandrescu@fb.com)
22  */
23
24 #ifndef SYNCHRONIZED_H_
25 #define SYNCHRONIZED_H_
26
27 #include <type_traits>
28 #include <mutex>
29 #include <boost/thread.hpp>
30 #include "folly/Preprocessor.h"
31 #include "folly/Traits.h"
32
33 namespace folly {
34
35 namespace detail {
36 enum InternalDoNotUse {};
37
38 /**
39  * Free function adaptors for std:: and boost::
40  */
41
42 /**
43  * Yields true iff T has .lock() and .unlock() member functions. This
44  * is done by simply enumerating the mutexes with this interface in
45  * std and boost.
46  */
47 template <class T>
48 struct HasLockUnlock {
49   enum { value = IsOneOf<T,
50          std::mutex, std::recursive_mutex,
51          boost::mutex, boost::recursive_mutex, boost::shared_mutex,
52 #ifndef __APPLE__ // OSX doesn't have timed mutexes
53          std::timed_mutex, std::recursive_timed_mutex,
54          boost::timed_mutex, boost::recursive_timed_mutex
55 #endif
56          >::value };
57 };
58
59 /**
60  * Acquires a mutex for reading by calling .lock(). The exception is
61  * boost::shared_mutex, which has a special read-lock primitive called
62  * .lock_shared().
63  */
64 template <class T>
65 typename std::enable_if<
66   HasLockUnlock<T>::value && !std::is_same<T, boost::shared_mutex>::value>::type
67 acquireRead(T& mutex) {
68   mutex.lock();
69 }
70
71 /**
72  * Special case for boost::shared_mutex.
73  */
74 template <class T>
75 typename std::enable_if<std::is_same<T, boost::shared_mutex>::value>::type
76 acquireRead(T& mutex) {
77   mutex.lock_shared();
78 }
79
80 /**
81  * Acquires a mutex for reading with timeout by calling .timed_lock(). This
82  * applies to three of the boost mutex classes as enumerated below.
83  */
84 template <class T>
85 typename std::enable_if<std::is_same<T, boost::shared_mutex>::value, bool>::type
86 acquireRead(T& mutex,
87             unsigned int milliseconds) {
88   return mutex.timed_lock_shared(boost::posix_time::milliseconds(milliseconds));
89 }
90
91 /**
92  * Acquires a mutex for reading and writing by calling .lock().
93  */
94 template <class T>
95 typename std::enable_if<HasLockUnlock<T>::value>::type
96 acquireReadWrite(T& mutex) {
97   mutex.lock();
98 }
99
100 #ifndef __APPLE__ // OSX doesn't have timed mutexes
101 /**
102  * Acquires a mutex for reading and writing with timeout by calling
103  * .try_lock_for(). This applies to two of the std mutex classes as
104  * enumerated below.
105  */
106 template <class T>
107 typename std::enable_if<
108   IsOneOf<T, std::timed_mutex, std::recursive_timed_mutex>::value, bool>::type
109 acquireReadWrite(T& mutex,
110                  unsigned int milliseconds) {
111   return mutex.try_lock_for(std::chrono::milliseconds(milliseconds));
112 }
113
114 /**
115  * Acquires a mutex for reading and writing with timeout by calling
116  * .timed_lock(). This applies to three of the boost mutex classes as
117  * enumerated below.
118  */
119 template <class T>
120 typename std::enable_if<
121   IsOneOf<T, boost::shared_mutex, boost::timed_mutex,
122           boost::recursive_timed_mutex>::value, bool>::type
123 acquireReadWrite(T& mutex,
124                  unsigned int milliseconds) {
125   return mutex.timed_lock(boost::posix_time::milliseconds(milliseconds));
126 }
127 #endif // __APPLE__
128
129 /**
130  * Releases a mutex previously acquired for reading by calling
131  * .unlock(). The exception is boost::shared_mutex, which has a
132  * special primitive called .unlock_shared().
133  */
134 template <class T>
135 typename std::enable_if<
136   HasLockUnlock<T>::value && !std::is_same<T, boost::shared_mutex>::value>::type
137 releaseRead(T& mutex) {
138   mutex.unlock();
139 }
140
141 /**
142  * Special case for boost::shared_mutex.
143  */
144 template <class T>
145 typename std::enable_if<std::is_same<T, boost::shared_mutex>::value>::type
146 releaseRead(T& mutex) {
147   mutex.unlock_shared();
148 }
149
150 /**
151  * Releases a mutex previously acquired for reading-writing by calling
152  * .unlock().
153  */
154 template <class T>
155 typename std::enable_if<HasLockUnlock<T>::value>::type
156 releaseReadWrite(T& mutex) {
157   mutex.unlock();
158 }
159
160 } // namespace detail
161
162 /**
163  * Synchronized<T> encapsulates an object of type T (a "datum") paired
164  * with a mutex. The only way to access the datum is while the mutex
165  * is locked, and Synchronized makes it virtually impossible to do
166  * otherwise. The code that would access the datum in unsafe ways
167  * would look odd and convoluted, thus readily alerting the human
168  * reviewer. In contrast, the code that uses Synchronized<T> correctly
169  * looks simple and intuitive.
170  *
171  * The second parameter must be a mutex type. Supported mutexes are
172  * std::mutex, std::recursive_mutex, std::timed_mutex,
173  * std::recursive_timed_mutex, boost::mutex, boost::recursive_mutex,
174  * boost::shared_mutex, boost::timed_mutex,
175  * boost::recursive_timed_mutex, and the folly/RWSpinLock.h
176  * classes.
177  *
178  * You may define Synchronized support by defining 4-6 primitives in
179  * the same namespace as the mutex class (found via ADL).  The
180  * primitives are: acquireRead, acquireReadWrite, releaseRead, and
181  * releaseReadWrite. Two optional primitives for timout operations are
182  * overloads of acquireRead and acquireReadWrite. For signatures,
183  * refer to the namespace detail below, which implements the
184  * primitives for mutexes in std and boost.
185  */
186 template <class T, class Mutex = boost::shared_mutex>
187 struct Synchronized {
188   /**
189    * Default constructor leaves both members call their own default
190    * constructor.
191    */
192   Synchronized() = default;
193
194   /**
195    * Copy constructor copies the data (with locking the source and
196    * all) but does NOT copy the mutex. Doing so would result in
197    * deadlocks.
198    */
199   Synchronized(const Synchronized& rhs) {
200     auto guard = rhs.operator->();
201     datum_ = rhs.datum_;
202   }
203
204   /**
205    * Move constructor moves the data (with locking the source and all)
206    * but does not move the mutex.
207    */
208   Synchronized(Synchronized&& rhs) {
209     auto guard = rhs.operator->();
210     datum_ = std::move(rhs.datum_);
211   }
212
213   /**
214    * Constructor taking a datum as argument copies it. There is no
215    * need to lock the constructing object.
216    */
217   explicit Synchronized(const T& rhs) : datum_(rhs) {}
218
219   /**
220    * Constructor taking a datum rvalue as argument moves it. Again,
221    * there is no need to lock the constructing object.
222    */
223   explicit Synchronized(T&& rhs) : datum_(std::move(rhs)) {}
224
225   /**
226    * The canonical assignment operator only assigns the data, NOT the
227    * mutex. It locks the two objects in ascending order of their
228    * addresses.
229    */
230   Synchronized& operator=(const Synchronized& rhs) {
231     if (this == &rhs) {
232       // Self-assignment, pass.
233     } else if (this < &rhs) {
234       auto guard1 = operator->();
235       auto guard2 = rhs.operator->();
236       datum_ = rhs.datum_;
237     } else {
238       auto guard1 = rhs.operator->();
239       auto guard2 = operator->();
240       datum_ = rhs.datum_;
241     }
242     return *this;
243   }
244
245   /**
246    * Move assignment operator, only assigns the data, NOT the
247    * mutex. It locks the two objects in ascending order of their
248    * addresses.
249    */
250   Synchronized& operator=(Synchronized&& rhs) {
251     if (this == &rhs) {
252       // Self-assignment, pass.
253     } else if (this < &rhs) {
254       auto guard1 = operator->();
255       auto guard2 = rhs.operator->();
256       datum_ = std::move(rhs.datum_);
257     } else {
258       auto guard1 = rhs.operator->();
259       auto guard2 = operator->();
260       datum_ = std::move(rhs.datum_);
261     }
262     return *this;
263   }
264
265   /**
266    * Lock object, assign datum.
267    */
268   Synchronized& operator=(const T& rhs) {
269     auto guard = operator->();
270     datum_ = rhs;
271     return *this;
272   }
273
274   /**
275    * Lock object, move-assign datum.
276    */
277   Synchronized& operator=(T&& rhs) {
278     auto guard = operator->();
279     datum_ = std::move(rhs);
280     return *this;
281   }
282
283   /**
284    * A LockedPtr lp keeps a modifiable (i.e. non-const)
285    * Synchronized<T> object locked for the duration of lp's
286    * existence. Because of this, you get to access the datum's methods
287    * directly by using lp->fun().
288    */
289   struct LockedPtr {
290     /**
291      * Found no reason to leave this hanging.
292      */
293     LockedPtr() = delete;
294
295     /**
296      * Takes a Synchronized and locks it.
297      */
298     explicit LockedPtr(Synchronized* parent) : parent_(parent) {
299       acquire();
300     }
301
302     /**
303      * Takes a Synchronized and attempts to lock it for some
304      * milliseconds. If not, the LockedPtr will be subsequently null.
305      */
306     LockedPtr(Synchronized* parent, unsigned int milliseconds) {
307       using namespace detail;
308       if (acquireReadWrite(parent->mutex_, milliseconds)) {
309         parent_ = parent;
310         return;
311       }
312       // Could not acquire the resource, pointer is null
313       parent_ = NULL;
314     }
315
316     /**
317      * This is used ONLY inside SYNCHRONIZED_DUAL. It initializes
318      * everything properly, but does not lock the parent because it
319      * "knows" someone else will lock it. Please do not use.
320      */
321     LockedPtr(Synchronized* parent, detail::InternalDoNotUse)
322         : parent_(parent) {
323     }
324
325     /**
326      * Copy ctor adds one lock.
327      */
328     LockedPtr(const LockedPtr& rhs) : parent_(rhs.parent_) {
329       acquire();
330     }
331
332     /**
333      * Assigning from another LockedPtr results in freeing the former
334      * lock and acquiring the new one. The method works with
335      * self-assignment (does nothing).
336      */
337     LockedPtr& operator=(const LockedPtr& rhs) {
338       if (parent_ != rhs.parent_) {
339         if (parent_) parent_->mutex_.unlock();
340         parent_ = rhs.parent_;
341         acquire();
342       }
343       return *this;
344     }
345
346     /**
347      * Destructor releases.
348      */
349     ~LockedPtr() {
350       using namespace detail;
351       if (parent_) releaseReadWrite(parent_->mutex_);
352     }
353
354     /**
355      * Safe to access the data. Don't save the obtained pointer by
356      * invoking lp.operator->() by hand. Also, if the method returns a
357      * handle stored inside the datum, don't use this idiom - use
358      * SYNCHRONIZED below.
359      */
360     T* operator->() {
361       return parent_ ? &parent_->datum_ : NULL;
362     }
363
364     /**
365      * This class temporarily unlocks a LockedPtr in a scoped
366      * manner. It is used inside of the UNSYNCHRONIZED macro.
367      */
368     struct Unsynchronizer {
369       explicit Unsynchronizer(LockedPtr* p) : parent_(p) {
370         using namespace detail;
371         releaseReadWrite(parent_->parent_->mutex_);
372       }
373       Unsynchronizer(const Unsynchronizer&) = delete;
374       Unsynchronizer& operator=(const Unsynchronizer&) = delete;
375       ~Unsynchronizer() {
376         parent_->acquire();
377       }
378       LockedPtr* operator->() const {
379         return parent_;
380       }
381     private:
382       LockedPtr* parent_;
383     };
384     friend struct Unsynchronizer;
385     Unsynchronizer typeHackDoNotUse();
386
387     template <class P1, class P2>
388     friend void lockInOrder(P1& p1, P2& p2);
389
390   private:
391     void acquire() {
392       using namespace detail;
393       if (parent_) acquireReadWrite(parent_->mutex_);
394     }
395
396     // This is the entire state of LockedPtr.
397     Synchronized* parent_;
398   };
399
400   /**
401    * ConstLockedPtr does exactly what LockedPtr does, but for const
402    * Synchronized objects. Of interest is that ConstLockedPtr only
403    * uses a read lock, which is faster but more restrictive - you only
404    * get to call const methods of the datum.
405    *
406    * Much of the code between LockedPtr and
407    * ConstLockedPtr is identical and could be factor out, but there
408    * are enough nagging little differences to not justify the trouble.
409    */
410   struct ConstLockedPtr {
411     ConstLockedPtr() = delete;
412     explicit ConstLockedPtr(const Synchronized* parent) : parent_(parent) {
413       acquire();
414     }
415     ConstLockedPtr(const Synchronized* parent, detail::InternalDoNotUse)
416         : parent_(parent) {
417     }
418     ConstLockedPtr(const ConstLockedPtr& rhs) : parent_(rhs.parent_) {
419       acquire();
420     }
421     explicit ConstLockedPtr(const LockedPtr& rhs) : parent_(rhs.parent_) {
422       acquire();
423     }
424     ConstLockedPtr(const Synchronized* parent, unsigned int milliseconds) {
425       if (parent->mutex_.timed_lock(
426             boost::posix_time::milliseconds(milliseconds))) {
427         parent_ = parent;
428         return;
429       }
430       // Could not acquire the resource, pointer is null
431       parent_ = NULL;
432     }
433
434     ConstLockedPtr& operator=(const ConstLockedPtr& rhs) {
435       if (parent_ != rhs.parent_) {
436         if (parent_) parent_->mutex_.unlock_shared();
437         parent_ = rhs.parent_;
438         acquire();
439       }
440     }
441     ~ConstLockedPtr() {
442       using namespace detail;
443       if (parent_) releaseRead(parent_->mutex_);
444     }
445
446     const T* operator->() const {
447       return parent_ ? &parent_->datum_ : NULL;
448     }
449
450     struct Unsynchronizer {
451       explicit Unsynchronizer(ConstLockedPtr* p) : parent_(p) {
452         using namespace detail;
453         releaseRead(parent_->parent_->mutex_);
454       }
455       Unsynchronizer(const Unsynchronizer&) = delete;
456       Unsynchronizer& operator=(const Unsynchronizer&) = delete;
457       ~Unsynchronizer() {
458         using namespace detail;
459         acquireRead(parent_->parent_->mutex_);
460       }
461       ConstLockedPtr* operator->() const {
462         return parent_;
463       }
464     private:
465       ConstLockedPtr* parent_;
466     };
467     friend struct Unsynchronizer;
468     Unsynchronizer typeHackDoNotUse();
469
470     template <class P1, class P2>
471     friend void lockInOrder(P1& p1, P2& p2);
472
473   private:
474     void acquire() {
475       using namespace detail;
476       if (parent_) acquireRead(parent_->mutex_);
477     }
478
479     const Synchronized* parent_;
480   };
481
482   /**
483    * This accessor offers a LockedPtr. In turn. LockedPtr offers
484    * operator-> returning a pointer to T. The operator-> keeps
485    * expanding until it reaches a pointer, so syncobj->foo() will lock
486    * the object and call foo() against it.
487   */
488   LockedPtr operator->() {
489     return LockedPtr(this);
490   }
491
492   /**
493    * Same, for constant objects. You will be able to invoke only const
494    * methods.
495    */
496   ConstLockedPtr operator->() const {
497     return ConstLockedPtr(this);
498   }
499
500   /**
501    * Attempts to acquire for a given number of milliseconds. If
502    * acquisition is unsuccessful, the returned LockedPtr is NULL.
503    */
504   LockedPtr timedAcquire(unsigned int milliseconds) {
505     return LockedPtr(this, milliseconds);
506   }
507
508   /**
509    * As above, for a constant object.
510    */
511   ConstLockedPtr timedAcquire(unsigned int milliseconds) const {
512     return ConstLockedPtr(this, milliseconds);
513   }
514
515   /**
516    * Used by SYNCHRONIZED_DUAL.
517    */
518   LockedPtr internalDoNotUse() {
519     return LockedPtr(this, detail::InternalDoNotUse());
520   }
521
522   /**
523    * ditto
524    */
525   ConstLockedPtr internalDoNotUse() const {
526     return ConstLockedPtr(this, detail::InternalDoNotUse());
527   }
528
529   /**
530    * Sometimes, although you have a mutable object, you only want to
531    * call a const method against it. The most efficient way to achieve
532    * that is by using a read lock. You get to do so by using
533    * obj.asConst()->method() instead of obj->method().
534    */
535   const Synchronized& asConst() const {
536     return *this;
537   }
538
539   /**
540    * Swaps with another Synchronized. Protected against
541    * self-swap. Only data is swapped. Locks are acquired in increasing
542    * address order.
543    */
544   void swap(Synchronized& rhs) {
545     if (this == &rhs) {
546       return;
547     }
548     if (this > &rhs) {
549       return rhs.swap(*this);
550     }
551     auto guard1 = operator->();
552     auto guard2 = rhs.operator->();
553
554     using std::swap;
555     swap(datum_, rhs.datum_);
556   }
557
558   /**
559    * Swap with another datum. Recommended because it keeps the mutex
560    * held only briefly.
561    */
562   void swap(T& rhs) {
563     LockedPtr guard = operator->();
564
565     using std::swap;
566     swap(datum_, rhs);
567   }
568
569   /**
570    * Copies datum to a given target.
571    */
572   void copy(T* target) const {
573     ConstLockedPtr guard = operator->();
574     *target = datum_;
575   }
576
577   /**
578    * Returns a fresh copy of the datum.
579    */
580   T copy() const {
581     ConstLockedPtr guard = operator->();
582     return datum_;
583   }
584
585 private:
586   T datum_;
587   mutable Mutex mutex_;
588 };
589
590 // Non-member swap primitive
591 template <class T, class M>
592 void swap(Synchronized<T, M>& lhs, Synchronized<T, M>& rhs) {
593   lhs.swap(rhs);
594 }
595
596 /**
597  * SYNCHRONIZED is the main facility that makes Synchronized<T>
598  * helpful. It is a pseudo-statement that introduces a scope where the
599  * object is locked. Inside that scope you get to access the unadorned
600  * datum.
601  *
602  * Example:
603  *
604  * Synchronized<vector<int>> svector;
605  * ...
606  * SYNCHRONIZED (svector) { ... use svector as a vector<int> ... }
607  * or
608  * SYNCHRONIZED (v, svector) { ... use v as a vector<int> ... }
609  *
610  * Refer to folly/docs/Synchronized.md for a detailed explanation and more
611  * examples.
612  */
613 #define SYNCHRONIZED(...)                                       \
614   if (bool SYNCHRONIZED_state = false) {} else                  \
615     for (auto SYNCHRONIZED_lockedPtr =                          \
616            (FB_ARG_2_OR_1(__VA_ARGS__)).operator->();           \
617          !SYNCHRONIZED_state; SYNCHRONIZED_state = true)        \
618       for (auto& FB_ARG_1(__VA_ARGS__) =                        \
619              *SYNCHRONIZED_lockedPtr.operator->();              \
620            !SYNCHRONIZED_state; SYNCHRONIZED_state = true)
621
622 #define TIMED_SYNCHRONIZED(timeout, ...)                           \
623   if (bool SYNCHRONIZED_state = false) {} else                     \
624     for (auto SYNCHRONIZED_lockedPtr =                             \
625            (FB_ARG_2_OR_1(__VA_ARGS__)).timedAcquire(timeout);     \
626          !SYNCHRONIZED_state; SYNCHRONIZED_state = true)           \
627       for (auto FB_ARG_1(__VA_ARGS__) =                            \
628              SYNCHRONIZED_lockedPtr.operator->();                  \
629            !SYNCHRONIZED_state; SYNCHRONIZED_state = true)
630
631 /**
632  * Similar to SYNCHRONIZED, but only uses a read lock.
633  */
634 #define SYNCHRONIZED_CONST(...)                         \
635   SYNCHRONIZED(FB_ARG_1(__VA_ARGS__),                   \
636                (FB_ARG_2_OR_1(__VA_ARGS__)).asConst())
637
638 /**
639  * Similar to TIMED_SYNCHRONIZED, but only uses a read lock.
640  */
641 #define TIMED_SYNCHRONIZED_CONST(timeout, ...)                  \
642   TIMED_SYNCHRONIZED(timeout, FB_ARG_1(__VA_ARGS__),            \
643                      (FB_ARG_2_OR_1(__VA_ARGS__)).asConst())
644
645 /**
646  * Temporarily disables synchronization inside a SYNCHRONIZED block.
647  */
648 #define UNSYNCHRONIZED(name)                                    \
649   for (decltype(SYNCHRONIZED_lockedPtr.typeHackDoNotUse())      \
650          SYNCHRONIZED_state3(&SYNCHRONIZED_lockedPtr);          \
651        !SYNCHRONIZED_state; SYNCHRONIZED_state = true)          \
652     for (auto name = *SYNCHRONIZED_state3.operator->();         \
653          !SYNCHRONIZED_state; SYNCHRONIZED_state = true)
654
655 /**
656  * Locks two objects in increasing order of their addresses.
657  */
658 template <class P1, class P2>
659 void lockInOrder(P1& p1, P2& p2) {
660   if (static_cast<const void*>(p1.operator->()) >
661       static_cast<const void*>(p2.operator->())) {
662     p2.acquire();
663     p1.acquire();
664   } else {
665     p1.acquire();
666     p2.acquire();
667   }
668 }
669
670 /**
671  * Synchronizes two Synchronized objects (they may encapsulate
672  * different data). Synchronization is done in increasing address of
673  * object order, so there is no deadlock risk.
674  */
675 #define SYNCHRONIZED_DUAL(n1, e1, n2, e2)                       \
676   if (bool SYNCHRONIZED_state = false) {} else                  \
677     for (auto SYNCHRONIZED_lp1 = (e1).internalDoNotUse();       \
678          !SYNCHRONIZED_state; SYNCHRONIZED_state = true)        \
679       for (auto& n1 = *SYNCHRONIZED_lp1.operator->();           \
680            !SYNCHRONIZED_state;  SYNCHRONIZED_state = true)     \
681         for (auto SYNCHRONIZED_lp2 = (e2).internalDoNotUse();   \
682              !SYNCHRONIZED_state;  SYNCHRONIZED_state = true)   \
683           for (auto& n2 = *SYNCHRONIZED_lp2.operator->();       \
684                !SYNCHRONIZED_state; SYNCHRONIZED_state = true)  \
685             if ((::folly::lockInOrder(                          \
686                    SYNCHRONIZED_lp1, SYNCHRONIZED_lp2),         \
687                  false)) {}                                     \
688             else
689
690 } /* namespace folly */
691
692 #endif // SYNCHRONIZED_H_