LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

When to call DSDisposeHandle when you have a DLL function acting as a repeating DS dynamic data source?

Solved!
Go to solution

Hi 

I have a DLL function acting as a dynamic data source within a LabVIEW application (see attachment) - I am using DSNewHandle to dynamically allocate 2D array handle storage via the LV memory manager for the arbitrary size data arrays to be passed into the application. I had assumed that these memory blocks would (magically) be disposed when appropriate by the LabVIEW built in processing blocks that sit downstream, however this is not the case and the LabVIEW system memory usage keeps increasing until LabVIEW environment is exited (not simply if just the offending application is stopped or closed).

 

So my question is how and where to mop up the used up array buffers in the application processing chain and how do I know when the buffers are actually exhausted and not being reused downstream (for instance the two 2D arrays are first combined into a complex 2D array by the re+im to complex labview operator - is the data memory out of this stage totally different from the inputs or is it some modified version of the input arrays - if totally different and the input 2D arrays are not wired into any other blocks why does the operator not dispose the input data stores?)

 

Perhaps Im going about this the wrong way having the DLL data source dynamically allocate the data space arrays? Any advice would be gratefully received

Regards

Steve

0 Kudos
Message 1 of 5
(2,909 Views)

 

 

Your C function will probably look similar to this:

 

struct {
    int32 numElm;
	double elm[0];
} My1DArrRec, **My1DArrHdl;

int32 DataGetFloatDll(My1DArrHdl *arr, int16 isBigEndian, int16 reverseChannels, int16 normalize);

LabVIEW will in most cases (except during the first iteration) pass in a valid array handle in *arr and you can not simply allocate a new handle and overwrite that handle that LabVIEW passes in, as that leaks the memory of that handle. If you work with LabVIEW handles you have to follow the contract of them and that is that whoever receives a valid handle (indicated by a non NULL handle value gets to manage it, either by reusing it and passing it further (in your case back to the LabVIEW diagram) or deallocate it.

 

NOTE: This contract is not valid for the diagram level as LabVIEW manages the memory for you there, but in C code you are not anymore on diagram level and are fully responsible to adhere to this management model of LabVIEW data.

 

So you would really have to do something like:

 

if (*arr)
{
    err = DSSetHandleSize(*arr, sizeof(int32) + newSize);
}
else
{
    *arr = (My1DArrHdl)DSNewHandleClr(sizeof(int32) + newSize);
    if (!*arr)
        err = mFullErr;
}
if (err)
    return err;

// Fill in the array data

 

However LabVIEW has a convinient function for this that handles all the hassle about checking for NULL and either resizing it or allocating a new handle. It also takes care about adjusting for the extra int32 numElm value automatically as well as possible padding on some CPU architectures that require for instance doubles to be aligned to their natural size in order to not invoke a terrible performance penalty:

 

err = NumericArrayResize(fD, 1, (UHandle*)arr, newSize);
if (err)
    return err;

// Fill in the array data

 

 

Rolf Kalbermatter
My Blog
0 Kudos
Message 2 of 5
(2,900 Views)

Think you have got me sorted - but a little more detail from me - the dynamically allocated memory is for the three bottom parameters (pointers to array handle) in the call library configuration I attached - as you can see there is nothing wired to the input side of these parameters but labview helpfully calls the library function with valid pointer parameters with null values so I assumed that these would be managed in the block diagram:

 

so the input parameter looks like this:

DLLEXPORT int32_t DataGetFloatDll(
...

Array2DFloat ***p_samples_2d_i,

...
)

 

and the pointer to handle gets populated with the newly created handle thus: 

...

if ( p_samples_2d_i )
{
*p_samples_2d_i = (Array2DFloat **) DSNewHandle( ( sizeof(int32_t) * 2 ) + ( sizeof(float) * channel_count * sample_count ) );
(**p_samples_2d_i)->Rows = channel_count;
(**p_samples_2d_i)->Columns = sample_count;
}

...

 

The above giving the memory management issue I described in the original post. Will experiment and post back if solved from your help

thanks

steve

 

0 Kudos
Message 3 of 5
(2,882 Views)
Solution
Accepted by topic author Steve_Mowbray

Then instead of doing:

 

 

DLLEXPORT int32_t DataGetFloatDll(... , Array2DFloat ***p_samples_2d_i, ...)
{
    ...

    if ( p_samples_2d_i )
    {
// *p_samples_2d_i can be non NULL, because of performance optimization where LabVIEW will pass in the same handle
// that you returned in a previous call from this function, unless some other LabVIEW diagram took ownership of the handle.
// Your C code can't really take ownership of the handle, it owns the handle for the duration of the function call and either
// has to pass it back or deallocate it (and if you deallocate it you better NULL out the handle before returning from the
// function or return a different newly allocated handle. A NULL handle for an array is valid and treated as empty array. *p_samples_2d_i = (Array2DFloat **) DSNewHandle( ( sizeof(int32_t) * 2 ) + ( sizeof(float) * channel_count * sample_count ) );

// Generally you should first try to insert the data into the array before adjusting the size
// the most safe would be to adjust the size after filling in the data if the array gets bigger in respect to the passed in array
// and do the opposite if the adjusted handle happened to get smaller. This is only really important though if your C code can
// bail out of the code path because of error conditions between adjusting the handle size and adjusting the array sizes.
// You should definitely avoid to return from this function with the array dimensions indicating a bigger size than what the
// handle really is allocated for, which can happen if the array was resized to a smaller size and you then return because of errors
// before adjusting the dimension sizes in the array.
........
(**p_samples_2d_i)->Rows = channel_count; (**p_samples_2d_i)->Columns = sample_count; } ...
}

 you should be doing:

 

DLLEXPORT int32_t DataGetFloatDll(... , Array2DFloat ***p_samples_2d_i, ...)
{
    ...

MgErr err = NumericArrayResize(fS /* array of singles */, 2 /* number of dims */, (UHandle*)p_samples_2d_i, channel_count * sample_count); if (!err) {
// Fill in the data somehow .....

// Adjust the dimension sizes
(**p_samples_2d_i)->Rows = channel_count; (**p_samples_2d_i)->Columns = sample_count; } ...
}
Rolf Kalbermatter
My Blog
0 Kudos
Message 4 of 5
(2,874 Views)
Hi Rolf Labview application memory usage now behaves using your suggestions - many thanks I am very grateful for your informative and effective advice and the speedy response. Many regards Steve
0 Kudos
Message 5 of 5
(2,851 Views)