291 lines
No EOL
6.5 KiB
C++
291 lines
No EOL
6.5 KiB
C++
#include <nano/lib/config.hpp>
|
|
#include <nano/lib/locks.hpp>
|
|
#include <nano/lib/utility.hpp>
|
|
|
|
#include <boost/format.hpp>
|
|
|
|
#include <cstring>
|
|
#include <iostream>
|
|
|
|
#if USING_NANO_TIMED_LOCKS
|
|
namespace nano
|
|
{
|
|
// These mutexes must have std::mutex interface in addition to "const char* get_name ()" method
|
|
template <typename Mutex>
|
|
void output (const char * str, std::chrono::milliseconds time, Mutex & mutex)
|
|
{
|
|
static nano::mutex cout_mutex;
|
|
auto stacktrace = nano::generate_stacktrace ();
|
|
// Guard standard out to keep the output from being interleaved
|
|
std::lock_guard guard (cout_mutex);
|
|
std::cout << (boost::format ("%1% Mutex %2% %3% for %4%ms\n%5%") % std::addressof (mutex) % mutex.get_name () % str % time.count () % stacktrace).str ()
|
|
<< std::endl;
|
|
}
|
|
|
|
template <typename Mutex>
|
|
void output_if_held_long_enough (nano::timer<std::chrono::milliseconds> & timer, Mutex & mutex)
|
|
{
|
|
auto time_held = timer.since_start ();
|
|
if (time_held >= std::chrono::milliseconds (NANO_TIMED_LOCKS))
|
|
{
|
|
std::unique_lock lk (nano::mutex_to_filter_mutex);
|
|
if (!nano::any_filters_registered () || (nano::mutex_to_filter == &mutex))
|
|
{
|
|
lk.unlock ();
|
|
output ("held", time_held, mutex);
|
|
}
|
|
}
|
|
if (timer.current_state () != nano::timer_state::stopped)
|
|
{
|
|
timer.stop ();
|
|
}
|
|
}
|
|
|
|
#ifndef NANO_TIMED_LOCKS_IGNORE_BLOCKED
|
|
template <typename Mutex>
|
|
void output_if_blocked_long_enough (nano::timer<std::chrono::milliseconds> & timer, Mutex & mutex)
|
|
{
|
|
auto time_blocked = timer.since_start ();
|
|
if (time_blocked >= std::chrono::milliseconds (NANO_TIMED_LOCKS))
|
|
{
|
|
std::unique_lock lk (nano::mutex_to_filter_mutex);
|
|
if (!nano::any_filters_registered () || (nano::mutex_to_filter == &mutex))
|
|
{
|
|
lk.unlock ();
|
|
output ("blocked", time_blocked, mutex);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
lock_guard<nano::mutex>::lock_guard (nano::mutex & mutex) :
|
|
mut (mutex)
|
|
{
|
|
timer.start ();
|
|
|
|
mut.lock ();
|
|
#ifndef NANO_TIMED_LOCKS_IGNORE_BLOCKED
|
|
output_if_blocked_long_enough (timer, mut);
|
|
#endif
|
|
}
|
|
|
|
lock_guard<nano::mutex>::~lock_guard () noexcept
|
|
{
|
|
mut.unlock ();
|
|
output_if_held_long_enough (timer, mut);
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
unique_lock<Mutex, U>::unique_lock (Mutex & mutex) :
|
|
mut (std::addressof (mutex))
|
|
{
|
|
lock_impl ();
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
unique_lock<Mutex, U>::unique_lock (Mutex & mutex, std::defer_lock_t) noexcept :
|
|
mut (std::addressof (mutex))
|
|
{
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
void unique_lock<Mutex, U>::lock_impl ()
|
|
{
|
|
timer.start ();
|
|
|
|
mut->lock ();
|
|
owns = true;
|
|
#ifndef NANO_TIMED_LOCKS_IGNORE_BLOCKED
|
|
output_if_blocked_long_enough (timer, *mut);
|
|
#endif
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
unique_lock<Mutex, U> & unique_lock<Mutex, U>::operator= (unique_lock<Mutex, U> && other) noexcept
|
|
{
|
|
if (this != std::addressof (other))
|
|
{
|
|
if (owns)
|
|
{
|
|
mut->unlock ();
|
|
owns = false;
|
|
|
|
output_if_held_long_enough (timer, *mut);
|
|
}
|
|
|
|
mut = other.mut;
|
|
owns = other.owns;
|
|
timer = other.timer;
|
|
|
|
other.mut = nullptr;
|
|
other.owns = false;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
unique_lock<Mutex, U>::~unique_lock () noexcept
|
|
{
|
|
if (owns)
|
|
{
|
|
mut->unlock ();
|
|
owns = false;
|
|
|
|
output_if_held_long_enough (timer, *mut);
|
|
}
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
void unique_lock<Mutex, U>::lock ()
|
|
{
|
|
validate ();
|
|
lock_impl ();
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
bool unique_lock<Mutex, U>::try_lock ()
|
|
{
|
|
validate ();
|
|
owns = mut->try_lock ();
|
|
|
|
if (owns)
|
|
{
|
|
timer.start ();
|
|
}
|
|
|
|
return owns;
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
void unique_lock<Mutex, U>::unlock ()
|
|
{
|
|
if (!mut || !owns)
|
|
{
|
|
throw (std::system_error (std::make_error_code (std::errc::operation_not_permitted)));
|
|
}
|
|
|
|
mut->unlock ();
|
|
owns = false;
|
|
|
|
output_if_held_long_enough (timer, *mut);
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
bool unique_lock<Mutex, U>::owns_lock () const noexcept
|
|
{
|
|
return owns;
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
unique_lock<Mutex, U>::operator bool () const noexcept
|
|
{
|
|
return owns;
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
Mutex * unique_lock<Mutex, U>::mutex () const noexcept
|
|
{
|
|
return mut;
|
|
}
|
|
|
|
template <typename Mutex, typename U>
|
|
void unique_lock<Mutex, U>::validate () const
|
|
{
|
|
if (!mut)
|
|
{
|
|
throw (std::system_error (std::make_error_code (std::errc::operation_not_permitted)));
|
|
}
|
|
|
|
if (owns)
|
|
{
|
|
throw (std::system_error (std::make_error_code (std::errc::resource_deadlock_would_occur)));
|
|
}
|
|
}
|
|
|
|
// Explicit instantiations for allowed types
|
|
template class unique_lock<nano::mutex>;
|
|
|
|
void condition_variable::notify_one () noexcept
|
|
{
|
|
cnd.notify_one ();
|
|
}
|
|
|
|
void condition_variable::notify_all () noexcept
|
|
{
|
|
cnd.notify_all ();
|
|
}
|
|
|
|
void condition_variable::wait (nano::unique_lock<nano::mutex> & lk)
|
|
{
|
|
if (!lk.mut || !lk.owns)
|
|
{
|
|
throw (std::system_error (std::make_error_code (std::errc::operation_not_permitted)));
|
|
}
|
|
|
|
output_if_held_long_enough (lk.timer, *lk.mut);
|
|
// Start again in case cnd.wait calls unique_lock::lock/unlock () depending on some implementations
|
|
lk.timer.start ();
|
|
cnd.wait (lk);
|
|
lk.timer.restart ();
|
|
}
|
|
template class unique_lock<nano::mutex>;
|
|
|
|
nano::mutex * mutex_to_filter{ nullptr };
|
|
nano::mutex mutex_to_filter_mutex;
|
|
|
|
bool should_be_filtered (const char * name)
|
|
{
|
|
return std::strcmp (name, xstr (NANO_TIMED_LOCKS_FILTER)) == 0;
|
|
}
|
|
|
|
bool any_filters_registered ()
|
|
{
|
|
return std::strcmp ("", xstr (NANO_TIMED_LOCKS_FILTER)) != 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
char const * nano::mutex_identifier (mutexes mutex)
|
|
{
|
|
switch (mutex)
|
|
{
|
|
case mutexes::active:
|
|
return "active";
|
|
case mutexes::block_arrival:
|
|
return "block_arrival";
|
|
case mutexes::block_processor:
|
|
return "block_processor";
|
|
case mutexes::block_uniquer:
|
|
return "block_uniquer";
|
|
case mutexes::blockstore_cache:
|
|
return "blockstore_cache";
|
|
case mutexes::confirmation_height_processor:
|
|
return "confirmation_height_processor";
|
|
case mutexes::election_winner_details:
|
|
return "election_winner_details";
|
|
case mutexes::gap_cache:
|
|
return "gap_cache";
|
|
case mutexes::network_filter:
|
|
return "network_filter";
|
|
case mutexes::observer_set:
|
|
return "observer_set";
|
|
case mutexes::request_aggregator:
|
|
return "request_aggregator";
|
|
case mutexes::state_block_signature_verification:
|
|
return "state_block_signature_verification";
|
|
case mutexes::telemetry:
|
|
return "telemetry";
|
|
case mutexes::vote_generator:
|
|
return "vote_generator";
|
|
case mutexes::vote_processor:
|
|
return "vote_processor";
|
|
case mutexes::vote_uniquer:
|
|
return "vote_uniquer";
|
|
case mutexes::votes_cache:
|
|
return "votes_cache";
|
|
case mutexes::work_pool:
|
|
return "work_pool";
|
|
}
|
|
|
|
throw std::runtime_error ("Invalid mutexes enum specified");
|
|
} |