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