CyAPI.NET bugs

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

cross mob
Anonymous
Not applicable
        I believe I may have found a few bugs with the CyAPI.NET library. I hope to create some discussion to see whether or not these are really bugs.   
   
First, a doc error. The help says that OverlapSignalAllocSize is a public read-only Property of the CyUSB class. This is not true. It is actually a member of CyUSBEndPoint. This is unfortunate, because it means we need an actual instance of a CyUSBEndPoint in order to call OverlapSignalAllocSize. It would be better if it was a static method as part of the CyConst class, so that it could be called without an instance to any objects.   
   
   
Second, I want to know if there's a reason that the data transfer buffers are all ref'd. I believe they are ref'd because, in the previous C++ implementation, we needed to pass objects by reference to avoid calling the copy constructor. This is not necessary in C#, because the caller passes the callee a    copy of the reference. What the ref keyword does is pass the    caller's original reference instead of a copy.   
   
I think this was an honest mistake made by a C++ programmer who was learning .NET and didn't know that the ref keyword is very different from the & symbol in a C++ parameter list. However, by forcing the buffers to be ref, the CyAPI.NET library becomes much more awkward to use, as there are severe limitations on what you can pass to a ref parameter.   
   
   
Finally, and    most seriously, I believe there is a major bug involving passing buffers from Managed to Unmanaged code. This bug could be why everyone has such problems with calling the asynchronous Begin/Wait/FinishDataXfer methods.   
   
First, a brief review of garbage collection. To keep the heap from becoming too fragmented, the CLR is allowed to re-arrange the contents of memory on a whim. It then updates all the Managed references to point to the re-arranged contents accurately. However, the CLR has no way to tell Unmanaged pointers that it has moved the contents of memory around.   
   
Now, I suggest pulling up the help file for BeginDataXfer() and looking at the example. Note the use of the fixed { } block surrounding the creation of the Overlapped's hEvent member. That fixed block is there likely because the compiler complains otherwise, but there's a good reason why we need it.   
   
The fixed keyword tells the CLR that the parameter (in this case, the ovLaps buffer) must be pinned in memory, so that the unmanaged code in the block doesn't get burned by the CLR's garbage collector. This is important because otherwise ovLaps could move somewhere else between the cast to an OVERLAPPED* and the call to CreateEvent, causing CreateEvent to write into memory that might be owned by another process.   
   
Finally, the problem. I was transferring back and forth between the device using worker threads, and noticed that if I used the menu bar, the application would lock up or crash the main thread. I believe that the menu bar was causing a lot of small objects to be created and then freed, which fragmented the heap, causing the CLR to re-arrange memory, moving my buffer while the driver was in the middle of transferring.   
   
This would cause all sorts of problems. Sometimes, my app would lock up. Not even Visual Studio's stop button would end the process. Other times, the main thread (NOT the worker thread!) would throw a NullReferenceException, or even the exotic ExecutionEngineException; I believe this was because the driver was running in the main thread's context while the garbage collector moved the buffer. While somewhat random, the crashes were easy to reproduce and typically happened after only a few seconds. The exceptions and freezing would stop if I commented out the transfer.   
   
The solution is to pin the buffers for the duration of the transfer. ALL of the buffers; the single transfer, the overlapped, and the data buffer. You could do this by using three fixed blocks, however I prefer the    GCHandle class for pinning objects.   
   
I created a class called PinnedBuffer and, in the constructor, I used GCHandle to pin the buffer. I then use this PinnedBuffer class for all three buffers. The byte[] needs to be a public field so that you can use it with the data transfer methods' ref parameters (see the previous issue). I haven't had any problems since I started pinning all my buffers.   
   
I don't know whether the XferData method pins all of its buffers for the duration. I do know that their example code for their asynchronous methods only momentarily pins the overlap buffer and that is probably why people have so many problems with the asynch code.    All three byte[] buffers need to be pinned before the transfer begins until after it finishes.   
0 Likes
14 Replies
Anonymous
Not applicable
        I actually ended up writing my own code to implement IOCompletion ports due to some pretty high-speed requirements of my system. I battled this problem you mention for quite a while believing it was MY code that was improperly pinning the objects being marshaled to the driver. In fact, it was the CyUSB.NET API code that I was using for the other endpoints. So, I switched all my other endpoints over to use the ASync. code that I wrote and I've been pumping high-speed data back-and-forth without a hitch since.   
   
I made the support team well aware of this problem though it seems they haven't done anything to address the issue. It cost me a lot of time and frustration.   
   
In fact, I used an MSDN developer support center call to help debug it. Microsoft engineers spent HOURS helping me track it down. In the end,the furthest they could possibly dig proved that an OVERLAPPED structure was being prematurely garbage collected. They COMPLETELY vetted my code and we had to conclude that after all our work the only possible source of the bug was in the Cypress API.   
0 Likes
Anonymous
Not applicable
        BTW, I was NOT using the Begin/Wait/End async. API methods on my non-async endpoints either. But using reflector, I discovered that the blocking calls (XferData, etc.) are just wrappers around those functions called synchronously in the background.   
   
