LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Leaking Handles When Calling C API Via "Call Library Function" (example attached)

Solved!
Go to solution

The best thing to do here is to do all the memory allocation within your C code. As a general rule, you should not write code in which one component relies on memory allocated by another component to stay constant - if a component needs a buffer, it should provide functions to allocate and deallocate that memory and provide an opaque reference to it, rather than the calling component needing to know the exact details of what to allocate and know not to modify or reuse that space. This is how most C APIs handle this situation, which is why the problem you're seeing rarely occurs.

 

If you don't want to do that, you can force LabVIEW to leave a buffer in the same place, by directly allocating it as a pointer, using DSNewPtr (from the LabVIEW Memory Manager functions, which you access through a Call Library Function Node with the library name configured as "LabVIEW"). In this case you get a pointer to a buffer of a fixed size (that you set when you call DSNewPtr), and you manage within your LabVIEW code, meaning you must release it when you're done using DSDisposePtr. As far as LabVIEW is concerned, that value is just a pointer-sized integer (either 32- or 64-bits, depending on platform). When you want access to that data in LabVIEW, you copy it onto a LabVIEW wire using MoveBlock (which, despite the name, actually makes a copy), or vice versa to move data from LabVIEW back to that buffer. You can find help on these functions in the LabVIEW help.

 

Sorry for the misleading suggestion about the shift register, I did not look through your code thoroughly enough before posting to fully understand what it's doing.

0 Kudos
Message 11 of 21
(1,648 Views)

But I do wonder why you do what you do. You take a snapshot of the static array to be queried (enumerated) later on. I assume your static array is in reality located in some hardware interface.

 

Instead of copying the snapshot into a LabVIEW array handle you should allocate and manage the memory for that in your DLL yourself. Copying the data into a LabVIEW array only to try to reference that array through asynchronous pointers later on is a bad idea. It would only be useful if you would do the querying on the LabVIEW diagram itself through LabVIEW native nodes that investigate and interprete the data in the byte array or you could pass the entire array to the SeqFindNext429Ex Call Library Node and back out and not store pointers to the current location into that array but offsets instead. Or you allocate that array in your DLL yourself and simply query from there in the way you do now. But the way you do it now you take the worst from both solutions and try to make it work.

 

If you allocate the snapshot buffer yourself you will have to define a moment when it will be deallocated but that is the priice you have to pay. Either managing your own memory and having all control over it, or let LabVIEW do it and live with its rules.

Rolf Kalbermatter
My Blog
0 Kudos
Message 12 of 21
(1,643 Views)

DSNewPtr sounds promising. I understand and agree about your comments with respect to a component managing its own memory. I am sort of stuck shoehorning the wrapper to our existing C API in this case.

 

I will take a look and see if this can resolve the issue.

 

Rolf, I also appreciate your stance on this as this being an inelegant approach. Again, stuck with the API I'm given, here.

0 Kudos
Message 13 of 21
(1,643 Views)

