| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #ifndef BUTIL_BAIDU_SCOPED_LOCK_H |
| #define BUTIL_BAIDU_SCOPED_LOCK_H |
| |
| #include "butil/build_config.h" |
| |
| #if defined(BUTIL_CXX11_ENABLED) |
| #include <mutex> // std::lock_guard |
| #endif |
| |
| #include "butil/synchronization/lock.h" |
| #include "butil/macros.h" |
| #include "butil/logging.h" |
| #include "butil/errno.h" |
| |
| #if !defined(BUTIL_CXX11_ENABLED) |
| #define BAIDU_SCOPED_LOCK(ref_of_lock) \ |
| std::lock_guard<BAIDU_TYPEOF(ref_of_lock)> \ |
| BAIDU_CONCAT(scoped_locker_dummy_at_line_, __LINE__)(ref_of_lock) |
| #else |
| |
| // NOTE(gejun): c++11 deduces additional reference to the type. |
| namespace butil { |
| namespace detail { |
| template <typename T> |
| std::lock_guard<typename std::remove_reference<T>::type> get_lock_guard(); |
| } // namespace detail |
| } // namespace butil |
| |
| #define BAIDU_SCOPED_LOCK(ref_of_lock) \ |
| decltype(::butil::detail::get_lock_guard<decltype(ref_of_lock)>()) \ |
| BAIDU_CONCAT(scoped_locker_dummy_at_line_, __LINE__)(ref_of_lock) |
| #endif |
| |
| namespace std { |
| |
| #if !defined(BUTIL_CXX11_ENABLED) |
| |
| // Do not acquire ownership of the mutex |
| struct defer_lock_t {}; |
| static const defer_lock_t defer_lock = {}; |
| |
| // Try to acquire ownership of the mutex without blocking |
| struct try_to_lock_t {}; |
| static const try_to_lock_t try_to_lock = {}; |
| |
| // Assume the calling thread already has ownership of the mutex |
| struct adopt_lock_t {}; |
| static const adopt_lock_t adopt_lock = {}; |
| |
| template <typename Mutex> class lock_guard { |
| public: |
| explicit lock_guard(Mutex & mutex) : _pmutex(&mutex) { _pmutex->lock(); } |
| ~lock_guard() { _pmutex->unlock(); } |
| private: |
| DISALLOW_COPY_AND_ASSIGN(lock_guard); |
| Mutex* _pmutex; |
| }; |
| |
| template <typename Mutex> class unique_lock { |
| DISALLOW_COPY_AND_ASSIGN(unique_lock); |
| public: |
| typedef Mutex mutex_type; |
| unique_lock() : _mutex(NULL), _owns_lock(false) {} |
| explicit unique_lock(mutex_type& mutex) |
| : _mutex(&mutex), _owns_lock(true) { |
| mutex.lock(); |
| } |
| unique_lock(mutex_type& mutex, defer_lock_t) |
| : _mutex(&mutex), _owns_lock(false) |
| {} |
| unique_lock(mutex_type& mutex, try_to_lock_t) |
| : _mutex(&mutex), _owns_lock(mutex.try_lock()) |
| {} |
| unique_lock(mutex_type& mutex, adopt_lock_t) |
| : _mutex(&mutex), _owns_lock(true) |
| {} |
| |
| ~unique_lock() { |
| if (_owns_lock) { |
| _mutex->unlock(); |
| } |
| } |
| |
| void lock() { |
| if (_owns_lock) { |
| CHECK(false) << "Detected deadlock issue"; |
| return; |
| } |
| _owns_lock = true; |
| _mutex->lock(); |
| } |
| |
| bool try_lock() { |
| if (_owns_lock) { |
| CHECK(false) << "Detected deadlock issue"; |
| return false; |
| } |
| _owns_lock = _mutex->try_lock(); |
| return _owns_lock; |
| } |
| |
| void unlock() { |
| if (!_owns_lock) { |
| CHECK(false) << "Invalid operation"; |
| return; |
| } |
| _mutex->unlock(); |
| _owns_lock = false; |
| } |
| |
| void swap(unique_lock& rhs) { |
| std::swap(_mutex, rhs._mutex); |
| std::swap(_owns_lock, rhs._owns_lock); |
| } |
| |
| mutex_type* release() { |
| mutex_type* saved_mutex = _mutex; |
| _mutex = NULL; |
| _owns_lock = false; |
| return saved_mutex; |
| } |
| |
| mutex_type* mutex() { return _mutex; } |
| bool owns_lock() const { return _owns_lock; } |
| operator bool() const { return owns_lock(); } |
| |
| private: |
| mutex_type* _mutex; |
| bool _owns_lock; |
| }; |
| |
| #endif // !defined(BUTIL_CXX11_ENABLED) |
| |
| #if defined(OS_POSIX) |
| |
| template<> class lock_guard<pthread_mutex_t> { |
| public: |
| explicit lock_guard(pthread_mutex_t & mutex) : _pmutex(&mutex) { |
| #if !defined(NDEBUG) |
| const int rc = pthread_mutex_lock(_pmutex); |
| if (rc) { |
| LOG(FATAL) << "Fail to lock pthread_mutex_t=" << _pmutex << ", " << berror(rc); |
| _pmutex = NULL; |
| } |
| #else |
| pthread_mutex_lock(_pmutex); |
| #endif // NDEBUG |
| } |
| |
| ~lock_guard() { |
| #ifndef NDEBUG |
| if (_pmutex) { |
| pthread_mutex_unlock(_pmutex); |
| } |
| #else |
| pthread_mutex_unlock(_pmutex); |
| #endif |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(lock_guard); |
| pthread_mutex_t* _pmutex; |
| }; |
| |
| template<> class lock_guard<pthread_spinlock_t> { |
| public: |
| explicit lock_guard(pthread_spinlock_t & spin) : _pspin(&spin) { |
| #if !defined(NDEBUG) |
| const int rc = pthread_spin_lock(_pspin); |
| if (rc) { |
| LOG(FATAL) << "Fail to lock pthread_spinlock_t=" << _pspin << ", " << berror(rc); |
| _pspin = NULL; |
| } |
| #else |
| pthread_spin_lock(_pspin); |
| #endif // NDEBUG |
| } |
| |
| ~lock_guard() { |
| #ifndef NDEBUG |
| if (_pspin) { |
| pthread_spin_unlock(_pspin); |
| } |
| #else |
| pthread_spin_unlock(_pspin); |
| #endif |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(lock_guard); |
| pthread_spinlock_t* _pspin; |
| }; |
| |
| template<> class unique_lock<pthread_mutex_t> { |
| DISALLOW_COPY_AND_ASSIGN(unique_lock); |
| public: |
| typedef pthread_mutex_t mutex_type; |
| unique_lock() : _mutex(NULL), _owns_lock(false) {} |
| explicit unique_lock(mutex_type& mutex) |
| : _mutex(&mutex), _owns_lock(true) { |
| pthread_mutex_lock(_mutex); |
| } |
| unique_lock(mutex_type& mutex, defer_lock_t) |
| : _mutex(&mutex), _owns_lock(false) |
| {} |
| unique_lock(mutex_type& mutex, try_to_lock_t) |
| : _mutex(&mutex), _owns_lock(pthread_mutex_trylock(&mutex) == 0) |
| {} |
| unique_lock(mutex_type& mutex, adopt_lock_t) |
| : _mutex(&mutex), _owns_lock(true) |
| {} |
| |
| ~unique_lock() { |
| if (_owns_lock) { |
| pthread_mutex_unlock(_mutex); |
| } |
| } |
| |
| void lock() { |
| if (_owns_lock) { |
| CHECK(false) << "Detected deadlock issue"; |
| return; |
| } |
| #if !defined(NDEBUG) |
| const int rc = pthread_mutex_lock(_mutex); |
| if (rc) { |
| LOG(FATAL) << "Fail to lock pthread_mutex=" << _mutex << ", " << berror(rc); |
| return; |
| } |
| _owns_lock = true; |
| #else |
| _owns_lock = true; |
| pthread_mutex_lock(_mutex); |
| #endif // NDEBUG |
| } |
| |
| bool try_lock() { |
| if (_owns_lock) { |
| CHECK(false) << "Detected deadlock issue"; |
| return false; |
| } |
| _owns_lock = !pthread_mutex_trylock(_mutex); |
| return _owns_lock; |
| } |
| |
| void unlock() { |
| if (!_owns_lock) { |
| CHECK(false) << "Invalid operation"; |
| return; |
| } |
| pthread_mutex_unlock(_mutex); |
| _owns_lock = false; |
| } |
| |
| void swap(unique_lock& rhs) { |
| std::swap(_mutex, rhs._mutex); |
| std::swap(_owns_lock, rhs._owns_lock); |
| } |
| |
| mutex_type* release() { |
| mutex_type* saved_mutex = _mutex; |
| _mutex = NULL; |
| _owns_lock = false; |
| return saved_mutex; |
| } |
| |
| mutex_type* mutex() { return _mutex; } |
| bool owns_lock() const { return _owns_lock; } |
| operator bool() const { return owns_lock(); } |
| |
| private: |
| mutex_type* _mutex; |
| bool _owns_lock; |
| }; |
| |
| template<> class unique_lock<pthread_spinlock_t> { |
| DISALLOW_COPY_AND_ASSIGN(unique_lock); |
| public: |
| typedef pthread_spinlock_t mutex_type; |
| unique_lock() : _mutex(NULL), _owns_lock(false) {} |
| explicit unique_lock(mutex_type& mutex) |
| : _mutex(&mutex), _owns_lock(true) { |
| pthread_spin_lock(_mutex); |
| } |
| |
| ~unique_lock() { |
| if (_owns_lock) { |
| pthread_spin_unlock(_mutex); |
| } |
| } |
| unique_lock(mutex_type& mutex, defer_lock_t) |
| : _mutex(&mutex), _owns_lock(false) |
| {} |
| unique_lock(mutex_type& mutex, try_to_lock_t) |
| : _mutex(&mutex), _owns_lock(pthread_spin_trylock(&mutex) == 0) |
| {} |
| unique_lock(mutex_type& mutex, adopt_lock_t) |
| : _mutex(&mutex), _owns_lock(true) |
| {} |
| |
| void lock() { |
| if (_owns_lock) { |
| CHECK(false) << "Detected deadlock issue"; |
| return; |
| } |
| #if !defined(NDEBUG) |
| const int rc = pthread_spin_lock(_mutex); |
| if (rc) { |
| LOG(FATAL) << "Fail to lock pthread_spinlock=" << _mutex << ", " << berror(rc); |
| return; |
| } |
| _owns_lock = true; |
| #else |
| _owns_lock = true; |
| pthread_spin_lock(_mutex); |
| #endif // NDEBUG |
| } |
| |
| bool try_lock() { |
| if (_owns_lock) { |
| CHECK(false) << "Detected deadlock issue"; |
| return false; |
| } |
| _owns_lock = !pthread_spin_trylock(_mutex); |
| return _owns_lock; |
| } |
| |
| void unlock() { |
| if (!_owns_lock) { |
| CHECK(false) << "Invalid operation"; |
| return; |
| } |
| pthread_spin_unlock(_mutex); |
| _owns_lock = false; |
| } |
| |
| void swap(unique_lock& rhs) { |
| std::swap(_mutex, rhs._mutex); |
| std::swap(_owns_lock, rhs._owns_lock); |
| } |
| |
| mutex_type* release() { |
| mutex_type* saved_mutex = _mutex; |
| _mutex = NULL; |
| _owns_lock = false; |
| return saved_mutex; |
| } |
| |
| mutex_type* mutex() { return _mutex; } |
| bool owns_lock() const { return _owns_lock; } |
| operator bool() const { return owns_lock(); } |
| |
| private: |
| mutex_type* _mutex; |
| bool _owns_lock; |
| }; |
| |
| #endif // defined(OS_POSIX) |
| |
| } // namespace std |
| |
| namespace butil { |
| |
| // Lock both lck1 and lck2 without the dead lock issue |
| template <typename Mutex1, typename Mutex2> |
| void double_lock(std::unique_lock<Mutex1> &lck1, std::unique_lock<Mutex2> &lck2) { |
| DCHECK(!lck1.owns_lock()); |
| DCHECK(!lck2.owns_lock()); |
| volatile void* const ptr1 = lck1.mutex(); |
| volatile void* const ptr2 = lck2.mutex(); |
| DCHECK_NE(ptr1, ptr2); |
| if (ptr1 < ptr2) { |
| lck1.lock(); |
| lck2.lock(); |
| } else { |
| lck2.lock(); |
| lck1.lock(); |
| } |
| } |
| |
| }; |
| |
| #endif // BUTIL_BAIDU_SCOPED_LOCK_H |