LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Pointers to Pointers, MoveBlock, and Stability

I have lately been working with a Call Library Function Node that returns values through a void pointer pointer - not a pointer to an array, but a pointer to a pointer to an array.  I have thus set up the CLN to treat this as a pointer to an unsigned 32-bit integer, and then use this integer as an input for MoveBlock, precisely as in this example.

This actually works pretty well - or at least, it appears to.  Unfortunately, working with the program tends to cause LabVIEW to become very unstable, and it usually crashes soon after I run the program.  I suspect memory corruption.  (Using DSNewPtr and DSDisposePtr elsewhere in the code probably doesn't help much either.)

Is there something I can do to make using MoveBlock less temperamental?  Maybe I can make a copy of the array and then use MoveBlock to put it back in its original location?  Is there something relatively simple I could put into a Call Interface Node that would call the function and return a pointer instead of a pointer to a pointer?

 
0 Kudos
Message 1 of 19
(9,962 Views)
Oh, buckets... That's just an Array Handle Pointer, isn't it?  Methinks I may proceed to pound my head vigorously.
0 Kudos
Message 2 of 19
(9,953 Views)
 

Nope, passing an Array Handle (or an Array Handle Pointer) just causes LabVIEW to crash, regardless of whether or not I wire up a larger-than-necessary pre-allocated array.

Is this in fact not what I'm looking for?  This article speaks ominously about "LabVIEW array handles", as if they're something different.  Or is the problem that the data I'm looking for is actually pre-allocated by a different function?

 

Message Edited by kehander on 10-19-2007 05:00 PM

0 Kudos
Message 3 of 19
(9,942 Views)
Any ideas?  Can someone at least help me get started with making a CIN?  (A C++ function that takes an array handle as an argument and returns the array would be three lines long, no?)
0 Kudos
Message 4 of 19
(9,915 Views)
Hi Kehander,
Could you please post the segment of the code that's causing the instability you are speaking of?
http://zone.ni.com/reference/en-XX/help/371361B-01/lvexcode/moveblock/
Note that Size is in bytes.

Yi Y.
Applications Engineer
National Instruments
http://www.ni.com/support
0 Kudos
Message 5 of 19
(9,887 Views)


@kehander wrote:
Oh, buckets... That's just an Array Handle Pointer, isn't it?  Methinks I may proceed to pound my head vigorously.


First, MoveBlock in itself works absolutely perfect but only IF and only IF provided with the correct parameters. There are no belts and suspenders around that function, like LabVIEW does so conviniently in normal diagram code, so one byte of and trouble start.

Second: A LabVIEW handle is a pointer to a pointer but the opposite is absolutely not true. A LabVIEW array (or string) handle prepends the data area with a int32 that tells it how many array elements do follow. And LabVIEW being careful to never mess this value up, does expect you to do the same. So this int better represents the number of elements that follow (can be less than the handle was allocated space for, but never even one single byte to much)! To make matters even more "pointerless", the master pointer (the first pointer) simply points to the second one, but the memory allocated "knows" to which master pointer it was allocated. This is a LabVIEW specific implementation for its handles and messing with that will turn LabVIEW belly up at some point. So LabVIEW handles are never a good idea to be passed to other external C code, unlees this code has been designed specifically to use these datatypes and also makes proper use of the according LabVIEW memory manager functions (which makes that external code useless for non LabVIEW hosts).

Your problem most probably is not in the MoveBlock function itself if you have done it like in the example but in what you do on that pointer you get from the MoveBlock function. Somewhere someone has allocated that pointer with some space and if you are not careful to never access any memory outside of that space (even just reading can be already fatal, but writing will mess up things for sure) you get sooner or later a crash or worse corrupted VIs and if you then happen to safe them you may be never able to load them again.

You might also run into threading issues. You call a function and get that double pointer back, turn it into a pointer and do something on it but some other part of the library you called has already decided that the pointer is not necessary anymore and deallocates it. You must be very careful in dataflow dependency and make sure that the function that could cause deallocation, is never ever called before you are done with your pointer completely. This sometimes requires artifical datadependancy in the diagram to make sure that some operation has finished before the function that could deallocate (or even reallocate) that pointer.

Rolf Kalbermatter

Message Edited by rolfk on 10-23-2007 08:45 AM

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 6 of 19
(9,884 Views)
Thanks for replying.  I think I actually found a way to do what I want using some less-tricky functions, but I'd still like to get to the bottom of this in case I need the other code in the future.

I guess what you're saying is that I'm stuck using MoveBlock to deal with this pointer-to-a-pointer since there's no way I can get the CLN to pass it properly on its own.

I always know the exact size of the block I want to move (and should be able to move).  Let me emphasize that the program generally appears to function perfectly, and LabVIEW doesn't necessarily even crash right away once it finishes running.  I had gotten the impression that MoveBlock was at fault since I had just read in another thread somewhere that MoveBlock does not deallocate RAM once it is finished (or something to that effect) so that there's no safe way to call it repeatedly (which is what I'm doing).

I suppose I could try to put together some presentable LabVIEW code from what I've done so far, but really I'm just trying to mimic the functionality of this C code:

buffer_size = frame_size * 3;
buffer = (uns16*)malloc( buffer_size );

/* Start the acquisition */
printf( "Collecting %i Frames\n", numberframes );
pl_exp_start_cont(hCam, buffer, buffer_size );

/* ACQUISITION LOOP */
while( numberframes ) {
    /* wait for data or error */
    while( pl_exp_check_cont_status( hCam, &status, &not_needed,&not_needed ) && (status != READOUT_COMPLETE && status != READOUT_FAILED) );
    /* Check Error Codes */
    if( status == READOUT_FAILED ) {
    printf( "Data collection error: %i\n", pl_error_code() );
    break;
    }
    if ( pl_exp_get_latest_frame( hCam, &address )) {
        /* address now points to valid data
        printf( "Center Three Points: %i, %i, %i\n",
        *((uns16*)address + frame_size/sizeof(uns16)/2 - 1),
        *((uns16*)address + frame_size/sizeof(uns16)/2),
        *((uns16*)address + frame_size/sizeof(uns16)/2 + 1) );
        numberframes--;
        printf( "Remaining Frames %i\n", numberframes );
    }
} /* End while */

So "buffer" holds three frames' worth of data that is constantly being overwritten automatically at a fairly high rate (hundreds of milliseconds), and pl_exp_get_latest_frame returns the address of the most recent frame within "buffer".  Thus, "&address" is the pointer to a void pointer; the address is an integral multiple (in this case 0, 1, or 2) of "frame_size" away from the start of "buffer".

The program seems like it's slightly less likely to cause instability if I make "buffer" much larger; I'm thinking bad things are happening when my code tries to read from the same section of the buffer that
is being written to, or something along those lines.  The memory inside "buffer" should of course not be deallocated until the program is finished.  Perhaps there is some way to use MoveBlock that avoids altering this memory unnecessarily?

Message Edited by kehander on 10-23-2007 10:34 AM

0 Kudos
Message 7 of 19
(9,863 Views)


@rolfk wrote:

Your problem most probably is not in the MoveBlock function itself if you have done it like in the example but in what you do on that pointer you get from the MoveBlock function. Somewhere someone has allocated that pointer with some space and if you are not careful to never access any memory outside of that space (even just reading can be already fatal, but writing will mess up things for sure) you get sooner or later a crash or worse corrupted VIs and if you then happen to safe them you may be never able to load them again.

You might also run into threading issues. You call a function and get that double pointer back, turn it into a pointer and do something on it but some other part of the library you called has already decided that the pointer is not necessary anymore and deallocates it. You must be very careful in dataflow dependency and make sure that the function that could cause deallocation, is never ever called before you are done with your pointer completely. This sometimes requires artifical datadependancy in the diagram to make sure that some operation has finished before the function that could deallocate (or even reallocate) that pointer.



I find that I am still running into problems, even though I am not dealing with these array handle pointers anymore.  LabVIEW still tends to crash shortly after I use my program, though it seems to crash right away (and fairly consistently) by using "Request deallocation" in the main VI.

Perhaps I am simply unclear on the precise functionality of MoveBlock.  The previously-mentioned example contains a pre-initialized array wired to the left "dst" terminal, the number of bytes in the pre-initialized array (four times the number of elements for a 32-bit integer) wired to the "size" terminal, and the memory address is passed to the "src" terminal as the Value of an unsigned 32-bit integer. 
MoveBlock is supposed to move the number of bytes starting from the address at the "src" terminal into the block of memory that held the pre-initialized array, right?
Does this automatically de-allocate the memory that used to be at the old memory address?
Would any other program be aware that the memory has moved?
Is the data in the pre-initialized array simply overwritten, never to be seen again?
What happens to the memory the next time the VI is run?  Does LabVIEW try to re-use the same chunk of memory?  Do I need to deallocate it first?
Why would deallocation of memory cause problems once the program has finished running, anyway?
0 Kudos
Message 8 of 19
(9,822 Views)

kehander wrote:
Perhaps I am simply unclear on the precise functionality of MoveBlock. 
There is absolutely no magic to MoveBlock. It is in fact just a memcpy() in disguise.
The previously-mentioned example contains a pre-initialized array wired to the left "dst" terminal, the number of bytes in the pre-initialized array (four times the number of elements for a 32-bit integer) wired to the "size" terminal, and the memory address is passed to the "src" terminal as the Value of an unsigned 32-bit integer. 
So you have configured the dst array parameter as array data pointer, right? Not as array handle, pointer to an array handle or something like that?
MoveBlock is supposed to move the number of bytes starting from the address at the "src" terminal into the block of memory that held the pre-initialized array, right?
Right!
Does this automatically de-allocate the memory that used to be at the old memory address?
MoveBlock has no notice of the kind of buffers it is operating on and therefore no way of knowing how to deallocate them.
Would any other program be aware that the memory has moved?
Nope! And it hasn't moved it just got copied over.
Is the data in the pre-initialized array simply overwritten, never to be seen again?
Yes
What happens to the memory the next time the VI is run?  Does LabVIEW try to re-use the same chunk of memory?  Do I need to deallocate it first?
As already said MoveBlock will not allocate nor deallocate any memory. The LabVIEW diagram that contains the Call Library Node however will!! You initialize an array (which allocates the memory for it) and pass it to the Call Library Node. Once the Call Library Node finishes execution the life time of that array will only be determined by how long a wire still goes through your diagram holding that array. If there is no wire coming out of the CLN or it stops at some point, LabVIEW will conclude that the array is not needed anymore and simply deallocate it at some point, but definitely before the loop or VI executes again. It could decide to reuse the memory for the next loop iteration for the same array initialization but there is no guarantee that it will, nor that the address in memory will be even remotely similar.
Why would deallocation of memory cause problems once the program has finished running, anyway?

Let's look at the previous point. You initialize an array and pass it to your DLL function as an array datapointer. Great! Now you do nothing with that array wire anymore and LabVIEW concludes: Hey that array is not needed anymore lets dispose it! Now your DLL goes and happens to try to copy data into the pointer it got previously and boom!! it overwrites some memory that now belongs to something entirely different. It may crash immediately but more likely it simply overwrites some data structures LabVIEW allocated to maintain some internal states and other information. Sooner or later LabVIEW will want to interpret this data again and walk into nirvana.

Theoretically LabVIEW could even reallocate the array in between loop iterations too and in that way it's address gets changed and your DLL then operates on an invalid address again. However LabVIEW uses DS handles for diagram data and they are guranteed to remain fixed in virtual memory address space as long as they are allocated, except when you happen to cause to resize them by inserting or appending elements to an array for instance.

But no matter what you do, once an array wire stops in the LabVIEW diagram or goes into a function that does a resize on that array the underlying memory location will be disposed of and eventually be reallocated, which will be almost always at a different location, so your first DLL function you passed that array pointer to simply will write into wrong memory.

Rolf Kalbermatter

Message Edited by rolfk on 11-01-2007 10:07 AM

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 9 of 19
(9,803 Views)

I am doing the similar to kehander things currently, meaning that I am trying to read the CCD camera data from the buffer using CLN of the Labview. Partially, it works but my Labview makes some things I can not explain for now. May be somebody here had a similar problem or in general have an idea what can be wrong.

 

In C++ I created my dll that in turn uses the functions from the CCD camera library. The part of the code looks like that:

 

uns16 *frame; // Pointer to the requested image

uns32 size; //Size of the buffer

 

int dims[2]={512,512};

rgn_type region={0, 511,1,0,511,1};

 

_declspec(dllexport) int AcquireStabdard(………, uns16 ARRAY[]){

Pl_exp_setup_seq(hCam,1,1,&region, TIMED_MODE, Exposure_Time,&size); // prepares camera to perform a readout

 //&size pointer is filled with the number of bytes of memory needed to buffer the full sequence

 Frame=(uns16*)malloc(size); //allocates memory buffer for data collection

 Pl_exp_start_seq(hCam, frame); //we know the address in memory where our data are

 

Then we use Moveblock(frame, ARRAY, (size_t) size) OR memcpy(ARRAY, frame, (size_t) size) to copy our Data to the ARRAY defined in Labview


}

 

In Labview I used CLN. ARRAY defined as array pointer. I initialized ARRAY with the size, in this example, 512x512 (my camera region is 512x512).

 

Now are the QUESTIONS!

 

1.Why when I run my VI first time I don’t see the data and when I run VI the second time and following times (until it crashes) I see the data???

 

2.Why when I use the small region, i.e. region={0, 99,1,0,99,1} all my ARRAY in Labview is filled with the data correctly and when I use {0, 511,1,0,511,1}only the first rows I filled and the rest are NULLS what shouldn't be. It looks leike the data was copied partially from the correct memory block and partially not.

 

Does anybody has any idea?

0 Kudos
Message 10 of 19
(9,001 Views)