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