1# libuv
2
3## 简介
4
5[libuv](http://libuv.org/)是一个跨平台库,基于事件驱动来实现异步I/O,适用于网络编程和文件系统操作。它是Node.js的核心库之一,也被其他语言的开发者广泛使用。
6
7## 支持的能力
8
9[libuv](http://libuv.org/)实现了跨平台的基于事件驱动的异步I/O。
10
11支持标准库接口。
12
13## 引入libuv能力
14
15如果开发者需要使用libuv相关功能,首先请添加头文件:
16
17```c
18#include <uv.h>
19```
20
21其次在CMakeLists.txt中添加以下动态链接库:
22
23```
24libuv.so
25```
26
27## 接口列表
28
29详见[libuv支持的API文档](http://docs.libuv.org/en/v1.x/api.html)。
30
31## OpenHarmony引入libuv的背景
32
33在OpenHarmony的早期版本中,为了兼容node的生态,将node的Node-API引入到系统中,方便node开发者快速接入OpenHarmony,扩展自己的js接口。同时引入了node的事件循环实现库——libuv。
34
35### 演进方向
36
37随着 OpenHarmony 的逐步完善,我们计划在未来的版本中,逐步将应用模型中的事件循环归一,并增强 OpenHarmony 自身的事件循环,以解决许多双 loop 机制下的调度问题,并为开发者提供更加完善的任务优先级、插队等与任务主循环交互的方法。
38
39开发者应尽可能避免在 `napi_get_uv_event_loop` 接口(已在API12中标记废弃)获取的应用主 loop 上使用 libuv 的 ndk 进行操作,因为这可能会带来各种问题,并给未来的兼容性变更带来大量的工作量。
40
41如果开发者希望跟主线程事件循环交互,比如插入任务等,应当使用[Node-API提供的接口](../../napi/napi-data-types-interfaces.md)。
42
43OpenHarmony 还将长期通过Node-API来为开发者提供和主线程交互及扩展js接口的能力,但会屏蔽实现层使用的事件循环。尽管我们在API12中给`napi_get_uv_event_loop`接口标记了废弃,但Node-API的主要功能接口将会长期维护,并保证与node的原生行为一致,来保证熟悉node.js的扩展机制的开发者方便地将自己的已有代码接入到OpenHarmony中来。
44
45如果您对 libuv 非常熟悉,并自信能够处理好所有的内存管理和多线程问题,您仍可以像使用原生 libuv 一样,自己启动线程,并在上面使用 libuv 完成自己的业务。在没有特殊版本要求的情况下,您不需要额外引入 libuv库到您的应用工程中。
46
47### 当前问题和解决方案
48
49根据现有机制,一个线程上只能存在一个事件循环,为了适配系统应用的主事件循环,在主线程上的js环境中,uvloop中的事件处理是由主事件循环监听其fd,触发一次`uv_run`来驱动的。因此部分依赖uvloop始终循环的功能无法生效。
50
51基于上述,比较常用的场景和解决方案有:
52
53#### 场景一、在JS主线程抛异步任务到工作线程执行,在主线程中执行JS代码处理返回结果
54
55**错误示例:**
56
57在native层直接通过调用`napi_get_uv_event_loop`接口获取系统loop,调用libuv NDK接口实现相关功能。
58
59```cpp
60#include "napi/native_api.h"
61#include "uv.h"
62#define LOG_DOMAIN 0X0202
63#define LOG_TAG "MyTag"
64#include <hilog/log.h>
65#include <thread>
66#include <sys/eventfd.h>
67#include <unistd.h>
68
69static void execute(uv_work_t *work) {
70    OH_LOG_INFO(LOG_APP, "ohos in execute");
71}
72
73static void complete(uv_work_t *work, int status) {
74    OH_LOG_INFO(LOG_APP, "ohos in complete"); 
75    delete work;
76}
77static napi_value Add(napi_env env, napi_callback_info info)
78{
79    napi_value work_name;
80    uv_loop_s *loop = nullptr;
81    /* 获取应用js主线程的uv_loop */
82    napi_get_uv_event_loop(env, &loop);
83    uv_work_t *work = new uv_work_t;
84    int ret = uv_queue_work(loop, work, execute, complete);
85    if (ret != 0) {
86        OH_LOG_INFO(LOG_APP, "delete work");
87        delete work;
88    }
89    return 0;
90}
91
92EXTERN_C_START
93static napi_value Init(napi_env env, napi_value exports){
94    napi_property_descriptor desc[] = {{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}};
95    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
96    return exports;
97}
98EXTERN_C_END
99    
100static napi_module demoModule = {
101    .nm_version = 1,
102    .nm_flags = 0,
103    .nm_filename = nullptr,
104    .nm_register_func = Init,
105    .nm_modname = "entry",
106    .nm_priv = ((void *)0),
107    .reserved = {0},
108};
109
110extern "C" __attribute__((constructor)) void RegisterEntryModule(void){
111    napi_module_register(&demoModule);
112}
113```
114
115**正确示例:**
116
117可通过`napi_create_async_work`、`napi_queue_async_work`搭配使用。
118
119```cpp
120#include "napi/native_api.h"
121#include "uv.h"
122#define LOG_DOMAIN 0X0202
123#define LOG_TAG "MyTag"
124#include <hilog/log.h>
125#include <thread>
126#include <sys/eventfd.h>
127#include <unistd.h>
128uv_loop_t *loop;
129napi_value jsCb;
130int fd = -1;
131
132static napi_value Add(napi_env env, napi_callback_info info)
133{
134    napi_value work_name;
135    napi_async_work work;
136    napi_create_string_utf8(env, "ohos", NAPI_AUTO_LENGTH, &work_name);
137    /* 第四个参数是异步线程的work任务,第五个参数为主线程的回调 */
138    napi_create_async_work(env, nullptr, work_name, [](napi_env env, void* data){
139        OH_LOG_INFO(LOG_APP, "ohos in execute");
140    }, [](napi_env env, napi_status status, void *data){
141        /* 不关心具体实现 */
142        OH_LOG_INFO(LOG_APP, "ohos in complete");
143        napi_delete_async_work(env, (napi_async_work)data);
144    }, nullptr, &work);
145    /* 通过napi_queue_async_work触发异步任务执行 */
146    napi_queue_async_work(env, work);
147    return 0;
148}
149
150EXTERN_C_START
151static napi_value Init(napi_env env, napi_value exports){
152    napi_property_descriptor desc[] = {{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}};
153    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
154    return exports;
155}
156EXTERN_C_END
157    
158static napi_module demoModule = {
159    .nm_version = 1,
160    .nm_flags = 0,
161    .nm_filename = nullptr,
162    .nm_register_func = Init,
163    .nm_modname = "entry",
164    .nm_priv = ((void *)0),
165    .reserved = {0},
166};
167
168extern "C" __attribute__((constructor)) void RegisterEntryModule(void){
169    napi_module_register(&demoModule);
170}
171```
172
173#### 场景二、在native侧向应用主循环抛fd事件,接口无法生效
174
175由于应用主循环仅仅接收fd事件,在监听了uvloop中的backend_fd后,只有该fd事件被触发才会执行一次`uv_run`。这就意味着,在应用主循环中调用uv接口,如果不触发一次fd事件,`uv_run`将永远不会被执行,最后导致libuv的接口正常调用时不生效(仅当应用中没有触发uvloop中的fd事件时)。
176
177**错误示例:**
178
179我们以`uv_poll_start`接口举例,来说明在OpenHarmony中,我们像使用原生libuv一样调用`uv_poll_start`接口时无法生效的问题。
180
181```cpp
182#include "napi/native_api.h"
183#include "uv.h"
184#define LOG_DOMAIN 0X0202
185#define LOG_TAG "MyTag"
186#include <hilog/log.h>
187#include <thread>
188#include <sys/eventfd.h>
189#include <unistd.h>
190uv_loop_t *loop;
191napi_value jsCb;
192int fd = -1;
193void poll_handler(uv_poll_t* handle,int status, int events){
194    OH_LOG_INFO(LOG_APP, "ohos poll print");
195}
196static napi_value TestClose(napi_env env, napi_callback_info info){
197    std::thread::id this_id = std::this_thread::get_id();
198    OH_LOG_INFO(LOG_APP, "ohos thread id : %{public}ld\n", this_id);
199    size_t argc = 1;
200    napi_value workBname;
201    
202    napi_create_string_utf8(env, "test", NAPI_AUTO_LENGTH, &workBname);
203    
204    napi_get_cb_info(env, info, &argc, &jsCb, nullptr, nullptr);
205    // 获取事件循环
206    napi_get_uv_event_loop(env, &loop);
207    // 创建一个eventfd
208    fd = eventfd(0, 0);
209    OH_LOG_INFO(LOG_APP, "fd is %{public}d\n",fd);
210    uv_poll_t* poll_handle = new uv_poll_t;
211    // 初始化一个poll句柄,并将其与eventfd关联
212    uv_poll_init(loop, poll_handle, fd);
213    // 开始监听poll事件
214    uv_poll_start(poll_handle, UV_READABLE, poll_handler);
215    // 创建一个新线程,向eventfd写入数据
216    std::thread mythread([](){
217        for (int i = 0; i < 8; i++){
218            int value = 10;
219            int ret = eventfd_write(fd, value);
220            if (ret == -1){
221                OH_LOG_INFO(LOG_APP, "write failed!\n");
222                continue;
223            }
224        }
225    });
226    mythread.detach();
227    return 0;
228}
229EXTERN_C_START
230static napi_value Init(napi_env env, napi_value exports){
231    napi_property_descriptor desc[] = {{"testClose", nullptr, TestClose, nullptr, nullptr, nullptr, napi_default, nullptr}};
232    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
233    return exports;
234}
235EXTERN_C_END
236    
237static napi_module demoModule = {
238    .nm_version = 1,
239    .nm_flags = 0,
240    .nm_filename = nullptr,
241    .nm_register_func = Init,
242    .nm_modname = "entry",
243    .nm_priv = ((void *)0),
244    .reserved = {0},
245};
246
247extern "C" __attribute__((constructor)) void RegisterEntryModule(void){
248    napi_module_register(&demoModule);
249}
250```
251
252在上述代码中,流程如下:
253
2541. 首先通过`napi_get_uv_event_loop`接口获取到应用主线程的uvloop。
2552. 然后创建一个eventfd。
2563. 初始化uv_poll_t,并启动该句柄使其生效,在eventfd可读时触发回调函数`poll_handler`。
2574. 新开一个线程,向eventfd里写入字符。
258
259执行上述代码,poll_handler并不能正常打印。这是由于应用主线程是靠fd驱动来执行`uv_run`的,而非以UV_RUN_DEFAULT模式来进行循环。尽管uvloop中的backend_fd已经被event_handler监听,但是当执行`uv_poll_start`的时候,fd并未通过`epoll_ctl`加入到backend_fd中被其监听,**而是在下一次`uv_run`中的`uv__io_poll`这个函数才会执行`epoll_ctl`函数。因此,如果应用进程中没有其他触发backend_fd事件的时候,libuv接口的正常使用可能不会达到开发者的预期。**
260
261**临时方案:**
262
263在当下的系统版本中,我们并不推荐开发者直接通过`napi_get_uv_event_loop`获取应用主线程的uvloop进行业务逻辑的开发。如果当前系统接口无法满足开发者的开发需求,确有必要使用libuv来实现业务功能,为了使libuv接口在主线程上生效,我们可以在调用类似*uv_xxx_start*后,执行一次`uv_async_send`的方式来主动触发应用主线程执行一次`uv_run`。这样可以保证该接口生效并正常执行。
264
265针对上述无法生效的代码示例,可以修改如下使其生效:
266
267```cpp
268#include "napi/native_api.h"
269#include "uv.h"
270#define LOG_DOMAIN 0x0202
271#define LOG_TAG "MyTag"
272#include <hilog/log.h>
273#include <thread>
274#include <sys/eventfd.h>
275#include <unistd.h>
276uv_loop_t *loop;
277napi_value jsCb;
278int fd = -1;
279void poll_handler(uv_poll_t* handle,int status, int events){
280    OH_LOG_INFO(LOG_APP, "ohos poll print");
281}
282static napi_value TestClose(napi_env env, napi_callback_info info){
283    std::thread::id this_id = std::this_thread::get_id();
284    OH_LOG_INFO(LOG_APP, "ohos thread id : %{public}ld\n", this_id);
285    size_t argc = 1;
286    napi_value workBName;
287    
288    napi_create_string_utf8(env, "test", NAPI_AUTO_LENGTH, &workBName);
289    
290    napi_get_cb_info(env, info, &argc, &jsCb, nullptr, nullptr);
291
292    napi_get_uv_event_loop(env, &loop);
293
294    fd = eventfd(0, 0);
295    OH_LOG_INFO(LOG_APP, "fd is %{public}d\n",fd);
296    uv_poll_t* poll_handle = new uv_poll_t;
297    uv_poll_init(loop, poll_handle, fd);
298    uv_poll_start(poll_handle, UV_READABLE, poll_handler);
299
300    // 主动触发一次fd事件,让主线程执行一次uv_run
301    uv_async_send(&loop->wq_async);
302    
303    std::thread mythread([](){
304        for (int i = 0; i < 8; i++){
305            int value = 10;
306            int ret = eventfd_write(fd, value);
307            if (ret == -1){
308                OH_LOG_INFO(LOG_APP, "write failed!\n");
309                continue;
310            }
311        }
312    });
313    mythread.detach();
314    return 0;
315}
316
317EXTERN_C_START
318static napi_value Init(napi_env env, napi_value exports){
319    napi_property_descriptor desc[] = {{"testClose", nullptr, TestClose, nullptr, nullptr, nullptr, napi_default, nullptr}};
320    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
321    return exports;
322}
323EXTERN_C_END
324    
325static napi_module demoModule = {
326    .nm_version = 1,
327    .nm_flags = 0,
328    .nm_filename = nullptr,
329    .nm_register_func = Init,
330    .nm_modname = "entry",
331    .nm_priv = ((void *)0),
332    .reserved = {0},
333};
334
335extern "C" __attribute__((constructor)) void RegisterEntryModule(void){
336    napi_module_register(&demoModule);
337}
338```
339
340## libuv使用指导
341
342**重要:libuv NDK中所有依赖`uv_run`的接口在当前系统的应用主循环中无法及时生效,并且可能会导致卡顿掉帧的现象。因此不建议直接在JS主线程上使用libuv NDK接口,对于异步任务执行及与使用线程安全函数与主线程通信,开发者可以直接调用Node-API接口来实现相关功能。**
343
344### libuv接口与Node-API接口对应关系
345
346当前OpenHarmony提供了一些Node-API接口,可以替换libuv接口的使用。主要包括异步任务相关接口,线程安全的函数调用接口。
347
348#### 异步任务接口
349
350当开发者需要执行一个比较耗时的操作但又不希望阻塞主线程执行时,libuv提供了底层接口`uv_queue_work`帮助开发者在异步线程中执行耗时操作,然后将结果回调到主线程上进行处理。
351
352在Node-API中,通常可以通过[napi_async_work](../../napi/use-napi-asynchronous-task.md)相关函数来实现异步开发的功能。
353
354相关函数为:
355
356```cpp
357// 创建一个新的异步工作
358// env:指向当前环境的指针
359// async_resource:可选的资源对象,用于跟踪异步操作
360// async_resource_name:可选的字符串,用于描述异步资源
361// execute:一个回调函数,它将在一个新的线程中执行异步操作
362// complete:一个回调函数,它将在异步操作完成后被调用
363// data:用户定义的数据,它将被传递给execute和complete回调函数
364// result:指向新创建的异步工作的指针
365napi_status napi_create_async_work(napi_env env,
366                                  napi_value async_resource,
367                                  napi_value async_resource_name,
368                                   napi_async_execute_callback execute,
369                                 napi_async_complete_callback complete,
370                                  void* data,
371                                  napi_async_work* result);
372
373// 将异步工作添加到队列中
374// env:指向当前环境的指针
375// work:指向异步工作的指针
376napi_status napi_queue_async_work(napi_env env, napi_async_work work);
377
378// 删除异步工作
379// env:指向当前环境的指针
380// work:指向异步工作的指针
381napi_status napi_delete_async_work(napi_env env, napi_async_work work);
382```
383
384#### 跨线程共享和调用的线程安全函数
385
386当开发者想传入某个回调函数到应用主线程上时,libuv的实现方式一般使用`uv_async_t`句柄用于线程间通信。
387
388相关函数包含:
389
390- uv_async_init()
391- uv_async_send()
392
393Node-API与之对应的接口为[napi_threadsafe_function](../../napi/use-napi-thread-safety.md)相关函数。
394
395相关函数:
396
397```cpp
398// 用于创建一个线程安全的函数,该函数可以在多个线程中调用,而不需要担心数据竞争或其他线程安全问题
399// env:指向NAPI环境的指针,用于创建和操作Javascript值
400// func:指向JavaScript函数的指针
401// resource_name:指向资源名称的指针,这个名称将用于日志和调试
402// max_queue_size:一个整数,表示队列的最大大小,当队列满时,新的调用将被丢弃
403// initial_thread_count:一个整数,表示初始线程数,这些线程将在创建函数时启动
404// context:指向上下文的指针,这个上下文将被传递给call_js_func函数
405// call_js_func:指向回调函数的指针,这个函数将在Javascript函数被调用时被调用
406// finalize:指向finalize函数的指针,这个函数将在线程安全函数被销毁时被调用
407// result:指向napi_threadsafe_function结构的指针,这个结构将被填充为新创建的线程安全函数
408napi_status napi_create_threadsafe_function(napi_env env,
409                                           napi_value func,
410                                           const char* resource_name,
411                                           size_t max_queue_size,
412                                           size_t initial_thread_count,
413                                           void* context,
414                                           napi_threadsafe_function_call_js call_js_func,
415                                           napi_threadsafe_function_finalize finalize,
416                                           napi_threadsafe_function* result);
417
418// 获取一个线程安全的函数
419// function:指向线程安全函数的指针
420napi_status napi_acquire_threadsafe_function(napi_threadsafe_function function);
421
422// 调用一个线程安全的函数
423// function:指向线程安全函数的指针
424// data:用户数据
425napi_status napi_call_threadsafe_function(napi_threadsafe_function function, void* data);
426
427// 释放一个线程安全的函数
428// function:指向线程安全函数的指针
429napi_status napi_release_threadsafe_function(napi_threadsafe_function function);
430
431```
432
433除此之外,如果开发者需要libuv其他原生接口来实现业务功能,为了让开发者正确使用libuv提供的接口能力,避免因为错误使用而陷入到问题当中。在后续章节,我们将逐步介绍libuv的一些基本概念和OpenHarmony系统中常用函数的正确使用方法,它仅仅可以保证开发者使用libuv接口的时候不会出现应用进程崩溃等现象。另外,我们还统计了在当前应用主线程上可以正常使用的接口,以及无法在应用主线程上使用的接口。
434
435### 接口汇总说明
436
437|  接口类型    |  接口汇总    |
438| ---- | ---- |
439|   [loop概念及相关接口](#libuv中的事件循环)   |  uv_loop_init    |
440|   [loop概念及相关接口](#libuv中的事件循环)   |   uv_loop_close   |
441|   [loop概念及相关接口](#libuv中的事件循环)   |  uv_default_loop    |
442|   [loop概念及相关接口](#libuv中的事件循环)   |   uv_run   |
443|   [loop概念及相关接口](#libuv中的事件循环)   |    uv_loop_alive  |
444|   [loop概念及相关接口](#libuv中的事件循环)   |  uv_stop    |
445|   [Handle概念及相关接口](#libuv中的handles和requests)   |  uv_poll\_\* |
446|   [Handle概念及相关接口](#libuv中的handles和requests)   |  uv_timer\_\* |
447|   [Handle概念及相关接口](#libuv中的handles和requests)   |  uv_async\_\* |
448|   [Handle概念及相关接口](#libuv中的handles和requests)   |   uv_signal\_\*   |
449|   [Handle概念及相关接口](#libuv中的handles和requests)   |   uv_fs\_\*  |
450|   [Request概念及相关接口](#libuv中的handles和requests)   |  uv_random    |
451|   [Request概念及相关接口](#libuv中的handles和requests)   |  uv_getaddrinfo    |
452|   [Request概念及相关接口](#libuv中的handles和requests)   |  uv_getnameinfo    |
453|   [Request概念及相关接口](#libuv中的handles和requests)   |  uv_queue_work    |
454|   [线程间通信原理及相关接口](#线程间通信)   |  uv_async_init    |
455|   [线程间通信原理及相关接口](#线程间通信)   |  uv_async_send    |
456|   [线程池概念及相关接口](#线程池)   |  uv_queue_work    |
457
458### 线程安全函数
459
460在libuv中,由于涉及到大量的异步任务,稍有不慎就会陷入到多线程问题中。在这里,我们对libuv中常用的线程安全函数和非线程安全函数做了汇总。若开发者在多线程编程中调用了非线程安全的函数,势必要对其进行加锁保护或者保证代码的正确运行时序。否则将陷入到crash问题中。
461
462线程安全函数:
463
464- uv_async_init():初始化异步句柄。
465- uv_async_send():向异步句柄发送信号,可以在任何线程中调用。
466- uv_thread_create():创建一个新线程并执行指定的函数,可以在任何线程中调用。
467- uv_fs\_\*():文件相关操作(uv\_fs\_\* 表示以uv\_fs\_开头的支持文件IO的系列函数)。
468- uv_poll\_\*():poll事件相关函数(uv\_poll\_\* 表示以uv\_poll\_开头的支持poll IO的系列函数)。
469- 锁相关的操作,如uv\_mutex\_lock()、uv\_mutex\_unlock()等等。
470
471**提示:所有形如uv_xxx_init的函数,即使它是以线程安全的方式实现的,但使用时要注意,避免多个线程同时调用uv_xxx_init,否则它依旧会引起多线程资源竞争的问题。最好的方式是在事件循环线程中调用该函数。**
472
473**注:uv_async_send函数被调用后,回调函数是被异步触发的。如果调用了多次uv_async_send,libuv只保证至少有一次回调会被执行。这就可能导致一旦对同一句柄触发了多次uv_async_send,libuv对回调的处理可能会违背开发者的预期。**
474
475非线程安全函数:
476
477- uv\_os\_unsetenv():删除环境变量
478- uv\_os\_setenv():设置环境变量
479- uv\_os\_getenv():获取环境变量
480- uv\_os\_environ():检索所有的环境变量
481- uv\_os\_tmpdir():获取临时目录
482- uv\_os\_homedir():获取家目录
483
484### libuv中的事件循环
485
486事件循环是libuv中最核心的一个概念,loop负责管理整个事件循环的所有资源,它贯穿于整个事件循环的生命周期。通常将`uv_run`所在的线程称为该事件循环的主线程。
487
488#### 事件循环运行的三种方式
489
490`UV_RUN_DEFAULT`:默认轮询方式,该模式将会一直运行下去,直到loop中没有活跃的句柄和请求。
491
492`UV_RUN_ONCE`:一次轮询模式,如果pending_queue中有回调函数,则执行,然后跳过`uv__io_poll`函数。此模式默认认为loop中一定有事件发生。
493
494`UV_RUN_NOWAIT`:非阻塞模式,该模式下不会执行pending_queue,而是直接执行一次I/O轮询(`uv__io_poll`)。
495
496#### 常用接口
497
498```cpp
499int uv_loop_init(uv_loop_t* loop);
500```
501
502  对loop进行初始化。
503
504```cpp
505int uv_loop_close(uv_loop_t* loop);
506```
507
508  关闭loop,该函数只有在loop中所有的句柄和请求都关闭后才能成功返回,否则将返回UV_EBUSY。
509
510```cpp
511uv_loop_t* uv_default_loop(void);
512```
513
514  该函数创建一个进程级的loop。在OpenHarmony中,由于目前的应用主循环及其他js工作线程还存在着libuv的loop。因此我们不建议开发者使用该函数来创建loop并实现业务功能。在系统的双loop改造完成后,开发者可以根据业务要求来使用该接口。
515
516```cpp
517int uv_run(uv_loop_t* loop, uv_run_mode mode);
518```
519
520  启动事件循环。运行模式可查看[事件循环运行的三种方式](#事件循环运行的三种方式)。
521
522```cpp
523int uv_loop_alive(uv_loop_t loop);
524```
525
526  判断loop是否处于活跃状态。
527
528```cpp
529void uv_stop(uv_loop_t* loop);
530```
531
532  该函数用来停止一个事件循环,在loop的下一次迭代中才会停止。如果该函数发生在I/O操作之前,将不会阻塞而是直接跳过`uv__io_poll`。
533
534**使用技巧**:在使用loop时,需要特别注意`uv_stop`函数的使用。开发者需要确保`uv_stop`前,通知与loop相关的所有线程的handle都关闭。参考代码如下:
535
536```cpp
537int stop_loop(uv_loop_t* loop)
538{
539    uv_stop(loop);
540    auto const ensure_close = [](uv_handle_t* handle, void*) {
541        if (uv_is_closing(handle)) {
542            return;
543        } else {
544            uv_close(handle, nullptr);
545        }
546    };
547    // 遍历所有句柄,如果handle处于活跃状态,调用ensure_close。
548    uv_walk(loop, ensure_close, nullptr);
549
550    // 继续运行uv_run,直到loop中不存在活跃的句柄和请求为止。
551    while(true) {
552        if (uv_run(loop, UV_RUN_DEFAULT) == 0) {
553            break;
554        }
555    }
556
557    // 最后检查loop状态。
558    if (uv_loop_alive(loop) != 0) {
559        return -1;
560    }
561    return 0;
562}
563```
564
565### libuv中的handles和requests
566
567handle表示一个持久性的对象,通常挂载到loop中对应的handle_queue队列上。如果handle处于活跃状态,每次`uv_run`都会处理handle中的回调函数。
568
569request表示一个短暂性的请求,一个request只触发一次回调操作。
570
571下面是OpenHarmony系统中最常用的几个Handles和Requests:
572
573```cpp
574/* Handle Type */
575typedef struct uv_handle_s uv_handle_t;
576typedef struct uv_timer_s uv_timer_t;
577typedef struct uv_async_s uv_async_t;
578typedef struct uv_signal_s uv_signal_t;
579
580/* Request Type */
581typedef struct uv_req_s uv_req_t;
582typedef struct uv_work_s uv_work_t;
583typedef struct uv_fs_s uv_fs_t;
584```
585
586**注:在handles中,uv_xxx_t继承了uv_handle_t;在requests中,uv_work_t继承了uv_req_t。**
587
588对于libuv中的handles,对其有个正确的认识并管理好它的生命周期至关重要。handle作为一个长期存在于loop中的句柄,在使用中,开发者应遵循下面的原则:
589
5901. 句柄的初始化工作应在事件循环的线程中进行。
5912. 若由于业务问题,句柄需要在其他工作线程初始化,在使用之前用原子变量判断是否初始化完成。
5923. 句柄在确定后续不再使用后,调用`uv_close`将句柄从loop中摘除。
593
594在这里,需要特别说明一下`uv_close`的使用方法。`uv_close`被用来关闭一个handle,但是它是异步地关闭handle。函数原型为:
595
596```cpp
597void uv_close(uv_handle_t* handle, uv_close_cb close_cb)
598```
599
600  handle:要关闭的句柄。
601  close_cb:处理该句柄的函数,用来进行内存管理等操作。
602
603`uv_close`调用后,它首先将要关闭的handle挂载到loop中的closing_handles队列上,然后等待loop所在线程运行`uv__run_closing_handles`函数。最后回调函数close_cb将会在loop的下一次迭代中执行。因此,释放内存等操作应该在close_cb中进行。并且这种异步的关闭操作会带来多线程上的问题,开发者需要谨慎处理`uv_close`的时序问题,并且保证在close_cb执行之前Handles的生命周期。这是一篇在系统中存在的一些典型代码示例,可供开发者参考。
604
605**Tips**:在[libuv官方文档](http://libuv.org/)中,有个经验法则需要在此提示一下。原文翻译:如果 uv_foo_t 类型的句柄具有 `uv_foo_start()` 函数,则从调用该函数的那一刻起,它就处于活动状态。 同样,`uv_foo_stop()`再次停用句柄。
606
607而对于libuv中的requests,开发者需要确保一点,通过动态申请的request,在loop所在线程的回调函数中释放它即可。用uv_work_t举例,代码可参考如下:
608
609```cpp
610uv_work_t* work = new uv_work_t;
611uv_queue_work(loop, work, [](uv_work_t* req) {
612    // 异步操作
613}, [](uv_work_t* req, int status) {
614    // 回调操作
615    delete req;
616});
617```
618
619此外,使用`uv_queue_work`需要注意如下几点:
620
6211. libuv中`uv_queue_work`的工作流程为:将`work_cb`挂在线程池中执行,然后将`after_work_cb`挂在回调队列wq中,然后触发一次fd事件;loop所在线程接收到fd事件,便会执行对应的after_work_cb。`uv_queue_work`调用完后,并不代表其中的任何一个任务执行完,仅代表将work_cb插入到异步线程池的任务队列中。
6222. `uv_queue_work`仅限于在loop所在的线程中调用,这样不会有多线程安全问题。不建议使用如下用法:A线程获取到B线程的loop,并通过`uv_queue_work`的方式,把回调放在B线程中执行。
6233. 为了避免低效的异步任务提交,请不要使用将work_cb实现为空任务,并将任务在after_work_cb中执行的方式调用`uv_queue_work`提交异步任务,请使用`napi_send_event`接口。
624
625`napi_send_event`函数的声明为:
626
627```cpp
628napi_status napi_send_event(napi_env env, const std::function<void()> cb, napi_event_priority priority);
629```
630
631#### libuv timer使用规范
632
633使用libuv timer需要遵守如下约定:
634
6351. 请不要在多个线程中使用libuv的接口(uv_timer_start、uv_timer_stop和uv_timer_again)同时操作同一个loop的timer heap,否则将导致崩溃,如果想要使用libuv的接口操作定时器,请**保持在与当前env绑定的loop所在线程上操作**;
6362. 如因业务需求往指定线程抛定时器,请使用`uv_async_send`线程安全函数实现。
637
638##### 错误使用timer示例
639
640以下错误示例中,由于在多个线程操作同一个loop的timer heap,崩溃率极高。
641
642ArkTS侧:
643
644```typescript
645import { hilog } from '@kit.PerformanceAnalysisKit';
646import testNapi from 'libentry.so'
647
648function waitforRunner(): number {
649    "use concurrent"
650    hilog.info(0xff, "testTag", "executed");
651    return 0;
652}
653
654@Entry
655@Component
656struct Index {
657  build() {
658    Row() {
659      Column() {
660        Button("TimerTest")
661          .width('40%')
662          .fontSize('14fp')
663          .onClick(() => {
664            let i: number = 20;
665            while (i--) {
666              setTimeout(waitforRunner, 200);
667              testNapi.testTimer();
668          }
669        }).margin(20)
670      }.width('100%')
671    }.height('100%')
672  }
673}
674```
675
676Native C++侧:
677
678```cpp
679#include <napi/native_api.h>
680#include <uv.h>
681#define LOG_DOMAIN 0x0202
682#define LOG_TAG "MyTag"
683#include "hilog/log.h"
684#include <thread>
685#include <unistd.h>
686
687static napi_value TestTimer(napi_env env, napi_callback_info info) {
688    uv_loop_t* loop = nullptr;
689    uv_timer_t* timer = new uv_timer_t;
690    
691    napi_get_uv_event_loop(env, &loop);
692    uv_timer_init(loop, timer);
693    std::thread t1([&loop, &timer](){
694        uv_timer_start(timer, [](uv_timer_t* timer){
695            uv_timer_stop(timer);
696        }, 1000, 0);
697    });
698    
699    t1.detach();
700    return 0;
701}
702
703EXTERN_C_START
704static napi_value Init(napi_env env, napi_value exports) {
705    napi_property_descriptor desc[] = {
706        {"testTimer", nullptr, TestTimer, nullptr, nullptr, nullptr, napi_default, nullptr},
707    };
708    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
709    return exports;
710}
711EXTERN_C_END
712    
713static napi_module demoModule = {
714    .nm_version = 1,
715    .nm_flags = 0,
716    .nm_filename = nullptr,
717    .nm_register_func = Init,
718    .nm_modname = "entry",
719    .nm_priv = ((void *)0),
720    .reserved = {0},
721};
722
723extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
724    napi_module_register(&demoModule);
725}
726```
727
728index.d.ts增加如下代码:
729
730```typescript
731export const testTimer:() => number;
732```
733
734##### 正确使用timer示例
735
736**场景一:** 在上述场景中,需保证在native主线程上进行timer的相关操作。将上述TestTimer函数的代码做如下修改,便可以避免崩溃发生。
737
738```cpp
739static napi_value TestTimer(napi_env env, napi_callback_info info) {
740    uv_loop_t* loop = nullptr;
741    uv_timer_t* timer = new uv_timer_t;
742    
743    napi_get_uv_event_loop(env, &loop);
744    uv_timer_init(loop, timer);
745    uv_timer_start(timer, [](uv_timer_t* timer){
746        uv_timer_stop(timer);
747    }, 1000, 0);
748
749    return 0;
750}
751```
752
753**场景二:** 如果需要在指定的子线程抛定时器,请使用线程安全函数`uv_async_send`实现。
754
755ArkTS测:
756
757```typescript
758import { hilog } from '@kit.PerformanceAnalysisKit';
759import testNapi from 'libentry.so'
760
761function waitforRunner(): number {
762    "use concurrent"
763    hilog.info(0xff, "testTag", "executed");
764    return 0;
765}
766
767@Entry
768@Component
769struct Index {
770  build() {
771    Row() {
772      Column() {
773        Button("TestTimerAsync")
774          .width('40%')
775          .fontSize('14fp')
776          .onClick(() => {
777              testNapi.testTimerAsync();  // 初始化async句柄
778        }).margin(20)
779          
780          Button("TestTimerAsyncSend")
781          .width('40%')
782          .fontSize('14fp')
783          .onClick(() => {
784              testNapi.testTimerAsyncSend();  // 子线程调用uv_async_send执行timer_cb
785        }).margin(20)
786      }.width('100%')
787    }.height('100%')
788  }
789}
790```
791
792Native C++测:
793
794```c++
795#include <napi/native_api.h>
796#include <uv.h>
797#define LOG_DOMAIN 0x0202
798#define LOG_TAG "MyTag"
799#include "hilog/log.h"
800#include <thread>
801#include <unistd.h>
802uv_async_t* async = new uv_async_t;
803
804// 执行创建定时器操作
805void timer_cb(uv_async_t* handle) {
806    auto loop = handle->loop;
807    uv_timer_t* timer = new uv_timer_t;
808    uv_timer_init(loop, timer);
809    
810    uv_timer_start(timer, [](uv_timer_t* timer){
811        uv_timer_stop(timer);
812    }, 1000, 0);
813}
814
815// 初始化async句柄,绑定对应的回调函数
816static napi_value TestTimerAsync(napi_env env, napi_callback_info info) {
817    uv_loop_t* loop = nullptr;
818	napi_get_uv_event_loop(env, &loop);
819    uv_async_init(loop, async, timer_cb);
820    return 0;
821}
822
823static napi_value TestTimerAsyncSend(napi_env env, napi_callback_info info) {
824    std::thread t([](){
825        uv_async_send(async);  // 在任意子线程中调用uv_async_send,通知主线程调用与async绑定的timer_cb
826    });
827    t.detach();
828    return 0;
829}
830
831EXTERN_C_START
832static napi_value Init(napi_env env, napi_value exports) {
833    napi_property_descriptor desc[] = {
834        {"testTimerAsync", nullptr, TestTimerAsync, nullptr, nullptr, nullptr, napi_default, nullptr},
835        {"testTimerAsyncSend", nullptr, TestTimerAsyncSend, nullptr, nullptr, nullptr, napi_default, nullptr},
836    };
837    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
838    return exports;
839}
840EXTERN_C_END
841    
842static napi_module demoModule = {
843    .nm_version = 1,
844    .nm_flags = 0,
845    .nm_filename = nullptr,
846    .nm_register_func = Init,
847    .nm_modname = "entry",
848    .nm_priv = ((void *)0),
849    .reserved = {0},
850};
851
852extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
853    napi_module_register(&demoModule);
854}
855```
856
857### 线程间通信
858
859上面简单介绍了一些libuv中的基本概念,在这里我们将着重介绍libuv中的线程间通信。
860
861libuv的线程间通信是通过uv_async_t句柄来进行的,相关函数如下:
862
863```cpp
864int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb)
865```
866
867  loop:事件循环loop。
868
869  handle:线程间通信句柄。
870
871  async_cb:回调函数。
872
873  返回:成功,返回0。失败,返回错误码。
874
875```cpp
876int uv_async_send(uv_async_t* handle)
877```
878
879  handle:线程间通信句柄。
880
881  返回:成功,返回0。失败,返回错误码。
882> 说明
883>
884> 1. uv_async_t从调用`uv_async_init`开始后就一直处于活跃状态,除非用`uv_close`将其关闭。
885>
886> 2. uv_async_t的执行顺序严格按照`uv_async_init`的顺序,而非通过`uv_async_send`的顺序来执行的。因此按照初始化的顺序来管理好时序问题是必要的。
887
888![线程间通信原理](./figures/libuv-image-1.png)
889
890示例代码:
891
892```cpp
893#include <bits/stdc++.h>
894#include "uv.h"
895
896uv_loop_t* loop = nullptr;
897uv_async_t* async = nullptr;
898void async_handler(uv_async_t* handle)
899{
900    printf("ohos async print\n");
901}
902
903int main()
904{
905    loop = uv_default_loop();
906    async = new uv_async_t;
907    uv_async_init(loop, async, async_handler);
908    std::thread subThread([]() {
909        for (int i = 0; i < 10; i++) {
910            usleep(100);
911            printf("%dth: subThread triggered\n", i);
912            uv_async_send(async);
913        }
914        // 调用uv_close关闭async,在主循环中释放内存。
915        uv_close((uv_handle_t*)async, [](uv_handle_t* handle) {
916            printf("delete async\n");
917            delete (uv_async_t*)handle;
918        });
919        uv_stop(loop);
920    });
921    subThread.detach();
922    return uv_run(loop, UV_RUN_DEFAULT);
923}
924```
925
926该示例代码仅仅描述了一个简单的场景,步骤如下:
927
9281. 在主线程中初始化async句柄
9292. 新建一个子线程,在里面每隔100毫秒触发一次`uv_async_send`。10次以后调用`uv_close`关闭async句柄。
9303. 在主线程运行事件循环。
931
932可以看到,每触发一次,主线程都会执行一次回调函数。
933
934```
9350th:subThread triggered
936ohos async print
9371th:subThread triggered
938ohos async print
9392th:subThread triggered
940ohos async print
9413th:subThread triggered
942ohos async print
9434th:subThread triggered
944ohos async print
9455th:subThread triggered
946ohos async print
9476th:subThread triggered
948ohos async print
9497th:subThread triggered
950ohos async print
9518th:subThread triggered
952ohos async print
9539th:subThread triggered
954delete async
955```
956
957### 线程池
958
959线程池是libuv的一个核心功能,libuv中的线程池是通过uv_loop_t中的成员变量wq_async来控制工作线程与主线程的通信。核心函数如下:
960
961```cpp
962int uv_queue_work(uv_loop_t* loop,
963                  uv_work_t* req,
964                  uv_work_cb work_cb,
965                  uv_after_work_cb after_work_cb)
966```
967
968work_cb:提交给工作线程的任务。
969
970after_work_cb:loop所在线程的要执行的回调函数。
971
972**注意:** work_cb与after_work_cb的执行有一个时序问题,只有work_cb执行完,通过`uv_async_send(loop->wq_async)`触发fd事件,loop所在线程在下一次迭代中才会执行after_work_cb。只有执行到after_work_cb时,与之相关的uv_work_t生命周期才算结束。
973
974下图为libuv的线程池工作流程,图中流程已简化,默认句柄的pending标志为1,worker线程个数不代表线程池中线程的真实数量。
975
976![libuv线程池工作原理](./figures/libuv-image-3.png)
977
978### OpenHarmony中libuv的使用现状
979
980当前OpenHarmony系统中涉及到libuv的线程主要有主线程、JS Worker线程、Taskpool中的TaskWorker线程以及IPC线程。除了主线程内采用了eventhandler作为主循环,其他线程都是使用libuv中的UV_RUN_DEFAULT运行模式作为当前线程的事件主循环来执行任务。在主线程中,eventhandler通过fd驱动的方式来触发任务的执行,eventhandler监听了uv_loop中的backend_fd。当loop中有fd事件触发的时候,eventhandler会执行一次`uv_run`来执行libuv中的任务。
981
982综上所述,开发者会发现这样一种现象:**同样的libuv接口在主线程上不生效,但在JS Worker线程中就没问题。这主要还是因为主线程上所有不通过触发fd来驱动的uv接口都不会得到及时的响应。**
983
984另外,在应用主线程中,所有的异步任务尽管最终都是通过libuv得到执行的。但是在当前系统中,[libuv的线程池已经对接到了FFRT中](https://gitee.com/openharmony/third_party_libuv/wikis/06-Wiki-%E6%8A%80%E6%9C%AF%E8%B5%84%E6%BA%90/%20libuv%E5%B7%A5%E4%BD%9C%E7%BA%BF%E7%A8%8B%E6%8E%A5%E5%85%A5FFRT%E6%96%B9%E6%A1%88%E5%88%86%E6%9E%90),任何抛向libuv的异步任务都会在FFRT的线程中得到调度。应用主线程的回调函数也通过PostTask接口插入到eventhandler的队列上。这就意味着ffrt线程上的异步任务完成后不再通过`uv_async_send`的方式触发主线程的回调。过程如下图:
985
986![libuv的异步线程池在OpenHarmony中的应用现状](./figures/libuv-ffrt.png)
987
988我们总结了五种类型的请求任务是直接可以按照正常用法在应用主循环中生效的:
989
990- uv_random_t
991
992  函数原型:
993
994```cpp
995/**
996* 将一个工作请求添加到事件循环的队列中。
997* 
998* @param loop 事件循环
999* @param req 随机数请求
1000* @param buf 存储随机数的缓冲区
1001* @param buflen 缓冲区的长度
1002* @param flags 一个无符号整数,表示生成随机数的选项
1003* @param cb  随机数生成完成后的回调函数
1004*
1005* @return 成功返回0,失败返回错误码
1006*/
1007int uv_random(uv_loop_t* loop,
1008             uv_random_t* req,
1009             void* buf,
1010             size_t buflen,
1011             unsigned flags,
1012             uv_random_cb cb);
1013```
1014
1015- uv_work_t
1016
1017    函数原型:
1018
1019```cpp
1020/**
1021* 将一个工作请求添加到事件循环的队列中。
1022* 
1023* 当事件循环在下一次迭代时,work_cb函数将会在一个新的线程中被调用。
1024* 当work_cb函数完成时,after_work_cb函数将会在事件循环的线程中被调用。
1025* 
1026* @param loop 事件循环
1027* @param req 工作请求
1028* @param work_cb 在新线程中被调用的函数
1029* @param after_work_cb 在事件循环线程中被调用的函数
1030*
1031* @return 成功返回0,失败返回-1
1032*/
1033int uv_queue_work(uv_loop_t* loop,
1034                  uv_work_t* req,
1035                  uv_work_cb work_cb,
1036                  uv_after_work_cb after_work_cb);
1037```
1038
1039- uv_fs_t
1040
1041    文件类提供的所有异步接口,在应用主线程中都是可以生效的。主要有如下:
1042
1043```cpp
1044/**
1045* 异步读取文件
1046*
1047* @param loop 事件循环
1048* @param req 文件操作请求
1049* @param file 文件描述符
1050* @param bufs 读取数据的缓冲区
1051* @param nbufs 缓冲区的数量
1052* @param off 文件的偏移量
1053* @param cb 完成后的回调函数
1054* @return 成功返回0,失败返回-1
1055*/
1056int uv_fs_read(uv_loop_t* loop, uv_fs_t* req,
1057              uv_file file, 
1058              const uv_buf_t bufs[],
1059              unsigned int nbufs,
1060              int64_t off,
1061              uv_fs_cb cb);
1062
1063/**
1064* 异步打开文件
1065*
1066* @param loop 事件循环
1067* @param req 文件操作请求
1068* @param path 文件路径
1069* @param flags 打开文件的方式
1070* @param mode 文件权限
1071* @param cb 完成后的回调函数
1072*
1073* @return 成功返回0,失败返回-1
1074*/
1075int uv_fs_open(uv_loop_t* loop, 
1076               uv_fs_t* req,
1077               const char* path,
1078               int flags,
1079               int mode,
1080               uv_fs_cb cb);
1081
1082/**
1083* 异步发送文件
1084*
1085* @param loop 事件循环
1086* @param req 文件操作请求
1087* @param out_fd 输出文件描述符
1088* @param in_fd 输入文件描述符
1089* @param off 文件的偏移量
1090* @param len 发送的长度
1091* @param cb 完成后的回调函数
1092*
1093* @return 成功返回0,失败返回-1
1094*/
1095int uv_fs_sendfile(uv_loop_t* loop,
1096                   uv_fs_t* req,
1097                   uv_file out_fd,
1098                   uv_file in_fd,
1099                   int64_t off,
1100                   size_t len,
1101                   uv_fs_cb cb);
1102
1103/**
1104* 异步写入文件
1105*
1106* @param loop 事件循环
1107* @param req 文件操作请求
1108* @param file 文件描述符
1109* @param bufs 要写入的数据
1110* @param nbufs 数据的数量
1111* @param off 文件的偏移量
1112* @param cb 完成后的回调函数
1113*
1114* @return 成功返回0,失败返回-1
1115*/
1116int uv_fs_write(uv_loop_t* loop, 
1117                uv_fs_t* req,
1118                uv_file file,
1119                const uv_buf_t bufs[],
1120                unsigned int nbufs,
1121                int64_t off,
1122                uv_fs_cb cb);
1123
1124/**
1125* 异步复制文件
1126*
1127* @param loop 事件循环
1128* @param req 文件操作请求
1129* @param path 源文件路径
1130* @param new_path 目标文件路径
1131* @param flags 复制选项
1132* @param cb 完成后的回调函数
1133*
1134* @return 成功返回0,失败返回-1
1135*/
1136int uv_fs_copyfile(uv_loop_t* loop,
1137                   uv_fs_t* req,
1138                   const char* path,
1139                   const char* new_path
1140                   int flags,
1141                   uv_fs_cb cb);
1142```
1143
1144- uv_getaddrinfo_t
1145
1146     函数原型:
1147
1148```cpp
1149/**
1150* 异步获取地址信息
1151*
1152* @param loop 事件循环
1153* @param req 地址信息请求
1154* @param cb 完成后的回调函数
1155* @param hostname 主机名
1156* @param service 服务名
1157* @param hints 地址信息提示
1158*
1159* @return 成功返回0,失败返回-1
1160*/
1161int uv_getaddrinfo(uv_loop_t* loop,
1162                   uv_getaddrinfo_t* req,
1163                   uv_getaddrinfo_cb cb,
1164                   const char* hostname,
1165                   const char* service,
1166                   const struct addrinfo* hints);
1167```
1168
1169- uv_getnameinfo_t
1170
1171     函数原型:
1172
1173```cpp
1174/**
1175* 异步获取名称信息
1176*
1177* @param loop 事件循环
1178* @param req 名称信息请求
1179* @param getnameinfo_cb 完成后的回调函数
1180* @param addr 地址
1181* @param flags 标志
1182*
1183* @return 成功返回0,失败返回-1
1184*/
1185int uv_getnameinfo(uv_loop_t* loop,
1186                   uv_getnameinfo_t* req,
1187                   uv_getnameinfo_cb getnameinfo_cb,
1188                   const struct sockaddr* addr,
1189                   int flags);
1190```
1191
1192在应用主线程上不生效的接口主要包括:
1193
1194- idle句柄
1195- prepare句柄
1196- check句柄
1197- signal相关函数
1198- tcp及udp相关函数
1199
1200## 技术案例
1201
1202[libuv中主线程timer回调事件触发时间不正确原因](https://gitee.com/openharmony/third_party_libuv/wikis/06-Wiki-%E6%8A%80%E6%9C%AF%E8%B5%84%E6%BA%90/libuv%E4%B8%AD%E4%B8%BB%E7%BA%BF%E7%A8%8Btimer%E5%9B%9E%E8%B0%83%E4%BA%8B%E4%BB%B6%E8%A7%A6%E5%8F%91%E6%97%B6%E9%97%B4%E4%B8%8D%E6%AD%A3%E7%A1%AE%E5%8E%9F%E5%9B%A0)
1203
1204[libuv工作线程接入FFRT方案分析](https://gitee.com/openharmony/third_party_libuv/wikis/06-Wiki-%E6%8A%80%E6%9C%AF%E8%B5%84%E6%BA%90/%20libuv%E5%B7%A5%E4%BD%9C%E7%BA%BF%E7%A8%8B%E6%8E%A5%E5%85%A5FFRT%E6%96%B9%E6%A1%88%E5%88%86%E6%9E%90)
1205
1206[QoS感知的libuv、Node-API异步接口整改FAQ](https://gitee.com/openharmony/third_party_libuv/wikis/06-Wiki-%E6%8A%80%E6%9C%AF%E8%B5%84%E6%BA%90/QoS%E6%84%9F%E7%9F%A5%E7%9A%84libuv%E3%80%81napi%E5%BC%82%E6%AD%A5%E6%8E%A5%E5%8F%A3%E6%95%B4%E6%94%B9FAQ)
1207