03-17-2015 02:16 PM - edited 03-17-2015 02:17 PM
Hi All,
Thanks for any support you can give. I am a pretty good C programmer and a mediocre LabVIEW programmer so keep that in mind. I have created a simple VI and C API to demonstrate the issue (all source included, built with Visual Studio 2005)
I am wrapping a C API DLL with LabVIEW as you can probably guess. There is a tricky portion of the C API that requires some pointer math to work. This is the part that is leaking handles (seen in Task Manager, below. Over 2 million handles and growing). The leak is only seen in LabVIEW and not when calling my API directly from C code (example code for the C example is also attached)
)
The VI that demonstrates the issue is quite simple.
LabVIEW is moving the buffer returned from "Card Seq Blk Rd.vi" around and causing "Card Seq Find Next429Ex.vi" to fail. This is my first hint something is awry - I don't believe my buffer should move each time. I actually do some pointer math in my real API to fix this up but did not bother with this example. The issue is the same regardless of the fix-up.
The attached zip contains my example VI, as well as the C Source code for the API and a C Example using the API (that does not leak handles).
I'm sure I'm not grasping a relatively simple LabVIEW memory management concept here, but again mediocre so be gentle.
Regards and thanks in advance,
Brad
Solved! Go to Solution.
03-17-2015 03:05 PM - edited 03-17-2015 03:06 PM
LabVIEW has its own memory management. An array is stored in a handle but only guaranteed to remain at the same location for the duration of your Call Library Node call. So you can not pass in an array handle and store it's pointer somewhere else and hope to access that pointer after your C function has returned. It's that simple. All you can assume about LabVIEW arrays and strings is, that they stay in memory at the actual location until your C function returns. After that LabVIEW reserves the right to move, reuse or simoly deallocate the memory as it pleases, accordinig to rather involved algorithmes that are meant to reduce both memory allocations as well as copying of memory areas.
03-17-2015 03:09 PM
I appreciate the feedback.
Based on your description of LabVIEW memory management, I assume what is happening is LabVIEW is never releasing the array handle and allocates a new one each time the Call Library Node is accessed. What have I done wrong to prevent it releasing the old handles?
03-17-2015 03:32 PM
Try making one small change - force LabVIEW to reuse the same buffer each time, by allocating it in a shift register and moving the initialization outside the loop, as shown below. On my machine this results in completely stable memory allocation (at least over a short run). This also matches what your example C application does - it allocates one buffer before the loop starts. Your LabVIEW code, on the other hand, allocates a new array on each iteration (with Initialize Array). Normally I would have expected LabVIEW to reuse that array, but perhaps because it's being passed to a DLL (where it could still be in use, LabVIEW can't tell), maybe it isn't being reused.
03-17-2015 03:36 PM - edited 03-17-2015 03:40 PM
@btbradj wrote:
I appreciate the feedback.
Based on your description of LabVIEW memory management, I assume what is happening is LabVIEW is never releasing the array handle and allocates a new one each time the Call Library Node is accessed. What have I done wrong to prevent it releasing the old handles?
No LabVIEW is smarter than that. It will reuse memory whenever possible. But that means that your pointer may contain something completely different than the original array.
But your Task manager picture is not tracking LabVIEW handles. LabVIEW manages them itself. What is probably happening is that your pointer magic destroys memory that is long ago used for something else than your original array, overwriting windows handles (objects) that LabVIEW was using and in that way leaking them.
And while nathans suggestion may work, it is not a solution but only a workaround. It's a very fragile thing that can break anytime as you edit your diagram and add a seemingly harmless node to the array that may resize the array in any way.
03-17-2015 04:44 PM
I did add the shift register and did not see a difference. Again, this is not a memory allocation issue but rather a handle count issue. Task Manager hides the handle count column by default so it must be added (View > Select Columns...)
03-17-2015 05:16 PM - edited 03-17-2015 05:18 PM
I didn't do the shift register the same as you did, so that wasn't a fair test shown in my last post. I also found a small wiring issue in my original example (need to route function return, not return_value to Seq Find Init).
Even still, I am seeing the handle leak. I've even moved the sfinfo cluster into a shift register in case that helps.
03-17-2015 05:27 PM
As I already said, your trying to fiix a broken system with bandaids. It may seem to solve your problem but will break again as soon as you do some modifcations to the code. An absolute nightmare for maintainable code and whoever is going to work on that code later on is going to wish you to the moon or worse.
Don't do this, but use proper memory management rules. LabVIEW handles can not be stored anywhere to be accessed by external code later on. They will usually be gone, changed, reused or whatever by then. If they don't you are simply unlucky and will run into troubles as soon as you have deployed the system to a mining post in the arctic where you have no remote access to fix things.
03-17-2015 05:34 PM
What you are basically saying is that a C API cannot ever rely on a LabVIEW allocated buffer ever staying in the same place between DLL calls. This concept is so foreign to me (a C programmer) that it sounds absurd.
I do not even modify buffer contents from C, I'm simply using a C function to update a bookkeeping cluster (sfinfo) that tells me where I'm at in the buffer LabVIEW allocated. In other words, other than copying the data into the LabVIEW buffer in SeqBlkRd (which you said I could do earlier as the memory will stay put through the Call Library Function), I am not modifying the buffer in any way. I'm simply relying on it to stay put.
Is there any documentation that I could read that will confirm what you are saying? Again, this memory model is astounding to me and I don't see how there is any hope of interacting with many C APIs from LabVIEW if this is true.
Appreciate the help.
03-17-2015 05:44 PM - edited 03-17-2015 06:01 PM
Well there is certainly hope. I have interfaced to many C APIs very successfully. But asychronous memory access is going to be a pita in C too. Try to do what you try to do in a multithreading C application and all hell breaks loose. LabVIEW being inherently multithreading simply has to have a strict memory model to even be able to let you write an application that behaves always as intended.
It does all the difficult stuff for you and doesn't bother you with things you would have to break your brain about in C(++). At the cost of making asynchonous C APIs more difficult. Of course if you only write single threaded C code, this all may seem way to complicated and useless, but it isn't.