434 lines
13 KiB
C++
434 lines
13 KiB
C++
// 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.
|
|
|
|
// Date: 2015/03/06 17:13:17
|
|
|
|
#ifndef BVAR_LOCK_TIMER_H
|
|
#define BVAR_LOCK_TIMER_H
|
|
|
|
#include "butil/time.h" // butil::Timer
|
|
#include "butil/scoped_lock.h" // std::lock_guard std::unique_lock
|
|
#include "butil/macros.h" // DISALLOW_COPY_AND_ASSIGN
|
|
|
|
#include "bvar/recorder.h" // IntRecorder
|
|
#include "bvar/latency_recorder.h" // LatencyRecorder
|
|
|
|
// Monitor the time for acquiring a lock.
|
|
// We provide some wrappers of mutex which can also be maintained by
|
|
// std::lock_guard and std::unique_lock to record the time it takes to wait for
|
|
// the acquisition of the very mutex (in microsecond) except for the contention
|
|
// caused by condition variables which unlock the mutex before waiting and lock
|
|
// the same mutex after waking up.
|
|
//
|
|
// About Performance:
|
|
// The utilities are designed and implemented to be suitable to measure the
|
|
// mutex from all the common scenarios. Saying that you can use them freely
|
|
// without concerning about the overhead. Except that the mutex is very
|
|
// frequently acquired (>1M/s) with very little contention, in which case, the
|
|
// overhead of timers and bvars is noticable.
|
|
//
|
|
// There are two kinds of special Mutex:
|
|
// - MutexWithRecorder: Create a mutex along with a shared IntRecorder which
|
|
// only records the average latency from intialization
|
|
// - MutexWithLatencyRecorder: Create a mutex along with a shared
|
|
// LatencyRecorder which also provides percentile
|
|
// calculation, time window management besides
|
|
// IntRecorder.
|
|
//
|
|
// Examples:
|
|
// #include "bvar/utils/lock_timer.h"
|
|
//
|
|
// bvar::LatencyRecorder
|
|
// g_mutex_contention("my_mutex_contention");
|
|
// // ^^^
|
|
// // you can replace this with a meaningful name
|
|
//
|
|
// typedef ::bvar::MutexWithLatencyRecorder<pthread_mutex_t> my_mutex_t;
|
|
// // ^^^
|
|
// // you can use std::mutex (since c++11)
|
|
// // or bthread_mutex_t (in bthread)
|
|
//
|
|
// // Define the mutex
|
|
// my_mutex_t mutex(g_mutex_contention);
|
|
//
|
|
// // Use it with std::lock_guard
|
|
// void critical_routine_with_lock_guard() {
|
|
// std::lock_guard<my_mutex_t> guard(mutex);
|
|
// // ^^^
|
|
// // Or you can use BAIDU_SCOPED_LOCK(mutex) to make it simple
|
|
// ...
|
|
// doing something inside the critical section
|
|
// ...
|
|
// // and |mutex| is auto unlocked and this contention is recorded out of
|
|
// // the scope
|
|
// }
|
|
//
|
|
// // Use it with unique_lock
|
|
// void critical_routine_with_unique_lock() {
|
|
// std::unique_lock<my_mutex_t> lck(mutex);
|
|
// std::condition_variable cond; // available since C++11
|
|
// ...
|
|
// doing something inside the critical section
|
|
// ...
|
|
// cond.wait(lck); // It's ok if my_mutex_t is defined with the template
|
|
// // parameter being std::mutex
|
|
// ...
|
|
// doing other things when come back into the critical section
|
|
// ...
|
|
// // and |mutex| is auto unlocked and this contention is recorded out of
|
|
// // the scope
|
|
// }
|
|
|
|
namespace bvar {
|
|
namespace utils {
|
|
// To be compatible with the old version
|
|
using namespace ::bvar;
|
|
} // namespace utils
|
|
} // namespace bvar
|
|
|
|
namespace bvar {
|
|
|
|
// Specialize MutexConstructor and MutexDestructor for the Non-RAII mutexes such
|
|
// as pthread_mutex_t
|
|
template <typename Mutex>
|
|
struct MutexConstructor {
|
|
bool operator()(Mutex*) const { return true; }
|
|
};
|
|
|
|
template <typename Mutex>
|
|
struct MutexDestructor {
|
|
bool operator()(Mutex*) const { return true; }
|
|
};
|
|
|
|
// Specialize for pthread_mutex_t
|
|
template <>
|
|
struct MutexConstructor<pthread_mutex_t> {
|
|
bool operator()(pthread_mutex_t* mutex) const {
|
|
#ifndef NDEBUG
|
|
const int rc = pthread_mutex_init(mutex, NULL);
|
|
CHECK_EQ(0, rc) << "Fail to init pthread_mutex, " << berror(rc);
|
|
return rc == 0;
|
|
#else
|
|
return pthread_mutex_init(mutex, NULL) == 0;
|
|
#endif
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct MutexDestructor<pthread_mutex_t> {
|
|
bool operator()(pthread_mutex_t* mutex) const {
|
|
#ifndef NDEBUG
|
|
const int rc = pthread_mutex_destroy(mutex);
|
|
CHECK_EQ(0, rc) << "Fail to destroy pthread_mutex, " << berror(rc);
|
|
return rc == 0;
|
|
#else
|
|
return pthread_mutex_destroy(mutex) == 0;
|
|
#endif
|
|
}
|
|
};
|
|
|
|
namespace detail {
|
|
|
|
template <typename Mutex, typename Recorder,
|
|
typename MCtor, typename MDtor>
|
|
class MutexWithRecorderBase {
|
|
DISALLOW_COPY_AND_ASSIGN(MutexWithRecorderBase);
|
|
public:
|
|
typedef Mutex mutex_type;
|
|
typedef Recorder recorder_type;
|
|
typedef MutexWithRecorderBase<Mutex, Recorder,
|
|
MCtor, MDtor> self_type;
|
|
|
|
explicit MutexWithRecorderBase(recorder_type &recorder)
|
|
: _recorder(&recorder) {
|
|
MCtor()(&_mutex);
|
|
}
|
|
|
|
MutexWithRecorderBase() : _recorder(NULL) {
|
|
MCtor()(&_mutex);
|
|
}
|
|
|
|
~MutexWithRecorderBase() {
|
|
MDtor()(&_mutex);
|
|
}
|
|
|
|
void set_recorder(recorder_type& recorder) {
|
|
_recorder = &recorder;
|
|
}
|
|
|
|
mutex_type& mutex() { return _mutex; }
|
|
operator mutex_type&() { return _mutex; }
|
|
|
|
template <typename T>
|
|
self_type& operator<<(T value) {
|
|
if (_recorder) {
|
|
*_recorder << value;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
mutex_type _mutex;
|
|
// We don't own _recorder. Make sure it is valid before the destruction of
|
|
// this instance
|
|
recorder_type *_recorder;
|
|
};
|
|
|
|
template <typename Mutex>
|
|
class LockGuardBase {
|
|
DISALLOW_COPY_AND_ASSIGN(LockGuardBase);
|
|
public:
|
|
LockGuardBase(Mutex& m)
|
|
: _timer(m), _lock_guard(m.mutex()) {
|
|
_timer.timer.stop();
|
|
}
|
|
private:
|
|
// This trick makes the recoding happens after the destructor of _lock_guard
|
|
struct TimerAndMutex {
|
|
TimerAndMutex(Mutex &m)
|
|
: timer(butil::Timer::STARTED), mutex(&m) {}
|
|
~TimerAndMutex() {
|
|
*mutex << timer.u_elapsed();
|
|
}
|
|
butil::Timer timer;
|
|
Mutex* mutex;
|
|
};
|
|
// Don't change the order of the fields as the implementation depends on
|
|
// the order of the constructors and destructors
|
|
TimerAndMutex _timer;
|
|
std::lock_guard<typename Mutex::mutex_type> _lock_guard;
|
|
};
|
|
|
|
template <typename Mutex>
|
|
class UniqueLockBase {
|
|
DISALLOW_COPY_AND_ASSIGN(UniqueLockBase);
|
|
public:
|
|
typedef Mutex mutex_type;
|
|
explicit UniqueLockBase(mutex_type& mutex)
|
|
: _timer(butil::Timer::STARTED), _lock(mutex.mutex()),
|
|
_mutex(&mutex) {
|
|
_timer.stop();
|
|
}
|
|
|
|
UniqueLockBase(mutex_type& mutex, std::defer_lock_t defer_lock)
|
|
: _timer(), _lock(mutex.mutex(), defer_lock), _mutex(&mutex) {
|
|
}
|
|
|
|
UniqueLockBase(mutex_type& mutex, std::try_to_lock_t try_to_lock)
|
|
: _timer(butil::Timer::STARTED)
|
|
, _lock(mutex.mutex(), try_to_lock)
|
|
, _mutex(&mutex) {
|
|
|
|
_timer.stop();
|
|
if (!owns_lock()) {
|
|
*_mutex << _timer.u_elapsed();
|
|
}
|
|
}
|
|
|
|
~UniqueLockBase() {
|
|
if (_lock.owns_lock()) {
|
|
unlock();
|
|
}
|
|
}
|
|
|
|
operator std::unique_lock<typename Mutex::mutex_type>&() { return _lock; }
|
|
void lock() {
|
|
_timer.start();
|
|
_lock.lock();
|
|
_timer.stop();
|
|
}
|
|
|
|
bool try_lock() {
|
|
_timer.start();
|
|
const bool rc = _lock.try_lock();
|
|
_timer.stop();
|
|
if (!rc) {
|
|
_mutex->recorder() << _timer.u_elapsed();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void unlock() {
|
|
_lock.unlock();
|
|
// Recorde the time out of the critical section
|
|
*_mutex << _timer.u_elapsed();
|
|
}
|
|
|
|
mutex_type* release() {
|
|
if (_lock.owns_lock()) {
|
|
// We have to recorde this time in the critical section owtherwise
|
|
// the event will be lost
|
|
*_mutex << _timer.u_elapsed();
|
|
}
|
|
mutex_type* saved_mutex = _mutex;
|
|
_mutex = NULL;
|
|
_lock.release();
|
|
return saved_mutex;
|
|
}
|
|
|
|
mutex_type* mutex() { return _mutex; }
|
|
bool owns_lock() const { return _lock.owns_lock(); }
|
|
operator bool() const { return static_cast<bool>(_lock); }
|
|
|
|
#if __cplusplus >= 201103L
|
|
template <class Rep, class Period>
|
|
bool try_lock_for(
|
|
const std::chrono::duration<Rep, Period>& timeout_duration) {
|
|
_timer.start();
|
|
const bool rc = _lock.try_lock_for(timeout_duration);
|
|
_timer.stop();
|
|
if (!rc) {
|
|
*_mutex << _timer.u_elapsed();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
template <class Clock, class Duration>
|
|
bool try_lock_until(
|
|
const std::chrono::time_point<Clock,Duration>& timeout_time ) {
|
|
_timer.start();
|
|
const bool rc = _lock.try_lock_until(timeout_time);
|
|
_timer.stop();
|
|
if (!rc) {
|
|
// Out of the criticle section. Otherwise the time will be recorded
|
|
// in unlock
|
|
*_mutex << _timer.u_elapsed();
|
|
}
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
// Don't change the order or timer and _lck;
|
|
butil::Timer _timer;
|
|
std::unique_lock<typename Mutex::mutex_type> _lock;
|
|
mutex_type* _mutex;
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
// Wappers of Mutex along with a shared LatencyRecorder
|
|
template <typename Mutex>
|
|
struct MutexWithRecorder
|
|
: public detail::MutexWithRecorderBase<
|
|
Mutex, IntRecorder,
|
|
MutexConstructor<Mutex>, MutexDestructor<Mutex> > {
|
|
typedef detail::MutexWithRecorderBase<
|
|
Mutex, IntRecorder,
|
|
MutexConstructor<Mutex>, MutexDestructor<Mutex> > Base;
|
|
|
|
explicit MutexWithRecorder(IntRecorder& recorder)
|
|
: Base(recorder)
|
|
{}
|
|
|
|
MutexWithRecorder() : Base() {}
|
|
|
|
};
|
|
|
|
// Wappers of Mutex along with a shared LatencyRecorder
|
|
template <typename Mutex>
|
|
struct MutexWithLatencyRecorder
|
|
: public detail::MutexWithRecorderBase<
|
|
Mutex, LatencyRecorder,
|
|
MutexConstructor<Mutex>, MutexDestructor<Mutex> > {
|
|
typedef detail::MutexWithRecorderBase<
|
|
Mutex, LatencyRecorder,
|
|
MutexConstructor<Mutex>, MutexDestructor<Mutex> > Base;
|
|
|
|
explicit MutexWithLatencyRecorder(LatencyRecorder& recorder)
|
|
: Base(recorder)
|
|
{}
|
|
MutexWithLatencyRecorder() : Base() {}
|
|
};
|
|
|
|
} // namespace bvar
|
|
|
|
namespace std {
|
|
|
|
// Specialize lock_guard and unique_lock
|
|
template <typename Mutex>
|
|
class lock_guard<bvar::MutexWithRecorder<Mutex> >
|
|
: public ::bvar::detail::
|
|
LockGuardBase< ::bvar::MutexWithRecorder<Mutex> > {
|
|
public:
|
|
typedef ::bvar::detail::
|
|
LockGuardBase<bvar::MutexWithRecorder<Mutex> > Base;
|
|
explicit lock_guard(::bvar::MutexWithRecorder<Mutex> &mutex)
|
|
: Base(mutex)
|
|
{}
|
|
};
|
|
|
|
template <typename Mutex>
|
|
class lock_guard<bvar::MutexWithLatencyRecorder<Mutex> >
|
|
: public ::bvar::detail::
|
|
LockGuardBase< ::bvar::MutexWithLatencyRecorder<Mutex> > {
|
|
public:
|
|
typedef ::bvar::detail::
|
|
LockGuardBase<bvar::MutexWithLatencyRecorder<Mutex> > Base;
|
|
explicit lock_guard(::bvar::MutexWithLatencyRecorder<Mutex> &mutex)
|
|
: Base(mutex)
|
|
{}
|
|
};
|
|
|
|
template <typename Mutex>
|
|
class unique_lock<bvar::MutexWithRecorder<Mutex> >
|
|
: public ::bvar::detail::
|
|
UniqueLockBase< ::bvar::MutexWithRecorder<Mutex> > {
|
|
public:
|
|
typedef ::bvar::detail::
|
|
UniqueLockBase< ::bvar::MutexWithRecorder<Mutex> > Base;
|
|
typedef typename Base::mutex_type mutex_type;
|
|
|
|
explicit unique_lock(mutex_type& mutex)
|
|
: Base(mutex)
|
|
{}
|
|
|
|
unique_lock(mutex_type& mutex, std::defer_lock_t defer_lock)
|
|
: Base(mutex, defer_lock)
|
|
{}
|
|
|
|
unique_lock(mutex_type& mutex, std::try_to_lock_t try_to_lock)
|
|
: Base(mutex, try_to_lock)
|
|
{}
|
|
};
|
|
|
|
template <typename Mutex>
|
|
class unique_lock<bvar::MutexWithLatencyRecorder<Mutex> >
|
|
: public ::bvar::detail::
|
|
UniqueLockBase< ::bvar::MutexWithLatencyRecorder<Mutex> > {
|
|
public:
|
|
typedef ::bvar::detail::
|
|
UniqueLockBase< ::bvar::MutexWithLatencyRecorder<Mutex> > Base;
|
|
typedef typename Base::mutex_type mutex_type;
|
|
|
|
explicit unique_lock(mutex_type& mutex)
|
|
: Base(mutex)
|
|
{}
|
|
|
|
unique_lock(mutex_type& mutex, std::defer_lock_t defer_lock)
|
|
: Base(mutex, defer_lock)
|
|
{}
|
|
|
|
unique_lock(mutex_type& mutex, std::try_to_lock_t try_to_lock)
|
|
: Base(mutex, try_to_lock)
|
|
{}
|
|
};
|
|
|
|
} // namespace std
|
|
|
|
#endif // BVAR_LOCK_TIMER_H
|