CyAPI.NET bug

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Anonymous
Not applicable
        The C# .NET library that Cypress provides has a    catastrophic bug; it does not pin managed byte[] buffers before sending them to the driver, so when the .NET garbage collector runs and compacts memory, it will move the byte[] buffer while the driver is reading/writing to it. Any .NET application that uses the Cypress C# library will crash, given enough time, even if you're using XferData!   
   
Myself and another poster here, andy, both experienced this problem and found solutions. Andy tried to contact Cypress and tell them about it, but he says they just ignored him. I posted here so that anyone else who runs into the problem might find a solution. We had a nice thread going three weeks ago, but it's not here anymore. I assume Cypress removed it, so I won't be surprised if this gets removed, too.   
   
At least the C++ library and driver work.   
0 Likes
23 Replies
Anonymous
Not applicable
        Yeah,   
   
what was the reason of removing it?   
   
I added that thread to my bookmarks and now wonder why it is unreachable...   
   
I agree with you, there is a bug - when you not pin the byte buffers before pass them to the xfer functions of the lib, the application can freeze or end in mysterious exceptions.   
0 Likes
MR_41
Employee
Employee
First like received
        andrewsobotka,   
I would like to assure you that we did not remove the thread in question from the forum. In the past, I have faced a similar issue with a post in psoc forums, which was then restored by our web team.   
   
peretus4,   
Can you provide me with the link to the thread (from your bookmark) that disappeared so that I can try to locate it?   
   
I would also like to assure you that I will bring up this issue with the concerned team and initiate corrective actions to fix any bug int he C#.NET library.   
0 Likes
Anonymous
Not applicable
        Hi Ganesh,   
   
I have an old response notification email. The URL was   
   
    http://www.cypress.com/forums/...readid=8246&forumid=1    
   
There was more to it than "need to pin buffers". There was an overlapped size allocation issue and an unnecessary ref issue.   
0 Likes
Anonymous
Not applicable
        Sorry guys...I don't really have too much time to go into the issue right now. I also, sadly, don't have anything of use to cut and paste from my correspondences with Microsoft or Cypress. I LOT of my time was wasted chasing this bug (along with loads of help from MS engineers debugging in Windows source) So, I hope we can prevent any others from falling in the same trap...   
   
Basically, the Overlapped structure (wraps the buffers and commands to pass over the fence to unmanaged) gets moved or cleaned by the GC. It seems fairly rare but if you are pushing a lot of data through multiple endpoints, you WILL experience an ExecutionEngineException or NullReferenceException in Mscorlib.dll often enough to render your code "un-depoyable".   
   
My solution: Only use the Cyusb.dll to for initialization and housekeeping. Since I am almost saturating the bandwidth and doing a lot of SW processing, I chose a very asynchronous approach. Here is a VERY terse description (check out the "concurrent affairs" series of articles published in MSDN magazine by Jeffery Richter for details...I based my approach off of his model)   
   
Once you have an OS handle you can use Threadpool.bindhandle()   
-this causes the threadpool to process the callbacks for overlappedIO operations   
   
Implement IAsyncResult to handle the Packing and unPacking of the NativeOverlapped structure and the basic async pattern   
   
Call DeviceIO Control with a callback function. I allocate a byte[] buffer inside my ReadData method and create a callback (using a lambda expression) that uses this same byte buffer to pass along to my DeviceIoControl method. The buffers get pinned using GCHandle.Alloc & get "Pack()"ed along with the commands and callback into the NativeOverlapped structure. All of these are passed into DeviceIoControl.   
   
This allows you to queue up multiple commands to the endpoints with pre-allocated buffers that are automatically called one-at-a-time by the OS and processed (by the callback) by the threadpool as it's scheduled. "Set it and forget it!" as Ron Popeil would say...   
   
Anyway, the gist is: Even the blocking "non asynchronous" XferData functions provided in the API are just blocking wrappers around the asynchronous calls. However, they are not implemented correctly and leave the door wide open for the GC to corrupt the heap.   
   
PM or e-mail me if you would like more info and I'll try to help as best I can.   
0 Likes
Anonymous
Not applicable
        andy - it's not just the Overlapped object which is getting moved by the GC. ANY of the three byte[] buffers (Overlapped, SingleXfer, or the data buffer itself) can be moved by the GC. The error happened very quickly when I was writing 64k data buffers.   
   
You don't need to write unmanaged code to solve the problem. Just use the    GCHandle class to pin all three byte[] buffers before passing them to any of the CyAPI Xfer functions. Cypress could easily pin the three buffers inside XferData before sending them to the driver, and un-pin before returning from XferData. Six lines of code and XferData would no longer cause crashes.   
   
   
GCHandle bufHandle1 = GCHandle.Alloc(data, GCHandleType.Pinned);   
GCHandle bufHandle2 = GCHandle.Alloc(singleXfer, GCHandleType.Pinned);   
GCHandle bufHandle3 = GCHandle.Alloc(overlap, GCHandleType.Pinned);   
   
