LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

CIN readback of an array given its pointer -- possible without copying?

In the process of interfacing with a framegrabber library, I run into a problem which I solved with the help of CIN snippets. For generic C programming, this library assumes that

a) one malloc()s an array,
b) one passes the pointer to this array to the grabbing routine, which fills the associated memory
c) one reads back the array knowing its original pointer.

I saw on the forum that this is a recurrent question, and found useful leads to create CINs which do the jobs of a) and c). So far so good, all works. However, it occurs to me that in order to do c) I'm doing an unnecessary initialization, and a dummy memory copy. At video speed, this might be a penalty. I'm trying to avoid that, but manage only to get access violations, garbage reads and similar "pointer" errors. Is there something basic I do not understand about CIN array handles, perhaps?

The working code I'm using for c) is:


#include "extcode.h"

typedef struct {
int32 dimSize;
uInt8 DataByte[1];
} TD1;
typedef TD1 **TD1Hdl;

CIN MgErr CINRun(uInt32 *Pointer, TD1Hdl OutArr);
CIN MgErr CINRun(uInt32 *Pointer, TD1Hdl OutArr) {
int32 i;
uInt8 *u8Pointer; // Create a temporary pointer
u8Pointer = *Pointer; // Set temporary pointer to the value specified by the input Pointer
for (i=0; i< (*OutArr)->dimSize; i++) //Loop nr of bytes in array
{
(*OutArr)->DataByte[i]=(uInt8) *u8Pointer; //Read value of pointer
u8Pointer = u8Pointer+1; //Increase pointer value
}
return noErr;
}

where the CIN node receives an initialized labview array of prescribed size as input for OutArr. I thought this can simply be replaced by:


typedef struct {
int32 dimSize;
uInt8 *DataByte;
} TD1;
typedef TD1 **TD1Hdl;

MgErr CINRun(uInt32 *Pointer, uInt32 *NrOfBytes, TD1Hdl OutArr);
MgErr CINRun(uInt32 *Pointer, uInt32 *NrOfBytes, TD1Hdl OutArr)
{
(*OutArr)->dimSize= (int32) *NrOfBytes;
(*OutArr)->DataByte= *Pointer;
return noErr;
}


Indeed, if I DbgPrintf() individual elements of (*OutArr)->DataByte[i] before the return, I get the correct values. Moreover, the array returned by the node has the correct size in Labview. However, the content is garbage and the crash for any respectable size is almost sure, as if the pointer was completely wrong. Why?

Thanks in advance for any advice,
Enrico
0 Kudos
Message 1 of 25
(4,702 Views)
Hello Enrico,

It sounds like you have done your homework on this. I'm sure you have probably already checked this, but just in case you haven't there is a great pdf document on using external code in LabVIEW.

http://digital.ni.com/manuals.nsf/websearch/4f1447f7cd83d6d88625690d00637ced

You said if you dbgprintf() individual elements you do get the correct results. Can you give us more information on the data you see in LabVIEW and what type of crash you are seeing. Does it crash LabVIEW or does it crash your whole system, etc...

Regards,
Chris J
0 Kudos
Message 2 of 25
(4,678 Views)
Thanks, I have been perusing the External Code Manual, but I wasn't able to get through the problem.

Since you ask for details about crashes, I'll be verbose:
I tried the game on linux-i586 and on windows xp, both with labview 7.1. At the moment I have the linux stuff at hand, so I can speak for it. The neat thing in linux is that only labview crashes and not the system, I can't say the same about windows. Also, the build process in linux is much easier, I need just a shell script to remake all the *lsb files involved. In windows I have separate VC++6 projects for each CIN. In the "long" way, which uses the perhaps unnecessary copy, anyway, all works on both platform.

I attach here a minimal test suite, for the linux case to save space. It includes a vi called Master.vi which uses 4 sub vis, respectively for 1) allocating an array, 2) writing data into it, 3) reading back the data and 4) finally freeing the array. These 4 subvis are just calls to CINs. The problem is seen by opening the block diagram of Master.vi, and replacing the 3rd subVi, pointerReadArray.vi, which calls the first version of the code, with WrongpointerReadArray.vi, which uses the second. One has just to write whichever data in the input array of Master.vi, run it, and check that the output is identical to the input.
All goes well with pointerReadArray.vi; with WrongpointerReadArray.vi, if labview doesn't crash at once, it usually goes in an unstable state. Repeated runs of Master.vi lead to a sure crash; the longer the input array the higher the probability.

Typically the message after a crash is something like:

LabVIEW caught fatal signal
7.1.1 - Received SIGSEGV
Reason: address not mapped to object
Attempt to reference address: 0x24

[3] Segmentation fault labview Master.vi WrongpointerReadArray.vi


Also, this crash message occurs frequently, typically after having run Master.vi with WrongpointerReadArray.vi once, having gone to the block diagram of the latter, having right-button chosen "Reload Code Resource", and trying to save the modified vi.

