12-11-2025 12:57 PM
I have been dealing with an issue where calls to our threadsafe variables fail and we get a Non Fatal Runtime Exception. When these happen, the line number is the macro:
DefineThreadSafeScalarVar (BLADE_STATUS, BladeStatusData, 0 /*TSV_ALLOW_UNLIMITED_NESTING*/);
A coworker suggested looking at the macros and see if we could inline the actual code they generate to see where the problem actually is. For above, it turns into this:
BLADE_DATA *GetPointerToBladeData(void)
{
void *_lockedBladeDataPtr = ((void *)0);
CmtGetTSVPtr(_lockedBladeData, &_lockedBladeDataPtr);
if ((0) >= 0 && (_lockedBladeDatacount > (0)))
{
ErrorPrintf("The maximum (%d) nested Thread Safe Variable "
"lock count, you specified, has been exceeded. "
"You have probably not matched a call to %s "
"with a call to %s. Use the \"Up Call Stack\" "
"Run menu item to find the current call to %s",
(int)(0), "GetPointerToBladeData", "ReleasePointerToBladeData", "GetPointerToBladeData");
Breakpoint();
}
_lockedBladeDatacount++;
;
return (BLADE_DATA *)_lockedBladeDataPtr;
}
According to the built-in help, CmtGetTSVPtr() will return an error to indicate if it fails, but the macro does not make use of it. We use LabWindows/CVI 2017 -- has this changed with any later edition?
I am considering making new macros that will use this call and return NULL or something so code can error check them the same way as using malloc() or whatever.
I wanted to post this here in case someone else has already been down this road, and has a better alternative.
Cheers,
12-30-2025 03:59 AM
The CmtGetTSVPtr call inside DefineThreadSafeScalarVar has always been a bit of a mystery. The macro just assumes that it worked and adds one to the nesting counter. As far as that behavior goes, nothing has changed in newer versions of CVI. So if you're getting intermittent lock failures, you won't see the actual return code unless you unwrap the macro like you did.
I've been there and done exactly what you're suggesting: a thin wrapper around CmtGetTSVPtr() that passes on the error and returns NULL so the caller can leave cleanly instead of triggering the non-fatal runtime handler. It makes debugging a lot easier, especially when you have a lot of nested locks in different modules.
The only problem is that you need to make sure your Release... macro matches the changes in nesting, or you'll be chasing phantom overruns forever.