libuv裡的幾個缺陷,libuv裡缺陷

來源:互聯網
上載者:User

libuv裡的幾個缺陷,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;        // 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時一定要小心了。

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.