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. apache commons Java包简介

    更多信息,请参考:http://commons.apache.org/ 一.Commons BeanUtils说明:针对Bean的一个工具集.由于Bean往往是有一堆get和set组成,所以BeanU ...

  2. VC++打开对话框选择一个文件夹路径 BROWSEINFO结构

    typedef struct _browseinfoW { HWND hwndOwner; PCIDLIST_ABSOLUTE pidlRoot; LPWSTR pszDisplayName; // ...

  3. Python3基础 print 自带换行功能

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  4. UVA 11404 五 Palindromic Subsequence

     Palindromic Subsequence Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu ...

  5. CodeForces 508C Anya and Ghosts

     Anya and Ghosts Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u S ...

  6. jquery 之height(),innerHeight(),outerHeight()方法区别详解

    在jQuery中,获取元素高度的函数有3个,它们分别是height(). innerHeight().outerHeight(). 与此相对应的是,获取元素宽度的函数也有3个,它们分别是width() ...

  7. Java——Socket编程(一)

    1. 网络基础知识 两台机器之间需要进行通信,需要满足的条件: 每个机器有一个唯一的标识符(IP地址): 他们之间进行通信需要用同一种语言(协议): 每台主机上面有多个应用程序,如QQ,微博,迅雷等, ...

  8. An Example of Pre-Query and Post-Query Triggers in Oracle Forms With Using Display_Item to Highlight Dynamically

    Example is given for Pre-Query and Post-Query triggers in Oracle Forms, with using Display_Itembuilt ...

  9. python中的类中属性元素加self.和不加self.的区别

    在类中,self只能在函数中使用,表示的是实例属性,就是每个实例可以设置不值,而不相互影响.如果在类级别使用没有self的属性,是类属性,一般作为全局变量来用的.事实上:就是一个是类属性 一个是对象属 ...

  10. 基于ADL5317的雪崩光电二极管(APD)偏压控制/光功率监测电路的设计

    1 引言 目前,雪崩光电二极管(APD)作为一种高灵敏.能精确接收数据和测量光功率的光探测器件广泛应用于光纤传感.光纤通信网络中.它借助于内部强电场作用产生雪崩倍增效应,具有极高的内部增益(可达102 ...