// send buffers to driver   
// wait for driver to finish using the buffers   
// get the results of the transfer   
   
bufHandle1.Free();   
bufHandle2.Free();   
bufHandle3.Free();   
0 Likes
MR_41
Employee
Employee
First like received
        Hi,   
   
That thread had disappeared due to a bug. It has been restored. I have also written to the concerned team to have a look at this issue.   
0 Likes
Anonymous
Not applicable
        Awesome! Thanks Ganesh.   
   
If you could, pass these on too.   
   
1) OverlapSignalAllocSize should be a static member of CyConst, but instead it's a non-static member of CyUSBEndPoint. This is contrary to the documentation.   
   
2) byte[] does not need to be passed as a ref parameter. Only value-types need to be passed as a ref parameter. When an array, or any other reference type, is passed as a non-ref parameter, the    reference (NOT the array!) is passed by value (i.e. a pointer is copied).   
   
This is very different from C++, where an array must be passed by reference using & or the copy constructor would make a copy of the whole array. Using a ref parameter for byte[] is like passing a pointer by reference in C++, like say   
   
C#   
bool XferData(ref byte[] buf, ref int len);   
   
is like   
   
C++   
bool XferData(PCHAR &data, LONG &len);   
   
i.e. passing a pointer by reference.   
   
---   
   
This does not match the actual C++ library,   
   
bool XferData(PCHAR data, LONG &len);   
   
Which looks like this in C#   
   
bool XferData(byte[] buf, ref int len);   
   
i.e. passing a pointer by value   
   
Passing byte[] as a ref parameter adds unnecessary complexity to your customer's code by adding serious and unnecessary restrictions on what may be used as an argument.   
0 Likes
Anonymous
Not applicable
        Hi Andrew   
   
The software guys went through the XferData API implementation and did not find any loop hole in it. It does pining of all allocation properly.   
- XferData API is being used by all C# application(Bulk Loopback / Control Center / Screamer), which passed through the stress testing and did not find such issue.   
   
Find the source code for the XferData API below.   
   
public unsafe virtual bool XferData(ref byte[] buf, ref int len)   
{   
byte[] ovLap = new byte[OverlapSignalAllocSize];   
   
fixed( byte *fixedOvLap = ovLap)   
{   
OVERLAPPED *ovLapStatus = (OVERLAPPED*) fixedOvLap;   
ovLapStatus->hEvent = PInvoke.CreateEvent(0, 0, 0, 0);   
   
// This SINGLE_TRANSFER buffer must be allocated at this level.   
int bufSz = CyConst.SINGLE_XFER_LEN + ((XferMode == XMODE.DIRECT) ? 0 : len);   
byte[] cmdBuf = new byte[bufSz];   
   
// These nested fixed blocks ensure that the buffers don't move in memory   
// While we're doing the asynchronous IO - Begin/Wait/Finish   
fixed (byte* tmp1 = cmdBuf, tmp2 = buf)   
{   
bool bResult = BeginDataXfer(ref cmdBuf, ref buf, ref len, ref ovLap);   
//   
// This waits for driver to call IoRequestComplete on the IRP   
// we just sent.   
//   
bool wResult = WaitForIO(ovLapStatus->hEvent);   
bool fResult = FinishDataXfer(ref cmdBuf, ref buf, ref len, ref ovLap);   
   
PInvoke.CloseHandle(ovLapStatus->hEvent);   
   
return wResult && fResult;   
}   
}   
}   
   
Adding this API instead of callling CyUSB.dll XferData API will probably help.   
   
Thanks   
VIVK   
0 Likes
Anonymous
Not applicable
        Hi VIVK,   
   
Thank you for the reply. I really appreciate the support that you are providing.   
   
The fixed pointers tmp1 and tmp2 are not referenced in the fixed block. The compiler will warn you about this and potentially optimize the fixed block out. To make things more complicated, this optimization will only happen with Release build, so if your developers were testing a Debug build, you would not encounter this error.   
   
To verify, I suggest using Reflector on the Release mode assembly and looking at XferData to see if the fixed block survived optimization.   
   
Cheers,   
Andrew Sobotka   
0 Likes
Anonymous
Not applicable
        Hi Andrew,   
   
I created a technical support case with you as a contact and routed it to the concerned engineer. Let me know if you are able to view the case.   
   
Once the issue is resolved, I will post the same in the forum for the benefit of the others.   
   
Cheers   
VIVK   
0 Likes
Anonymous
Not applicable
        VIVK,   
   
