CyUSB Threadsafe???

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

cross mob
Anonymous
Not applicable
        I cannot find anyplace in the documentation that this is mentioned. When I strip down to bulk IO calls from the main thread, all is OK. When I have a worker threadfor EP6 calling xferdata and a thread for EP8 calling xferdata , and each thread stuff the data into a c# Queue<> protected by Lock I am getting some really horrid lockups that hang the dev environment until I unplug the USB.   
   
The Queue stuff with lock protecting it shoudl be fine. Lots do that all over the net.   
   
Luckily it reproduces easily in seconds. But perhaps two threads each calling bulk xferdata isn't allowed?   
0 Likes
1 Reply
Anonymous
Not applicable
        I used XferData, as well as the asynchronous BeginDataXfer/WaitForXfer/FinishDataXfer, across multiple threads without any problems, but I was not placing the results in a shared queue.   
   
What kind of locking mechanism are you using? lock (this) { /* do stuff */} is a good way to get deadlocks because other code can lock on this for different reasons. You should create an object specifically for locking data transfers and lock only the transfers on only that code.   
   
Object DataTransferLockObject = new Object();   
lock (DataTransferLockObject) { /* do stuff */ }   
   
Actually you should really use the Queue.Synchronized method to get a queue wrapper that doesn't need protected by a lock; see    http://msdn.microsoft.com/en-u...ueue.synchronized.aspx   
   
To narrow down your problem, you could try some other things. Keep both XferData's going but comment out adding them to the queue. Add them to two separate queues.   
   
That said, I have had problems with the CyAPI causing not only my app, but Visual Studio to lock up until I pulled the plug. I believe the problem deals with passing Managed byte[] arrays to the Unmanaged driver. During the transfer, the CLR decides to move the buffer around in memory, probably in an effort to keep the heap from becoming too fragmented. However, since the Cypress driver is unmanaged, it has no way of knowing that the CLR is moving things behind its back, and so it continues to read/write to old memory. This can cause all kinds of problems; the app might lock, or throw a NullReferenceException or the even more exotic ExecutionEngineException, but for me the exceptions were always thrown on the main thread and not the worker thread (perhaps the driver thread was executing in the context of the main thread instead of the calling thread?)   
   
Consider that when you add an item to the queue, it may cause new memory to be allocated and old memory to be freed. Eventually, memory becomes so fragmented that the CLR moves the buffers around, and if the CLR moves your buffer while the driver is actively transferring to/from it, you will get crashes.   
   
The solution is to use a fixed block for duration of the buffers' scopes, or to use the GCHandle class to pin the buffers in memory so the CLR won't move them around. Note that I said "buffers", plural; these rules apply not only to the data buffer, but to the overlapped and single-transfer buffers as well!   
   
I ended up writing a PinnedBuffer class that wraps a byte[] which pins the buffer in the constructor. Unfortunately, the byte[] must be a public field, because the Cypress API uses ref parameters and the list of valid arguments for ref parameters is actually much shorter than you think...   
0 Likes