| // 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 |