$ Insane object at FPHP+1B48 in "WrongpointerReadArray.vi": {dsitem } (0x400): FrontPanelDataController (DCO )
Insane object at FPHP+58 in "WrongpointerReadArray.vi": {dsitem } (0x400): Panel (FPSC)
Fatal Internal Error : "fpsane.cpp", line 321
LabVIEW version 7.1.1
You will lose any unsaved work. For assistance in resolving this problem, please relaunch LabVIEW, or contact National Instruments.


Thanks once more for any possible hint,
Enrico
0 Kudos
Message 3 of 25
(4,672 Views)


@enrico Segre wrote:
I thought this can simply be replaced by:


typedef struct {
int32 dimSize;
uInt8 *DataByte;
} TD1;
typedef TD1 **TD1Hdl;

MgErr CINRun(uInt32 *Pointer, uInt32 *NrOfBytes, TD1Hdl OutArr);
MgErr CINRun(uInt32 *Pointer, uInt32 *NrOfBytes, TD1Hdl OutArr)
{
(*OutArr)->dimSize= (int32) *NrOfBytes;
(*OutArr)->DataByte= *Pointer;
return noErr;
}


Indeed, if I DbgPrintf() individual elements of (*OutArr)->DataByte[i] before the return, I get the correct values. Moreover, the array returned by the node has the correct size in Labview. However, the content is garbage and the crash for any respectable size is almost sure, as if the pointer was completely wrong. Why?

Thanks in advance for any advice,
Enrico




You can't do that. A LabVIEW handle is a special pointer to pointer where LabVIEW keeps track of what pointer it allocated for which handle. Replacing behind LabVIEWs back a pointer in a handle by another pointer not originally allocated as handle by LabVIEW itself, LabVIEW is sooner or later bound to crash.

What you would need to do somehow is:

struct {
int32 size;
uInt8 data[0];
} **LVArrayHdl;

MgErr GetData(LVArrayHdl hdl)
{
MgErr err;

len = CalculateRequiredSize();

err = NumericArrayResize(uB, 1, &hdl, len);
if (err)
return err;

err = GetDataFromFrameGrabber(something, (*hdl)->data, len);
(*hdl)->size = len;

return TranslateFrameGrabberError(err);
}

No need to do a CIN. A DLL will work too!

Rolf Kalbermatter
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 4 of 25
(4,665 Views)
Thanks Rolf, I'm trying a solution along your lines, but it doesn't seem to fit yet.

>You can't do that. A LabVIEW handle is a special pointer to pointer where LabVIEW keeps track of what pointer it allocated for >which handle. Replacing behind LabVIEWs back a pointer in a handle by another pointer not originally allocated as handle by >LabVIEW itself, LabVIEW is sooner or later bound to crash.

First of all, what is "behind LabVIEWs back"? The pointer is originally allocated by a CIN (Alloc2pointer.c/.vi in the example I submitted) which contains

*arrpoint=DSNewPtr(*size);

What else does labview need to know beyond that? How does the CIN programmer control it?

I can still try the homework of transforming this Alloc2pointer.vi into something which makes explicit use of the LVArrayHdl structure (==TD1Hdl of my example, not so?) and returns it, to see if it makes any difference.

Plainly adding a NumericArrayResize() either before or after the pointer reassignment, like in

MgErr CINRun(uInt32 *Pointer, uInt32 *NrOfBytes, TD1Hdl OutArr)
{
MgErr err;
(*OutArr)->dimSize= (int32) *NrOfBytes;
(*OutArr)->DataByte= *Pointer;
err = NumericArrayResize(uB, 1, &OutArr, *NrOfBytes);
return err;
}

still doesn't help. Again, am I missing something?

Then, side remarks:

>What you would need to do somehow is:
>
> len = CalculateRequiredSize();
> err = NumericArrayResize(uB, 1, &hdl, len);
> err = GetDataFromFrameGrabber(something, (*hdl)->data, len);

Dataflow considerations impose that I split the process in three different routines. Hence my question.

>No need to do a CIN. A DLL will work too!

It might, but CINs look to me more cross-platform portable that DLLs. Besides, building the .lsbs under linux is at snap, or at least I figured out how to do it.

Enrico
0 Kudos
Message 5 of 25
(4,654 Views)
Just believe me that a handle is not a simple pointer inside a pointer. LabVIEW does something along these lines for handle allocation:

struct {
UHandle handle;
int32 size;
int32 flags;
} hdl_header, *phdl_header;

UHandle DSNewHandle(int32 size)
{
char **hdl = malloc(sizeof(char*));
if (hdl)
{
phdl_header ptr = malloc(size + sizeof(hdl_header));

if (ptr)
{
ptr->handle = hdl;
ptr->size = size;
ptr->flags = some_flags;
*hdl = (char*)ptr + sizeof(hdl_header);

add_heap_manager(DSHEAP, hdl);
}
else
{
free(hdl);
hdl = NULL;
}
}
return hdl;
}

