Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encapsulate and lock stat_datapoint, fixes core_test tsan #1665

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion nano/node/node.cpp
Expand Up @@ -1435,7 +1435,8 @@ void nano::signature_checker::set_thread_names (unsigned num_threads)

for (auto i = 0u; i < num_threads; ++i)
{
boost::asio::post (thread_pool, [&cv, &ready, &pending, &mutex_l, &promise = promises[i] ]() {
// clang-format off
boost::asio::post (thread_pool, [&cv, &ready, &pending, &mutex_l, &promise = promises[i]]() {
std::unique_lock<std::mutex> lk (mutex_l);
nano::thread_role::set (nano::thread_role::name::signature_checking);
if (--pending == 0)
Expand All @@ -1452,6 +1453,7 @@ void nano::signature_checker::set_thread_names (unsigned num_threads)
}
promise.set_value ();
});
// clang-format on
}

// Wait until all threads have finished
Expand Down
16 changes: 8 additions & 8 deletions nano/node/stats.cpp
Expand Up @@ -205,14 +205,14 @@ void nano::stat::log_counters_impl (stat_log_sink & sink)

for (auto & it : entries)
{
std::time_t time = std::chrono::system_clock::to_time_t (it.second->counter.timestamp);
std::time_t time = std::chrono::system_clock::to_time_t (it.second->counter.get_timestamp ());
tm local_tm = *localtime (&time);

auto key = it.first;
std::string type = type_to_string (key);
std::string detail = detail_to_string (key);
std::string dir = dir_to_string (key);
sink.write_entry (local_tm, type, detail, dir, it.second->counter.value);
sink.write_entry (local_tm, type, detail, dir, it.second->counter.get_value ());
}
sink.entries ()++;
sink.finalize ();
Expand Down Expand Up @@ -247,9 +247,9 @@ void nano::stat::log_samples_impl (stat_log_sink & sink)

for (auto & datapoint : it.second->samples)
{
std::time_t time = std::chrono::system_clock::to_time_t (datapoint.timestamp);
std::time_t time = std::chrono::system_clock::to_time_t (datapoint.get_timestamp ());
tm local_tm = *localtime (&time);
sink.write_entry (local_tm, type, detail, dir, datapoint.value);
sink.write_entry (local_tm, type, detail, dir, datapoint.get_value ());
}
}
sink.entries ()++;
Expand All @@ -267,9 +267,9 @@ void nano::stat::update (uint32_t key_a, uint64_t value)
auto entry (get_entry_impl (key_a, config.interval, config.capacity));

// Counters
auto old (entry->counter.value);
auto old (entry->counter.get_value ());
entry->counter.add (value);
entry->count_observers.notify (old, entry->counter.value);
entry->count_observers.notify (old, entry->counter.get_value ());

std::chrono::duration<double, std::milli> duration = now - log_last_count_writeout;
if (config.log_interval_counters > 0 && duration.count () > config.log_interval_counters)
Expand All @@ -289,9 +289,9 @@ void nano::stat::update (uint32_t key_a, uint64_t value)
entry->sample_start_time = now;

// Make a snapshot of samples for thread safety and to get a stable container
entry->sample_current.timestamp = std::chrono::system_clock::now ();
entry->sample_current.set_timestamp (std::chrono::system_clock::now ());
entry->samples.push_back (entry->sample_current);
entry->sample_current.value = 0;
entry->sample_current.set_value (0);

if (entry->sample_observers.observers.size () > 0)
{
Expand Down
49 changes: 44 additions & 5 deletions nano/node/stats.hpp
Expand Up @@ -6,6 +6,7 @@
#include <chrono>
#include <map>
#include <memory>
#include <mutex>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/utility.hpp>
Expand Down Expand Up @@ -59,20 +60,58 @@ class stat_config
class stat_datapoint
{
public:
/** Value of the sample interval */
uint64_t value{ 0 };
/** When the sample was added. This is wall time (system_clock), suitable for display purposes. */
std::chrono::system_clock::time_point timestamp{ std::chrono::system_clock::now () };
stat_datapoint () = default;
stat_datapoint (stat_datapoint const & other_a)
{
std::lock_guard<std::mutex> lock (other_a.datapoint_mutex);
value = other_a.value;
timestamp = other_a.timestamp;
}
stat_datapoint & operator= (stat_datapoint const & other_a)
{
std::lock_guard<std::mutex> lock (other_a.datapoint_mutex);
value = other_a.value;
timestamp = other_a.timestamp;
return *this;
}

uint64_t get_value ()
{
std::lock_guard<std::mutex> lock (datapoint_mutex);
return value;
}
void set_value (uint64_t value_a)
{
std::lock_guard<std::mutex> lock (datapoint_mutex);
value = value_a;
}
std::chrono::system_clock::time_point get_timestamp ()
{
std::lock_guard<std::mutex> lock (datapoint_mutex);
return timestamp;
}
void set_timestamp (std::chrono::system_clock::time_point timestamp_a)
{
std::lock_guard<std::mutex> lock (datapoint_mutex);
timestamp = timestamp_a;
}
/** Add \addend to the current value and optionally update the timestamp */
void add (uint64_t addend, bool update_timestamp = true)
{
std::lock_guard<std::mutex> lock (datapoint_mutex);
value += addend;
if (update_timestamp)
{
timestamp = std::chrono::system_clock::now ();
}
}

private:
mutable std::mutex datapoint_mutex;
/** Value of the sample interval */
uint64_t value{ 0 };
/** When the sample was added. This is wall time (system_clock), suitable for display purposes. */
std::chrono::system_clock::time_point timestamp{ std::chrono::system_clock::now () };
};

/** Bookkeeping of statistics for a specific type/detail/direction combination */
Expand Down Expand Up @@ -383,7 +422,7 @@ class stat
/** Returns current value for the given counter at the detail level */
uint64_t count (stat::type type, stat::detail detail, stat::dir dir = stat::dir::in)
{
return get_entry (key_of (type, detail, dir))->counter.value;
return get_entry (key_of (type, detail, dir))->counter.get_value ();
}

/** Returns the number of seconds since clear() was last called, or node startup if it's never called. */
Expand Down