Loading...
Searching...
No Matches
PotRegistry.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12#include "eon/PotRegistry.h"
13#include <fstream>
14#include <iomanip>
15#include <sstream>
16
17namespace eonc {
18
19namespace {
20std::string format_timepoint(PotRegistry::TimePoint tp) {
21 auto time_t_val = PotRegistry::Clock::to_time_t(tp);
22 auto us = std::chrono::duration_cast<std::chrono::microseconds>(
23 tp.time_since_epoch()) %
24 std::chrono::seconds{1};
25 std::tm tm_buf{};
26#ifdef _WIN32
27 localtime_s(&tm_buf, &time_t_val);
28#else
29 localtime_r(&time_t_val, &tm_buf);
30#endif
31 std::ostringstream oss;
32 oss << std::put_time(&tm_buf, "%Y-%m-%dT%H:%M:%S");
33 oss << '.' << std::setfill('0') << std::setw(6) << us.count();
34 return oss.str();
35}
36
37// Escape a string for JSON (handles quotes and backslashes)
38std::string json_escape(std::string_view sv) {
39 std::string out;
40 out.reserve(sv.size());
41 for (char c : sv) {
42 if (c == '"' || c == '\\')
43 out += '\\';
44 out += c;
45 }
46 return out;
47}
48} // namespace
49
51 // Heap-allocated and never deleted. Potential destructors call
52 // on_destroyed, and those objects can outlive C++ static
53 // destructors (interpreter finalization, leaked shared_ptrs). A
54 // function-local static would already be destroyed on those paths,
55 // so on_destroyed would lock a destroyed mutex.
56 static PotRegistry *instance = new PotRegistry;
57 return *instance;
58}
59
61 for (auto &ts : m_type_stats) {
62 ts.force_calls.store(0, std::memory_order_relaxed);
63 ts.created.store(0, std::memory_order_relaxed);
64 ts.alive.store(0, std::memory_order_relaxed);
65 }
66 std::lock_guard<std::mutex> lock(m_records_mutex);
67 m_records.clear();
68 m_next_id.store(1, std::memory_order_relaxed);
69}
70
71uint64_t PotRegistry::on_created(PotType t) noexcept {
72 auto idx = static_cast<size_t>(magic_enum::enum_index(t).value_or(0));
73 m_type_stats[idx].created.fetch_add(1, std::memory_order_relaxed);
74 m_type_stats[idx].alive.fetch_add(1, std::memory_order_relaxed);
75 return m_next_id.fetch_add(1, std::memory_order_relaxed);
76}
77
78void PotRegistry::on_destroyed(uint64_t id, PotType t, size_t force_calls,
79 TimePoint created_at) {
80 auto idx = static_cast<size_t>(magic_enum::enum_index(t).value_or(0));
81 m_type_stats[idx].alive.fetch_sub(1, std::memory_order_relaxed);
82
83 InstanceRecord rec{id, t, created_at, Clock::now(), force_calls};
84 std::lock_guard<std::mutex> lock(m_records_mutex);
85 m_records.push_back(rec);
86}
87
89 auto idx = static_cast<size_t>(magic_enum::enum_index(t).value_or(0));
90 m_type_stats[idx].force_calls.fetch_add(1, std::memory_order_relaxed);
91}
92
93size_t PotRegistry::type_force_calls(PotType t) const noexcept {
94 auto idx = static_cast<size_t>(magic_enum::enum_index(t).value_or(0));
95 return m_type_stats[idx].force_calls.load(std::memory_order_relaxed);
96}
97
98size_t PotRegistry::total_force_calls() const noexcept {
99 size_t total = 0;
100 for (const auto &ts : m_type_stats) {
101 total += ts.force_calls.load(std::memory_order_relaxed);
102 }
103 return total;
104}
105
106size_t PotRegistry::type_alive(PotType t) const noexcept {
107 auto idx = static_cast<size_t>(magic_enum::enum_index(t).value_or(0));
108 return m_type_stats[idx].alive.load(std::memory_order_relaxed);
109}
110
111void PotRegistry::write_summary(const std::string &path) const {
112 std::vector<InstanceRecord> snapshot;
113 {
114 std::lock_guard<std::mutex> lock(const_cast<std::mutex &>(m_records_mutex));
115 snapshot = m_records;
116 }
117
118 std::ofstream ofs(path);
119 if (!ofs.is_open())
120 return;
121
122 ofs << "[\n";
123 for (size_t i = 0; i < snapshot.size(); ++i) {
124 const auto &r = snapshot[i];
125 auto type_name = magic_enum::enum_name(r.type);
126 ofs << " {\n";
127 ofs << " \"id\": " << r.id << ",\n";
128 ofs << " \"type\": \"" << json_escape(type_name) << "\",\n";
129 ofs << " \"created_at\": \"" << format_timepoint(r.created_at)
130 << "\",\n";
131 ofs << " \"destroyed_at\": \"" << format_timepoint(r.destroyed_at)
132 << "\",\n";
133 ofs << " \"force_calls\": " << r.force_calls << "\n";
134 ofs << " }";
135 if (i + 1 < snapshot.size())
136 ofs << ',';
137 ofs << '\n';
138 }
139 ofs << "]\n";
140}
141
142} // namespace eonc
std::atomic< uint64_t > m_next_id
Definition PotRegistry.h:48
std::array< TypeStats, magic_enum::enum_count< PotType >()> m_type_stats
Definition PotRegistry.h:43
void on_destroyed(uint64_t id, PotType t, size_t force_calls, TimePoint created_at)
uint64_t on_created(PotType t) noexcept
static PotRegistry & get() noexcept
Process-lifetime singleton.
size_t type_force_calls(PotType t) const noexcept
size_t total_force_calls() const noexcept
void on_force_call(PotType t) noexcept
std::mutex m_records_mutex
Definition PotRegistry.h:45
size_t type_alive(PotType t) const noexcept
Clock::time_point TimePoint
Definition PotRegistry.h:27
void write_summary(const std::string &path="_potcalls.json") const
std::vector< InstanceRecord > m_records
Definition PotRegistry.h:46
RAII resource manager for the ARTn C library with global synchronization.