Skip to content

Commit

Permalink
THREAD
Browse files Browse the repository at this point in the history
  • Loading branch information
pwojcikdev committed Feb 10, 2024
1 parent f1b3569 commit 13e6bd9
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 43 deletions.
116 changes: 86 additions & 30 deletions nano/lib/stats.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/locks.hpp>
#include <nano/lib/stats.hpp>
#include <nano/lib/thread_roles.hpp>
#include <nano/lib/tomlconfig.hpp>

#include <boost/format.hpp>
Expand Down Expand Up @@ -130,10 +131,31 @@ nano::stats::stats (nano::stats_config config) :
{
}

nano::stats::~stats ()
{
// Thread must be stopped before destruction
debug_assert (!thread.joinable ());
}

void nano::stats::start ()
{
thread = std::thread ([this] {
nano::thread_role::set (nano::thread_role::name::stats);
run ();
});
}

void nano::stats::stop ()
{
std::lock_guard guard{ mutex };
stopped = true;
{
std::lock_guard guard{ mutex };
stopped = true;
}
condition.notify_all ();
if (thread.joinable ())
{
thread.join ();
}
}

void nano::stats::clear ()
Expand Down Expand Up @@ -318,39 +340,73 @@ void nano::stats::log_samples_impl (stat_log_sink & sink, tm & tm)
sink.finalize ();
}

// TODO: Run periodically in a separate thread
void nano::stats::update ()
std::chrono::milliseconds nano::stats::calculate_run_interval () const
{
static file_writer log_count (config.log_counters_filename);
static file_writer log_sample (config.log_samples_filename);
std::chrono::milliseconds interval = std::chrono::milliseconds::max ();
if (config.log_counters_interval.count () > 0)
{
interval = std::min (interval, config.log_counters_interval);
}
if (config.log_samples_interval.count () > 0)
{
interval = std::min (interval, config.log_samples_interval);
}
return interval;
}

std::lock_guard guard{ mutex };
if (!stopped)
void nano::stats::run ()
{
auto const interval = calculate_run_interval ();

if (interval == std::chrono::milliseconds::max ())
{
auto now = std::chrono::steady_clock::now (); // Only sample clock if necessary as this impacts node performance due to frequent usage
return;
}

// TODO: Replace with a proper std::chrono time
std::time_t time = std::chrono::system_clock::to_time_t (std::chrono::system_clock::now ());
tm local_tm = *localtime (&time);
std::unique_lock lock{ mutex };
while (!stopped)
{
condition.wait_for (lock, interval);

// Counters
if (config.log_counters_interval.count () > 0)
if (!stopped)
{
if (nano::elapsed (log_last_count_writeout, config.log_counters_interval))
{
log_counters_impl (log_count, local_tm);
log_last_count_writeout = now;
}
run_one (lock);
debug_assert (lock.owns_lock ());
}
}
}

// Samples
if (config.log_samples_interval.count () > 0)
void nano::stats::run_one (std::unique_lock<std::shared_mutex> & lock)
{
static file_writer log_count{ config.log_counters_filename };
static file_writer log_sample{ config.log_samples_filename };

debug_assert (!mutex.try_lock ());
debug_assert (lock.owns_lock ());

auto now = std::chrono::steady_clock::now (); // Only sample clock if necessary as this impacts node performance due to frequent usage

// TODO: Replace with a proper std::chrono time
std::time_t time = std::chrono::system_clock::to_time_t (std::chrono::system_clock::now ());
tm local_tm = *localtime (&time);

// Counters
if (config.log_counters_interval.count () > 0)
{
if (nano::elapsed (log_last_count_writeout, config.log_counters_interval))
{
if (nano::elapsed (log_last_sample_writeout, config.log_samples_interval))
{
log_samples_impl (log_sample, local_tm);
log_last_sample_writeout = now;
}
log_counters_impl (log_count, local_tm);
log_last_count_writeout = now;
}
}

// Samples
if (config.log_samples_interval.count () > 0)
{
if (nano::elapsed (log_last_sample_writeout, config.log_samples_interval))
{
log_samples_impl (log_sample, local_tm);
log_last_sample_writeout = now;
}
}
}
Expand All @@ -364,20 +420,20 @@ std::chrono::seconds nano::stats::last_reset ()

std::string nano::stats::dump (category category)
{
auto sink = log_sink_json ();
json_writer sink;
switch (category)
{
case category::counters:
log_counters (*sink);
log_counters (sink);
break;
case category::samples:
log_samples (*sink);
log_samples (sink);
break;
default:
debug_assert (false, "missing stat_category case");
break;
}
return sink->to_string ();
return sink.to_string ();
}

/*
Expand Down
22 changes: 9 additions & 13 deletions nano/lib/stats.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <mutex>
#include <shared_mutex>
#include <string>
#include <thread>

namespace nano
{
Expand Down Expand Up @@ -70,15 +71,10 @@ class stats final
using sampler_value_t = int64_t;

public:
/** Constructor using the default config values */
stats () = default;
explicit stats (nano::stats_config = {});
~stats ();

/**
* Initialize stats with a config.
*/
explicit stats (nano::stats_config);

/** Stop stats being output */
void start ();
void stop ();

/** Clear all stats */
Expand Down Expand Up @@ -203,11 +199,9 @@ class stats final
void update_sampler (sampler_key key, std::function<void (sampler_entry &)> const & updater);

private:
/**
* Update count and sample and call any observers on the key
* @value Amount to add to the counter
*/
void update ();
void run ();
void run_one (std::unique_lock<std::shared_mutex> & lock);
std::chrono::milliseconds calculate_run_interval () const;

/** Unlocked implementation of log_counters() to avoid using recursive locking */
void log_counters_impl (stat_log_sink & sink, tm & tm);
Expand All @@ -226,6 +220,8 @@ class stats final

/** Whether stats should be output */
bool stopped{ false };
std::thread thread;
nano::condition_variable condition;

mutable std::shared_mutex mutex;
};
Expand Down
3 changes: 3 additions & 0 deletions nano/lib/thread_roles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ std::string nano::thread_role::get_string (nano::thread_role::name role)
case nano::thread_role::name::scheduler_priority:
thread_role_name_string = "Sched Priority";
break;
case nano::thread_role::name::stats:
thread_role_name_string = "Stats";
break;
default:
debug_assert (false && "nano::thread_role::get_string unhandled thread role");
}
Expand Down
1 change: 1 addition & 0 deletions nano/lib/thread_roles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ enum class name
scheduler_manual,
scheduler_optimistic,
scheduler_priority,
stats,
};

/*
Expand Down
1 change: 1 addition & 0 deletions nano/node/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ void nano::node::start ()
}
websocket.start ();
telemetry.start ();
stats.start ();
}

void nano::node::stop ()
Expand Down
1 change: 1 addition & 0 deletions nano/test_common/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ void nano::test::system::stop ()
{
i->stop ();
}
stats.stop ();
work.stop ();
}

Expand Down

0 comments on commit 13e6bd9

Please sign in to comment.