The C API typically bulk copies data from the hardware device (that I'm emulating here) to a buffer provided and managed by the caller. You just give the API the size and pointer and the function takes care of the rest.

 

That's all assuming the caller is C code where things stay put and not something crazy like LabVIEW 😉

0 Kudos
Message 14 of 21
(1,641 Views)

@btbradj wrote:

DSNewPtr sounds promising. I understand and agree about your comments with respect to a component managing its own memory. I am sort of stuck shoehorning the wrapper to our existing C API in this case.

 

I will take a look and see if this can resolve the issue.

 

Rolf, I also appreciate your stance on this as this being an inelegant approach. Again, stuck with the API I'm given, here.


Well imagine Windows would require you to allocate memory to a window structure and pass it to all its windowing functions. That would be a nightmare and pretty much what you do here now.

 

Instead Windows has a function that creates a window and returns you a Handle to that window. This handle is passed to all other windowing functions. So the windows kernel implementing the window is allocating and managing the memory location that contains all information about the window. It is in fact the only instance knowing how the windows memory structure is build. You only get a handle to it and don't have to nor can you bother about its internal structure. When Windows XY adds new features and needs extra storage for that in the window structure it simiply does so. Your application still only sees a handle and nothing really changes for you but Windows can handle the extra feature.

Rolf Kalbermatter
My Blog
0 Kudos
Message 15 of 21
(1,633 Views)

@btbradj wrote:

The C API typically bulk copies data from the hardware device (that I'm emulating here) to a buffer provided and managed by the caller. You just give the API the size and pointer and the function takes care of the rest.

 

That's all assuming the caller is C code where things stay put and not something crazy like LabVIEW 😉

 

Your caller (the DLL) is still C code and that is where you need to do that! What LabVIEW does is not crazy but the only sensible way that allows it to manage it's memory efficiently in a multithreading system. If iit would leave arrays in memory forever there would have to be much more memory copying and memory allocation and you would be the first moaning about how much slower it is compared to your single threaded C routine.

Rolf Kalbermatter
My Blog
0 Kudos
Message 16 of 21
(1,630 Views)

The caller is actually typicallly a C program (where the API is called from). We have a few customers who use LabVIEW but want to support them as well.

 

Again, I can see your viewpoint but we aren't talking about a fancy Windows structure or anything arcane that is being used by the API. It's just a pointer to a buffer with a size defined by the caller. Not different from your plain old sprintf function - which relies on the caller allocating and freeing the buffer. At this point - we are just talking about design philosophies. Our API design works fine in C land, not so great in LabVIEW land.

0 Kudos
Message 17 of 21
(1,625 Views)

It's very different to sprintf(). With sprintf() you the caller provides a buffer for the function to fill in at runtime of THAT function never to be used by any other function behind the scenes UNLESS passed to another function EXPLICITEDLY. What you do is passing a buffer to one function to do some magic on it then call another function to operate on that buffer WITHOUT passing that buffer to this other function explicitedly. That is a fundamental difference.

 

If you would route the LabVIEW array through your SeqFindNext429Ex() function you would be right here, but you don't. Your C API may work when called from C but will only really work when you explicitedly manage that buffer yourself. You can do that in LabVIEW too as Nathan told you but your C API is from a design point of view not very nice and will have problems to be called from every environment that attempts to do automatic memory management such as Visual Basic and many others. It can be done but you have to explicitedly create the pointer for that buffer yourself exactly as you do in C. The difference is here that in LabVIEW you normally don't have to do it, in C you always have to do it, but that is not a flaw of LabVIEW.

 

Your C API could already fail when being called from C++ with automatic C++ pointers that could go away automatically especially if the callers application is multithreading. LabVIEW memory simply is always automatic unless you explicitedly call memory manager functions yourself, while in C++ you have to go to some troubles to create a situation where your API will fail too, but it can perfeclty be made to fail without obvious errors in the code.

 

Please note that sprintf() has other problems eventhough it is not using this magic behind the scene operation you have here. For one it is not thread safe when the same buffer could be used in parallel threads and there is no way for the function to know how large the buffer is, so buffer overflow errors are a serious and real threat when using that function. But calling sprintf() from LabVIEW is a completely possible operation aside from these safety concerns and only requires you to tell LabVIEW in some way to resize the buffer to the necessary length before the Call Library Node is called.

Rolf Kalbermatter
My Blog
0 Kudos
Message 18 of 21
(1,600 Views)

double post and deleted

Rolf Kalbermatter
My Blog
0 Kudos
Message 19 of 21
(1,599 Views)
Solution
Accepted by btbradj

I have resolved the issue by taking care of the buffer bookkeeping directly in LabVIEW rather than in C. I couldn't even rely on DSNewPtr to leave things in the correct place and was still getting handle leaks. Just in case anyone is interested - solution is below:

 

 

SeqFindNext429Fix.jpg

0 Kudos
Message 20 of 21
(1,562 Views)