同步方式
libusb_bulk_transfer(devh, ep_bulk, buf, CAM_BUF_SZ, &len, timeout);
进入libusb研究,发现libusb是采用异步方式来实现的。在do_sync_bulk_transfer中:
 
staticint do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
unsignedchar endpoint,unsignedchar*buffer,int length,
int*transferred,unsignedint timeout,unsignedchar type)
{
libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length,
bulk_transfer_cb,&completed, timeout);
transfer->type = type;
 
r = libusb_submit_transfer(transfer);
if(r <0){
libusb_free_transfer(transfer);
return r;
}
 
while(!completed){
r = libusb_handle_events(HANDLE_CTX(dev_handle));
}
}
这里libusb_fill_bulk_transfer来填充bulk transfer,然后libusb_submit_transfer提交bulk transfer,最后用libusb_handle_events来等待完成。当收到回应后,bulk_transfer_cb回调设置completed,从而阻塞被接触,函数返回。
 
分段处理bulk transfer
libusb_submit_transfer最终调到了submit_bulk_transfer。该函数会检查buffer大小,如果大于MAX_BULK_BUFFER_LENGTH(16K),则分成若干16K大小的urb,每个urb指向用户buffer的适当位置,然后用ioctl(IOCTL_USBFS_SUBMITURB)提交每个urb。
 
收到数据时会判断是否收齐所有urb。在handle_bulk_completion中,若urb_idx为最后一个urb,则认为收齐了所有的urb。最终会调用usbi_handle_transfer_completion来调用urb callback。
 
timeout处理
若libusb_bulk_transfer传入的timeout为0,则没有timeout,libusb会一直等待数据。在libusb_handle_events中设置了一个2s的poll timeout,libusb会在while中一直poll,每次poll的timeout为2s。
 
若设置了timeout,libusb_submit_transfer会按照timeout升序将transfer插入到libusb_context的flying_transfers列表中,然后提交transfer(当然会分段了,如上所述)
 
libusb_handle_events会根据自己设置的2s timeout和flying_transfers中的timeout得出实际timeout,然后用poll查询usb fd。
 
问题根源
libusb阻塞的原因就是超时。有时usb指纹头返回数据较慢,在指定的timeout时间内没有完成所有urb请求,进入超时处理。handle_timeout()会cancel掉为完成的urb(IOCTL_USBFS_DISCARDURB)。在do_sync_bulk_transfer中,由于未完成所有urb,bulk_transfer_cb没有被调用,从而会阻塞。libusb_handle_events会继续以2s超时来查询fd,但由于urb已经取消,设备会返回-2(ENOENT)。
 
按道理讲,即使超时,libusb也应该在超时后返回。libusb为什么没有返回呢?handle_bulk_completion函数中,若读到ENOENT,awaiting_discard会递减至0。与awaiting_discard一起的还有awaiting_reap,若二者均为0,也会调用bulk_transfer_cb通知用户。
 
awaiting_discard在超时时cancel urb时被设置。若ioctl(IOCTL_USBFS_DISCARDURB)返回EINVAL,则awaiting_reap会递增。
 
经过打印验证,发现在cancel urb时,对于已经完成的urb,会返回EINVAL。而未完成的urb,则返回0。我想这大概是一个bug,正确做法应该是不要取消已经完成的urb。

