13 #include <unordered_map>
14 #include <unordered_set>
17 #include <emscripten.h>
22 #include "GDCore/String.h"
43 static void add(
const void* ptr,
const char* className) {
44 if (!className)
return;
45 dead()[className].erase(ptr);
46 alive()[className].insert(ptr);
49 static void remove(
const void* ptr,
const char* className) {
50 if (!className)
return;
51 alive()[className].erase(ptr);
52 dead()[className].insert(ptr);
53 deadContexts()[className].push(ptr, currentCallContextId_(), nowMs());
56 static bool isDead(
const void* ptr,
const char* className) {
57 if (!className)
return false;
58 auto it = dead().find(className);
59 return it != dead().end() && it->second.count(ptr) > 0;
63 static void add(
long ptr,
const gd::String& className) {
64 add(
reinterpret_cast<const void*
>(
static_cast<uintptr_t
>(ptr)),
68 static void remove(
long ptr,
const gd::String& className) {
69 remove(
reinterpret_cast<const void*
>(
static_cast<uintptr_t
>(ptr)),
73 static bool isDead(
long ptr,
const gd::String& className) {
75 reinterpret_cast<const void*
>(
static_cast<uintptr_t
>(ptr)),
79 static long getDeadCount() {
81 for (
auto& kv : dead()) total +=
static_cast<long>(kv.second.size());
85 static void pruneDead(
long maxSize) {
86 if (getDeadCount() > maxSize) {
88 deadContexts().clear();
92 static long getAliveCount() {
94 for (
auto& kv : alive()) total +=
static_cast<long>(kv.second.size());
98 static long getAliveCountForClass(
const gd::String& className) {
99 auto it = alive().find(className.
c_str());
100 return it != alive().end() ?
static_cast<long>(it->second.size()) : 0;
103 static long getDeadCountForClass(
const gd::String& className) {
104 auto it = dead().find(className.
c_str());
105 return it != dead().end() ?
static_cast<long>(it->second.size()) : 0;
112 currentCallContextId_() =
static_cast<int>(id);
120 auto* ctx = findDeadContext(
121 reinterpret_cast<const void*
>(
static_cast<uintptr_t
>(ptr)),
123 return ctx ?
static_cast<long>(ctx->callContextId) : -1;
131 auto* ctx = findDeadContext(
132 reinterpret_cast<const void*
>(
static_cast<uintptr_t
>(ptr)),
134 return ctx ? ctx->timestampMs : 0.0;
138 using PtrSet = std::unordered_set<const void*>;
139 using ClassMap = std::unordered_map<std::string, PtrSet>;
143 struct DestructionContext {
144 const void* ptr =
nullptr;
145 int callContextId = -1;
146 double timestampMs = 0.0;
149 static constexpr
size_t kRingCapacity = 64;
152 std::array<DestructionContext, kRingCapacity> entries{};
155 void push(
const void* p,
int contextId,
double timestamp) {
156 DestructionContext& entry = entries[count % kRingCapacity];
158 entry.callContextId = contextId;
159 entry.timestampMs = timestamp;
164 const DestructionContext* find(
const void* p)
const {
165 size_t n = std::min(count, kRingCapacity);
166 for (
size_t i = 0; i < n; i++) {
167 size_t idx = (count - 1 - i) % kRingCapacity;
168 if (entries[idx].ptr == p)
return &entries[idx];
174 using RingMap = std::unordered_map<std::string, ClassRing>;
178 static ClassMap& alive() {
179 static auto* m =
new ClassMap();
183 static ClassMap& dead() {
184 static auto* m =
new ClassMap();
188 static RingMap& deadContexts() {
189 static auto* m =
new RingMap();
193 static int& currentCallContextId_() {
201 static double nowMs() {
202 #ifdef __EMSCRIPTEN__
203 return emscripten_get_now();
205 return static_cast<double>(
206 std::chrono::duration_cast<std::chrono::milliseconds>(
207 std::chrono::system_clock::now().time_since_epoch())
212 static const DestructionContext* findDeadContext(
const void* ptr,
213 const char* className) {
214 if (!className)
return nullptr;
215 auto it = deadContexts().find(className);
216 if (it == deadContexts().end())
return nullptr;
217 return it->second.find(ptr);
241 : owner_(owner), className_(className) {
242 if (className_) MemoryTrackedRegistry::add(owner_, className_);
245 ~
MemoryTracked() {
if (className_) MemoryTrackedRegistry::remove(owner_, className_); }
255 const char* className_;
A non-copyable, non-movable member object that registers/unregisters its owner with MemoryTrackedRegi...
Definition: MemoryTrackedRegistry.h:238
A static registry tracking the lifetime of C++ objects exposed to JavaScript via Emscripten/WebIDL bi...
Definition: MemoryTrackedRegistry.h:40
static long getDeadContextId(long ptr, const gd::String &className)
Definition: MemoryTrackedRegistry.h:119
static void setCurrentCallContextId(long id)
Definition: MemoryTrackedRegistry.h:111
static double getDeadContextTimeMs(long ptr, const gd::String &className)
Definition: MemoryTrackedRegistry.h:130
String represents an UTF8 encoded string.
Definition: String.h:33
const char * c_str() const
Get the C-string.
Definition: String.h:360
Definition: CommonTools.h:24