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