blob: 11c7eae821b5920c9d49d806370c6af920204b2d [file] [log] [blame]
// 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