libuv是node.js使用的基础库,主要包括主循环,文件和网络接口。虽然libuv是为node.js而生的,但它本身是一个独立的库,加上使用简单方便,所以在node.js之外也有不少人使用。最近整合libuv到V8里时发现几个问题:

1.uv_fs相关函数无法传回调函数需要的上下文信息(如read的buffer),只能通过全局变量来保存数据(官方例子都是用的全局变量)。uv_fs相关函数都可以提供一个回调函数,如果回调函数不为空,当前调用自动变成一个异步调用,在操作完成时调用提供的回调函数。一般来说,回调函数都需要一个变量来作为它的上下文(提供参数或保存结果),文件操作相关函数的上下文是uv_fs_t结构,但里面无法保存调用者提供的额外信息。

  1. uv_fs_t open_req;
  2. uv_fs_t read_req;
  3. uv_fs_t write_req;
  4. static char buffer[1024];
  5. static uv_buf_t iov;
  6. ...
  7. void on_read(uv_fs_t *req) {
  8. if (req->result < 0) {
  9. fprintf(stderr, "Read error: %s\n", uv_strerror(req->result));
  10. }
  11. else if (req->result == 0) {
  12. uv_fs_t close_req;
  13. // synchronous
  14. uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
  15. }
  16. else if (req->result > 0) {
  17. iov.len = req->result;
  18. uv_fs_write(uv_default_loop(), &write_req, 1, &iov, 1, -1, on_write);
  19. }
  20. }
  21. void on_open(uv_fs_t *req) {
  22. // The request passed to the callback is the same as the one the call setup
  23. // function was passed.
  24. assert(req == &open_req);
  25. if (req->result >= 0) {
  26. iov = uv_buf_init(buffer, sizeof(buffer));
  27. uv_fs_read(uv_default_loop(), &read_req, req->result,
  28. &iov, 1, -1, on_read);
  29. }
  30. else {
  31. fprintf(stderr, "error opening file: %s\n", uv_strerror((int)req->result));
  32. }
  33. }
  34. ...
  35. 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来串行化:

  1. int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \
  2. if (uv__is_active(handle)) return 0; \
  3. if (cb == NULL) return -EINVAL; \
  4. QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \
  5. handle->name##_cb = cb; \
  6. uv__handle_start(handle); \
  7. return 0; \
  8. }

3.uv_async无法传递数据。用uv_idle不行,我决定用uv_async。这次倒是不崩溃了,事件好像也收到了,但游戏里的反应却有些怪异,仔细分析LOG信息,发现touchmove和touchend收到了,但是没有收到touchstart。明明uv_async_send都执行了,为什么主循环却没有处理这个事件呢?继续看代码:

  1. void uv__async_send(struct uv__async* wa) {
  2. const void* buf;
  3. ssize_t len;
  4. int fd;
  5. int r;
  6. buf = "";
  7. len = 1;
  8. fd = wa->wfd;
  9. #if defined(__linux__)
  10. if (fd == -1) {
  11. static const uint64_t val = 1;
  12. buf = &val;
  13. len = sizeof(val);
  14. fd = wa->io_watcher.fd; /* eventfd */
  15. }
  16. #endif
  17. do
  18. r = write(fd, buf, len);
  19. while (r == -1 && errno == EINTR);
  20. if (r == len)
  21. return;
  22. if (r == -1)
  23. if (errno == EAGAIN || errno == EWOULDBLOCK)
  24. return;
  25. abort();
  26. }

async_send是同步执行的,从上面的代码看不出什么问题,再看接受部分:

  1. static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
  2. struct uv__async* wa;
  3. char buf[1024];
  4. unsigned n;
  5. ssize_t r;
  6. n = 0;
  7. for (;;) {
  8. r = read(w->fd, buf, sizeof(buf));
  9. if (r > 0)
  10. n += r;
  11. if (r == sizeof(buf))
  12. continue;
  13. if (r != -1)
  14. break;
  15. if (errno == EAGAIN || errno == EWOULDBLOCK)
  16. break;
  17. if (errno == EINTR)
  18. continue;
  19. abort();
  20. }
  21. wa = container_of(w, struct uv__async, io_watcher);
  22. #if defined(__linux__)
  23. if (wa->wfd == -1) {
  24. uint64_t val;
  25. assert(n == sizeof(val));
  26. memcpy(&val, buf, sizeof(val)); /* Avoid alignment issues. */
  27. wa->cb(loop, wa, val);
  28. return;
  29. }
  30. #endif
  31. wa->cb(loop, wa, n);
  32. }

全部数据读取完了,只是在最后调了一次回调。更郁闷的是我对async的理解是错的:uv_async_t里的data成员并不能用来传递数据,它是在两个线程中无保护的情况下共享的。下面的代码是官方提供的示例,这种方法在低速通信时没问题,速度一快后面的数据自动覆盖前面的数据,所以touchstart被touchmove覆盖而丢失。

  1. void fake_download(uv_work_t *req) {
  2. int size = *((int*) req->data);
  3. int downloaded = 0;
  4. double percentage;
  5. while (downloaded < size) {
  6. percentage = downloaded*100.0/size;
  7. async.data = (void*) &percentage;
  8. uv_async_send(&async);
  9. sleep(1);
  10. downloaded += (200+random())%1000; // can only download max 1000bytes/sec,
  11. // but at least a 200;
  12. }
  13. }