My device uses 2 Interrupt endpoints for very high-speed analog data and the rest of the endpoints are interrupt endpoints used for back-and-forth command, control and telemetry. The 2 data endpoints were always using my "custom" IoCompletion Port/Threadpool code. As I switched the other endpoints one-by-one over to that custom unmanaged interface, I could see that the occurrence of these mysterious exceptions reduced in with frequency closely matching the specific duty-cycles of each of those endpoints. Pretty conclusive evidence, if you ask me!   
0 Likes
Anonymous
Not applicable
        So you're skipping the Cypress API and calling DeviceIOControl yourself? Are you still using the Cypress API to get a handle to the device or did you roll you own Open() code too?   
   
Between the two of us, I concur on the conclusive nature of the evidence. It's good to see some other skilled developers coming to the same conclusion, though it is unfortunate that XferData also suffers from this pinning bug. No one will be able to use the CyAPI.NET in a commercial product without some kind of work-around.   
   
It is possible to solve this problem without resorting to unmanaged interfaces. I ended up making my own USBTransfer class that worked around the pinning bug (and the ref "bug") using my PinnedBuffer class. I haven't had any problems since.   
0 Likes
Anonymous
Not applicable
        "No one will be able to use the CyAPI.NET in a commercial product without some kind of work-around. "   
   
That's why the bug mystified me so much. The FX2 is an obviously popular product that's been deployed in commercial applications. I work in a small company so I might have a different perspective. But, maybe most corporate design teams are rolling their own drivers or using C++ for their apps. Also, I am doing real-time data display and processing of two analog channels for a scientific instrument. Most, high-volume consumer applications are not pushing the limits and many probably leverage the built-in device classes as well.   
   
To answer your question: I DO use the API calls for all of the setup and "device management".   
   
I grab a handle using:   
safeHandle = new SafeFileHandle(CyUSBDevice.DeviceHandle, false);   
   
and bind it to the threadpool:   
ThreadPool.BindHandle(safeHandle);   
   
I pin the buffers using GCHandle and Pack() those into a NativeOverlapped along with a callback. Then you can pre-allocate data and queue up multiple endpoint operations which allows the driver to read/write and store data independent of the OS/CLR scheduling. That way, you can have multiple read/write operations along with associated buffers ready to service the driver without having to worry about the threadpool processing the finished callbacks and dispatching another operation in time to keep up with the data. Basically, compensating for the variable lag inherent in any non-real time OS.   
0 Likes
Anonymous
Not applicable
        Another FYI:   
My approach to Overlapped IO using the threadpool was based heavily on a series of articles called "Concurrent Affairs" by Jeffery Richter from MSDN magazine.   
0 Likes
Anonymous
Not applicable

Hello Andy,

   

 

   

same problem here. I would love to take a closer look at your code. I would even buy it if I had to.

0 Likes
Anonymous
Not applicable

So do I correctly conclude that CyUSB.NET cannot be used as is, due to fatal issues?

   

There have been some workarounds proposed. Unfortunately I'm no power-user and don't understand much of what was said. I therefore suppose I have to stay away from the CyUSB.NET till we get a new version?

   

Thank you

0 Likes
Anonymous
Not applicable
0 Likes
Anonymous
Not applicable

 M8online,

   

I haven't been regularly following these forums. Sorry I was unable to offer assistance. I realize that this is almost a year later! However, if you still need help post again here and we'll figure out how to exchange contact info.

   

The design of this site is severely lacking. There NEEDS to be a way to contact other forum members DIRECTLY. It's kinda pathetic that nobody thought of this during a site design. Cypress, take a look at Microchip (or any of the million other internet forums) to see how it's done...seriously.

0 Likes
Anonymous
Not applicable

Recently I downloade the latest version of CyAPI.net (3.4.5) and done some experiments. Sadly I found this bug is still not solved by Cypress. I hope there will be an official solution to this problem.  

0 Likes
Anonymous
Not applicable

Hi mczhao,

   

This bug was resolved. I thought you were using CyAPI.lib??

   

Regards,

   

Anand

0 Likes
Anonymous
Not applicable

Hi Anand,

   

We are using the latest .NET API and CyUSB.dll. And we have to use the GCHandle to pin the OVERLAPPED struct. Otherwise some strange exceptions or bugs will occur.

   

Thanks,

   

Mczhao 

0 Likes
Anonymous
Not applicable

If possible can you get sample code and error messages of the exceptions that you're talking about?

   

Regards,

   

Anand

0 Likes
Anonymous
Not applicable

Hi mczhao,

   

Can you help me with sample code or the exception message so that we can validate if the bug still exists.

   

Regards,

   

Anand

0 Likes