Fix ANSAWS3 multiple instance creation

This commit is contained in:
2026-04-18 20:33:44 +10:00
parent a47ea25e8b
commit 247c91db50
2 changed files with 161 additions and 41 deletions

View File

@@ -2,10 +2,23 @@
#include "pch.h"
#include "ANSUtilities.h"
#include <cstdint>
#include <cstdio>
#include <cstdarg>
#include <unordered_map>
#include <condition_variable>
#include <mutex>
// ── DebugView logger (filter on "[ANSAWS]") ──
static void AWSDbg(const char* fmt, ...) {
char buf[512];
va_list ap;
va_start(ap, fmt);
int n = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (n < 0) return;
OutputDebugStringA(buf);
}
// ── ANSUtilities handle registry ──
static std::unordered_map<ANSCENTER::ANSUtilities*, int>& UtilHandleRegistry() {
static std::unordered_map<ANSCENTER::ANSUtilities*, int> s;
@@ -90,13 +103,31 @@ static std::condition_variable& AWSHandleRegistryCV() {
static void RegisterAWSHandle(ANSCENTER::ANSAWSS3* h) {
std::lock_guard<std::mutex> lk(AWSHandleRegistryMutex());
AWSHandleRegistry()[h] = 1;
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();
}
static ANSCENTER::ANSAWSS3* AcquireAWSHandle(ANSCENTER::ANSAWSS3* h) {
std::lock_guard<std::mutex> lk(AWSHandleRegistryMutex());
auto it = AWSHandleRegistry().find(h);
if (it == AWSHandleRegistry().end()) return nullptr;
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);
}
return nullptr;
}
it->second++;
AWSDbg("[ANSAWS] Acquire OK: handle=%p refcount=%d\n", (void*)h, it->second);
return h;
}
@@ -116,7 +147,11 @@ static bool ReleaseAWSHandleRef(ANSCENTER::ANSAWSS3* h) {
static bool UnregisterAWSHandle(ANSCENTER::ANSAWSS3* h) {
std::unique_lock<std::mutex> lk(AWSHandleRegistryMutex());
auto it = AWSHandleRegistry().find(h);
if (it == AWSHandleRegistry().end()) return false;
if (it == AWSHandleRegistry().end()) {
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--;
bool ok = AWSHandleRegistryCV().wait_for(lk, std::chrono::seconds(120), [&]() {
auto it2 = AWSHandleRegistry().find(h);
@@ -1185,19 +1220,36 @@ extern "C" ANSULT_API int ANSUnescapeDoubleEscapedUnicode(const char* str, LStrH
}
// AWSS3
//
// Pure constructor: every call allocates a brand-new ANSAWSS3 and writes it to
// *Handle. *Handle(in) is ignored completely -- LabVIEW's CLF Node marshalling
// reuses the same temp buffer per call site, so *Handle(in) often contains
// leftover bytes from the previous Create's output even when the actual LabVIEW
// wire is a different, freshly-allocated instance. Inspecting *Handle(in) for
// "is this live?" detection cannot distinguish that case from a genuine
// double-Create-on-same-wire, so we don't try -- we trust the caller.
//
// Trade-off: if a caller really does call Create twice on the same wire without
// Release, the first handle leaks. That is the caller's bug. The alternative
// (releasing live objects we "see" in the input) destroys legitimate parallel
// instances and is far worse.
//
// Return codes:
// 1 : success, *Handle now points to a new registered instance
// 0 : invalid arg, alloc failure, or Initialize() failed
extern "C" ANSULT_API int CreateANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* licenseKey) {
AWSDbg("[ANSAWS] Create called: HandlePtr=%p, *Handle(in)=%p (input ignored, always allocates new)\n",
(void*)Handle, Handle ? (void*)*Handle : nullptr);
if (Handle == nullptr || licenseKey == nullptr) return 0;
if (*Handle) {
if (UnregisterAWSHandle(*Handle)) {
delete *Handle;
}
*Handle = nullptr;
}
*Handle = nullptr;
try {
*Handle = new ANSCENTER::ANSAWSS3();
if (*Handle == nullptr) return 0;
if ((*Handle)->Initialize(licenseKey)) {
RegisterAWSHandle(*Handle);
AWSDbg("[ANSAWS] Create OK: *Handle(out)=%p (uint=%llu) -- LabVIEW will see this number\n",
(void*)*Handle, (unsigned long long)(uintptr_t)*Handle);
return 1;
}
// Initialize failed - clean up to prevent memory leak
@@ -1223,16 +1275,26 @@ extern "C" ANSULT_API int CreateANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, cons
}
static int ReleaseANSAWSHandle_Impl(ANSCENTER::ANSAWSS3** Handle) {
try {
if (!Handle || !*Handle) return 1;
if (!UnregisterAWSHandle(*Handle)) {
if (!Handle || !*Handle) {
AWSDbg("[ANSAWS] Release: noop (Handle or *Handle was null)\n");
return 1;
}
ANSCENTER::ANSAWSS3* h = *Handle;
AWSDbg("[ANSAWS] Release called: handle=%p (uint=%llu)\n",
(void*)h, (unsigned long long)(uintptr_t)h);
if (!UnregisterAWSHandle(h)) {
AWSDbg("[ANSAWS] Release: handle %p not in registry, clearing wire only\n", (void*)h);
*Handle = nullptr;
return 1;
}
delete *Handle;
delete h;
*Handle = nullptr;
AWSDbg("[ANSAWS] Release OK: handle %p deleted, registry now has %zu entries\n",
(void*)h, AWSHandleRegistry().size());
return 1;
}
catch (...) {
AWSDbg("[ANSAWS] Release EXCEPTION: clearing wire and returning 0\n");
if (Handle) *Handle = nullptr;
return 0;
}
@@ -1243,57 +1305,102 @@ extern "C" ANSULT_API int ReleaseANSAWSHandle(ANSCENTER::ANSAWSS3** Handle) {
return ReleaseANSAWSHandle_Impl(Handle);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
AWSDbg("[ANSAWS] Release SEH EXCEPTION: clearing wire and returning 0\n");
if (Handle) *Handle = nullptr;
return 0;
}
}
extern "C" ANSULT_API int ConnectANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* baseDomain, const char* bucketRegion, const char* serviceName, int port, int bTls, int autoReconnect, int* awsPath) {
AWSDbg("[ANSAWS] Connect called: *Handle=%p, baseDomain=%s, region=%s, service=%s, port=%d, tls=%d, autoReconnect=%d\n",
Handle ? (void*)*Handle : nullptr,
baseDomain ? baseDomain : "(null)",
bucketRegion ? bucketRegion : "(null)",
serviceName ? serviceName : "(null)",
port, bTls, autoReconnect);
if (Handle == nullptr || *Handle == nullptr || awsPath == nullptr) {
AWSDbg("[ANSAWS] Connect: returning 0 (null arg)\n");
return 0;
}
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
if (!guard) return 0;
if (!guard) {
AWSDbg("[ANSAWS] Connect: returning 0 (handle %p not in registry)\n", (void*)*Handle);
return 0;
}
try {
bool bTlsBool = (bTls == 1);
bool autoReconnectBool = (autoReconnect == 1);
bool awsPathBool = true;
int result = guard.get()->Connect(baseDomain, bucketRegion, serviceName, port, bTlsBool, autoReconnectBool, awsPathBool);
*awsPath = awsPathBool ? 1 : 0;
AWSDbg("[ANSAWS] Connect result: %d (1=connected, 0=failed, 2=no internet) awsPath=%d\n", result, *awsPath);
return result; // 1 = connected, 0 = failed, 2 = no internet (retrying)
}
catch (std::exception& e) {
AWSDbg("[ANSAWS] Connect EXCEPTION: %s\n", e.what());
return 0;
}
catch (...) {
AWSDbg("[ANSAWS] Connect EXCEPTION: unknown\n");
return 0;
}
}
extern "C" ANSULT_API int SetProxyANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* proxyHost, int proxyPort, const char* proxyUsername, const char* proxyPassword) {
AWSDbg("[ANSAWS] SetProxy called: *Handle=%p, host=%s, port=%d, user=%s\n",
Handle ? (void*)*Handle : nullptr,
proxyHost ? proxyHost : "(null)",
proxyPort,
proxyUsername ? proxyUsername : "(null)");
if (Handle == nullptr || *Handle == nullptr) {
AWSDbg("[ANSAWS] SetProxy: returning 0 (null arg)\n");
return 0;
}
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
if (!guard) return 0;
if (!guard) {
AWSDbg("[ANSAWS] SetProxy: returning 0 (handle %p not in registry)\n", (void*)*Handle);
return 0;
}
try {
if (guard.get()->SetServerProxy(proxyHost, proxyPort, proxyUsername, proxyPassword)) return 1;
else return 0;
if (guard.get()->SetServerProxy(proxyHost, proxyPort, proxyUsername, proxyPassword)) {
AWSDbg("[ANSAWS] SetProxy OK\n");
return 1;
}
else {
AWSDbg("[ANSAWS] SetProxy FAILED (SetServerProxy returned false)\n");
return 0;
}
}
catch (std::exception& e) {
AWSDbg("[ANSAWS] SetProxy EXCEPTION: %s\n", e.what());
return 0;
}
catch (...) {
AWSDbg("[ANSAWS] SetProxy EXCEPTION: unknown\n");
return 0;
}
}
extern "C" ANSULT_API int SetAuthenticationANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* accessKey, const char* secretKey) {
AWSDbg("[ANSAWS] SetAuth called: *Handle=%p, accessKey=%.6s... (length-only secretKey=%zu)\n",
Handle ? (void*)*Handle : nullptr,
accessKey ? accessKey : "(null)",
secretKey ? strlen(secretKey) : 0);
if (Handle == nullptr || *Handle == nullptr ) {
AWSDbg("[ANSAWS] SetAuth: returning 0 (null arg)\n");
return 0;
}
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
if (!guard) return 0;
if (!guard) {
AWSDbg("[ANSAWS] SetAuth: returning 0 (handle %p not in registry)\n", (void*)*Handle);
return 0;
}
try {
if (guard.get()->SetAuthentication(accessKey, secretKey)) return 1;
else return 0;
if (guard.get()->SetAuthentication(accessKey, secretKey)) {
AWSDbg("[ANSAWS] SetAuth OK\n");
return 1;
}
else {
AWSDbg("[ANSAWS] SetAuth FAILED (SetAuthentication returned false)\n");
return 0;
}
}
catch (std::exception& e) {
return 0;
@@ -1775,11 +1882,11 @@ extern "C" ANSULT_API int DownloadFileStreamANSAWSHandle(ANSCENTER::ANSAWSS3**
}
}
extern "C" ANSULT_API int UploadJpegImageANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, LStrHandle uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || jpeg_string == nullptr || bufferLength <= 0 || fileName == nullptr || uploadedFilePath == nullptr) {
return 0;
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || jpeg_string == nullptr || bufferLength <= 0 || fileName == nullptr) {
return 0; // Handle error
}
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
if (!guard) return 0;
if (!guard) return -1; // guard error
try {
std::string outPath;
if (guard.get()->UploadJpegImage(bucketName, jpeg_string, bufferLength, fileName, outPath)) {
@@ -1791,25 +1898,37 @@ extern "C" ANSULT_API int UploadJpegImageANSAWSHandle(ANSCENTER::ANSAWSS3** Han
}
return 1;
}
else return 0;
else return -2;// failed upload
}
catch (std::exception& e) {
return 0;
return -3;//excception
}
catch (...) {
return 0;
return -4;//exception
}
}
extern "C" ANSULT_API int UploadPrefixJpegImageANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, LStrHandle uploadedFilePath) {
AWSDbg("[ANSAWS] Upload called: HandlePtr=%p, *Handle=%p (uint=%llu), bucket=%s, prefix=%s, file=%s, len=%d\n",
(void*)Handle,
Handle ? (void*)*Handle : nullptr,
Handle && *Handle ? (unsigned long long)(uintptr_t)*Handle : 0ULL,
bucketName ? bucketName : "(null)",
prefix ? prefix : "(null)",
fileName ? fileName : "(null)",
bufferLength);
if (Handle == nullptr || *Handle == nullptr ||
bucketName == nullptr || prefix == nullptr ||
jpeg_string == nullptr || bufferLength <= 0 ||
fileName == nullptr || uploadedFilePath == nullptr)
fileName == nullptr)
{
AWSDbg("[ANSAWS] Upload: returning 0 (null/invalid arg)\n");
return 0;
}
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
if (!guard) return 0;
if (!guard) {
AWSDbg("[ANSAWS] Upload: returning -1 (handle %p not in registry)\n", (void*)*Handle);
return -1;
}
try {
std::string outPath;
if (guard.get()->UploadPrefixJpegImage(bucketName, prefix, jpeg_string, bufferLength, fileName, outPath)) {
@@ -1819,19 +1938,29 @@ extern "C" ANSULT_API int UploadPrefixJpegImageANSAWSHandle(ANSCENTER::ANSAWSS3
(*uploadedFilePath)->cnt = size;
memcpy((*uploadedFilePath)->str, outPath.c_str(), size);
}
AWSDbg("[ANSAWS] Upload OK (1): file=%s -> %s\n",
fileName ? fileName : "(null)", outPath.c_str());
return 1;
}
else return 0;
else {
AWSDbg("[ANSAWS] Upload FAILED (-2): file=%s, UploadPrefixJpegImage returned false\n",
fileName ? fileName : "(null)");
return -2;
}
}
catch (std::exception& e) {
return 0;
AWSDbg("[ANSAWS] Upload EXCEPTION (-3): file=%s, what=%s\n",
fileName ? fileName : "(null)", e.what());
return -3;
}
catch (...) {
return 0;
AWSDbg("[ANSAWS] Upload EXCEPTION (-4): file=%s, unknown exception\n",
fileName ? fileName : "(null)");
return -4;
}
}
extern "C" ANSULT_API int UploadPrefixMultiPartDataANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* fileDataPath, const char* objectName, int fileSize, LStrHandle uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || fileDataPath == nullptr || fileSize <= 0 || uploadedFilePath == nullptr) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || fileDataPath == nullptr || fileSize <= 0 ) {
return 0;
}
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
@@ -1857,7 +1986,7 @@ extern "C" ANSULT_API int UploadPrefixMultiPartDataANSAWSHandle(ANSCENTER::ANSA
}
}
extern "C" ANSULT_API int UploadPrefixBinaryDataANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* binaryDataPath, const char* objectName, LStrHandle uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || binaryDataPath == nullptr || objectName == nullptr || uploadedFilePath == nullptr) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || binaryDataPath == nullptr || objectName == nullptr) {
return 0;
}
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
@@ -1882,9 +2011,7 @@ extern "C" ANSULT_API int UploadPrefixBinaryDataANSAWSHandle(ANSCENTER::ANSAWSS
return 0;
}
}
// ── C++ test APIs (std::string& instead of LStrHandle) ──
extern "C" ANSULT_API int UploadTextDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* textDataPath, std::string& uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || textDataPath == nullptr) return 0;
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
@@ -1893,7 +2020,6 @@ extern "C" ANSULT_API int UploadTextDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** H
return guard.get()->UploadTextData(bucketName, textDataPath, uploadedFilePath) ? 1 : 0;
} catch (...) { return 0; }
}
extern "C" ANSULT_API int UploadBinaryDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* binaryDataPath, std::string& uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || binaryDataPath == nullptr) return 0;
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
@@ -1902,7 +2028,6 @@ extern "C" ANSULT_API int UploadBinaryDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3**
return guard.get()->UploadBinaryData(bucketName, binaryDataPath, uploadedFilePath) ? 1 : 0;
} catch (...) { return 0; }
}
extern "C" ANSULT_API int UploadFileStreamDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* fileDataPath, std::string& uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || fileDataPath == nullptr) return 0;
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
@@ -1911,7 +2036,6 @@ extern "C" ANSULT_API int UploadFileStreamDataANSAWSHandle_CPP(ANSCENTER::ANSAWS
return guard.get()->UploadFileStream(bucketName, fileDataPath, uploadedFilePath) ? 1 : 0;
} catch (...) { return 0; }
}
extern "C" ANSULT_API int UploadMultiPartDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* fileDataPath, int fileSize, std::string& uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || fileDataPath == nullptr) return 0;
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
@@ -1920,7 +2044,6 @@ extern "C" ANSULT_API int UploadMultiPartDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS
return guard.get()->UploadMultipartData(bucketName, fileDataPath, uploadedFilePath, fileSize) ? 1 : 0;
} catch (...) { return 0; }
}
extern "C" ANSULT_API int UploadJpegImageANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, std::string& uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || jpeg_string == nullptr || bufferLength <= 0 || fileName == nullptr) return 0;
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
@@ -1929,7 +2052,6 @@ extern "C" ANSULT_API int UploadJpegImageANSAWSHandle_CPP(ANSCENTER::ANSAWSS3**
return guard.get()->UploadJpegImage(bucketName, jpeg_string, bufferLength, fileName, uploadedFilePath) ? 1 : 0;
} catch (...) { return 0; }
}
extern "C" ANSULT_API int UploadPrefixBinaryDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* binaryDataPath, const char* objectName, std::string& uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || binaryDataPath == nullptr || objectName == nullptr) return 0;
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
@@ -1938,7 +2060,6 @@ extern "C" ANSULT_API int UploadPrefixBinaryDataANSAWSHandle_CPP(ANSCENTER::ANSA
return guard.get()->UploadPrefixBinaryData(bucketName, prefix, binaryDataPath, objectName, uploadedFilePath) ? 1 : 0;
} catch (...) { return 0; }
}
extern "C" ANSULT_API int UploadPrefixMultiPartDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* fileDataPath, const char* objectName, int fileSize, std::string& uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || fileDataPath == nullptr || fileSize <= 0) return 0;
AWSHandleGuard guard(AcquireAWSHandle(*Handle));
@@ -1947,7 +2068,6 @@ extern "C" ANSULT_API int UploadPrefixMultiPartDataANSAWSHandle_CPP(ANSCENTER::A
return guard.get()->UploadPrefixMultipartData(bucketName, prefix, fileDataPath, objectName, uploadedFilePath, fileSize) ? 1 : 0;
} catch (...) { return 0; }
}
extern "C" ANSULT_API int UploadPrefixJpegImageANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, std::string& uploadedFilePath) {
if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || jpeg_string == nullptr || bufferLength <= 0 || fileName == nullptr) return 0;
AWSHandleGuard guard(AcquireAWSHandle(*Handle));

View File

@@ -170,7 +170,7 @@ int ANSAWSTest() {
return 1;
}
// Upload a jpeg image to bucket
std::string jpegFilePath = "E:\\Programs\\DemoAssets\\NV\\Test.jpg";
std::string jpegFilePath = "C:\\ProgramData\\ANSCENTER\\Shared\\Test.jpg";
std::string objectName = "test/test.jpg";
std::string uploadedUrl;