libuv里的几个缺陷
libuv是node.js使用的基础库,主要包括主循环,文件和网络接口。虽然libuv是为node.js而生的,但它本身是一个独立的库,加上使用简单方便,所以在node.js之外也有不少人使用。最近整合libuv到V8里时发现几个问题:
1.uv_fs相关函数无法传回调函数需要的上下文信息(如read的buffer),只能通过全局变量来保存数据(官方例子都是用的全局变量)。uv_fs相关函数都可以提供一个回调函数,如果回调函数不为空,当前调用自动变成一个异步调用,在操作完成时调用提供的回调函数。一般来说,回调函数都需要一个变量来作为它的上下文(提供参数或保存结果),文件操作相关函数的上下文是uv_fs_t结构,但里面无法保存调用者提供的额外信息。
uv_fs_t open_req;
uv_fs_t read_req;
uv_fs_t write_req;static char buffer[1024];static uv_buf_t iov;
...
void on_read(uv_fs_t *req) {if (req->result < 0) {fprintf(stderr, "Read error: %s\n", uv_strerror(req->result));}else if (req->result == 0) {uv_fs_t close_req;// synchronousuv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);}else if (req->result > 0) {iov.len = req->result;uv_fs_write(uv_default_loop(), &write_req, 1, &iov, 1, -1, on_write);}
}void on_open(uv_fs_t *req) {// The request passed to the callback is the same as the one the call setup// function was passed.assert(req == &open_req);if (req->result >= 0) {iov = uv_buf_init(buffer, sizeof(buffer));uv_fs_read(uv_default_loop(), &read_req, req->result,&iov, 1, -1, on_read);}else {fprintf(stderr, "error opening file: %s\n", uv_strerror((int)req->result));}
}
...
uv_fs_open(uv_default_loop(), &open_req, argv[1], O_RDONLY, 0, on_open);
我开始以为data成员是保存用户数据的地方,通过data把buffer传过去,但是data总是被清空了,看里面的代码才知道data是fs内部的使用的。上面官方例子都是通过全局变量传过去的,真是太变态了!
2.其它线程不能访问缺省主循环。最近让cantk-runtime-v8里支持Touch/Key事件遇到这个问题:Touch/Key事件是UI线程里的,而V8是在GLSurfaceView的Render线程里的,直接通过JNI调用V8里的JS会导致程序崩溃,所以我想通过uv_idle来串行化,但结果是程序仍然崩溃。记得glib loop里idle是允许多线程访问的,我在设计FTK的主循环时也通过一个pipe来串行化多线程访问主循环的,呵呵,我以为所有的loop都应该支持多线程,一看代码才知道,它并没有加锁也没有用pipe来串行化:
int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \if (uv__is_active(handle)) return 0; \if (cb == NULL) return -EINVAL; \QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \handle->name##_cb = cb; \uv__handle_start(handle); \return 0; \}
3.uv_async无法传递数据。用uv_idle不行,我决定用uv_async。这次倒是不崩溃了,事件好像也收到了,但游戏里的反应却有些怪异,仔细分析LOG信息,发现touchmove和touchend收到了,但是没有收到touchstart。明明uv_async_send都执行了,为什么主循环却没有处理这个事件呢?继续看代码:
void uv__async_send(struct uv__async* wa) {const void* buf;ssize_t len;int fd;int r;buf = "";len = 1;fd = wa->wfd;#if defined(__linux__)if (fd == -1) {static const uint64_t val = 1;buf = &val;len = sizeof(val);fd = wa->io_watcher.fd; /* eventfd */}
#endifdor = write(fd, buf, len);while (r == -1 && errno == EINTR);if (r == len)return;if (r == -1)if (errno == EAGAIN || errno == EWOULDBLOCK)return;abort();
}
async_send是同步执行的,从上面的代码看不出什么问题,再看接受部分:
static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {struct uv__async* wa;char buf[1024];unsigned n;ssize_t r;n = 0;for (;;) {r = read(w->fd, buf, sizeof(buf));if (r > 0)n += r;if (r == sizeof(buf))continue;if (r != -1)break;if (errno == EAGAIN || errno == EWOULDBLOCK)break;if (errno == EINTR)continue;abort();}wa = container_of(w, struct uv__async, io_watcher);#if defined(__linux__)if (wa->wfd == -1) {uint64_t val;assert(n == sizeof(val));memcpy(&val, buf, sizeof(val)); /* Avoid alignment issues. */wa->cb(loop, wa, val);return;}
#endifwa->cb(loop, wa, n);
}
全部数据读取完了,只是在最后调了一次回调。更郁闷的是我对async的理解是错的:uv_async_t里的data成员并不能用来传递数据,它是在两个线程中无保护的情况下共享的。下面的代码是官方提供的示例,这种方法在低速通信时没问题,速度一快后面的数据自动覆盖前面的数据,所以touchstart被touchmove覆盖而丢失。
void fake_download(uv_work_t *req) {int size = *((int*) req->data);int downloaded = 0;double percentage;while (downloaded < size) {percentage = downloaded*100.0/size;async.data = (void*) &percentage;uv_async_send(&async);sleep(1);downloaded += (200+random())%1000; // can only download max 1000bytes/sec,// but at least a 200;}
}
libuv以上这些问题在node.js恰恰不是问题,但独立使用libuv时一定要小心了。
libuv里的几个缺陷相关推荐
- Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)
UIL( Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)) http://blog.csdn.net/vipzjyno1/article/detai ...
- libuv 高性能事件驱动库 简介
libuv是一个高性能事件驱动库,屏蔽了各种操作系统的差异从而提供了统一的API.libuv严格使用异步.事件驱动的编程风格.其核心工作是提供事件循环及基于 I/O或其他活动事件的回调机制.libuv ...
- Libuv源码分析 —— 8. 线程池
网络I/O 在 上一节 的学习中,我们已经搞明白了网络I/O的基本过程,并通过了解进程/线程间通信来熟悉这个流程.下面,让咱们学习线程池中的线程如何工作.并和主进程进行通信的吧! 线程池 Libuv ...
- 【缺陷管理】9:偶尔出现的缺陷如何处理?
(1) 考虑各方面的因素来判断缺陷的严重级别和优先级别. 首先判断严重级别:严重级别比较容易判断,和其他能复现的缺陷一样处理.然后判断优先级别,就需要看对用户的影响,即需要知道这个缺陷能被复现的概率, ...
- 软件缺陷是什么以及缺陷的管理
1 软件测试缺陷 软件缺陷的定义 软件缺陷,通常又被叫做Bug或者defect,即为软件或程序中存在的某种破坏正常运行能力的问题.错误.其存在会导致软件产品在某种程度上不能满足用户的需求. 软件缺陷是 ...
- 专访蒋宇捷:技术管理者应具备哪些能力?
编程初体验 CSDN:你何时开始接触计算机,又如何走上编程道路的? 蒋宇捷:小学的时候,父母单位的苹果机.386和586都可以让我在空闲时候使用.而在那时代彩色显示器非常少见,即使一个小小的黑白屏幕的 ...
- 介绍 Java 平台的 Jazzy:一种新的拼写检查器 API
计算机擅长执行快速搜索操作,可以根据给定的搜索词,对大量存储的信息快速进行搜索.但是,拼写检查应用程序所要求的搜索能力,不仅仅是正确的字符串匹配.在这篇文章中,我将介绍搜索算法的一些历史,包括语音匹配 ...
- 基于正样本的表面缺陷检测
表面缺陷检测在工业生产中起着非常重要的作用,基于机器视觉的表面缺陷检测可以极大的提升工业生产的效率.随着近年来深度学习在计算机视觉领域的发展,卷积神经网络在诸多图像任务上都取得了显著的效果,然而这些方 ...
- 读《图解HTTP》总结--第九章
基于HTTP的功能追加协议 虽然HTTP协议即简单又简捷,但随着时代的发展,其功能使用上捉襟见肘的疲态已经凸显.本篇主要讲解基于HTTP新增的功能的协议. 9.1 基于HTTP的协议 在建立HTTP ...
最新文章
- Redis运行流程源码解析
- SpringSecurity过滤器链汇总
- 刚才我提出要把数据库处理部分放到代码里,但是有人提出,存储过程有缓存,速度快。我该怎么说啊?...
- 【计算理论】计算复杂性 ( 两个带子的图灵机的时间复杂度 | 证明多个带子图灵机时间复杂度 )
- Win7下Android模拟器中没有3G网络信号的解决办法
- 【英语学习】【Daily English】U10 Education L01 Is this certificate a must?
- 快手与美团达成互联互通合作:美团将在快手上线小程序
- Eclipse探秘-第一章-Eclipse启动(1)
- 中国地图经纬度json
- 西门子200PLC控制台达伺服电机正反转,步科触摸屏,模拟量控制
- 一杯咖啡带你读懂状态机
- 前端tif文件在线预览
- 芯片级维修学习课程安排
- C++数组能开多大?
- 视觉欺骗:你绝不会相信A和B颜色相同!
- 华夏ERP没有找到新增功能
- JS 实现驼峰式转下横线,下横线转驼峰式2
- 多线程的实现与多线程的同步机制-让你轻松掌握多线程编程
- Qt编写可视化大屏电子看板系统5-恢复布局
- 如何整理企业的知识库?