From 8d2526176d85d3fb97f6042f23524cc488fc37bf Mon Sep 17 00:00:00 2001 From: Florian Schmaus <flow@cs.fau.de> Date: Sun, 11 Apr 2021 11:46:06 +0200 Subject: [PATCH] Fix race condition in io::Stats The workerStats std::vector was modified concurrently without being synchronized. The Stats are constructed together with IoContext and the worker IoContexts are constructed concurrently at the beginning of the worker loop. And in the Stats constructor the workerStats std::vector was modified by adding the Stats instance that is currently being constructed to it. Turns out, we don't need the workerStats data structure at all. We simply provide the global and worker IoContexts to io::Stats::printStats(). This results IMHO in a cleaner design of printStats() since there is no longer a data structure called workerStats, that in fact, not only holds the worker's stats. And while we at it, we rename io::Stats::printWorkerStats() to io::Stats::printsStats(), since it does, in fact, not only print the worker stats, but more or less all related worker stats. --- emper/Runtime.cpp | 2 +- emper/io/IoContext.hpp | 2 ++ emper/io/Stats.cpp | 13 ++++++------- emper/io/Stats.hpp | 16 ++++++++-------- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/emper/Runtime.cpp b/emper/Runtime.cpp index 5c36d3b4..a49128b9 100644 --- a/emper/Runtime.cpp +++ b/emper/Runtime.cpp @@ -165,7 +165,7 @@ Runtime::~Runtime() { // objects bound to the runtime's lifetime if constexpr (emper::STATS) { printLastRuntimeStats(); - emper::io::Stats::printWorkerStats(); + emper::io::Stats::printStats(globalIo, ioContexts); } for (unsigned int i = 0; i < workerCount; ++i) { diff --git a/emper/io/IoContext.hpp b/emper/io/IoContext.hpp index 2f34771e..ee4d78eb 100644 --- a/emper/io/IoContext.hpp +++ b/emper/io/IoContext.hpp @@ -181,5 +181,7 @@ class IoContext : public Logger<LogSubsystem::IO> { } } } + + auto getStats() -> Stats & { return stats; }; }; } // namespace emper::io diff --git a/emper/io/Stats.cpp b/emper/io/Stats.cpp index ba4ffe4b..62b792c8 100644 --- a/emper/io/Stats.cpp +++ b/emper/io/Stats.cpp @@ -7,13 +7,12 @@ #include <string> // for operator<<, to_string #include <utility> // for pair +#include "io/IoContext.hpp" #include "io/Operation.hpp" // for Operation, operator<<, Operation::RECV #include "lib/math.hpp" namespace emper::io { -std::vector<Stats*> Stats::workerStats; - auto operator<<(std::ostream& os, const Stats::CompletionType& t) -> std::ostream& { switch (t) { case Stats::CompletionType::FullCompletion: @@ -118,10 +117,9 @@ auto operator<<(std::ostream& os, const Stats& s) -> std::ostream& { return os; } -void Stats::printWorkerStats() { - auto* globalStats = workerStats.front(); - workerStats.erase(workerStats.begin()); - std::cout << *globalStats << std::endl; +void Stats::printStats(IoContext* globalIoContext, + const std::vector<IoContext*>& workerIoContexts) { + std::cout << globalIoContext->getStats() << std::endl; // Use a stats object to calculate the averages Stats avgs; @@ -130,7 +128,8 @@ void Stats::printWorkerStats() { // calculate avgs int i = 1; - for (auto& stats : workerStats) { + for (auto* workerIoContext : workerIoContexts) { + auto* stats = &workerIoContext->getStats(); for (const auto& op_pair : stats->io_uring_completions) { auto op = op_pair.first; auto op_map = op_pair.second; diff --git a/emper/io/Stats.hpp b/emper/io/Stats.hpp index 776d247a..5114800e 100644 --- a/emper/io/Stats.hpp +++ b/emper/io/Stats.hpp @@ -24,6 +24,11 @@ #include "lib/math.hpp" class Runtime; // lines 28-28 +namespace emper { +namespace io { +class IoContext; +} +} // namespace emper namespace math = emper::lib::math; @@ -52,8 +57,6 @@ class Stats { int workerId = 0; static_assert(sizeof(int) > sizeof(workerid_t)); - static std::vector<Stats*> workerStats; - enum CompletionType { FullCompletion = 0, ErrorCompletion, @@ -125,13 +128,10 @@ class Stats { unsigned reReapCount_max = 0; boost::circular_buffer<unsigned> reReapCount_last; - static void printWorkerStats(); + static void printStats(IoContext* globalIoContext, + const std::vector<IoContext*>& workerIoContexts); - Stats() : reReapCount_last(10) { - if constexpr (emper::STATS) { - workerStats.push_back(this); - } - } + Stats() : reReapCount_last(10) {} void recordCompletion(const Future& f, const int32_t& res) { record_completion(f.op, res, PartialCompletableFuture::DISABLE_PARTIAL_COMPLETION, f.len); -- GitLab