Fix unregister issue

This commit is contained in:
2026-04-19 14:47:29 +10:00
parent 2de11c4b0c
commit 51fe507dfd
8 changed files with 191 additions and 122 deletions

View File

@@ -20,8 +20,12 @@ static void AWSDbg(const char* fmt, ...) {
}
// ── ANSUtilities handle registry ──
static std::unordered_map<ANSCENTER::ANSUtilities*, int>& UtilHandleRegistry() {
static std::unordered_map<ANSCENTER::ANSUtilities*, int> s;
// destructionStarted: set by the first Unregister caller; blocks new Acquires
// and causes subsequent Unregister calls to return false without deleting.
// Prevents double-free when two threads race Release on the same handle.
struct UtilEntry { int refcount; bool destructionStarted; };
static std::unordered_map<ANSCENTER::ANSUtilities*, UtilEntry>& UtilHandleRegistry() {
static std::unordered_map<ANSCENTER::ANSUtilities*, UtilEntry> s;
return s;
}
static std::mutex& UtilHandleRegistryMutex() {
@@ -35,14 +39,15 @@ static std::condition_variable& UtilHandleRegistryCV() {
static void RegisterUtilHandle(ANSCENTER::ANSUtilities* h) {
std::lock_guard<std::mutex> lk(UtilHandleRegistryMutex());
UtilHandleRegistry()[h] = 1;
UtilHandleRegistry()[h] = { 1, false };
}
static ANSCENTER::ANSUtilities* AcquireUtilHandle(ANSCENTER::ANSUtilities* h) {
std::lock_guard<std::mutex> lk(UtilHandleRegistryMutex());
auto it = UtilHandleRegistry().find(h);
if (it == UtilHandleRegistry().end()) return nullptr;
it->second++;
if (it->second.destructionStarted) return nullptr;
it->second.refcount++;
return h;
}
@@ -50,23 +55,26 @@ static bool ReleaseUtilHandleRef(ANSCENTER::ANSUtilities* h) {
std::lock_guard<std::mutex> lk(UtilHandleRegistryMutex());
auto it = UtilHandleRegistry().find(h);
if (it == UtilHandleRegistry().end()) return false;
it->second--;
if (it->second <= 0) {
UtilHandleRegistry().erase(it);
it->second.refcount--;
if (it->second.refcount <= 0) {
UtilHandleRegistryCV().notify_all();
return true;
}
return false;
return false; // Only Unregister deletes. Ref drop just signals the CV.
}
static bool UnregisterUtilHandle(ANSCENTER::ANSUtilities* h) {
std::unique_lock<std::mutex> lk(UtilHandleRegistryMutex());
auto it = UtilHandleRegistry().find(h);
if (it == UtilHandleRegistry().end()) return false;
it->second--;
if (it->second.destructionStarted) {
// Another thread is already tearing this handle down; let it own the delete.
return false;
}
it->second.destructionStarted = true;
it->second.refcount--;
bool ok = UtilHandleRegistryCV().wait_for(lk, std::chrono::seconds(30), [&]() {
auto it2 = UtilHandleRegistry().find(h);
return it2 == UtilHandleRegistry().end() || it2->second <= 0;
return it2 == UtilHandleRegistry().end() || it2->second.refcount <= 0;
});
if (!ok) {
OutputDebugStringA("WARNING: UnregisterUtilHandle timed out waiting for in-flight operations\n");
@@ -87,8 +95,13 @@ public:
};
// ── ANSAWSS3 handle registry ──
static std::unordered_map<ANSCENTER::ANSAWSS3*, int>& AWSHandleRegistry() {
static std::unordered_map<ANSCENTER::ANSAWSS3*, int> s;
// destructionStarted: set by the first Unregister caller; blocks new Acquires
// and causes subsequent Unregister calls to return false without deleting.
// Prevents double-free when two threads race Release on the same handle
// (e.g. LabVIEW double-Release on the same wire, or Release during heap reuse).
struct AWSEntry { int refcount; bool destructionStarted; };
static std::unordered_map<ANSCENTER::ANSAWSS3*, AWSEntry>& AWSHandleRegistry() {
static std::unordered_map<ANSCENTER::ANSAWSS3*, AWSEntry> s;
return s;
}
static std::mutex& AWSHandleRegistryMutex() {
@@ -102,14 +115,15 @@ static std::condition_variable& AWSHandleRegistryCV() {
static void RegisterAWSHandle(ANSCENTER::ANSAWSS3* h) {
std::lock_guard<std::mutex> lk(AWSHandleRegistryMutex());
AWSHandleRegistry()[h] = 1;
AWSHandleRegistry()[h] = { 1, false };
AWSDbg("[ANSAWS] Register: handle=%p (uint=%llu) registrySize=%zu\n",
(void*)h, (unsigned long long)(uintptr_t)h, AWSHandleRegistry().size());
}
static bool IsAWSHandleLive(ANSCENTER::ANSAWSS3* h) {
std::lock_guard<std::mutex> lk(AWSHandleRegistryMutex());
return AWSHandleRegistry().find(h) != AWSHandleRegistry().end();
auto it = AWSHandleRegistry().find(h);
return it != AWSHandleRegistry().end() && !it->second.destructionStarted;
}
static ANSCENTER::ANSAWSS3* AcquireAWSHandle(ANSCENTER::ANSAWSS3* h) {
@@ -118,16 +132,20 @@ static ANSCENTER::ANSAWSS3* AcquireAWSHandle(ANSCENTER::ANSAWSS3* h) {
if (it == AWSHandleRegistry().end()) {
AWSDbg("[ANSAWS] Acquire FAIL: handle=%p (uint=%llu) NOT in registry. registrySize=%zu\n",
(void*)h, (unsigned long long)(uintptr_t)h, AWSHandleRegistry().size());
// dump current registry contents so you can compare what IS there
size_t i = 0;
for (auto& kv : AWSHandleRegistry()) {
AWSDbg("[ANSAWS] registry[%zu] = %p (uint=%llu) refcount=%d\n",
i++, (void*)kv.first, (unsigned long long)(uintptr_t)kv.first, kv.second);
AWSDbg("[ANSAWS] registry[%zu] = %p (uint=%llu) refcount=%d destructionStarted=%d\n",
i++, (void*)kv.first, (unsigned long long)(uintptr_t)kv.first,
kv.second.refcount, kv.second.destructionStarted ? 1 : 0);
}
return nullptr;
}
it->second++;
AWSDbg("[ANSAWS] Acquire OK: handle=%p refcount=%d\n", (void*)h, it->second);
if (it->second.destructionStarted) {
AWSDbg("[ANSAWS] Acquire FAIL: handle=%p is being destroyed (destructionStarted=true)\n", (void*)h);
return nullptr;
}
it->second.refcount++;
AWSDbg("[ANSAWS] Acquire OK: handle=%p refcount=%d\n", (void*)h, it->second.refcount);
return h;
}
@@ -135,13 +153,11 @@ static bool ReleaseAWSHandleRef(ANSCENTER::ANSAWSS3* h) {
std::lock_guard<std::mutex> lk(AWSHandleRegistryMutex());
auto it = AWSHandleRegistry().find(h);
if (it == AWSHandleRegistry().end()) return false;
it->second--;
if (it->second <= 0) {
AWSHandleRegistry().erase(it);
it->second.refcount--;
if (it->second.refcount <= 0) {
AWSHandleRegistryCV().notify_all();
return true;
}
return false;
return false; // Only Unregister deletes. Ref drop just signals the CV.
}
static bool UnregisterAWSHandle(ANSCENTER::ANSAWSS3* h) {
@@ -151,11 +167,16 @@ static bool UnregisterAWSHandle(ANSCENTER::ANSAWSS3* h) {
AWSDbg("[ANSAWS] Unregister: handle=%p NOT in registry (already gone)\n", (void*)h);
return false;
}
AWSDbg("[ANSAWS] Unregister: handle=%p starting (refcount before=%d)\n", (void*)h, it->second);
it->second--;
if (it->second.destructionStarted) {
AWSDbg("[ANSAWS] Unregister: handle=%p already being destroyed by another thread, returning false\n", (void*)h);
return false;
}
AWSDbg("[ANSAWS] Unregister: handle=%p starting (refcount before=%d)\n", (void*)h, it->second.refcount);
it->second.destructionStarted = true;
it->second.refcount--;
bool ok = AWSHandleRegistryCV().wait_for(lk, std::chrono::seconds(120), [&]() {
auto it2 = AWSHandleRegistry().find(h);
return it2 == AWSHandleRegistry().end() || it2->second <= 0;
return it2 == AWSHandleRegistry().end() || it2->second.refcount <= 0;
});
if (!ok) {
OutputDebugStringA("WARNING: UnregisterAWSHandle timed out waiting for in-flight operations\n");