libuv以上这些问题在node.js恰恰不是问题,但独立使用libuv时一定要小心了。

libuv里的几个缺陷的更多相关文章

  1. libuv 初窥--转

    过年了,人都走光了,结果一个人活也干不了.所以我便想找点东西玩玩. 今天想试一下 libev 写点代码.原本在我那台 ubuntu 机器上一点问题都没有,可在 windows 机上用 mingw 编译 ...

  2. libuv 简单使用

    libuv 简单使用 来源:https://zhuanlan.zhihu.com/p/50497450 前序:说说为啥要研究libuv,其实在很久之前(大概2年前吧)玩nodejs的时候就对这个核心库 ...

  3. HTTP基础08--追加协议

    消除 HTTP 瓶颈的 SPDY HTTP 的瓶颈 Web 网站为了保存这些新增内容,在很短的时间内就会发生大量的内容更新;为了尽可能实时地显示这些更新的内容,服务器上一有内容更新,就需要直接把那些内 ...

  4. <转>cookie和缓存解析

    原文来自:http://www.cnblogs.com/cuihongyu3503319/archive/2008/04/18/1160083.html 缓存cache 为了提高访问网页的速度,浏览器 ...

  5. 【C++探索之旅】第一部分第二课:C++编程的必要软件

    内容简介 1.第一部分第二课:C++编程的必要软件 2.第一部分第三课预告:第一个C++程序 C++编程的必要软件 经过上一课之后,大家是不是摩拳擦掌,准备大干一场了呢. 这一课我们来做一些C++开发 ...

  6. 【C语言探索之旅】 第二课:工欲善其事,必先利其器

    内容简介 1.课程大纲 2.第一部分第二课:工欲善其事,必先利其器 3.第一部分第三课预告:你的第一个程序 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C ...

  7. http协议--笔记

    HTTP协议的缺点:1.通信使用明文(不加密),内容可能会被窃听2.不验证通信方的身份,因此有可能遭遇伪装3.无法证明报文的完整性,所以有可能已遭篡改 防止窃听保护信息的几种对策:加密技术通信的加密H ...

  8. BUG关闭原因

    已解决:缺陷已经修复. 重复缺陷:是指在系统里相同原因的缺陷已经被其他人报告.在此缺陷被作为重复缺陷返回时,先不要立即取消.必须等到核查修复后,才在系统里取消.这是因为有些缺陷被误认为是重复缺陷,实际 ...

  9. 第九章 基于HTTP的功能追加协议

    第九章 基于HTTP的功能追加协议 通过在HTTP的基础上添加新的功能来提高性能和功能. 一.消除HTTP瓶颈的SPDY SPDY(SPeeDY)目的是提高HTTP性能,缩短Web页面的加载时间(50 ...

随机推荐

  1. 2016年10月30日 星期日 --出埃及记 Exodus 19:15

    2016年10月30日 星期日 --出埃及记 Exodus 19:15 Then he said to the people, "Prepare yourselves for the thi ...

  2. java.lang.NoClassDefFoundError: org/w3c/dom/ElementTraversal

    今天用maven编写Selenium测试程序时,调用 HtmlUnitDriver driver = new HtmlUnitDriver(true); 反法时报错如下: java.lang.NoCl ...

  3. ContentProvider官方教程(9)定义一个provider完整示例:实现方法,定义权限等

    Creating a Content Provider In this document Designing Data Storage Designing Content URIs Implement ...

  4. Intent官方教程(4)用Intent构造应用选择框

    Forcing an app chooser When there is more than one app that responds to your implicit intent, the us ...

  5. AJAX和jQuery Ajax总结

    AJAX全称为“Asynchronous JavaScript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用,改善用户体验,实现无刷新效果的技术. 使用AJAX的优 ...

  6. 【Linux】vi(vim)起步学起来有些困难,一步一步温习

    以Tomcat的配置文件service.xml为例,记录.学习vi的最常用操作. > 什么是vi or vim? [nicchagil@localhost bak]$ man vi VIM() ...

  7. django cycle标签

    django 模板系统 有很多标签,其中cycle我觉得不好理解,至少网上文档也不好理解. 这些标签可以通过渲染模板文件而获得我们预期的效果和文字,常用的有如下这些标签: 标签:{% 标签名 %}{% ...

  8. 四、java中的数组

    总结: 数组的特点: 1.存储的数据类型单一. 2.数组的大小不可变. 3.Arrays工具类.

  9. 修改客户端Webbrowser对应IE版本步骤

    制作注册表文件的过程 1,” HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\InternetExplorer\MAIN\ FeatureControl\FEATURE_B ...

  10. Linux中安装Cisco Packet Tracer

    Cisco Packet tracer是什么? Cisco Packet Tracer是一个强大的网络模拟工具,用于进行Cisco认证时的培训.它为我们 提供了各个路由器和网络设备的良好的接口视图,这 ...