I have looked in the list of support cases which I have previously had open, and all of the cases are closed. There are no new ones.   
   
By the way, I took the time to do a test on the XferData example you provided above. Using Reflector, I was able to verify that the problem only occurs for Release builds.   
   
Specifically, when the "Optomize Code" checkbox in the Project Properties' Build box is checked, like for Release builds, the compiler will optimize the fixed block away because the fixed pointers are unreferenced. If Optimize Code is not checked, like for Debug builds, the fixed block survives.   
   
I have a copy of the Reflector output to share with the engineer when I gain access to the technical support case.   
   
Cheers,   
Andrew   
0 Likes
Anonymous
Not applicable
        A new support case for this issue was opened for my account. (#1L2YGYZ) Being that Andrew and I have the same first name and similar last names, there might have been a mix-up.   
   
I don't mind helping. But, I'm probably gonna be too busy to contribute much over the next few months.   
0 Likes
MR_41
Employee
Employee
First like received
        Andrew,   
   
Can you please provide the case number of any of your closed cases? That will help us to locate your account and reassign this new case to your account so that you can view it.   
0 Likes
Anonymous
Not applicable
        This one's my favorite. 13UULHN - USB Domain and Slave FIFO Peripheral Domain Losing Synchronization. It's from early 2007.   
0 Likes
MR_41
Employee
Employee
First like received
        I have created a case 1V4LNRI on this and have routed it to the concerned engineers. Please let me know if you can access this case. I will keep a close eye on the case.   
0 Likes
Anonymous
Not applicable
        I have seen and replied to the case you opened. I also attached a text file showing that the unreferenced fixed pointers were optimized out.   
0 Likes
Anonymous
Not applicable

 hi all, 

   

i meet this issue. Did this issus is fixed by cypress?

   

Or we just only use the solution by andrew provided

   

==========================================================================
GCHandle bufHandle1 = GCHandle.Alloc(data, GCHandleType.Pinned); 
GCHandle bufHandle2 = GCHandle.Alloc(singleXfer, GCHandleType.Pinned); 
GCHandle bufHandle3 = GCHandle.Alloc(overlap, GCHandleType.Pinned); 

// send buffers to driver 
// wait for driver to finish using the buffers 
// get the results of the transfer 

bufHandle1.Free(); 
bufHandle2.Free(); 
bufHandle3.Free(); 

   

==========================================================================

   

any help is appreciate!

   

regards, 

0 Likes
Anonymous
Not applicable

It was fixed long back. Are you seeing the exact same error behavior?

   

Regards,

   

Anand

0 Likes
BrCo_299421
Level 1
Level 1

I am having a similar problem using the cy8c24894 as a low speed USB control interface (and some other functions) to an FPGA.  I keep having issues using xfrdata.  I did a one-button test app  in C# (VS10) that sends a string to the PSoC, which then echos it back.  When I click a button the main thread sends a string.  A separate timer thread checks periodically for the echo and puts the echo on the test app GUI.  It works for a time, but fails with memory issues.  It sometimes won't terminate until I unplug the PSoC, then it terminates immediately.  When it terminates on its own, I can re-run the app without resetting the PSoC and it works for a while before it fails again.  The one-button test app is copied from the template in the CyUSB.net suite with a timer added.  The error I get most often is:

   

The program '[1896] Template.vshost.exe: Managed (v2.0.50727)' has exited with code -1073740940 (0xc0000374).

   

Any help would be appreciated; I am trying to bring up the first of two boards which will use this basic architecture.

   

Thanks.

0 Likes
BrCo_299421
Level 1
Level 1

 Update: I got rid of the separate timer thread and just put in a loop that sends a 4 byte buffer string and then waits for a response (the echo) on each iteration of the loop.  It almost always hangs on the receive of the 32nd packet.

0 Likes
BrCo_299421
Level 1
Level 1

I concur there is a bug.  I made my own xferdata function using the code posted on 3/10 and replacing the fixed directives with Andrew's GCHandle.Alloc suggestions.  At this point I can make the problem come and go by changing the name of the called function from the re-written xferdata function to the library function without changing anything else.  Proof positive for me, but I don't understand why it works. The fixed directives should do exactly the thing (ie. prevent the garbage collector from prematurely reclaiming transfer buffers), but one works and one doesn't.

0 Likes
Anonymous
Not applicable

 For what it's worth:

   

I've also ran into this issue, and Andrew's solution solved it for me.

   

I'm using a queue, so after creating each buffer for that queue, I pinned them. At the very end of program operation, I Abort() my enpoint once for each queued call, and then Free() the handle. This way, we're reaching 19MB/s in a stable way (possibly more, but we're targeting 19MB/s).

0 Likes
Anonymous
Not applicable

 Hi,

   

Thanks for sharing this details here.

   

-PRJI

0 Likes