In subsequent calls to memory manager functions it does use the information in the header such as the size to decide if and how to reallocate memory. Also all handles are added to some internal heap management but pointers not. This is because LabVIEW considers handles to be resizeable but pointers are always fixed size (you don't have a DSReallocPtr() function).

Swapping pointers inside the handle behind LabVIEWs back does create inconsistencies in LabVIEWs memory heap management and will fail sooner or later. This whole memory manager business inside of LabVIEW seems all a little bit overkill nowadays with 32bit OSes and quite good memory management in the OS itself but back in the old days LabVIEW had to run on Windows 3.1 and Mac OS for 68k and there such a flexible and controllable memory management was absolutely mandatory to be able to create a software program like LabVIEW. Actually memory management on the Mac very much resembled the interface LabVIEW does provide. And changing it now would break all sorts of routines all over the place and is therefore no option.

Depending on your DLL you have some options. If you have a synchronous call to retrieve the data, just make the data large enough on the LabVIEW diagram and pass its pointer directly to the DLL function using the Call Library Node.

Otherwise (asynchronous operation) you will have to be a little creative. You can configure a Call Library Node to pass the pointer to a handle to a function. Inside this function you can resize that handle to the size you are expecting to be necessary and store it in some place your asynchronous capture routine can get at it. To keep LabVIEW happy create your own empty handle and pass that back to the diagram in the same parameter you received your input handle in. In the next iteration you swap the incoming handle with the now hopefully filled in handle. You have to make sure that you do not pass back the handle, which is at the moment filled by the asynchronous function though, as that would certainly crash very fast. You would probably use semaphores for this. Once your DLL function returns, LabVIEW is free to do with the returned handle whatever it likes and that always means reusing it for other means or simply deallocating it and that would be fatal if your asynchronous function is still trying to copy data into it.

Rolf Kalbermatter
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 6 of 25
(4,652 Views)
> Just believe me that a handle is not a simple pointer inside a pointer. LabVIEW does something along these lines for handle allocation:

hmm, I see. Well, be it.

Then I thought: let's allocate an array of a proper size in labview, pass it to a CIN node which just returns its pointer by decomposing its handle. Pass this pointer to the grabber DDL. So far so good. But the problem still arises when I try to read the result. If I just wire the array wire through a sequence, and use the this wire after I wrote to the pointed memory, I find I still have the initial values (they are buffered by lv?); If I devise a CIN which takes as input the original array wire (handle), and outputs the result array at the same argument position, I discover (Tools/Advanced/Show Buffer allocation) that new buffers are allocated there, i.e. the handle is not preserved. What can I do?

I'm leaving out the synchronization issues for the moment; in principle the grabber routines can be of either kind, it is clear that race conditions will have to be solved properly.

Still experimenting...

Enrico
0 Kudos
Message 7 of 25
(4,640 Views)
I have two comments. However, please bear in mind that I haven't seen your VIs, only your CIN code (computer on network doesn't have LabVIEW, computer with LabVIEW doesn't have network... argh).

It's not clear to me why you feel you need a CIN/DLL in the first place. Don't get me wrong, I love calling external code from LabVIEW. You might say I wrote the book on it. However, I find that, precisely because of issues like this, it's generally easiest to do as little as possible in external code. I may have misunderstood something, but why not simply allocate the memory for the framegrabber by using LabVIEW's initialize array primitive? Then, when the data is returned, simply index into the array to find the data of interest.



Enrico Segre wrote:It might, but CINs look to me more cross-platform portable that DLLs. Besides, building the .lsbs under linux is at snap, or at least I figured out how to do it.

Enrico




A CIN is not any more or less cross-platform compatible than a DLL/Shared Library/Shared Object. On most platforms--come to think of it, all platforms, these days--you have to build a DLL in the process of building the lsb. The only advantage, and a dubious one at that, is that the CIN code is stored inside the VI, whereas a DLL is not. It's a dubious advantage because LabVIEW has to extract the CIN code to an external file in order to run it anyway, which eats time on every run. Might as well start with the code already external.
0 Kudos
Message 8 of 25
(4,634 Views)
>but why not simply allocate the memory for the framegrabber by using LabVIEW's initialize array primitive? Then, when the data is returned, simply index into the array to find the data of interest.

hehe, that's the point, the data is NOT returned, the user is supposed to get it back from memory knowing the pointer, which was previously allocated. The DLL routine which writes into this area doesn't use this pointer, not even as input. (it uses a proprietary handle instead, the handle is created by a preceding DLL routine, the structure referenced by this handle is undocumented... all details which I omitted in first instance for clarity).


>A CIN is not any more or less cross-platform compatible than a DLL/Shared Library/Shared Object. On most platforms--come to think of it, all platforms, these days--you have to build a DLL in the process of building the lsb. The only advantage, and a dubious one at that, is that the CIN code is stored inside the VI, whereas a DLL is not. It's a dubious advantage because LabVIEW has to extract the CIN code to an external file in order to run it anyway, which eats time on every run. Might as well start with the code already external.

here I can agree. My point is that it was obvious for me to write a csh script for building the CIN under linux, and the C snippet is portable, forgetting about BOOL Winapi ... which is yes platform dependent.

Enrico
0 Kudos
Message 9 of 25
(4,633 Views)
ps: here is an older thread reporting the same issue. I tried the solution of wiring through sequences, at no avail.

http://forums.ni.com/ni/board/message?board.id=170&message.id=49251&requireLogin=False
0 Kudos
Message 10 of 25
(4,630 Views)