| 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 |  | 
|---|
| 44 | namespace folly { | 
|---|
| 45 |  | 
|---|
| 46 | template <class LockedType, class Mutex, class LockPolicy> | 
|---|
| 47 | class LockedPtrBase; | 
|---|
| 48 | template <class LockedType, class LockPolicy> | 
|---|
| 49 | class 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 | */ | 
|---|
| 62 | template <class Mutex> | 
|---|
| 63 | using 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 | */ | 
|---|
| 74 | template <class Subclass, detail::MutexLevel level> | 
|---|
| 75 | class 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 | */ | 
|---|
| 83 | template <class Subclass> | 
|---|
| 84 | class 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 | */ | 
|---|
| 237 | template <class Subclass> | 
|---|
| 238 | class 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 | */ | 
|---|
| 331 | template <class Subclass> | 
|---|
| 332 | class 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 | */ | 
|---|
| 455 | template <class T, class Mutex = SharedMutex> | 
|---|
| 456 | struct 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 |  | 
|---|
| 792 | template <class SynchronizedType, class LockPolicy> | 
|---|
| 793 | class ScopedUnlocker; | 
|---|
| 794 |  | 
|---|
| 795 | namespace 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 | */ | 
|---|
| 800 | template <class SynchronizedType> | 
|---|
| 801 | using 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 | */ | 
|---|
| 809 | template <class SynchronizedType> | 
|---|
| 810 | using LockedPtrType = typename std::conditional< | 
|---|
| 811 | std::is_const<SynchronizedType>::value, | 
|---|
| 812 | typename SynchronizedType::ConstLockedPtr, | 
|---|
| 813 | typename SynchronizedType::LockedPtr>::type; | 
|---|
| 814 |  | 
|---|
| 815 | template < | 
|---|
| 816 | typename Synchronized, | 
|---|
| 817 | typename LockFunc, | 
|---|
| 818 | typename TryLockFunc, | 
|---|
| 819 | typename... Args> | 
|---|
| 820 | class 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 |  | 
|---|
| 850 | template < | 
|---|
| 851 | typename Synchronized, | 
|---|
| 852 | typename LockFunc, | 
|---|
| 853 | typename TryLockFunc, | 
|---|
| 854 | typename... Args> | 
|---|
| 855 | auto 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 | */ | 
|---|
| 888 | template <typename... SynchronizedLocker> | 
|---|
| 889 | auto 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 |  | 
|---|
| 942 | template <typename Synchronized, typename... Args> | 
|---|
| 943 | auto 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 | } | 
|---|
| 952 | template <typename Synchronized, typename... Args> | 
|---|
| 953 | auto 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 | } | 
|---|
| 962 | template <typename Synchronized, typename... Args> | 
|---|
| 963 | auto 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 | } | 
|---|
| 972 | template <typename Synchronized, typename... Args> | 
|---|
| 973 | auto 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 | */ | 
|---|
| 1003 | template <class SynchronizedType, class Mutex, class LockPolicy> | 
|---|
| 1004 | class 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 | */ | 
|---|
| 1136 | template <class SynchronizedType, class LockPolicy> | 
|---|
| 1137 | class 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 | */ | 
|---|
| 1264 | template <class SynchronizedType, class LockPolicy> | 
|---|
| 1265 | class 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 | */ | 
|---|
| 1304 | template <class SynchronizedType, class LockPolicy> | 
|---|
| 1305 | class 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 | */ | 
|---|
| 1536 | template <typename D, typename M, typename... Args> | 
|---|
| 1537 | auto wlock(Synchronized<D, M>& synchronized, Args&&... args) { | 
|---|
| 1538 | return detail::wlock(synchronized, std::forward<Args>(args)...); | 
|---|
| 1539 | } | 
|---|
| 1540 | template <typename D, typename M, typename... Args> | 
|---|
| 1541 | auto wlock(const Synchronized<D, M>& synchronized, Args&&... args) { | 
|---|
| 1542 | return detail::wlock(synchronized, std::forward<Args>(args)...); | 
|---|
| 1543 | } | 
|---|
| 1544 | template <typename Data, typename Mutex, typename... Args> | 
|---|
| 1545 | auto rlock(const Synchronized<Data, Mutex>& synchronized, Args&&... args) { | 
|---|
| 1546 | return detail::rlock(synchronized, std::forward<Args>(args)...); | 
|---|
| 1547 | } | 
|---|
| 1548 | template <typename D, typename M, typename... Args> | 
|---|
| 1549 | auto ulock(Synchronized<D, M>& synchronized, Args&&... args) { | 
|---|
| 1550 | return detail::ulock(synchronized, std::forward<Args>(args)...); | 
|---|
| 1551 | } | 
|---|
| 1552 | template <typename D, typename M, typename... Args> | 
|---|
| 1553 | auto lock(Synchronized<D, M>& synchronized, Args&&... args) { | 
|---|
| 1554 | return detail::lock(synchronized, std::forward<Args>(args)...); | 
|---|
| 1555 | } | 
|---|
| 1556 | template <typename D, typename M, typename... Args> | 
|---|
| 1557 | auto 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 | */ | 
|---|
| 1574 | template <typename Func, typename... SynchronizedLockers> | 
|---|
| 1575 | decltype(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 | */ | 
|---|
| 1613 | template <typename LockableOne, typename LockableTwo, typename... Lockables> | 
|---|
| 1614 | void 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 | */ | 
|---|
| 1646 | template <class Sync1, class Sync2> | 
|---|
| 1647 | std::tuple<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>> | 
|---|
| 1648 | acquireLocked(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 | */ | 
|---|
| 1664 | template <class Sync1, class Sync2> | 
|---|
| 1665 | std::pair<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>> | 
|---|
| 1666 | acquireLockedPair(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 | 
|---|
| 1677 | template <class T, class M> | 
|---|
| 1678 | void 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 |  | 
|---|