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;
// synchronous
uv_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 */
}
#endif do
r = 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;
}
#endif wa->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里的几个缺陷的更多相关文章

  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. android 入门 002 (拨打电话,发送短信)

    一.拨打电话 1.首先做好界面,代码如下: layout =>activity_main.xml 中 <LinearLayout xmlns:android="http://sc ...

  2. InstallShield 打包时需要注意

    32位的程序可以在64位运行,但64位的程序不能再32位上运行 32位的包和64位的包要分开打,不然在32位的机器上安装不上

  3. js九宫格的碰撞检测

    说来惭愧,我一直以为四四方方的拖拽碰撞检测是一个比较容易的事情,后来试过一次,真是让我耗费了无数的脑细胞,原理其实不难,但是具体做起来可就让我很恶心,这可能跟我驾驭的代码数量有关系,我一般也就写半屏幕 ...

  4. 用PyAIML开发简单的对话机器人

    AIML files are a subset of Extensible Mark-up Language (XML) that can store different text patterns ...

  5. 业务对象(BO)设计

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  6. T-SQL排名函数

    提到排名函数我们首先可能想到的是order by,这个是排序,不是排名,排名需要在前面加个名次序号的,order by是没有这个功能的.还可能会想到identity(1,1),它也给了一个序号,但是不 ...

  7. SQL 调优专题总结

    oracle 的优化器: oracle 有两种优化器:基于规则的优化器(rbo/rule based optimizer)和基于代价的优化器(cbo/cost based optimizer). 有时 ...

  8. PostgreSQL数据库服务端监听设置及客户端连接方法教程

    众所周知,PostgreSQL 是一个自由的对象-关系数据库服务器(数据库管理系统),是一个可以免费使用的开放源代码数据库系统.本文详细介绍了PostgreSQL数据库服务端监听设置及客户端连接方法, ...

  9. JavaWeb学习总结(一)—JavaWeb开发入门及环境搭建

    一.基本概念 1.1.软件体系结构 1.C/S:Client/Servlet,例如QQ就是CS结构需要编写服务器端程序和客户端程序.缺点:更新需要两端,总要求客户下载新的客户端程序优点:安全性比较好2 ...

  10. python的最最最最最基本语法(2)

    函数的定义: 使用def语句,依次写出函数名.括号.括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回. 当用return 返回多个值时,返回的其实是一个tuple, ...