1/*
2 * Copyright 2011-present 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 * This module implements a Synchronized abstraction useful in
18 * mutex-based concurrency.
19 *
20 * The Synchronized<T, Mutex> class is the primary public API exposed by this
21 * module. See folly/docs/Synchronized.md for a more complete explanation of
22 * this class and its benefits.
23 */
24
25#pragma once
26
27#include <folly/Function.h>
28#include <folly/Likely.h>
29#include <folly/LockTraits.h>
30#include <folly/Preprocessor.h>
31#include <folly/SharedMutex.h>
32#include <folly/Traits.h>
33#include <folly/Utility.h>
34#include <folly/container/Foreach.h>
35#include <folly/functional/ApplyTuple.h>
36#include <glog/logging.h>
37
38#include <array>
39#include <mutex>
40#include <tuple>
41#include <type_traits>
42#include <utility>
43
44namespace folly {
45
46template <class LockedType, class Mutex, class LockPolicy>
47class LockedPtrBase;
48template <class LockedType, class LockPolicy>
49class LockedPtr;
50
51/**
52 * Public version of LockInterfaceDispatcher that contains the MutexLevel enum
53 * for the passed in mutex type
54 *
55 * This is decoupled from MutexLevelValueImpl in LockTraits.h because this
56 * ensures that a heterogenous mutex with a different API can be used. For
57 * example - if a mutex does not have a lock_shared() method but the
58 * LockTraits specialization for it supports a static non member
59 * lock_shared(Mutex&) it can be used as a shared mutex and will provide
60 * rlock() and wlock() functions.
61 */
62template <class Mutex>
63using MutexLevelValue = detail::MutexLevelValueImpl<
64 true,
65 LockTraits<Mutex>::is_shared,
66 LockTraits<Mutex>::is_upgrade>;
67
68/**
69 * SynchronizedBase is a helper parent class for Synchronized<T>.
70 *
71 * It provides wlock() and rlock() methods for shared mutex types,
72 * or lock() methods for purely exclusive mutex types.
73 */
74template <class Subclass, detail::MutexLevel level>
75class SynchronizedBase;
76
77/**
78 * SynchronizedBase specialization for shared mutex types.
79 *
80 * This class provides wlock() and rlock() methods for acquiring the lock and
81 * accessing the data.
82 */
83template <class Subclass>
84class SynchronizedBase<Subclass, detail::MutexLevel::SHARED> {
85 public:
86 using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>;
87 using ConstWLockedPtr =
88 ::folly::LockedPtr<const Subclass, LockPolicyExclusive>;
89 using ConstLockedPtr = ::folly::LockedPtr<const Subclass, LockPolicyShared>;
90
91 using TryWLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyTryExclusive>;
92 using ConstTryWLockedPtr =
93 ::folly::LockedPtr<const Subclass, LockPolicyTryExclusive>;
94 using TryRLockedPtr = ::folly::LockedPtr<const Subclass, LockPolicyTryShared>;
95
96 /**
97 * Acquire an exclusive lock, and return a LockedPtr that can be used to
98 * safely access the datum.
99 *
100 * LockedPtr offers operator -> and * to provide access to the datum.
101 * The lock will be released when the LockedPtr is destroyed.
102 */
103 LockedPtr wlock() {
104 return LockedPtr(static_cast<Subclass*>(this));
105 }
106 ConstWLockedPtr wlock() const {
107 return ConstWLockedPtr(static_cast<const Subclass*>(this));
108 }
109
110 /**
111 * Attempts to acquire the lock in exclusive mode. If acquisition is
112 * unsuccessful, the returned LockedPtr will be null.
113 *
114 * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
115 * validity.)
116 */
117 TryWLockedPtr tryWLock() {
118 return TryWLockedPtr{static_cast<Subclass*>(this)};
119 }
120 ConstTryWLockedPtr tryWLock() const {
121 return ConstTryWLockedPtr{static_cast<const Subclass*>(this)};
122 }
123
124 /**
125 * Acquire a read lock, and return a ConstLockedPtr that can be used to
126 * safely access the datum.
127 */
128 ConstLockedPtr rlock() const {
129 return ConstLockedPtr(static_cast<const Subclass*>(this));
130 }
131
132 /**
133 * Attempts to acquire the lock in shared mode. If acquisition is
134 * unsuccessful, the returned LockedPtr will be null.
135 *
136 * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
137 * validity.)
138 */
139 TryRLockedPtr tryRLock() const {
140 return TryRLockedPtr{static_cast<const Subclass*>(this)};
141 }
142
143 /**
144 * Attempts to acquire the lock, or fails if the timeout elapses first.
145 * If acquisition is unsuccessful, the returned LockedPtr will be null.
146 *
147 * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
148 * validity.)
149 */
150 template <class Rep, class Period>
151 LockedPtr wlock(const std::chrono::duration<Rep, Period>& timeout) {
152 return LockedPtr(static_cast<Subclass*>(this), timeout);
153 }
154 template <class Rep, class Period>
155 LockedPtr wlock(const std::chrono::duration<Rep, Period>& timeout) const {
156 return LockedPtr(static_cast<const Subclass*>(this), timeout);
157 }
158
159 /**
160 * Attempts to acquire the lock, or fails if the timeout elapses first.
161 * If acquisition is unsuccessful, the returned LockedPtr will be null.
162 *
163 * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
164 * validity.)
165 */
166 template <class Rep, class Period>
167 ConstLockedPtr rlock(
168 const std::chrono::duration<Rep, Period>& timeout) const {
169 return ConstLockedPtr(static_cast<const Subclass*>(this), timeout);
170 }
171
172 /**
173 * Invoke a function while holding the lock exclusively.
174 *
175 * A reference to the datum will be passed into the function as its only
176 * argument.
177 *
178 * This can be used with a lambda argument for easily defining small critical
179 * sections in the code. For example:
180 *
181 * auto value = obj.withWLock([](auto& data) {
182 * data.doStuff();
183 * return data.getValue();
184 * });
185 */
186 template <class Function>
187 auto withWLock(Function&& function) {
188 return function(*wlock());
189 }
190 template <class Function>
191 auto withWLock(Function&& function) const {
192 return function(*wlock());
193 }
194
195 /**
196 * Invoke a function while holding the lock exclusively.
197 *
198 * This is similar to withWLock(), but the function will be passed a
199 * LockedPtr rather than a reference to the data itself.
200 *
201 * This allows scopedUnlock() to be called on the LockedPtr argument if
202 * desired.
203 */
204 template <class Function>
205 auto withWLockPtr(Function&& function) {
206 return function(wlock());
207 }
208 template <class Function>
209 auto withWLockPtr(Function&& function) const {
210 return function(wlock());
211 }
212
213 /**
214 * Invoke a function while holding an the lock in shared mode.
215 *
216 * A const reference to the datum will be passed into the function as its
217 * only argument.
218 */
219 template <class Function>
220 auto withRLock(Function&& function) const {
221 return function(*rlock());
222 }
223
224 template <class Function>
225 auto withRLockPtr(Function&& function) const {
226 return function(rlock());
227 }
228};
229
230/**
231 * SynchronizedBase specialization for upgrade mutex types.
232 *
233 * This class provides all the functionality provided by the SynchronizedBase
234 * specialization for shared mutexes and a ulock() method that returns an
235 * upgrade lock RAII proxy
236 */
237template <class Subclass>
238class SynchronizedBase<Subclass, detail::MutexLevel::UPGRADE>
239 : public SynchronizedBase<Subclass, detail::MutexLevel::SHARED> {
240 public:
241 using UpgradeLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyUpgrade>;
242 using ConstUpgradeLockedPtr =
243 ::folly::LockedPtr<const Subclass, LockPolicyUpgrade>;
244
245 using TryUpgradeLockedPtr =
246 ::folly::LockedPtr<Subclass, LockPolicyTryUpgrade>;
247 using ConstTryUpgradeLockedPtr =
248 ::folly::LockedPtr<const Subclass, LockPolicyTryUpgrade>;
249
250 /**
251 * Acquire an upgrade lock and return a LockedPtr that can be used to safely
252 * access the datum
253 *
254 * And the const version
255 */
256 UpgradeLockedPtr ulock() {
257 return UpgradeLockedPtr(static_cast<Subclass*>(this));
258 }
259
260 /**
261 * Attempts to acquire the lock in upgrade mode. If acquisition is
262 * unsuccessful, the returned LockedPtr will be null.
263 *
264 * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
265 * validity.)
266 */
267 TryUpgradeLockedPtr tryULock() {
268 return TryUpgradeLockedPtr{static_cast<Subclass*>(this)};
269 }
270
271 /**
272 * Acquire an upgrade lock and return a LockedPtr that can be used to safely
273 * access the datum
274 *
275 * And the const version
276 */
277 template <class Rep, class Period>
278 UpgradeLockedPtr ulock(const std::chrono::duration<Rep, Period>& timeout) {
279 return UpgradeLockedPtr(static_cast<Subclass*>(this), timeout);
280 }
281
282 /**
283 * Invoke a function while holding the lock.
284 *
285 * A reference to the datum will be passed into the function as its only
286 * argument.
287 *
288 * This can be used with a lambda argument for easily defining small critical
289 * sections in the code. For example:
290 *
291 * auto value = obj.withULock([](auto& data) {
292 * data.doStuff();
293 * return data.getValue();
294 * });
295 *
296 * This is probably not the function you want. If the intent is to read the
297 * data object and determine whether you should upgrade to a write lock then
298 * the withULockPtr() method should be called instead, since it gives access
299 * to the LockedPtr proxy (which can be upgraded via the
300 * moveFromUpgradeToWrite() method)
301 */
302 template <class Function>
303 auto withULock(Function&& function) {
304 return function(*ulock());
305 }
306
307 /**
308 * Invoke a function while holding the lock exclusively.
309 *
310 * This is similar to withULock(), but the function will be passed a
311 * LockedPtr rather than a reference to the data itself.
312 *
313 * This allows scopedUnlock() and getUniqueLock() to be called on the
314 * LockedPtr argument.
315 *
316 * This also allows you to upgrade the LockedPtr proxy to a write state so
317 * that changes can be made to the underlying data
318 */
319 template <class Function>
320 auto withULockPtr(Function&& function) {
321 return function(ulock());
322 }
323};
324
325/**
326 * SynchronizedBase specialization for non-shared mutex types.
327 *
328 * This class provides lock() methods for acquiring the lock and accessing the
329 * data.
330 */
331template <class Subclass>
332class SynchronizedBase<Subclass, detail::MutexLevel::UNIQUE> {
333 public:
334 using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>;
335 using ConstLockedPtr =
336 ::folly::LockedPtr<const Subclass, LockPolicyExclusive>;
337
338 using TryLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyTryExclusive>;
339 using ConstTryLockedPtr =
340 ::folly::LockedPtr<const Subclass, LockPolicyTryExclusive>;
341
342 /**
343 * Acquire a lock, and return a LockedPtr that can be used to safely access
344 * the datum.
345 */
346 LockedPtr lock() {
347 return LockedPtr(static_cast<Subclass*>(this));
348 }
349
350 /**
351 * Acquire a lock, and return a ConstLockedPtr that can be used to safely
352 * access the datum.
353 */
354 ConstLockedPtr lock() const {
355 return ConstLockedPtr(static_cast<const Subclass*>(this));
356 }
357
358 /**
359 * Attempts to acquire the lock in exclusive mode. If acquisition is
360 * unsuccessful, the returned LockedPtr will be null.
361 *
362 * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for
363 * validity.)
364 */
365 TryLockedPtr tryLock() {
366 return TryLockedPtr{static_cast<Subclass*>(this)};
367 }
368 ConstTryLockedPtr tryLock() const {
369 return ConstTryLockedPtr{static_cast<const Subclass*>(this)};
370 }
371
372 /**
373 * Attempts to acquire the lock, or fails if the timeout elapses first.
374 * If acquisition is unsuccessful, the returned LockedPtr will be null.
375 */
376 template <class Rep, class Period>
377 LockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) {
378 return LockedPtr(static_cast<Subclass*>(this), timeout);
379 }
380
381 /**
382 * Attempts to acquire the lock, or fails if the timeout elapses first.
383 * If acquisition is unsuccessful, the returned LockedPtr will be null.
384 */
385 template <class Rep, class Period>
386 ConstLockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) const {
387 return ConstLockedPtr(static_cast<const Subclass*>(this), timeout);
388 }
389
390 /**
391 * Invoke a function while holding the lock.
392 *
393 * A reference to the datum will be passed into the function as its only
394 * argument.
395 *
396 * This can be used with a lambda argument for easily defining small critical
397 * sections in the code. For example:
398 *
399 * auto value = obj.withLock([](auto& data) {
400 * data.doStuff();
401 * return data.getValue();
402 * });
403 */
404 template <class Function>
405 auto withLock(Function&& function) {
406 return function(*lock());
407 }
408 template <class Function>
409 auto withLock(Function&& function) const {
410 return function(*lock());
411 }
412
413 /**
414 * Invoke a function while holding the lock exclusively.
415 *
416 * This is similar to withWLock(), but the function will be passed a
417 * LockedPtr rather than a reference to the data itself.
418 *
419 * This allows scopedUnlock() and getUniqueLock() to be called on the
420 * LockedPtr argument.
421 */
422 template <class Function>
423 auto withLockPtr(Function&& function) {
424 return function(lock());
425 }
426 template <class Function>
427 auto withLockPtr(Function&& function) const {
428 return function(lock());
429 }
430};
431
432/**
433 * Synchronized<T> encapsulates an object of type T (a "datum") paired
434 * with a mutex. The only way to access the datum is while the mutex
435 * is locked, and Synchronized makes it virtually impossible to do
436 * otherwise. The code that would access the datum in unsafe ways
437 * would look odd and convoluted, thus readily alerting the human
438 * reviewer. In contrast, the code that uses Synchronized<T> correctly
439 * looks simple and intuitive.
440 *
441 * The second parameter must be a mutex type. Any mutex type supported by
442 * LockTraits<Mutex> can be used. By default any class with lock() and
443 * unlock() methods will work automatically. LockTraits can be specialized to
444 * teach Synchronized how to use other custom mutex types. See the
445 * documentation in LockTraits.h for additional details.
446 *
447 * Supported mutexes that work by default include std::mutex,
448 * std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex,
449 * folly::SharedMutex, folly::RWSpinLock, and folly::SpinLock.
450 * Include LockTraitsBoost.h to get additional LockTraits specializations to
451 * support the following boost mutex types: boost::mutex,
452 * boost::recursive_mutex, boost::shared_mutex, boost::timed_mutex, and
453 * boost::recursive_timed_mutex.
454 */
455template <class T, class Mutex = SharedMutex>
456struct Synchronized : public SynchronizedBase<
457 Synchronized<T, Mutex>,
458 MutexLevelValue<Mutex>::value> {
459 private:
460 using Base =
461 SynchronizedBase<Synchronized<T, Mutex>, MutexLevelValue<Mutex>::value>;
462 static constexpr bool nxCopyCtor{
463 std::is_nothrow_copy_constructible<T>::value};
464 static constexpr bool nxMoveCtor{
465 std::is_nothrow_move_constructible<T>::value};
466
467 // used to disable copy construction and assignment
468 class NonImplementedType;
469
470 public:
471 using LockedPtr = typename Base::LockedPtr;
472 using ConstLockedPtr = typename Base::ConstLockedPtr;
473 using DataType = T;
474 using MutexType = Mutex;
475
476 /**
477 * Default constructor leaves both members call their own default
478 * constructor.
479 */
480 Synchronized() = default;
481
482 public:
483 /**
484 * Copy constructor; deprecated
485 *
486 * Enabled only when the data type is copy-constructible.
487 *
488 * Takes a shared-or-exclusive lock on the source mutex while performing the
489 * copy-construction of the destination data from the source data. No lock is
490 * taken on the destination mutex.
491 *
492 * May throw even when the data type is is nothrow-copy-constructible because
493 * acquiring a lock may throw.
494 */
495 /* implicit */ Synchronized(typename std::conditional<
496 std::is_copy_constructible<T>::value,
497 const Synchronized&,
498 NonImplementedType>::type rhs) /* may throw */
499 : Synchronized(rhs.copy()) {}
500
501 /**
502 * Move constructor; deprecated
503 *
504 * Move-constructs from the source data without locking either the source or
505 * the destination mutex.
506 *
507 * Semantically, assumes that the source object is a true rvalue and therefore
508 * that no synchronization is required for accessing it.
509 */
510 Synchronized(Synchronized&& rhs) noexcept(nxMoveCtor)
511 : Synchronized(std::move(rhs.datum_)) {}
512
513 /**
514 * Constructor taking a datum as argument copies it. There is no
515 * need to lock the constructing object.
516 */
517 explicit Synchronized(const T& rhs) noexcept(nxCopyCtor) : datum_(rhs) {}
518
519 /**
520 * Constructor taking a datum rvalue as argument moves it. Again,
521 * there is no need to lock the constructing object.
522 */
523 explicit Synchronized(T&& rhs) noexcept(nxMoveCtor)
524 : datum_(std::move(rhs)) {}
525
526 /**
527 * Lets you construct non-movable types in-place. Use the constexpr
528 * instance `in_place` as the first argument.
529 */
530 template <typename... Args>
531 explicit Synchronized(in_place_t, Args&&... args)
532 : datum_(std::forward<Args>(args)...) {}
533
534 /**
535 * Lets you construct the synchronized object and also pass construction
536 * parameters to the underlying mutex if desired
537 */
538 template <typename... DatumArgs, typename... MutexArgs>
539 Synchronized(
540 std::piecewise_construct_t,
541 std::tuple<DatumArgs...> datumArgs,
542 std::tuple<MutexArgs...> mutexArgs)
543 : Synchronized{std::piecewise_construct,
544 std::move(datumArgs),
545 std::move(mutexArgs),
546 make_index_sequence<sizeof...(DatumArgs)>{},
547 make_index_sequence<sizeof...(MutexArgs)>{}} {}
548
549 /**
550 * Copy assignment operator; deprecated
551 *
552 * Enabled only when the data type is copy-constructible and move-assignable.
553 *
554 * Move-assigns from a copy of the source data.
555 *
556 * Takes a shared-or-exclusive lock on the source mutex while copying the
557 * source data to a temporary. Takes an exclusive lock on the destination
558 * mutex while move-assigning from the temporary.
559 *
560 * This technique consts an extra temporary but avoids the need to take locks
561 * on both mutexes together.
562 */
563 Synchronized& operator=(typename std::conditional<
564 std::is_copy_constructible<T>::value &&
565 std::is_move_assignable<T>::value,
566 const Synchronized&,
567 NonImplementedType>::type rhs) {
568 return *this = rhs.copy();
569 }
570
571 /**
572 * Move assignment operator; deprecated
573 *
574 * Takes an exclusive lock on the destination mutex while move-assigning the
575 * destination data from the source data. The source mutex is not locked or
576 * otherwise accessed.
577 *
578 * Semantically, assumes that the source object is a true rvalue and therefore
579 * that no synchronization is required for accessing it.
580 */
581 Synchronized& operator=(Synchronized&& rhs) {
582 return *this = std::move(rhs.datum_);
583 }
584
585 /**
586 * Lock object, assign datum.
587 */
588 Synchronized& operator=(const T& rhs) {
589 if (&datum_ != &rhs) {
590 auto guard = operator->();
591 datum_ = rhs;
592 }
593 return *this;
594 }
595
596 /**
597 * Lock object, move-assign datum.
598 */
599 Synchronized& operator=(T&& rhs) {
600 if (&datum_ != &rhs) {
601 auto guard = operator->();
602 datum_ = std::move(rhs);
603 }
604 return *this;
605 }
606
607 /**
608 * Acquire an appropriate lock based on the context.
609 *
610 * If the mutex is a shared mutex, and the Synchronized instance is const,
611 * this acquires a shared lock. Otherwise this acquires an exclusive lock.
612 *
613 * In general, prefer using the explicit rlock() and wlock() methods
614 * for read-write locks, and lock() for purely exclusive locks.
615 *
616 * contextualLock() is primarily intended for use in other template functions
617 * that do not necessarily know the lock type.
618 */
619 LockedPtr contextualLock() {
620 return LockedPtr(this);
621 }
622 ConstLockedPtr contextualLock() const {
623 return ConstLockedPtr(this);
624 }
625 template <class Rep, class Period>
626 LockedPtr contextualLock(const std::chrono::duration<Rep, Period>& timeout) {
627 return LockedPtr(this, timeout);
628 }
629 template <class Rep, class Period>
630 ConstLockedPtr contextualLock(
631 const std::chrono::duration<Rep, Period>& timeout) const {
632 return ConstLockedPtr(this, timeout);
633 }
634 /**
635 * contextualRLock() acquires a read lock if the mutex type is shared,
636 * or a regular exclusive lock for non-shared mutex types.
637 *
638 * contextualRLock() when you know that you prefer a read lock (if
639 * available), even if the Synchronized<T> object itself is non-const.
640 */
641 ConstLockedPtr contextualRLock() const {
642 return ConstLockedPtr(this);
643 }
644 template <class Rep, class Period>
645 ConstLockedPtr contextualRLock(
646 const std::chrono::duration<Rep, Period>& timeout) const {
647 return ConstLockedPtr(this, timeout);
648 }
649
650 /**
651 * This accessor offers a LockedPtr. In turn, LockedPtr offers
652 * operator-> returning a pointer to T. The operator-> keeps
653 * expanding until it reaches a pointer, so syncobj->foo() will lock
654 * the object and call foo() against it.
655 *
656 * NOTE: This API is planned to be deprecated in an upcoming diff.
657 * Prefer using lock(), wlock(), or rlock() instead.
658 */
659 LockedPtr operator->() {
660 return LockedPtr(this);
661 }
662
663 /**
664 * Obtain a ConstLockedPtr.
665 *
666 * NOTE: This API is planned to be deprecated in an upcoming diff.
667 * Prefer using lock(), wlock(), or rlock() instead.
668 */
669 ConstLockedPtr operator->() const {
670 return ConstLockedPtr(this);
671 }
672
673 /**
674 * Attempts to acquire for a given number of milliseconds. If
675 * acquisition is unsuccessful, the returned LockedPtr is nullptr.
676 *
677 * NOTE: This API is deprecated. Use lock(), wlock(), or rlock() instead.
678 * In the future it will be marked with a deprecation attribute to emit
679 * build-time warnings, and then it will be removed entirely.
680 */
681 LockedPtr timedAcquire(unsigned int milliseconds) {
682 return LockedPtr(this, std::chrono::milliseconds(milliseconds));
683 }
684
685 /**
686 * Attempts to acquire for a given number of milliseconds. If
687 * acquisition is unsuccessful, the returned ConstLockedPtr is nullptr.
688 *
689 * NOTE: This API is deprecated. Use lock(), wlock(), or rlock() instead.
690 * In the future it will be marked with a deprecation attribute to emit
691 * build-time warnings, and then it will be removed entirely.
692 */
693 ConstLockedPtr timedAcquire(unsigned int milliseconds) const {
694 return ConstLockedPtr(this, std::chrono::milliseconds(milliseconds));
695 }
696
697 /**
698 * Swaps with another Synchronized. Protected against
699 * self-swap. Only data is swapped. Locks are acquired in increasing
700 * address order.
701 */
702 void swap(Synchronized& rhs) {
703 if (this == &rhs) {
704 return;
705 }
706 if (this > &rhs) {
707 return rhs.swap(*this);
708 }
709 auto guard1 = operator->();
710 auto guard2 = rhs.operator->();
711
712 using std::swap;
713 swap(datum_, rhs.datum_);
714 }
715
716 /**
717 * Swap with another datum. Recommended because it keeps the mutex
718 * held only briefly.
719 */
720 void swap(T& rhs) {
721 LockedPtr guard(this);
722
723 using std::swap;
724 swap(datum_, rhs);
725 }
726
727 /**
728 * Assign another datum and return the original value. Recommended
729 * because it keeps the mutex held only briefly.
730 */
731 T exchange(T&& rhs) {
732 swap(rhs);
733 return std::move(rhs);
734 }
735
736 /**
737 * Copies datum to a given target.
738 */
739 void copyInto(T& target) const {
740 ConstLockedPtr guard(this);
741 target = datum_;
742 }
743
744 /**
745 * Returns a fresh copy of the datum.
746 */
747 T copy() const {
748 ConstLockedPtr guard(this);
749 return datum_;
750 }
751
752 private:
753 template <class LockedType, class MutexType, class LockPolicy>
754 friend class folly::LockedPtrBase;
755 template <class LockedType, class LockPolicy>
756 friend class folly::LockedPtr;
757
758 /**
759 * Helper constructors to enable Synchronized for
760 * non-default constructible types T.
761 * Guards are created in actual public constructors and are alive
762 * for the time required to construct the object
763 */
764 Synchronized(
765 const Synchronized& rhs,
766 const ConstLockedPtr& /*guard*/) noexcept(nxCopyCtor)
767 : datum_(rhs.datum_) {}
768
769 Synchronized(Synchronized&& rhs, const LockedPtr& /*guard*/) noexcept(
770 nxMoveCtor)
771 : datum_(std::move(rhs.datum_)) {}
772
773 template <
774 typename... DatumArgs,
775 typename... MutexArgs,
776 std::size_t... IndicesOne,
777 std::size_t... IndicesTwo>
778 Synchronized(
779 std::piecewise_construct_t,
780 std::tuple<DatumArgs...> datumArgs,
781 std::tuple<MutexArgs...> mutexArgs,
782 index_sequence<IndicesOne...>,
783 index_sequence<IndicesTwo...>)
784 : datum_{std::get<IndicesOne>(std::move(datumArgs))...},
785 mutex_{std::get<IndicesTwo>(std::move(mutexArgs))...} {}
786
787 // Synchronized data members
788 T datum_;
789 mutable Mutex mutex_;
790};
791
792template <class SynchronizedType, class LockPolicy>
793class ScopedUnlocker;
794
795namespace detail {
796/*
797 * A helper alias that resolves to "const T" if the template parameter
798 * is a const Synchronized<T>, or "T" if the parameter is not const.
799 */
800template <class SynchronizedType>
801using SynchronizedDataType = typename std::conditional<
802 std::is_const<SynchronizedType>::value,
803 typename SynchronizedType::DataType const,
804 typename SynchronizedType::DataType>::type;
805/*
806 * A helper alias that resolves to a ConstLockedPtr if the template parameter
807 * is a const Synchronized<T>, or a LockedPtr if the parameter is not const.
808 */
809template <class SynchronizedType>
810using LockedPtrType = typename std::conditional<
811 std::is_const<SynchronizedType>::value,
812 typename SynchronizedType::ConstLockedPtr,
813 typename SynchronizedType::LockedPtr>::type;
814
815template <
816 typename Synchronized,
817 typename LockFunc,
818 typename TryLockFunc,
819 typename... Args>
820class SynchronizedLocker {
821 public:
822 using LockedPtr = invoke_result_t<LockFunc&, Synchronized&, const Args&...>;
823
824 template <typename LockFuncType, typename TryLockFuncType, typename... As>
825 SynchronizedLocker(
826 Synchronized& sync,
827 LockFuncType&& lockFunc,
828 TryLockFuncType tryLockFunc,
829 As&&... as)
830 : synchronized{sync},
831 lockFunc_{std::forward<LockFuncType>(lockFunc)},
832 tryLockFunc_{std::forward<TryLockFuncType>(tryLockFunc)},
833 args_{std::forward<As>(as)...} {}
834
835 auto lock() const {
836 auto args = std::tuple<const Args&...>{args_};
837 return apply(lockFunc_, std::tuple_cat(std::tie(synchronized), args));
838 }
839 auto tryLock() const {
840 return tryLockFunc_(synchronized);
841 }
842
843 private:
844 Synchronized& synchronized;
845 LockFunc lockFunc_;
846 TryLockFunc tryLockFunc_;
847 std::tuple<Args...> args_;
848};
849
850template <
851 typename Synchronized,
852 typename LockFunc,
853 typename TryLockFunc,
854 typename... Args>
855auto makeSynchronizedLocker(
856 Synchronized& synchronized,
857 LockFunc&& lockFunc,
858 TryLockFunc&& tryLockFunc,
859 Args&&... args) {
860 using LockFuncType = std::decay_t<LockFunc>;
861 using TryLockFuncType = std::decay_t<TryLockFunc>;
862 return SynchronizedLocker<
863 Synchronized,
864 LockFuncType,
865 TryLockFuncType,
866 std::decay_t<Args>...>{synchronized,
867 std::forward<LockFunc>(lockFunc),
868 std::forward<TryLockFunc>(tryLockFunc),
869 std::forward<Args>(args)...};
870}
871
872/**
873 * Acquire locks for multiple Synchronized<T> objects, in a deadlock-safe
874 * manner.
875 *
876 * The function uses the "smart and polite" algorithm from this link
877 * http://howardhinnant.github.io/dining_philosophers.html#Polite
878 *
879 * The gist of the algorithm is that it locks a mutex, then tries to lock the
880 * other mutexes in a non-blocking manner. If all the locks succeed, we are
881 * done, if not, we release the locks we have held, yield to allow other
882 * threads to continue and then block on the mutex that we failed to acquire.
883 *
884 * This allows dynamically yielding ownership of all the mutexes but one, so
885 * that other threads can continue doing work and locking the other mutexes.
886 * See the benchmarks in folly/test/SynchronizedBenchmark.cpp for more.
887 */
888template <typename... SynchronizedLocker>
889auto lock(SynchronizedLocker... lockersIn)
890 -> std::tuple<typename SynchronizedLocker::LockedPtr...> {
891 // capture the list of lockers as a tuple
892 auto lockers = std::forward_as_tuple(lockersIn...);
893
894 // make a list of null LockedPtr instances that we will return to the caller
895 auto lockedPtrs = std::tuple<typename SynchronizedLocker::LockedPtr...>{};
896
897 // start by locking the first thing in the list
898 std::get<0>(lockedPtrs) = std::get<0>(lockers).lock();
899 auto indexLocked = 0;
900
901 while (true) {
902 auto couldLockAll = true;
903
904 for_each(lockers, [&](auto& locker, auto index) {
905 // if we should try_lock on the current locker then do so
906 if (index != indexLocked) {
907 auto lockedPtr = locker.tryLock();
908
909 // if we were unable to lock this mutex,
910 //
911 // 1. release all the locks,
912 // 2. yield control to another thread to be nice
913 // 3. block on the mutex we failed to lock, acquire the lock
914 // 4. break out and set the index of the current mutex to indicate
915 // which mutex we have locked
916 if (!lockedPtr) {
917 // writing lockedPtrs = decltype(lockedPtrs){} does not compile on
918 // gcc, I believe this is a bug D7676798
919 lockedPtrs = std::tuple<typename SynchronizedLocker::LockedPtr...>{};
920
921 std::this_thread::yield();
922 fetch(lockedPtrs, index) = locker.lock();
923 indexLocked = index;
924 couldLockAll = false;
925
926 return loop_break;
927 }
928
929 // else store the locked mutex in the list we return
930 fetch(lockedPtrs, index) = std::move(lockedPtr);
931 }
932
933 return loop_continue;
934 });
935
936 if (couldLockAll) {
937 return lockedPtrs;
938 }
939 }
940}
941
942template <typename Synchronized, typename... Args>
943auto wlock(Synchronized& synchronized, Args&&... args) {
944 return detail::makeSynchronizedLocker(
945 synchronized,
946 [](auto& s, auto&&... a) {
947 return s.wlock(std::forward<decltype(a)>(a)...);
948 },
949 [](auto& s) { return s.tryWLock(); },
950 std::forward<Args>(args)...);
951}
952template <typename Synchronized, typename... Args>
953auto rlock(Synchronized& synchronized, Args&&... args) {
954 return detail::makeSynchronizedLocker(
955 synchronized,
956 [](auto& s, auto&&... a) {
957 return s.rlock(std::forward<decltype(a)>(a)...);
958 },
959 [](auto& s) { return s.tryRLock(); },
960 std::forward<Args>(args)...);
961}
962template <typename Synchronized, typename... Args>
963auto ulock(Synchronized& synchronized, Args&&... args) {
964 return detail::makeSynchronizedLocker(
965 synchronized,
966 [](auto& s, auto&&... a) {
967 return s.ulock(std::forward<decltype(a)>(a)...);
968 },
969 [](auto& s) { return s.tryULock(); },
970 std::forward<Args>(args)...);
971}
972template <typename Synchronized, typename... Args>
973auto lock(Synchronized& synchronized, Args&&... args) {
974 return detail::makeSynchronizedLocker(
975 synchronized,
976 [](auto& s, auto&&... a) {
977 return s.lock(std::forward<decltype(a)>(a)...);
978 },
979 [](auto& s) { return s.tryLock(); },
980 std::forward<Args>(args)...);
981}
982
983} // namespace detail
984
985/**
986 * A helper base class for implementing LockedPtr.
987 *
988 * The main reason for having this as a separate class is so we can specialize
989 * it for std::mutex, so we can expose a std::unique_lock to the caller
990 * when std::mutex is being used. This allows callers to use a
991 * std::condition_variable with the mutex from a Synchronized<T, std::mutex>.
992 *
993 * We don't use std::unique_lock with other Mutex types since it makes the
994 * LockedPtr class slightly larger, and it makes the logic to support
995 * ScopedUnlocker slightly more complicated. std::mutex is the only one that
996 * really seems to benefit from the unique_lock. std::condition_variable
997 * itself only supports std::unique_lock<std::mutex>, so there doesn't seem to
998 * be any real benefit to exposing the unique_lock with other mutex types.
999 *
1000 * Note that the SynchronizedType template parameter may or may not be const
1001 * qualified.
1002 */
1003template <class SynchronizedType, class Mutex, class LockPolicy>
1004class LockedPtrBase {
1005 public:
1006 using MutexType = Mutex;
1007 friend class folly::ScopedUnlocker<SynchronizedType, LockPolicy>;
1008
1009 /**
1010 * Friend all instantiations of LockedPtr and LockedPtrBase
1011 */
1012 template <typename S, typename L>
1013 friend class folly::LockedPtr;
1014 template <typename S, typename M, typename L>
1015 friend class LockedPtrBase;
1016
1017 /**
1018 * Destructor releases.
1019 */
1020 ~LockedPtrBase() {
1021 if (parent_) {
1022 LockPolicy::unlock(parent_->mutex_);
1023 }
1024 }
1025
1026 /**
1027 * Unlock the synchronized data.
1028 *
1029 * The LockedPtr can no longer be dereferenced after unlock() has been
1030 * called. isValid() will return false on an unlocked LockedPtr.
1031 *
1032 * unlock() can only be called on a LockedPtr that is valid.
1033 */
1034 void unlock() {
1035 DCHECK(parent_ != nullptr);
1036 LockPolicy::unlock(parent_->mutex_);
1037 parent_ = nullptr;
1038 }
1039
1040 protected:
1041 LockedPtrBase() {}
1042 explicit LockedPtrBase(SynchronizedType* parent) : parent_(parent) {
1043 DCHECK(parent);
1044 if (!LockPolicy::lock(parent_->mutex_)) {
1045 parent_ = nullptr;
1046 }
1047 }
1048 template <class Rep, class Period>
1049 LockedPtrBase(
1050 SynchronizedType* parent,
1051 const std::chrono::duration<Rep, Period>& timeout) {
1052 if (LockPolicy::try_lock_for(parent->mutex_, timeout)) {
1053 this->parent_ = parent;
1054 }
1055 }
1056 LockedPtrBase(LockedPtrBase&& rhs) noexcept
1057 : parent_{exchange(rhs.parent_, nullptr)} {}
1058 LockedPtrBase& operator=(LockedPtrBase&& rhs) noexcept {
1059 assignImpl(*this, rhs);
1060 return *this;
1061 }
1062
1063 /**
1064 * Templated move construct and assignment operators
1065 *
1066 * These allow converting LockedPtr types that have the same unlocking
1067 * policy to each other. This allows us to write code like
1068 *
1069 * auto wlock = sync.wlock();
1070 * wlock.unlock();
1071 *
1072 * auto ulock = sync.ulock();
1073 * wlock = ulock.moveFromUpgradeToWrite();
1074 */
1075 template <typename LockPolicyType>
1076 LockedPtrBase(
1077 LockedPtrBase<SynchronizedType, Mutex, LockPolicyType>&& rhs) noexcept
1078 : parent_{exchange(rhs.parent_, nullptr)} {}
1079 template <typename LockPolicyType>
1080 LockedPtrBase& operator=(
1081 LockedPtrBase<SynchronizedType, Mutex, LockPolicyType>&& rhs) noexcept {
1082 assignImpl(*this, rhs);
1083 return *this;
1084 }
1085
1086 /**
1087 * Implementation for the assignment operator
1088 */
1089 template <typename LockPolicyLhs, typename LockPolicyRhs>
1090 void assignImpl(
1091 LockedPtrBase<SynchronizedType, Mutex, LockPolicyLhs>& lhs,
1092 LockedPtrBase<SynchronizedType, Mutex, LockPolicyRhs>& rhs) noexcept {
1093 if (lhs.parent_) {
1094 LockPolicy::unlock(lhs.parent_->mutex_);
1095 }
1096
1097 lhs.parent_ = exchange(rhs.parent_, nullptr);
1098 }
1099
1100 using UnlockerData = SynchronizedType*;
1101
1102 /**
1103 * Get a pointer to the Synchronized object from the UnlockerData.
1104 *
1105 * In the generic case UnlockerData is just the Synchronized pointer,
1106 * so we return it as is. (This function is more interesting in the
1107 * std::mutex specialization below.)
1108 */
1109 static SynchronizedType* getSynchronized(UnlockerData data) {
1110 return data;
1111 }
1112
1113 UnlockerData releaseLock() {
1114 DCHECK(parent_ != nullptr);
1115 auto current = parent_;
1116 parent_ = nullptr;
1117 LockPolicy::unlock(current->mutex_);
1118 return current;
1119 }
1120 void reacquireLock(UnlockerData&& data) {
1121 DCHECK(parent_ == nullptr);
1122 parent_ = data;
1123 LockPolicy::lock(parent_->mutex_);
1124 }
1125
1126 SynchronizedType* parent_ = nullptr;
1127};
1128
1129/**
1130 * LockedPtrBase specialization for use with std::mutex.
1131 *
1132 * When std::mutex is used we use a std::unique_lock to hold the mutex.
1133 * This makes it possible to use std::condition_variable with a
1134 * Synchronized<T, std::mutex>.
1135 */
1136template <class SynchronizedType, class LockPolicy>
1137class LockedPtrBase<SynchronizedType, std::mutex, LockPolicy> {
1138 public:
1139 using MutexType = std::mutex;
1140 friend class folly::ScopedUnlocker<SynchronizedType, LockPolicy>;
1141
1142 /**
1143 * Friend all instantiations of LockedPtr and LockedPtrBase
1144 */
1145 template <typename S, typename L>
1146 friend class folly::LockedPtr;
1147 template <typename S, typename M, typename L>
1148 friend class LockedPtrBase;
1149
1150 /**
1151 * Destructor releases.
1152 */
1153 ~LockedPtrBase() {
1154 // The std::unique_lock will automatically release the lock when it is
1155 // destroyed, so we don't need to do anything extra here.
1156 }
1157
1158 LockedPtrBase(LockedPtrBase&& rhs) noexcept
1159 : lock_{std::move(rhs.lock_)}, parent_{exchange(rhs.parent_, nullptr)} {}
1160 LockedPtrBase& operator=(LockedPtrBase&& rhs) noexcept {
1161 assignImpl(*this, rhs);
1162 return *this;
1163 }
1164
1165 /**
1166 * Templated move construct and assignment operators
1167 *
1168 * These allow converting LockedPtr types that have the same unlocking
1169 * policy to each other.
1170 */
1171 template <typename LockPolicyType>
1172 LockedPtrBase(LockedPtrBase<SynchronizedType, std::mutex, LockPolicyType>&&
1173 other) noexcept
1174 : lock_{std::move(other.lock_)},
1175 parent_{exchange(other.parent_, nullptr)} {}
1176 template <typename LockPolicyType>
1177 LockedPtrBase& operator=(
1178 LockedPtrBase<SynchronizedType, std::mutex, LockPolicyType>&&
1179 rhs) noexcept {
1180 assignImpl(*this, rhs);
1181 return *this;
1182 }
1183
1184 /**
1185 * Implementation for the assignment operator
1186 */
1187 template <typename LockPolicyLhs, typename LockPolicyRhs>
1188 void assignImpl(
1189 LockedPtrBase<SynchronizedType, std::mutex, LockPolicyLhs>& lhs,
1190 LockedPtrBase<SynchronizedType, std::mutex, LockPolicyRhs>&
1191 rhs) noexcept {
1192 lhs.lock_ = std::move(rhs.lock_);
1193 lhs.parent_ = exchange(rhs.parent_, nullptr);
1194 }
1195
1196 /**
1197 * Get a reference to the std::unique_lock.
1198 *
1199 * This is provided so that callers can use Synchronized<T, std::mutex>
1200 * with a std::condition_variable.
1201 *
1202 * While this API could be used to bypass the normal Synchronized APIs and
1203 * manually interact with the underlying unique_lock, this is strongly
1204 * discouraged.
1205 */
1206 std::unique_lock<std::mutex>& getUniqueLock() {
1207 return lock_;
1208 }
1209
1210 /**
1211 * Unlock the synchronized data.
1212 *
1213 * The LockedPtr can no longer be dereferenced after unlock() has been
1214 * called. isValid() will return false on an unlocked LockedPtr.
1215 *
1216 * unlock() can only be called on a LockedPtr that is valid.
1217 */
1218 void unlock() {
1219 DCHECK(parent_ != nullptr);
1220 lock_.unlock();
1221 parent_ = nullptr;
1222 }
1223
1224 protected:
1225 LockedPtrBase() {}
1226 explicit LockedPtrBase(SynchronizedType* parent)
1227 : lock_{parent->mutex_, std::adopt_lock}, parent_{parent} {
1228 DCHECK(parent);
1229 if (!LockPolicy::lock(parent_->mutex_)) {
1230 parent_ = nullptr;
1231 lock_.release();
1232 }
1233 }
1234
1235 using UnlockerData =
1236 std::pair<std::unique_lock<std::mutex>, SynchronizedType*>;
1237
1238 static SynchronizedType* getSynchronized(const UnlockerData& data) {
1239 return data.second;
1240 }
1241
1242 UnlockerData releaseLock() {
1243 DCHECK(parent_ != nullptr);
1244 UnlockerData data(std::move(lock_), parent_);
1245 parent_ = nullptr;
1246 data.first.unlock();
1247 return data;
1248 }
1249 void reacquireLock(UnlockerData&& data) {
1250 lock_ = std::move(data.first);
1251 lock_.lock();
1252 parent_ = data.second;
1253 }
1254
1255 // The specialization for std::mutex does have to store slightly more
1256 // state than the default implementation.
1257 std::unique_lock<std::mutex> lock_;
1258 SynchronizedType* parent_ = nullptr;
1259};
1260
1261/**
1262 * This class temporarily unlocks a LockedPtr in a scoped manner.
1263 */
1264template <class SynchronizedType, class LockPolicy>
1265class ScopedUnlocker {
1266 public:
1267 explicit ScopedUnlocker(LockedPtr<SynchronizedType, LockPolicy>* p)
1268 : ptr_(p), data_(ptr_->releaseLock()) {}
1269 ScopedUnlocker(const ScopedUnlocker&) = delete;
1270 ScopedUnlocker& operator=(const ScopedUnlocker&) = delete;
1271 ScopedUnlocker(ScopedUnlocker&& other) noexcept
1272 : ptr_(exchange(other.ptr_, nullptr)), data_(std::move(other.data_)) {}
1273 ScopedUnlocker& operator=(ScopedUnlocker&& other) = delete;
1274
1275 ~ScopedUnlocker() {
1276 if (ptr_) {
1277 ptr_->reacquireLock(std::move(data_));
1278 }
1279 }
1280
1281 /**
1282 * Return a pointer to the Synchronized object used by this ScopedUnlocker.
1283 */
1284 SynchronizedType* getSynchronized() const {
1285 return LockedPtr<SynchronizedType, LockPolicy>::getSynchronized(data_);
1286 }
1287
1288 private:
1289 using Data = typename LockedPtr<SynchronizedType, LockPolicy>::UnlockerData;
1290 LockedPtr<SynchronizedType, LockPolicy>* ptr_{nullptr};
1291 Data data_;
1292};
1293
1294/**
1295 * A LockedPtr keeps a Synchronized<T> object locked for the duration of
1296 * LockedPtr's existence.
1297 *
1298 * It provides access the datum's members directly by using operator->() and
1299 * operator*().
1300 *
1301 * The LockPolicy parameter controls whether or not the lock is acquired in
1302 * exclusive or shared mode.
1303 */
1304template <class SynchronizedType, class LockPolicy>
1305class LockedPtr : public LockedPtrBase<
1306 SynchronizedType,
1307 typename SynchronizedType::MutexType,
1308 LockPolicy> {
1309 private:
1310 using Base = LockedPtrBase<
1311 SynchronizedType,
1312 typename SynchronizedType::MutexType,
1313 LockPolicy>;
1314 using UnlockerData = typename Base::UnlockerData;
1315 // CDataType is the DataType with the appropriate const-qualification
1316 using CDataType = detail::SynchronizedDataType<SynchronizedType>;
1317 // Enable only if the unlock policy of the other LockPolicy is the same as
1318 // ours
1319 template <typename LockPolicyOther>
1320 using EnableIfSameUnlockPolicy = std::enable_if_t<std::is_same<
1321 typename LockPolicy::UnlockPolicy,
1322 typename LockPolicyOther::UnlockPolicy>::value>;
1323
1324 // friend other LockedPtr types
1325 template <typename SynchronizedTypeOther, typename LockPolicyOther>
1326 friend class LockedPtr;
1327
1328 public:
1329 using DataType = typename SynchronizedType::DataType;
1330 using MutexType = typename SynchronizedType::MutexType;
1331 using Synchronized = typename std::remove_const<SynchronizedType>::type;
1332 friend class ScopedUnlocker<SynchronizedType, LockPolicy>;
1333
1334 /**
1335 * Creates an uninitialized LockedPtr.
1336 *
1337 * Dereferencing an uninitialized LockedPtr is not allowed.
1338 */
1339 LockedPtr() {}
1340
1341 /**
1342 * Takes a Synchronized<T> and locks it.
1343 */
1344 explicit LockedPtr(SynchronizedType* parent) : Base(parent) {}
1345
1346 /**
1347 * Takes a Synchronized<T> and attempts to lock it, within the specified
1348 * timeout.
1349 *
1350 * Blocks until the lock is acquired or until the specified timeout expires.
1351 * If the timeout expired without acquiring the lock, the LockedPtr will be
1352 * null, and LockedPtr::isNull() will return true.
1353 */
1354 template <class Rep, class Period>
1355 LockedPtr(
1356 SynchronizedType* parent,
1357 const std::chrono::duration<Rep, Period>& timeout)
1358 : Base(parent, timeout) {}
1359
1360 /**
1361 * Move constructor.
1362 */
1363 LockedPtr(LockedPtr&& rhs) noexcept = default;
1364 template <
1365 typename LockPolicyType,
1366 EnableIfSameUnlockPolicy<LockPolicyType>* = nullptr>
1367 LockedPtr(LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept
1368 : Base{std::move(other)} {}
1369
1370 /**
1371 * Move assignment operator.
1372 */
1373 LockedPtr& operator=(LockedPtr&& rhs) noexcept = default;
1374 template <
1375 typename LockPolicyType,
1376 EnableIfSameUnlockPolicy<LockPolicyType>* = nullptr>
1377 LockedPtr& operator=(
1378 LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept {
1379 Base::operator=(std::move(other));
1380 return *this;
1381 }
1382
1383 /*
1384 * Copy constructor and assignment operator are deleted.
1385 */
1386 LockedPtr(const LockedPtr& rhs) = delete;
1387 LockedPtr& operator=(const LockedPtr& rhs) = delete;
1388
1389 /**
1390 * Destructor releases.
1391 */
1392 ~LockedPtr() {}
1393
1394 /**
1395 * Check if this LockedPtr is uninitialized, or points to valid locked data.
1396 *
1397 * This method can be used to check if a timed-acquire operation succeeded.
1398 * If an acquire operation times out it will result in a null LockedPtr.
1399 *
1400 * A LockedPtr is always either null, or holds a lock to valid data.
1401 * Methods such as scopedUnlock() reset the LockedPtr to null for the
1402 * duration of the unlock.
1403 */
1404 bool isNull() const {
1405 return this->parent_ == nullptr;
1406 }
1407
1408 /**
1409 * Explicit boolean conversion.
1410 *
1411 * Returns !isNull()
1412 */
1413 explicit operator bool() const {
1414 return this->parent_ != nullptr;
1415 }
1416
1417 /**
1418 * Access the locked data.
1419 *
1420 * This method should only be used if the LockedPtr is valid.
1421 */
1422 CDataType* operator->() const {
1423 return &this->parent_->datum_;
1424 }
1425
1426 /**
1427 * Access the locked data.
1428 *
1429 * This method should only be used if the LockedPtr is valid.
1430 */
1431 CDataType& operator*() const {
1432 return this->parent_->datum_;
1433 }
1434
1435 /**
1436 * Temporarily unlock the LockedPtr, and reset it to null.
1437 *
1438 * Returns an helper object that will re-lock and restore the LockedPtr when
1439 * the helper is destroyed. The LockedPtr may not be dereferenced for as
1440 * long as this helper object exists.
1441 */
1442 ScopedUnlocker<SynchronizedType, LockPolicy> scopedUnlock() {
1443 return ScopedUnlocker<SynchronizedType, LockPolicy>(this);
1444 }
1445
1446 /***************************************************************************
1447 * Upgrade lock methods.
1448 * These are disabled via SFINAE when the mutex is not an upgrade mutex.
1449 **************************************************************************/
1450 /**
1451 * Move the locked ptr from an upgrade state to an exclusive state. The
1452 * current lock is left in a null state.
1453 */
1454 template <
1455 typename SyncType = SynchronizedType,
1456 typename = typename std::enable_if<
1457 LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
1458 LockedPtr<SynchronizedType, LockPolicyFromUpgradeToExclusive>
1459 moveFromUpgradeToWrite() {
1460 return LockedPtr<SynchronizedType, LockPolicyFromUpgradeToExclusive>(
1461 exchange(this->parent_, nullptr));
1462 }
1463
1464 /**
1465 * Move the locked ptr from an exclusive state to an upgrade state. The
1466 * current lock is left in a null state.
1467 */
1468 template <
1469 typename SyncType = SynchronizedType,
1470 typename = typename std::enable_if<
1471 LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
1472 LockedPtr<SynchronizedType, LockPolicyFromExclusiveToUpgrade>
1473 moveFromWriteToUpgrade() {
1474 return LockedPtr<SynchronizedType, LockPolicyFromExclusiveToUpgrade>(
1475 exchange(this->parent_, nullptr));
1476 }
1477
1478 /**
1479 * Move the locked ptr from an upgrade state to a shared state. The
1480 * current lock is left in a null state.
1481 */
1482 template <
1483 typename SyncType = SynchronizedType,
1484 typename = typename std::enable_if<
1485 LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
1486 LockedPtr<SynchronizedType, LockPolicyFromUpgradeToShared>
1487 moveFromUpgradeToRead() {
1488 return LockedPtr<SynchronizedType, LockPolicyFromUpgradeToShared>(
1489 exchange(this->parent_, nullptr));
1490 }
1491
1492 /**
1493 * Move the locked ptr from an exclusive state to a shared state. The
1494 * current lock is left in a null state.
1495 */
1496 template <
1497 typename SyncType = SynchronizedType,
1498 typename = typename std::enable_if<
1499 LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
1500 LockedPtr<SynchronizedType, LockPolicyFromExclusiveToShared>
1501 moveFromWriteToRead() {
1502 return LockedPtr<SynchronizedType, LockPolicyFromExclusiveToShared>(
1503 exchange(this->parent_, nullptr));
1504 }
1505};
1506
1507/**
1508 * Helper functions that should be passed to either a lock() or synchronized()
1509 * invocation, these return implementation defined structs that will be used
1510 * to lock the synchronized instance appropriately.
1511 *
1512 * lock(wlock(one), rlock(two), wlock(three));
1513 * synchronized([](auto one, two) { ... }, wlock(one), rlock(two));
1514 *
1515 * For example in the above rlock() produces an implementation defined read
1516 * locking helper instance and wlock() a write locking helper
1517 *
1518 * Subsequent arguments passed to these locking helpers, after the first, will
1519 * be passed by const-ref to the corresponding function on the synchronized
1520 * instance. This means that if the function accepts these parameters by
1521 * value, they will be copied. Note that it is not necessary that the primary
1522 * locking function will be invoked at all (for eg. the implementation might
1523 * just invoke the try*Lock() method)
1524 *
1525 * // Try to acquire the lock for one second
1526 * synchronized([](auto) { ... }, wlock(one, 1s));
1527 *
1528 * // The timed lock acquire might never actually be called, if it is not
1529 * // needed by the underlying deadlock avoiding algorithm
1530 * synchronized([](auto, auto) { ... }, rlock(one), wlock(two, 1s));
1531 *
1532 * Note that the arguments passed to to *lock() calls will be passed by
1533 * const-ref to the function invocation, as the implementation might use them
1534 * many times
1535 */
1536template <typename D, typename M, typename... Args>
1537auto wlock(Synchronized<D, M>& synchronized, Args&&... args) {
1538 return detail::wlock(synchronized, std::forward<Args>(args)...);
1539}
1540template <typename D, typename M, typename... Args>
1541auto wlock(const Synchronized<D, M>& synchronized, Args&&... args) {
1542 return detail::wlock(synchronized, std::forward<Args>(args)...);
1543}
1544template <typename Data, typename Mutex, typename... Args>
1545auto rlock(const Synchronized<Data, Mutex>& synchronized, Args&&... args) {
1546 return detail::rlock(synchronized, std::forward<Args>(args)...);
1547}
1548template <typename D, typename M, typename... Args>
1549auto ulock(Synchronized<D, M>& synchronized, Args&&... args) {
1550 return detail::ulock(synchronized, std::forward<Args>(args)...);
1551}
1552template <typename D, typename M, typename... Args>
1553auto lock(Synchronized<D, M>& synchronized, Args&&... args) {
1554 return detail::lock(synchronized, std::forward<Args>(args)...);
1555}
1556template <typename D, typename M, typename... Args>
1557auto lock(const Synchronized<D, M>& synchronized, Args&&... args) {
1558 return detail::lock(synchronized, std::forward<Args>(args)...);
1559}
1560
1561/**
1562 * Acquire locks for multiple Synchronized<> objects, in a deadlock-safe
1563 * manner.
1564 *
1565 * Wrap the synchronized instances with the appropriate locking strategy by
1566 * using one of the four strategies - folly::lock (exclusive acquire for
1567 * exclusive only mutexes), folly::rlock (shared acquire for shareable
1568 * mutexes), folly::wlock (exclusive acquire for shareable mutexes) or
1569 * folly::ulock (upgrade acquire for upgrade mutexes) (see above)
1570 *
1571 * The locks will be acquired and the passed callable will be invoked with the
1572 * LockedPtr instances in the order that they were passed to the function
1573 */
1574template <typename Func, typename... SynchronizedLockers>
1575decltype(auto) synchronized(Func&& func, SynchronizedLockers&&... lockers) {
1576 return apply(
1577 std::forward<Func>(func),
1578 lock(std::forward<SynchronizedLockers>(lockers)...));
1579}
1580
1581/**
1582 * Acquire locks on many lockables or synchronized instances in such a way
1583 * that the sequence of calls within the function does not cause deadlocks.
1584 *
1585 * This can often result in a performance boost as compared to simply
1586 * acquiring your locks in an ordered manner. Even for very simple cases.
1587 * The algorithm tried to adjust to contention by blocking on the mutex it
1588 * thinks is the best fit, leaving all other mutexes open to be locked by
1589 * other threads. See the benchmarks in folly/test/SynchronizedBenchmark.cpp
1590 * for more
1591 *
1592 * This works differently as compared to the locking algorithm in libstdc++
1593 * and is the recommended way to acquire mutexes in a generic order safe
1594 * manner. Performance benchmarks show that this does better than the one in
1595 * libstdc++ even for the simple cases
1596 *
1597 * Usage is the same as std::lock() for arbitrary lockables
1598 *
1599 * folly::lock(one, two, three);
1600 *
1601 * To make it work with folly::Synchronized you have to specify how you want
1602 * the locks to be acquired, use the folly::wlock(), folly::rlock(),
1603 * folly::ulock() and folly::lock() helpers defined below
1604 *
1605 * auto [one, two] = lock(folly::wlock(a), folly::rlock(b));
1606 *
1607 * Note that you can/must avoid the folly:: namespace prefix on the lock()
1608 * function if you use the helpers, ADL lookup is done to find the lock function
1609 *
1610 * This will execute the deadlock avoidance algorithm and acquire a write lock
1611 * for a and a read lock for b
1612 */
1613template <typename LockableOne, typename LockableTwo, typename... Lockables>
1614void lock(LockableOne& one, LockableTwo& two, Lockables&... lockables) {
1615 auto locker = [](auto& lockable) {
1616 using Lockable = std::remove_reference_t<decltype(lockable)>;
1617 return detail::makeSynchronizedLocker(
1618 lockable,
1619 [](auto& l) { return std::unique_lock<Lockable>{l}; },
1620 [](auto& l) {
1621 auto lock = std::unique_lock<Lockable>{l, std::defer_lock};
1622 lock.try_lock();
1623 return lock;
1624 });
1625 };
1626 auto locks = lock(locker(one), locker(two), locker(lockables)...);
1627
1628 // release ownership of the locks from the RAII lock wrapper returned by the
1629 // function above
1630 for_each(locks, [&](auto& lock) { lock.release(); });
1631}
1632
1633/**
1634 * Acquire locks for multiple Synchronized<T> objects, in a deadlock-safe
1635 * manner.
1636 *
1637 * The locks are acquired in order from lowest address to highest address.
1638 * (Note that this is not necessarily the same algorithm used by std::lock().)
1639 * For parameters that are const and support shared locks, a read lock is
1640 * acquired. Otherwise an exclusive lock is acquired.
1641 *
1642 * use lock() with folly::wlock(), folly::rlock() and folly::ulock() for
1643 * arbitrary locking without causing a deadlock (as much as possible), with the
1644 * same effects as std::lock()
1645 */
1646template <class Sync1, class Sync2>
1647std::tuple<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>>
1648acquireLocked(Sync1& l1, Sync2& l2) {
1649 if (static_cast<const void*>(&l1) < static_cast<const void*>(&l2)) {
1650 auto p1 = l1.contextualLock();
1651 auto p2 = l2.contextualLock();
1652 return std::make_tuple(std::move(p1), std::move(p2));
1653 } else {
1654 auto p2 = l2.contextualLock();
1655 auto p1 = l1.contextualLock();
1656 return std::make_tuple(std::move(p1), std::move(p2));
1657 }
1658}
1659
1660/**
1661 * A version of acquireLocked() that returns a std::pair rather than a
1662 * std::tuple, which is easier to use in many places.
1663 */
1664template <class Sync1, class Sync2>
1665std::pair<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>>
1666acquireLockedPair(Sync1& l1, Sync2& l2) {
1667 auto lockedPtrs = acquireLocked(l1, l2);
1668 return {std::move(std::get<0>(lockedPtrs)),
1669 std::move(std::get<1>(lockedPtrs))};
1670}
1671
1672/************************************************************************
1673 * NOTE: All APIs below this line will be deprecated in upcoming diffs.
1674 ************************************************************************/
1675
1676// Non-member swap primitive
1677template <class T, class M>
1678void swap(Synchronized<T, M>& lhs, Synchronized<T, M>& rhs) {
1679 lhs.swap(rhs);
1680}
1681
1682/**
1683 * Disambiguate the name var by concatenating the line number of the original
1684 * point of expansion. This avoids shadowing warnings for nested
1685 * SYNCHRONIZEDs. The name is consistent if used multiple times within
1686 * another macro.
1687 * Only for internal use.
1688 */
1689#define SYNCHRONIZED_VAR(var) FB_CONCATENATE(SYNCHRONIZED_##var##_, __LINE__)
1690
1691/**
1692 * SYNCHRONIZED is the main facility that makes Synchronized<T>
1693 * helpful. It is a pseudo-statement that introduces a scope where the
1694 * object is locked. Inside that scope you get to access the unadorned
1695 * datum.
1696 *
1697 * Example:
1698 *
1699 * Synchronized<vector<int>> svector;
1700 * ...
1701 * SYNCHRONIZED (svector) { ... use svector as a vector<int> ... }
1702 * or
1703 * SYNCHRONIZED (v, svector) { ... use v as a vector<int> ... }
1704 *
1705 * Refer to folly/docs/Synchronized.md for a detailed explanation and more
1706 * examples.
1707 */
1708#define SYNCHRONIZED(...) \
1709 FOLLY_PUSH_WARNING \
1710 FOLLY_GNU_DISABLE_WARNING("-Wshadow") \
1711 FOLLY_MSVC_DISABLE_WARNING(4189) /* initialized but unreferenced */ \
1712 FOLLY_MSVC_DISABLE_WARNING(4456) /* declaration hides local */ \
1713 FOLLY_MSVC_DISABLE_WARNING(4457) /* declaration hides parameter */ \
1714 FOLLY_MSVC_DISABLE_WARNING(4458) /* declaration hides member */ \
1715 FOLLY_MSVC_DISABLE_WARNING(4459) /* declaration hides global */ \
1716 FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS \
1717 if (bool SYNCHRONIZED_VAR(state) = false) { \
1718 } else \
1719 for (auto SYNCHRONIZED_VAR(lockedPtr) = \
1720 (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).operator->(); \
1721 !SYNCHRONIZED_VAR(state); \
1722 SYNCHRONIZED_VAR(state) = true) \
1723 for (auto& FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) = \
1724 *SYNCHRONIZED_VAR(lockedPtr).operator->(); \
1725 !SYNCHRONIZED_VAR(state); \
1726 SYNCHRONIZED_VAR(state) = true) \
1727 FOLLY_POP_WARNING
1728
1729#define TIMED_SYNCHRONIZED(timeout, ...) \
1730 if (bool SYNCHRONIZED_VAR(state) = false) { \
1731 } else \
1732 for (auto SYNCHRONIZED_VAR(lockedPtr) = \
1733 (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).timedAcquire(timeout); \
1734 !SYNCHRONIZED_VAR(state); \
1735 SYNCHRONIZED_VAR(state) = true) \
1736 for (auto FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) = \
1737 (!SYNCHRONIZED_VAR(lockedPtr) \
1738 ? nullptr \
1739 : SYNCHRONIZED_VAR(lockedPtr).operator->()); \
1740 !SYNCHRONIZED_VAR(state); \
1741 SYNCHRONIZED_VAR(state) = true)
1742
1743/**
1744 * Similar to SYNCHRONIZED, but only uses a read lock.
1745 */
1746#define SYNCHRONIZED_CONST(...) \
1747 SYNCHRONIZED( \
1748 FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)), \
1749 as_const(FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))))
1750
1751/**
1752 * Similar to TIMED_SYNCHRONIZED, but only uses a read lock.
1753 */
1754#define TIMED_SYNCHRONIZED_CONST(timeout, ...) \
1755 TIMED_SYNCHRONIZED( \
1756 timeout, \
1757 FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)), \
1758 as_const(FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))))
1759
1760/**
1761 * Synchronizes two Synchronized objects (they may encapsulate
1762 * different data). Synchronization is done in increasing address of
1763 * object order, so there is no deadlock risk.
1764 */
1765#define SYNCHRONIZED_DUAL(n1, e1, n2, e2) \
1766 if (bool SYNCHRONIZED_VAR(state) = false) { \
1767 } else \
1768 for (auto SYNCHRONIZED_VAR(ptrs) = acquireLockedPair(e1, e2); \
1769 !SYNCHRONIZED_VAR(state); \
1770 SYNCHRONIZED_VAR(state) = true) \
1771 for (auto& n1 = *SYNCHRONIZED_VAR(ptrs).first; !SYNCHRONIZED_VAR(state); \
1772 SYNCHRONIZED_VAR(state) = true) \
1773 for (auto& n2 = *SYNCHRONIZED_VAR(ptrs).second; \
1774 !SYNCHRONIZED_VAR(state); \
1775 SYNCHRONIZED_VAR(state) = true)
1776
1777} /* namespace folly */
1778