| // 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. |
| |
| // bthread - An M:N threading library to make applications more concurrent. |
| |
| // Date: 2015/12/14 18:17:04 |
| |
| #ifndef BTHREAD_MUTEX_H |
| #define BTHREAD_MUTEX_H |
| |
| #include "bthread/types.h" |
| #include "butil/scoped_lock.h" |
| #include "bvar/utils/lock_timer.h" |
| |
| __BEGIN_DECLS |
| extern int bthread_mutex_init(bthread_mutex_t* __restrict mutex, |
| const bthread_mutexattr_t* __restrict attr); |
| extern int bthread_mutex_destroy(bthread_mutex_t* mutex); |
| extern int bthread_mutex_trylock(bthread_mutex_t* mutex); |
| extern int bthread_mutex_lock(bthread_mutex_t* mutex); |
| extern int bthread_mutex_timedlock(bthread_mutex_t* __restrict mutex, |
| const struct timespec* __restrict abstime); |
| extern int bthread_mutex_unlock(bthread_mutex_t* mutex); |
| extern bthread_t bthread_self(void); |
| __END_DECLS |
| |
| namespace bthread { |
| |
| // The C++ Wrapper of bthread_mutex |
| |
| // NOTE: Not aligned to cacheline as the container of Mutex is practically aligned |
| class Mutex { |
| public: |
| typedef bthread_mutex_t* native_handler_type; |
| Mutex() { |
| int ec = bthread_mutex_init(&_mutex, NULL); |
| if (ec != 0) { |
| throw std::system_error(std::error_code(ec, std::system_category()), |
| "Mutex constructor failed"); |
| } |
| } |
| ~Mutex() { CHECK_EQ(0, bthread_mutex_destroy(&_mutex)); } |
| native_handler_type native_handler() { return &_mutex; } |
| void lock() { |
| int ec = bthread_mutex_lock(&_mutex); |
| if (ec != 0) { |
| throw std::system_error(std::error_code(ec, std::system_category()), |
| "Mutex lock failed"); |
| } |
| } |
| void unlock() { bthread_mutex_unlock(&_mutex); } |
| bool try_lock() { return 0 == bthread_mutex_trylock(&_mutex); } |
| bool timed_lock(const struct timespec* abstime) { |
| return !bthread_mutex_timedlock(&_mutex, abstime); |
| } |
| // TODO(chenzhangyi01): Complement interfaces for C++11 |
| private: |
| DISALLOW_COPY_AND_ASSIGN(Mutex); |
| bthread_mutex_t _mutex; |
| }; |
| |
| namespace internal { |
| #ifdef BTHREAD_USE_FAST_PTHREAD_MUTEX |
| class FastPthreadMutex { |
| public: |
| FastPthreadMutex(); |
| void lock(); |
| void unlock(); |
| bool try_lock(); |
| bool timed_lock(const struct timespec* abstime); |
| private: |
| DISALLOW_COPY_AND_ASSIGN(FastPthreadMutex); |
| int lock_contended(const struct timespec* abstime); |
| |
| unsigned _futex; |
| // Note: Owner detection of the mutex comes with average execution |
| // slowdown of about 50%., so it is only used for debugging and is |
| // only available when the macro `BRPC_DEBUG_LOCK' = 1. |
| mutex_owner_t _owner; |
| }; |
| #else |
| typedef butil::Mutex FastPthreadMutex; |
| #endif |
| } |
| |
| class FastPthreadMutex { |
| public: |
| FastPthreadMutex() = default; |
| ~FastPthreadMutex() = default; |
| DISALLOW_COPY_AND_ASSIGN(FastPthreadMutex); |
| |
| void lock(); |
| void unlock(); |
| bool try_lock() { return _mutex.try_lock(); } |
| #if defined(BTHREAD_USE_FAST_PTHREAD_MUTEX) || HAS_PTHREAD_MUTEX_TIMEDLOCK |
| bool timed_lock(const struct timespec* abstime); |
| #endif // BTHREAD_USE_FAST_PTHREAD_MUTEX HAS_PTHREAD_MUTEX_TIMEDLOCK |
| |
| private: |
| internal::FastPthreadMutex _mutex; |
| }; |
| |
| } // namespace bthread |
| |
| // Specialize std::lock_guard and std::unique_lock for bthread_mutex_t |
| |
| namespace std { |
| |
| template <> class lock_guard<bthread_mutex_t> { |
| public: |
| explicit lock_guard(bthread_mutex_t& mutex) : _pmutex(&mutex) { |
| #if !defined(NDEBUG) |
| const int rc = bthread_mutex_lock(_pmutex); |
| if (rc) { |
| LOG(FATAL) << "Fail to lock bthread_mutex_t=" << _pmutex << ", " << berror(rc); |
| _pmutex = NULL; |
| } |
| #else |
| bthread_mutex_lock(_pmutex); |
| #endif // NDEBUG |
| } |
| |
| ~lock_guard() { |
| #ifndef NDEBUG |
| if (_pmutex) { |
| bthread_mutex_unlock(_pmutex); |
| } |
| #else |
| bthread_mutex_unlock(_pmutex); |
| #endif |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(lock_guard); |
| bthread_mutex_t* _pmutex; |
| }; |
| |
| template <> class unique_lock<bthread_mutex_t> { |
| DISALLOW_COPY_AND_ASSIGN(unique_lock); |
| public: |
| typedef bthread_mutex_t mutex_type; |
| unique_lock() : _mutex(NULL), _owns_lock(false) {} |
| explicit unique_lock(mutex_type& mutex) |
| : _mutex(&mutex), _owns_lock(false) { |
| 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(bthread_mutex_trylock(&mutex) == 0) |
| {} |
| unique_lock(mutex_type& mutex, adopt_lock_t) |
| : _mutex(&mutex), _owns_lock(true) |
| {} |
| |
| ~unique_lock() { |
| if (_owns_lock) { |
| unlock(); |
| } |
| } |
| |
| void lock() { |
| if (!_mutex) { |
| CHECK(false) << "Invalid operation"; |
| return; |
| } |
| if (_owns_lock) { |
| CHECK(false) << "Detected deadlock issue"; |
| return; |
| } |
| bthread_mutex_lock(_mutex); |
| _owns_lock = true; |
| } |
| |
| bool try_lock() { |
| if (!_mutex) { |
| CHECK(false) << "Invalid operation"; |
| return false; |
| } |
| if (_owns_lock) { |
| CHECK(false) << "Detected deadlock issue"; |
| return false; |
| } |
| _owns_lock = !bthread_mutex_trylock(_mutex); |
| return _owns_lock; |
| } |
| |
| void unlock() { |
| if (!_owns_lock) { |
| CHECK(false) << "Invalid operation"; |
| return; |
| } |
| if (_mutex) { |
| bthread_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; |
| }; |
| |
| } // namespace std |
| |
| namespace bvar { |
| |
| template <> |
| struct MutexConstructor<bthread_mutex_t> { |
| bool operator()(bthread_mutex_t* mutex) const { |
| return bthread_mutex_init(mutex, NULL) == 0; |
| } |
| }; |
| |
| template <> |
| struct MutexDestructor<bthread_mutex_t> { |
| bool operator()(bthread_mutex_t* mutex) const { |
| return bthread_mutex_destroy(mutex) == 0; |
| } |
| }; |
| |
| } // namespace bvar |
| |
| #endif //BTHREAD_MUTEX_H |