libusb_bulk_transfer 异步同步的更多相关文章

  1. 对JavaScript中异步同步机制以及线程深入了解

    今天在网上看到各种对Js异步同步单线程多线程的讨论 经过前辈们的洗礼 加上鄙人小小的理解 就来纸上谈兵一下吧~ Js本身就是单线程的 至于为什么Js是单线程的 那就要追溯到Js的历史了 总而言之 由于 ...

  2. 异步|同步&阻塞|非阻塞

    异步|同步:区别在于发出一个功能调用时,是否马上得到返回结果 阻塞|非阻塞:区别在于调用结果返回之前,当前线程是否挂起 node.js:单线程.异步非阻塞模型 单线程与异步不矛盾,与并发是矛盾的 ht ...

  3. AJAX异步同步

    为了更好的用户体验,AJAX的异步同步技术给了我们一个很好的用户体验下面是我做的一个例子. 1.客户端处理 UserId.HTML <!DOCTYPE html PUBLIC "-// ...

  4. 支持异步同步的分布式CommandBus MSMQ实现

    支持异步同步的分布式CommandBus MSMQ实现 先上一张本文所描述的适用场景图 分布式场景,共3台server: 前端Server Order App Server Warehouse App ...

  5. JavaScript学习-5——异步同步、回调函数

    ----------异步同步函数 ----------回调函数 一.异步同步函数 同步:发送一个请求,等待返回,然后再发送下一个请求 异步:发送一个请求,不等待返回,随时可以再发送下一个请求 同步可以 ...

  6. Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)

    Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...

  7. TTS异步+同步

    微软TTS使用说明 一.SAPI SDK的介绍 SAPI,全称是The Microsoft Speech API.就是微软的语音API.由Windows Speech SDK提供. Windows S ...

  8. (转)函数中使用 ajax 异步 同步 返回值错误 主函数显示返回值总是undefined -- ajax使用总结

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAloAAAE0CAIAAAB7LwoKAAAgAElEQVR4nO2dy6sc152A6+/R2mXwSn ...

  9. GCD之异步同步体会

    前面的博文也有写到同步异步,可能是看他人的博文,自己没有实验,感觉理解不深,所以就敲了些代码比较一下串行.并行分别对应的同步.异步. 1.首先创建串行.并行线程队列 1 2 dispatch_queu ...

随机推荐

  1. http与https的主要区别

    HTTP与HTTPS的主要区别如下: 1.https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用. 2.http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输 ...

  2. Linux下安装xrdp

    ubuntu 打开终端,依次输入如下命令: sudo apt-get install xrdp sudo apt-get install vnc4server tightvncserver cd /h ...

  3. vue去掉链接中的#

    在router.js中修改, const router = new VueRouter({ mode: 'history', routes: [...] })

  4. Python 解LeetCode:23. Merge k Sorted Lists

    题目描述:把k个排序的链表组成的列表合并成一个排序的链表 思路: 使用堆排序,遍历列表,把每个列表中链表的头指针的值和头指针本身作为一个元素放在堆中: 第一步中遍历完列表后,此时堆中最多会有n个元素, ...

  5. Python解Leetcode: 226. Invert Binary Tree

    leetcode 226. Invert Binary Tree 倒置二叉树 思路:分别倒置左边和右边的结点,然后把根结点的左右指针分别指向右左倒置后返回的根结点. # Definition for ...

  6. 【转载】Jave开发手册之正则表达式预编译

    今天又水一篇,java开发手册华山版 一.编程规约 (九)其它 第一条 解释:Pattern要定义为static final静态变量,以避免执行多次预编译. 错误用法: // 没有使用预编译 priv ...

  7. Pangu and Stones(HihoCoder-1636)(17北京OL)【区间DP】

    题意:有n堆石头,盘古每次可以选择连续的x堆合并,所需时间为x堆石头的数量之和,x∈[l,r],现在要求,能否将石头合并成一堆,如果能,最短时间是多少. 思路:(参考了ACM算法日常)DP[i][j] ...

  8. Centos安装elasticsearch,php连接使用

    一.下载安装JAVA 下载地址:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html ...

  9. php商城秒杀活动

    今天在网上看到一篇思路+代码的商城秒杀实例,我觉得非常不错,借鉴一下分享给大家: 一.前言 双十一刚过不久,大家都知道在天猫.京东.苏宁等等电商网站上有很多秒杀活动,例如在某一个时刻抢购一个原价199 ...

  10. Go语言学习之main包的讲解

    ### Go语言学习之main包的讲解 1.Go中main函数不支持任何返回值 2.可以通过os.Exit(0)来返回状态 func main(){ fmt.Println("hellow ...