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.js的生态,将Node.js的Node-API引入到系统中,方便Node.js开发者快速接入OpenHarmony,扩展自己的JS接口。同时引入了Node.js的事件循环实现库——libuv。 34 35### 演进方向 36 37随着OpenHarmony的逐步完善,我们计划在未来的版本中,逐步将应用模型中的事件循环归一,并增强OpenHarmony自身的事件循环,以解决许多双loop机制下的调度问题,并为开发者提供更加完善的任务优先级、插队等与任务主循环交互的方法。 38 39开发者应尽可能避免在`napi_get_uv_event_loop`接口获取的应用主loop上使用libuv的ndk进行操作,因为这可能会带来各种问题,并给未来的兼容性变更带来大量的工作量。 40 41如果开发者希望跟主线程事件循环交互,比如插入任务等,应当使用[Node-API提供的接口](../../napi/napi-data-types-interfaces.md)。 42 43OpenHarmony还将长期通过Node-API来为开发者提供和主线程交互及扩展JS接口的能力,但会屏蔽实现层使用的事件循环。Node-API的主要功能接口将会长期维护,并保证与Node.js的原生行为一致,来保证熟悉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 66static void execute(uv_work_t* work) 67{ 68 OH_LOG_INFO(LOG_APP, "ohos in execute"); 69} 70 71static void complete(uv_work_t* work, int status) 72{ 73 OH_LOG_INFO(LOG_APP, "ohos in complete"); 74 delete work; 75} 76static napi_value Add(napi_env env, napi_callback_info info) 77{ 78 uv_loop_s* loop = nullptr; 79 /* 获取应用JS主线程的uv_loop */ 80 napi_get_uv_event_loop(env, &loop); 81 uv_work_t* work = new uv_work_t; 82 int ret = uv_queue_work(loop, work, execute, complete); 83 if (ret != 0) { 84 OH_LOG_INFO(LOG_APP, "delete work"); 85 delete work; 86 } 87 return 0; 88} 89 90EXTERN_C_START 91static napi_value Init(napi_env env, napi_value exports) 92{ 93 napi_property_descriptor desc[] = {{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}}; 94 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 95 return exports; 96} 97EXTERN_C_END 98 99static napi_module demoModule = { 100 .nm_version = 1, 101 .nm_flags = 0, 102 .nm_filename = nullptr, 103 .nm_register_func = Init, 104 .nm_modname = "entry", 105 .nm_priv = ((void *)0), 106 .reserved = {0}, 107}; 108 109extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 110{ 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> 125uv_loop_t* loop = nullptr; 126napi_value jsCb; 127int fd = -1; 128 129static napi_value Add(napi_env env, napi_callback_info info) 130{ 131 napi_value work_name; 132 napi_async_work work; 133 napi_create_string_utf8(env, "ohos", NAPI_AUTO_LENGTH, &work_name); 134 /* 第四个参数是异步线程的work任务,第五个参数为主线程的回调 */ 135 napi_create_async_work( 136 env, nullptr, work_name, [](napi_env env, void* data){OH_LOG_INFO(LOG_APP, "ohos in execute"); }, 137 [](napi_env env, napi_status status, void* data){ 138 /* 不关心具体实现 */ 139 OH_LOG_INFO(LOG_APP, "ohos in complete"); 140 napi_delete_async_work(env, (napi_async_work)data); 141 }, 142 nullptr, &work); 143 /* 通过napi_queue_async_work触发异步任务执行 */ 144 napi_queue_async_work(env, work); 145 return 0; 146} 147 148EXTERN_C_START 149static napi_value Init(napi_env env, napi_value exports) 150{ 151 napi_property_descriptor desc[] = {{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}}; 152 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 153 return exports; 154} 155EXTERN_C_END 156 157static napi_module demoModule = { 158 .nm_version = 1, 159 .nm_flags = 0, 160 .nm_filename = nullptr, 161 .nm_register_func = Init, 162 .nm_modname = "entry", 163 .nm_priv = ((void *)0), 164 .reserved = {0}, 165}; 166 167extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 168{ 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 190uv_loop_t* loop = nullptr; 191napi_value jsCb; 192int fd = -1; 193 194void poll_handler(uv_poll_t* handle,int status, int events) 195{ 196 OH_LOG_INFO(LOG_APP, "ohos poll print"); 197} 198 199static napi_value TestClose(napi_env env, napi_callback_info info) 200{ 201 std::thread::id this_id = std::this_thread::get_id(); 202 OH_LOG_INFO(LOG_APP, "ohos thread id : %{public}ld", this_id); 203 size_t argc = 1; 204 napi_value workBname; 205 206 napi_create_string_utf8(env, "test", NAPI_AUTO_LENGTH, &workBname); 207 208 napi_get_cb_info(env, info, &argc, &jsCb, nullptr, nullptr); 209 // 获取事件循环 210 napi_get_uv_event_loop(env, &loop); 211 // 创建一个eventfd 212 fd = eventfd(0, 0); 213 OH_LOG_INFO(LOG_APP, "fd is %{public}d",fd); 214 uv_poll_t* poll_handle = new uv_poll_t; 215 // 初始化一个poll句柄,并将其与eventfd关联 216 uv_poll_init(loop, poll_handle, fd); 217 // 开始监听poll事件 218 uv_poll_start(poll_handle, UV_READABLE, poll_handler); 219 // 创建一个新线程,向eventfd写入数据 220 std::thread mythread([](){ 221 for (int i = 0; i < 8; i++){ 222 int value = 10; 223 int ret = eventfd_write(fd, value); 224 if (ret == -1){ 225 OH_LOG_INFO(LOG_APP, "write failed!"); 226 continue; 227 } 228 } 229 }); 230 mythread.detach(); 231 return 0; 232} 233 234EXTERN_C_START 235static napi_value Init(napi_env env, napi_value exports) 236{ 237 napi_property_descriptor desc[] = {{"testClose", nullptr, TestClose, nullptr, nullptr, nullptr, napi_default, nullptr}}; 238 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 239 return exports; 240} 241EXTERN_C_END 242 243static napi_module demoModule = { 244 .nm_version = 1, 245 .nm_flags = 0, 246 .nm_filename = nullptr, 247 .nm_register_func = Init, 248 .nm_modname = "entry", 249 .nm_priv = ((void *)0), 250 .reserved = {0}, 251}; 252 253extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 254{ 255 napi_module_register(&demoModule); 256} 257``` 258 259在上述代码中,流程如下: 260 2611. 首先通过`napi_get_uv_event_loop`接口获取到应用主线程的uvloop。 2622. 然后创建一个eventfd。 2633. 初始化uv_poll_t,并启动该句柄使其生效,在eventfd可读时触发回调函数`poll_handler`。 2644. 新开一个线程,向eventfd里写入字符。 265 266执行上述代码,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接口的正常使用可能不会达到开发者的预期。** 267 268**临时方案:** 269 270在当下的系统版本中,我们并不推荐开发者直接通过`napi_get_uv_event_loop`获取应用主线程的uvloop进行业务逻辑的开发。如果当前Node-API的接口无法满足开发者的开发需求,确有必要使用libuv来实现业务功能,为了使libuv接口在主线程上生效,开发者可以在调用类似*uv_xxx_start*后,执行一次`uv_async_send`的方式来主动触发应用主线程执行一次`uv_run`。这样可以保证该接口生效并正常执行。 271 272针对上述无法生效的代码示例,可以修改如下使其生效: 273 274```cpp 275#include "napi/native_api.h" 276#include "uv.h" 277#define LOG_DOMAIN 0x0202 278#define LOG_TAG "MyTag" 279#include <hilog/log.h> 280#include <thread> 281#include <sys/eventfd.h> 282 283uv_loop_t* loop = nullptr; 284napi_value jsCb; 285int fd = -1; 286 287void poll_handler(uv_poll_t* handle,int status, int events) 288{ 289 OH_LOG_INFO(LOG_APP, "ohos poll print"); 290} 291 292static napi_value TestClose(napi_env env, napi_callback_info info) 293{ 294 std::thread::id this_id = std::this_thread::get_id(); 295 OH_LOG_INFO(LOG_APP, "ohos thread id : %{public}ld", this_id); 296 size_t argc = 1; 297 napi_value workBName; 298 299 napi_create_string_utf8(env, "test", NAPI_AUTO_LENGTH, &workBName); 300 301 napi_get_cb_info(env, info, &argc, &jsCb, nullptr, nullptr); 302 303 napi_get_uv_event_loop(env, &loop); 304 305 fd = eventfd(0, 0); 306 OH_LOG_INFO(LOG_APP, "fd is %{public}d",fd); 307 uv_poll_t* poll_handle = new uv_poll_t; 308 uv_poll_init(loop, poll_handle, fd); 309 uv_poll_start(poll_handle, UV_READABLE, poll_handler); 310 311 // 主动触发一次fd事件,让主线程执行一次uv_run 312 uv_async_send(&loop->wq_async); 313 314 std::thread mythread([](){ 315 for (int i = 0; i < 8; i++){ 316 int value = 10; 317 int ret = eventfd_write(fd, value); 318 if (ret == -1){ 319 OH_LOG_INFO(LOG_APP, "write failed!"); 320 continue; 321 } 322 } 323 }); 324 mythread.detach(); 325 return 0; 326} 327 328EXTERN_C_START 329static napi_value Init(napi_env env, napi_value exports) 330{ 331 napi_property_descriptor desc[] = {{"testClose", nullptr, TestClose, nullptr, nullptr, nullptr, napi_default, nullptr}}; 332 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 333 return exports; 334} 335EXTERN_C_END 336 337static napi_module demoModule = { 338 .nm_version = 1, 339 .nm_flags = 0, 340 .nm_filename = nullptr, 341 .nm_register_func = Init, 342 .nm_modname = "entry", 343 .nm_priv = ((void *)0), 344 .reserved = {0}, 345}; 346 347extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 348{ 349 napi_module_register(&demoModule); 350} 351``` 352 353## libuv使用指导 354 355**重要:libuv NDK中所有依赖`uv_run`的接口在当前系统的应用主循环中无法及时生效,并且可能会导致卡顿掉帧的现象。因此不建议直接在JS主线程上使用libuv NDK接口,对于异步任务执行及与使用线程安全函数与主线程通信,开发者可以直接调用Node-API接口来实现相关功能。** 356 357### libuv接口与Node-API接口对应关系 358 359当前OpenHarmony提供了一些Node-API接口,可以替换libuv接口的使用。主要包括异步任务相关接口,线程安全的函数调用接口。 360 361#### 异步任务接口 362 363当开发者需要执行一个比较耗时的操作但又不希望阻塞主线程执行时,libuv提供了底层接口`uv_queue_work`帮助开发者在异步线程中执行耗时操作,然后将结果回调到主线程上进行处理。 364 365在Node-API中,通常可以通过[napi_async_work](../../napi/use-napi-asynchronous-task.md)相关函数来实现异步开发的功能。 366 367相关函数为: 368 369```cpp 370/** 371* @brief 创建一个新的异步工作 372* 373* @param env 指向当前环境的指针 374* @param async_resource 可选的资源对象,用于跟踪异步操作 375* @param async_resource_name 可选的字符串,用于描述异步资源 376* @param execute 一个回调函数,它将在一个新的线程中执行异步操作 377* @param complete 一个回调函数,它将在异步操作完成后被调用 378* @param data 用户定义的数据,它将被传递给execute和complete回调函数 379* @param result 指向新创建的异步工作的指针 380*/ 381napi_status napi_create_async_work(napi_env env, 382 napi_value async_resource, 383 napi_value async_resource_name, 384 napi_async_execute_callback execute, 385 napi_async_complete_callback complete, 386 void* data, 387 napi_async_work* result); 388 389/** 390* @brief 将异步工作添加到队列中 391* 392* @param env 指向当前环境的指针 393* @param work 指向异步工作的指针 394*/ 395napi_status napi_queue_async_work(napi_env env, napi_async_work work); 396 397/** 398* @brief 删除异步工作 399* 400* @param env 指向当前环境的指针 401* @param work 指向异步工作的指针 402*/ 403napi_status napi_delete_async_work(napi_env env, napi_async_work work); 404``` 405 406#### 跨线程共享和调用的线程安全函数 407 408当开发者想在任意子线程传递某个回调函数到应用主线程上执行时,libuv的实现方式一般使用`uv_async_t`句柄用于线程间通信。 409 410相关函数包含: 411 412- uv_async_init() 413- uv_async_send() 414 415Node-API与之对应的接口为[napi_threadsafe_function](../../napi/use-napi-thread-safety.md)相关函数。 416 417相关函数: 418 419```cpp 420/** 421* @brief 用于创建一个线程安全的函数,该函数可以在多个线程中调用,而不需要担心数据竞争或其他线程安全问题 422* 423* @param env 指向NAPI环境的指针,用于创建和操作Javascript值 424* @param func 指向JavaScript函数的指针 425* @param async_resource 异步资源,通常是一个表示异步操作的对象 426* @param async_resource_name 指向资源名称的指针,这个名称将用于日志和调试 427* @param max_queue_size 一个整数,表示队列的最大大小,当队列满时,新的调用将被丢弃 428* @param initial_thread_count 无符号整数,表示在创建线程安全函数时,初始的线程数量 429* @param thread_finalize_data 一个指向在所有线程之前需要清理的数据 430* @param napi_finalize thread_finalize_cb 回调函数,当所有线程完成时被调用,用于清理资源 431* @param context 指向上下文的指针,这个上下文将被传递给call_js_func函数 432* @param call_js_cb 指向回调函数的指针,这个函数将在Javascript函数被调用时被调用 433* @param result 指向napi_threadsafe_function结构的指针,这个结构将被填充为新创建的线程安全函数 434*/ 435napi_status napi_create_threadsafe_function(napi_env env, 436 napi_value func, 437 napi_value async_resource, 438 napi_value async_resource_name, 439 size_t max_queue_size, 440 size_t initial_thread_count, 441 void* thread_finalize_data, 442 napi_finalize thread_finalize_cb, 443 void* context, 444 napi_threadsafe_function_call_js call_js_cb, 445 napi_threadsafe_function* result); 446 447/** 448* @brief 获取一个线程安全的函数 449* 450* @param function 指向线程安全函数的指针 451*/ 452napi_status napi_acquire_threadsafe_function(napi_threadsafe_function function); 453 454/** 455* @brief 调用一个线程安全的函数 456* @param function 指向线程安全函数的指针 457* @param data 用户数据 458* @param is_blocking 枚举值,它决定调用JavaScript函数是阻塞的还是非阻塞的 459*/ 460napi_status napi_call_threadsafe_function(napi_threadsafe_function function, 461 void* data, 462 napi_threadsafe_function_call_mode is_blocking); 463/** 464* @brief 释放一个线程安全的函数 465* 466* @param function 指向线程安全函数的指针 467* @param is_blocking 枚举值,它决定调用JavaScript函数是阻塞的还是非阻塞的 468*/ 469napi_status napi_release_threadsafe_function(napi_threadsafe_function function, 470 napi_threadsafe_function_call_mode is_blocking); 471 472``` 473 474除此之外,如果开发者需要libuv其他原生接口来实现业务功能,为了让开发者正确使用libuv提供的接口能力,避免因为错误使用而陷入到问题当中。在后续章节,我们将逐步介绍libuv的一些基本概念和OpenHarmony系统中常用函数的正确使用方法,它仅仅可以保证开发者使用libuv接口的时候不会出现应用进程崩溃等现象。另外,我们还统计了在当前应用主线程上可以正常使用的接口,以及无法在应用主线程上使用的接口。 475 476### 接口汇总说明 477 478| 接口类型 | 接口汇总 | 479| ---- | ---- | 480| [loop概念及相关接口](#libuv中的事件循环) | uv_loop_init | 481| [loop概念及相关接口](#libuv中的事件循环) | uv_loop_close | 482| [loop概念及相关接口](#libuv中的事件循环) | uv_default_loop | 483| [loop概念及相关接口](#libuv中的事件循环) | uv_run | 484| [loop概念及相关接口](#libuv中的事件循环) | uv_loop_alive | 485| [loop概念及相关接口](#libuv中的事件循环) | uv_stop | 486| [Handle概念及相关接口](#libuv中的handles和requests) | uv_poll\_\* | 487| [Handle概念及相关接口](#libuv中的handles和requests) | uv_timer\_\* | 488| [Handle概念及相关接口](#libuv中的handles和requests) | uv_async\_\* | 489| [Handle概念及相关接口](#libuv中的handles和requests) | uv_signal\_\* | 490| [Handle概念及相关接口](#libuv中的handles和requests) | uv_fs\_\* | 491| [Request概念及相关接口](#libuv中的handles和requests) | uv_random | 492| [Request概念及相关接口](#libuv中的handles和requests) | uv_getaddrinfo | 493| [Request概念及相关接口](#libuv中的handles和requests) | uv_getnameinfo | 494| [Request概念及相关接口](#libuv中的handles和requests) | uv_queue_work | 495| [线程间通信原理及相关接口](#线程间通信) | uv_async_init | 496| [线程间通信原理及相关接口](#线程间通信) | uv_async_send | 497| [线程池概念及相关接口](#线程池) | uv_queue_work | 498 499### libuv单线程约束 500 501在OpenHarmony中使用libuv时,**务必注意:使用`uv_loop_init`接口初始化loop的线程和调用`uv_run`的线程应保持一致,称为loop线程,并且对uvloop的所有非线程安全操作,均需保证与loop同线程,否则将会有发生crash的风险**。OpenHarmony对libuv的使用有更严格的约束,对于非线程安全的函数,libuv将实现多线程检测机制,检测到多线程问题后输出警告日志。为了确保检测机制的准确性,协助开发者规避uv接口的不规范使用,我们建议在创建事件循环与执行uv_run始终保持在同一线程。 502 503#### 单线程约束 504 505根据loop来源的不同,可分为两种情况,即开发者创建loop和从env获取loop。 506 507##### 开发者创建loop 508 509开发者可以通过调用`uv_loop_new`创建loop或者`uv_loop_init`接口初始化loop,loop的生命周期由开发者自行维护。在这种情况下,如前文所述,需要保证`uv_run`执行在与创建/初始化loop操作相同的线程上,即loop线程上。此外,其余非线程安全操作,如timer相关操作等,均需要在loop线程上进行。 510 511如果因为业务需要,必须在其他线程往loop线程抛任务,请使用`uv_async_send`函数:即在async句柄初始化时,注册一个回调函数,并在该回调中实现相应的操作,当调用`uv_async_send`时,在主线程上执行该回调函数。见如下代码示例: 512 513```cpp 514#include <napi/native_api.h> 515#include <uv.h> 516#define LOG_DOMAIN 0x0202 517#define LOG_TAG "MyTag" 518#include "hilog/log.h" 519#include <thread> 520 521uv_async_t* async = new uv_async_t; 522bool cond1 = false; 523bool cond2 = false; 524 525// 使用技巧:在使用loop时, 需要特别注意uv_stop函数的使用, 开发者需要确保uv_stop前 526// 通知与loop相关的所有线程的handle都关闭, 参考stop_loop函数的实现 527int stop_loop(uv_loop_t* loop) 528{ 529 uv_stop(loop); 530 auto const ensure_close = [](uv_handle_t* handle, void*) { 531 if (uv_is_closing(handle)) { 532 return; 533 } else { 534 uv_close(handle, nullptr); 535 } 536 }; 537 // 遍历所有句柄, 如果handle处于活跃状态, 调用ensure_close 538 uv_walk(loop, ensure_close, nullptr); 539 // 继续运行uv_run, 直到loop中不存在活跃的句柄和请求为止 540 while(true) { 541 if (uv_run(loop, UV_RUN_DEFAULT) == 0) { 542 break; 543 } 544 } 545 546 // 最后检查loop状态 547 if (uv_loop_alive(loop) != 0) { 548 return -1; 549 } 550 return 0; 551} 552 553// 执行创建定时器操作 554void async_cb(uv_async_t* handle) { 555 auto loop = handle->loop; 556 uv_timer_t* timer = new uv_timer_t; 557 uv_timer_init(loop, timer); 558 559 uv_timer_start(timer, 560 [](uv_timer_t* timer){ 561 // do something 562 // 在适当的时机停掉timer 563 if (cond1) 564 uv_timer_stop(timer); 565 uv_close((uv_handle_t*)timer, [](uv_handle_t* handle){ 566 delete(uv_timer_t*)handle; 567 }); 568 }, 569 100, 100); 570 // 在适当的时机关闭async句柄 571 if (cond2) { 572 uv_close((uv_handle_t*)handle, [](uv_handle_t* handle){ 573 delete (uv_async_t*)handle; 574 }); 575 } 576} 577 578// 初始化async句柄, 绑定对应的回调函数 579static napi_value TestTimerAsync(napi_env env, napi_callback_info info) { 580 std::thread t([](){ // A线程,loop线程 581 uv_loop_t* loop = new uv_loop_t; 582 // 开发者自己创建loop, 请注意维护loop的生命周期 583 uv_loop_init(loop); 584 // 初始化一个async句柄, 注册回调函数 585 uv_async_init(loop, async, async_cb); 586 // 让loop开始运行 587 uv_run(loop, UV_RUN_DEFAULT); 588 // 清理所有的handle 589 stop_loop(loop); 590 // 释放loop 591 uv_loop_close(loop); 592 delete loop; 593 }); 594 t.detach(); 595 return 0; 596} 597 598// 在另一个线程上调用uv_async_send函数 599static napi_value TestTimerAsyncSend(napi_env env, napi_callback_info info) 600{ 601 std::thread t([](){ // B线程 602 uv_async_send(async); // 调用uv_async_send, 通知loop线程调用与async句柄绑定的timer_cb 603 uv_sleep(1000); 604 // 修改cond1和cond2示例 605 cond1 = true; 606 cond2 = true; 607 }); 608 t.detach(); 609 return 0; 610} 611 612EXTERN_C_START 613static napi_value Init(napi_env env, napi_value exports) 614{ 615 napi_property_descriptor desc[] = { 616 {"testTimerAsync", nullptr, TestTimerAsync, nullptr, nullptr, nullptr, napi_default, nullptr}, 617 {"testTimerAsyncSend", nullptr, TestTimerAsyncSend, nullptr, nullptr, nullptr, napi_default, nullptr}, 618 }; 619 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 620 return exports; 621} 622EXTERN_C_END 623 624static napi_module demoModule = { 625 .nm_version = 1, 626 .nm_flags = 0, 627 .nm_filename = nullptr, 628 .nm_register_func = Init, 629 .nm_modname = "entry", 630 .nm_priv = ((void *)0), 631 .reserved = {0}, 632}; 633 634extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 635{ 636 napi_module_register(&demoModule); 637} 638``` 639 640##### 从env获取loop 641 642开发者使用`napi_get_uv_event_loop`接口从env获取到的loop一般是系统创建的JS主线程的事件循环,因此应当避免在子线程中调用非线程安全函数。 643 644如因业务需要,必须在非loop线程上调用非线程安全函数,请使用线程安全函数`uv_async_send`将任务提交到loop线程。即定义一个uv_async_t*类型的句柄,初始化该句柄的时候,将需要在子线程调用的非线程安全函数在对应的async_cb中调用,然后在非loop线程上调用`uv_async_send`函数,并回到loop线程上执行async_cb。请参考[正确使用timer示例](#正确使用timer示例)的场景二。 645 646### 线程安全函数 647 648在libuv中,由于涉及到大量的异步任务,稍有不慎就会陷入到多线程问题中。在这里,我们对libuv中常用的线程安全函数和非线程安全函数做了汇总。若开发者在多线程编程中调用了非线程安全的函数,势必要对其进行加锁保护或者保证代码的正确运行时序,否则将陷入到crash问题中。 649 650线程安全函数: 651 652- uv_async_send():向异步句柄发送信号,可以在任何线程中调用。 653- uv_thread_create():创建一个新线程并执行指定的函数,可以在任何线程中调用。 654- 锁相关的操作,如uv\_mutex\_lock()、uv\_mutex\_unlock()等等。 655 656**提示:所有形如uv_xxx_init的函数,即使它是以线程安全的方式实现的,但使用时要注意,避免多个线程同时调用uv_xxx_init,否则它依旧会引起多线程资源竞争的问题。最好的方式是在事件循环线程中调用该函数。** 657 658**注:`uv_async_send`函数被调用后,回调函数是被异步触发的。如果调用了多次`uv_async_send`,libuv只保证至少有一次回调会被执行。这就可能导致一旦对同一句柄触发了多次`uv_async_send`,libuv对回调的处理可能会违背开发者的预期。多次对同一个async句柄进行send操作,还会导致任意两次相同句柄send操作之间提交的的其他async_cb任务丢失。** 而在native侧,可以保证回调的执行次数和开发者调用`napi_call_threadsafe_function`的次数保持一致。 659 660非线程安全函数: 661 662- uv\_os\_unsetenv():删除环境变量 663- uv\_os\_setenv():设置环境变量 664- uv\_os\_getenv():获取环境变量 665- uv\_os\_environ():检索所有的环境变量 666- uv\_os\_tmpdir():获取临时目录 667- uv\_os\_homedir():获取家目录 668 669### libuv中的事件循环 670 671事件循环是libuv中最核心的一个概念,loop负责管理整个事件循环的所有资源,它贯穿于整个事件循环的生命周期。通常将`uv_run`所在的线程称为该事件循环的主线程。 672 673#### 事件循环运行的三种方式 674 675`UV_RUN_DEFAULT`:默认轮询方式,该模式将会一直运行下去,直到loop中没有活跃的句柄和请求。 676 677`UV_RUN_ONCE`:一次轮询模式,如果pending_queue中有回调函数,则执行,然后跳过`uv__io_poll`函数。此模式默认认为loop中一定有事件发生。 678 679`UV_RUN_NOWAIT`:非阻塞模式,该模式下不会执行pending_queue,而是直接执行一次I/O轮询(`uv__io_poll`)。 680 681#### 常用接口 682 683```cpp 684int uv_loop_init(uv_loop_t* loop); 685``` 686 687 对loop进行初始化。 688 689```cpp 690int uv_loop_close(uv_loop_t* loop); 691``` 692 693 关闭loop,该函数只有在loop中所有的句柄和请求都关闭后才能成功返回,否则将返回UV_EBUSY。 694 695```cpp 696int uv_loop_delete(uv_loop_t* loop); 697``` 698 699释放loop,该接口会先调用`uv_loop_close`,然后再将loop释放掉。在OpenHarmony平台上,由于assert函数不生效,因此不论`uv_loop_close`函数是否成功清理loop上的资源,都会将loop释放掉。开发者使用该接口时,请务必确保在loop线程退出时,loop上的资源可以被正确释放,即挂在loop上的handle和request均被关闭,否则会导致资源泄漏。**开发者使用该接口时务必格外谨慎,建议非必要不使用。** 700 701```cpp 702uv_loop_t* uv_default_loop(void); 703``` 704 705 该函数创建一个进程级的loop。在OpenHarmony中,由于目前的应用主循环及其他JS工作线程还存在着libuv的loop。因此我们不建议开发者使用该函数来创建loop并实现业务功能。 706 707```cpp 708int uv_run(uv_loop_t* loop, uv_run_mode mode); 709``` 710 711 启动事件循环。运行模式可查看[事件循环运行的三种方式](#事件循环运行的三种方式)。 712 713```cpp 714int uv_loop_alive(uv_loop_t loop); 715``` 716 717 判断loop是否处于活跃状态。 718 719```cpp 720void uv_stop(uv_loop_t* loop); 721``` 722 723 该函数用来停止一个事件循环,在loop的下一次迭代中才会停止。如果该函数发生在I/O操作之前,将不会阻塞而是直接跳过`uv__io_poll`。 724 725 726### libuv中的handles和requests 727 728handle表示一个持久性的对象,通常挂载到loop中对应的handle_queue队列上。如果handle处于活跃状态,每次`uv_run`都会处理handle中的回调函数。 729 730request表示一个短暂性的请求,一个request只触发一次回调操作。 731 732下面是OpenHarmony系统中最常用的几个Handles和Requests: 733 734```cpp 735/* Handle Type */ 736typedef struct uv_handle_s uv_handle_t; 737typedef struct uv_timer_s uv_timer_t; 738typedef struct uv_async_s uv_async_t; 739typedef struct uv_signal_s uv_signal_t; 740 741/* Request Type */ 742typedef struct uv_req_s uv_req_t; 743typedef struct uv_work_s uv_work_t; 744typedef struct uv_fs_s uv_fs_t; 745``` 746 747**注:在handles中,uv_xxx_t继承了uv_handle_t;在requests中,uv_work_t继承了uv_req_t。** 748 749对于libuv中的handles,对其有正确的认识并管理好它的生命周期至关重要。handle作为一个长期存在于loop中的句柄,在使用中,开发者应遵循下面的原则: 750 7511. 句柄的初始化工作应在事件循环的线程中进行。 7522. 若由于业务问题,句柄需要在其他工作线程初始化,在使用之前用原子变量判断是否初始化完成。 7533. 句柄在确定后续不再使用后,调用`uv_close`将句柄从loop中摘除。 754 755在这里,需要特别说明一下`uv_close`的使用方法。`uv_close`被用来关闭一个handle,但是关闭handle的动作是异步的。函数原型为: 756 757```cpp 758void uv_close(uv_handle_t* handle, uv_close_cb close_cb) 759``` 760 761 handle:要关闭的句柄。 762 close_cb:处理该句柄的函数,用来进行内存管理等操作。 763 764调用`uv_close`后,首先将要关闭的handle挂载到loop的closing_handles队列上,然后等待loop所在线程运行`uv__run_closing_handles`函数。最后回调函数close_cb将会在loop的下一次迭代中执行。因此,释放内存等操作应该在close_cb中进行。并且这种异步的关闭操作会带来多线程问题,开发者需要谨慎处理`uv_close`的时序问题,并且保证在close_cb执行之前handles的生命周期。 765 766**Tips**:在[libuv官方文档](http://libuv.org/)中,有个经验法则需要在此提示一下。原文翻译:如果 uv_foo_t 类型的句柄具有 `uv_foo_start()` 函数,则从调用该函数的那一刻起,它就处于活动状态。 同样,`uv_foo_stop()`再次停用句柄。 767 768> **注意** 769> 770> 1. 所有的handle关闭前必须要调用`uv_close`,所有的内存操作都要在`uv_close`的close_cb中执行。 771> 772> 2. 所有的handle操作都不能通过获取其他线程loop的方式,在非loop线程上调用。 773 774对于libuv中的requests,开发者需要确保在进行异步任务提交时,**通过动态申请的request,要在loop所在线程执行的complete回调函数中释放**。用uv_work_t举例,代码可参考如下: 775 776```cpp 777uv_work_t* work = new uv_work_t; 778uv_queue_work(loop, work, [](uv_work_t* req) { 779 // 异步操作 780}, [](uv_work_t* req, int status) { 781 // 回调操作 782 delete req; 783}); 784``` 785 786#### libuv timer使用规范 787 788使用libuv timer需要遵守如下约定: 789 7901. 请不要在多个线程中使用libuv的接口(`uv_timer_start`、`uv_timer_stop`和`uv_timer_again`)同时操作同一个loop的timer heap,否则将导致崩溃,如果想要使用libuv的接口操作定时器,请**保持在与当前env绑定的loop所在线程上操作**; 7912. 如因业务需求往指定线程抛定时器,请使用`uv_async_send`线程安全函数实现。 792 793##### 错误使用timer示例 794 795以下错误示例中,由于在多个线程操作同一个loop的timer heap,崩溃率极高。 796 797ArkTS侧: 798 799```typescript 800import { hilog } from '@kit.PerformanceAnalysisKit'; 801import testNapi from 'libentry.so' 802 803function waitforRunner(): number { 804 "use concurrent" 805 hilog.info(0xff, "testTag", "executed"); 806 return 0; 807} 808 809@Entry 810@Component 811struct Index { 812 build() { 813 Row() { 814 Column() { 815 Button("TimerTest") 816 .width('40%') 817 .fontSize('14fp') 818 .onClick(() => { 819 let i: number = 20; 820 while (i--) { 821 setTimeout(waitforRunner, 200); 822 testNapi.testTimer(); 823 } 824 }).margin(20) 825 }.width('100%') 826 }.height('100%') 827 } 828} 829``` 830 831Native C++侧: 832 833```cpp 834#include <napi/native_api.h> 835#include <uv.h> 836#define LOG_DOMAIN 0x0202 837#define LOG_TAG "MyTag" 838#include "hilog/log.h" 839#include <thread> 840#include <unistd.h> 841 842static napi_value TestTimer(napi_env env, napi_callback_info info) 843{ 844 uv_loop_t* loop = nullptr; 845 uv_timer_t* timer = new uv_timer_t; 846 847 napi_get_uv_event_loop(env, &loop); 848 uv_timer_init(loop, timer); 849 std::thread t1([&loop, &timer](){ 850 uv_timer_start(timer, [](uv_timer_t* timer){ 851 uv_timer_stop(timer); 852 }, 1000, 0); 853 }); 854 855 t1.detach(); 856 return 0; 857} 858 859EXTERN_C_START 860static napi_value Init(napi_env env, napi_value exports) 861{ 862 napi_property_descriptor desc[] = { 863 {"testTimer", nullptr, TestTimer, nullptr, nullptr, nullptr, napi_default, nullptr}, 864 }; 865 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 866 return exports; 867} 868EXTERN_C_END 869 870static napi_module demoModule = { 871 .nm_version = 1, 872 .nm_flags = 0, 873 .nm_filename = nullptr, 874 .nm_register_func = Init, 875 .nm_modname = "entry", 876 .nm_priv = ((void *)0), 877 .reserved = {0}, 878}; 879 880extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 881{ 882 napi_module_register(&demoModule); 883} 884``` 885 886在index.d.ts增加如下代码: 887 888```typescript 889export const testTimer:() => number; 890``` 891 892##### 正确使用timer示例 893 894**场景一:** 在上述场景中,需保证在JS主线程上进行timer的相关操作。将上述TestTimer函数的代码做如下修改,便可以避免崩溃发生。 895 896```cpp 897static napi_value TestTimer(napi_env env, napi_callback_info info) 898{ 899 uv_loop_t* loop = nullptr; 900 uv_timer_t* timer = new uv_timer_t; 901 902 napi_get_uv_event_loop(env, &loop); 903 uv_timer_init(loop, timer); 904 uv_timer_start(timer, [](uv_timer_t* timer){ 905 uv_timer_stop(timer); 906 }, 1000, 0); 907 908 return 0; 909} 910``` 911 912**场景二:** 如果需要在指定的子线程抛定时器,请使用线程安全函数`uv_async_send`实现。 913 914ArkTS侧: 915 916```typescript 917import { hilog } from '@kit.PerformanceAnalysisKit'; 918import testNapi from 'libentry.so' 919 920function waitforRunner(): number { 921 "use concurrent" 922 hilog.info(0xff, "testTag", "executed"); 923 return 0; 924} 925 926@Entry 927@Component 928struct Index { 929 build() { 930 Row() { 931 Column() { 932 Button("TestTimerAsync") 933 .width('40%') 934 .fontSize('14fp') 935 .onClick(() => { 936 testNapi.testTimerAsync(); // 初始化async句柄 937 }).margin(20) 938 939 Button("TestTimerAsyncSend") 940 .width('40%') 941 .fontSize('14fp') 942 .onClick(() => { 943 testNapi.testTimerAsyncSend(); // 子线程调用uv_async_send执行timer_cb 944 }).margin(20) 945 }.width('100%') 946 }.height('100%') 947 } 948} 949``` 950 951Native C++侧: 952 953```c++ 954#include <napi/native_api.h> 955#include <uv.h> 956#define LOG_DOMAIN 0x0202 957#define LOG_TAG "MyTag" 958#include "hilog/log.h" 959#include <thread> 960#include <unistd.h> 961uv_async_t* async = new uv_async_t; 962 963// 执行创建定时器操作 964void async_cb(uv_async_t* handle) 965{ 966 auto loop = handle->loop; 967 uv_timer_t* timer = new uv_timer_t; 968 uv_timer_init(loop, timer); 969 970 uv_timer_start(timer, [](uv_timer_t* timer){ 971 uv_timer_stop(timer); 972 }, 1000, 0); 973} 974 975// 初始化async句柄,绑定对应的回调函数 976static napi_value TestTimerAsync(napi_env env, napi_callback_info info) 977{ 978 uv_loop_t* loop = nullptr; 979 napi_get_uv_event_loop(env, &loop); 980 uv_async_init(loop, async, async_cb); 981 return 0; 982} 983 984static napi_value TestTimerAsyncSend(napi_env env, napi_callback_info info) 985{ 986 std::thread t([](){ 987 uv_async_send(async); // 在任意子线程中调用uv_async_send,通知主线程调用与async绑定的timer_cb 988 }); 989 t.detach(); 990 return 0; 991} 992 993EXTERN_C_START 994static napi_value Init(napi_env env, napi_value exports) 995{ 996 napi_property_descriptor desc[] = { 997 {"testTimerAsync", nullptr, TestTimerAsync, nullptr, nullptr, nullptr, napi_default, nullptr}, 998 {"testTimerAsyncSend", nullptr, TestTimerAsyncSend, nullptr, nullptr, nullptr, napi_default, nullptr}, 999 }; 1000 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 1001 return exports; 1002} 1003EXTERN_C_END 1004 1005static napi_module demoModule = { 1006 .nm_version = 1, 1007 .nm_flags = 0, 1008 .nm_filename = nullptr, 1009 .nm_register_func = Init, 1010 .nm_modname = "entry", 1011 .nm_priv = ((void *)0), 1012 .reserved = {0}, 1013}; 1014 1015extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 1016{ 1017 napi_module_register(&demoModule); 1018} 1019``` 1020 1021### 线程间通信 1022 1023上面简单介绍了一些libuv中的基本概念,在这里我们将着重介绍libuv中的线程间通信。 1024 1025libuv的线程间通信是通过uv_async_t句柄来进行的,相关函数如下: 1026 1027```cpp 1028int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) 1029``` 1030 1031 loop:事件循环loop。 1032 1033 handle:线程间通信句柄。 1034 1035 async_cb:回调函数。 1036 1037 返回:成功,返回0。失败,返回错误码。 1038 1039```cpp 1040int uv_async_send(uv_async_t* handle) 1041``` 1042 1043 handle:线程间通信句柄。 1044 1045 返回:成功,返回0。失败,返回错误码。 1046> 说明 1047> 1048> 1. uv_async_t从调用`uv_async_init`开始后就一直处于活跃状态,除非用`uv_close`将其关闭。 1049> 1050> 2. uv_async_t的执行顺序严格按照`uv_async_init`的顺序,而非通过`uv_async_send`的顺序来执行的。因此按照初始化的顺序来管理好时序问题是必要的。 1051 1052 1053 1054示例代码: 1055 1056```cpp 1057#include <iostream> 1058#include <thread> 1059#include "uv.h" 1060 1061uv_loop_t* loop = nullptr; 1062uv_async_t* async = nullptr; 1063int g_counter = 10; 1064 1065void async_handler(uv_async_t* handle) 1066{ 1067 std::cout << "ohos async print" << std::endl; 1068 if (--g_counter == 0) { 1069 // 调用uv_close关闭async,在主循环中释放内存。 1070 uv_close((uv_handle_t*)async, [](uv_handle_t* handle) { 1071 std::cout << "delete async" << std::endl; 1072 delete (uv_async_t*)handle; 1073 }); 1074 } 1075} 1076 1077int main() 1078{ 1079 loop = uv_default_loop(); 1080 async = new uv_async_t; 1081 uv_async_init(loop, async, async_handler); 1082 std::thread subThread([]() { 1083 for (int i = 0; i < 10; i++) { 1084 usleep(100); // 避免多次调用uv_async_send只执行一次 1085 std::cout << i << "th: subThread triggered" << std::endl; 1086 uv_async_send(async); 1087 } 1088 }); 1089 subThread.detach(); 1090 return uv_run(loop, UV_RUN_DEFAULT); 1091} 1092``` 1093 1094该示例代码仅仅描述了一个简单的场景,步骤如下: 1095 10961. 在主线程中初始化async句柄 10972. 新建一个子线程,在里面每隔100毫秒触发一次`uv_async_send`。10次以后调用`uv_close`关闭async句柄。 10983. 在主线程运行事件循环。 1099 1100可以看到,每触发一次,主线程都会执行一次回调函数。 1101 1102``` 11030th:subThread triggered 1104ohos async print 11051th:subThread triggered 1106ohos async print 11072th:subThread triggered 1108ohos async print 11093th:subThread triggered 1110ohos async print 11114th:subThread triggered 1112ohos async print 11135th:subThread triggered 1114ohos async print 11156th:subThread triggered 1116ohos async print 11177th:subThread triggered 1118ohos async print 11198th:subThread triggered 1120ohos async print 11219th:subThread triggered 1122ohos async print 1123delete async 1124``` 1125 1126### 线程池 1127 1128线程池是libuv的一个核心功能,libuv中的线程池通过uv_loop_t中的成员变量wq_async来控制工作线程与主线程的通信。核心函数如下: 1129 1130```cpp 1131int uv_queue_work(uv_loop_t* loop, 1132 uv_work_t* req, 1133 uv_work_cb work_cb, 1134 uv_after_work_cb after_work_cb) 1135``` 1136 1137work_cb:提交给工作线程的任务。 1138 1139after_work_cb:loop所在线程要执行的回调函数。 1140 1141**注意:** work_cb与after_work_cb的执行有一个时序问题,只有work_cb执行完,通过`uv_async_send(loop->wq_async)`触发fd事件,loop所在线程在下一次迭代中才会执行after_work_cb。只有执行到after_work_cb时,与之相关的uv_work_t生命周期才算结束。 1142 1143#### 异步任务提交 1144 1145下图为原生libuv的线程池工作流程,图中流程已简化,默认句柄的pending标志为1,worker线程个数不代表线程池中线程的真实数量。 1146 1147 1148 1149#### 异步任务提交注意事项 1150##### uv_queue_work工作流程 1151 1152在OpenHarmony中,`uv_queue_work`函数在UI线程的工作流程为:将`work_cb`抛到FFRT对应优先级的线程池中,然后待FFRT调度执行该任务,并将`after_work_cb`抛到eventhandler对应优先级的event queue中,等待eventhandler调度并回到loop线程执行。需要注意的是,`uv_queue_work`调用完后,并不代表其中的任何一个任务执行完,仅代表将work_cb插入到FFRT对应优先级的线程池中。taskpool和jsworker线程的工作流程和原生libuv逻辑保持一致。 1153 1154对于一些特定场景,比如对内存开销敏感的场景中,同一个request可以重复使用,前提是保证同一类任务之间的顺序,并且要确保最后一次调用`uv_queue_work`时做好对该request的释放工作。 1155 1156```C 1157uv_work_t* work = new uv_work_t; 1158uv_queue_work(loop, work, [](uv_work_t* work) { 1159 //do something 1160 }, 1161 [](uv_work_t* work, int status) { 1162 // do something 1163 uv_queue_work(loop, work, [](...) {/* do something*/}, [](...) { 1164 //do something 1165 if (last_task) { // 最后一个任务执行完以后,释放该request 1166 delete work; 1167 } 1168 }); 1169 }, 1170 ) 1171``` 1172 1173##### uv_queue_work使用约束 1174 1175特别强调,开发者需要明确,`uv_queue_work`函数仅用于抛异步任务,**异步任务的execute回调被提交到线程池后会经过调度执行,因此并不保证多次提交的任务及其回调按照时序关系执行**。 1176 1177`uv_queue_work`仅限于在loop线程中调用,这样不会有多线程安全问题。**请不要把uv_queue_work作为线程间通信的手段,即A线程获取到B线程的loop,并通过`uv_queue_work`抛异步任务的方式,把execute置为空任务,而把complete回调放在B线程中执行。** 这种方式不仅低效,而且还增加了发生故障时定位问题的难度。为了避免低效的任务提交,请使用[napi_threadsafe_function相关函数](#跨线程共享和调用的线程安全函数)。 1178 1179### OpenHarmony中libuv的使用现状 1180 1181当前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中的任务。 1182 1183综上所述,开发者会发现这样一种现象:**同样的libuv接口在主线程上不生效,但在JS Worker线程中就没问题。这主要还是因为主线程上所有不通过触发fd来驱动的uv接口都不会得到及时的响应。** 1184 1185另外,在应用主线程中,所有的异步任务尽管最终都是通过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`的方式触发主线程的回调。过程如下图: 1186 1187 1188 1189我们总结了五种类型的请求任务是直接可以按照正常用法在应用主循环中生效的: 1190 1191- uv_random_t 1192 1193 函数原型: 1194 1195```cpp 1196/** 1197* @brief 将一个工作请求添加到事件循环的队列中。 1198* 1199* @param loop 事件循环 1200* @param req 随机数请求 1201* @param buf 存储随机数的缓冲区 1202* @param buflen 缓冲区的长度 1203* @param flags 一个无符号整数,表示生成随机数的选项 1204* @param cb 随机数生成完成后的回调函数 1205* 1206* @return 成功返回0,失败返回错误码 1207*/ 1208int uv_random(uv_loop_t* loop, 1209 uv_random_t* req, 1210 void* buf, 1211 size_t buflen, 1212 unsigned flags, 1213 uv_random_cb cb); 1214``` 1215 1216- uv_work_t 1217 1218 函数原型: 1219 1220```cpp 1221/** 1222* @brief 将一个工作请求添加到事件循环的队列中。当事件循环在下一次迭代时,work_cb函数将会在一个新的线程中被调用。当work_cb函数完成时,after_work_cb函数将会在事件循环的线程中被调用。 1223* 1224* @param loop 事件循环 1225* @param req 工作请求 1226* @param work_cb 在新线程中被调用的函数 1227* @param after_work_cb 在事件循环线程中被调用的函数 1228* 1229* @return 成功返回0,失败返回-1 1230*/ 1231int uv_queue_work(uv_loop_t* loop, 1232 uv_work_t* req, 1233 uv_work_cb work_cb, 1234 uv_after_work_cb after_work_cb); 1235``` 1236 1237- uv_fs_t 1238 1239 文件类提供的所有异步接口,在应用主线程中都是可以生效的。主要有如下: 1240 1241```cpp 1242/** 1243* @brief 异步读取文件 1244* 1245* @param loop 事件循环 1246* @param req 文件操作请求 1247* @param file 文件描述符 1248* @param bufs 读取数据的缓冲区 1249* @param nbufs 缓冲区的数量 1250* @param off 文件的偏移量 1251* @param cb 完成后的回调函数 1252* @return 成功返回0,失败返回-1 1253*/ 1254int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, 1255 uv_file file, 1256 const uv_buf_t bufs[], 1257 unsigned int nbufs, 1258 int64_t off, 1259 uv_fs_cb cb); 1260 1261/** 1262* @brief 异步打开文件 1263* 1264* @param loop 事件循环 1265* @param req 文件操作请求 1266* @param path 文件路径 1267* @param flags 打开文件的方式 1268* @param mode 文件权限 1269* @param cb 完成后的回调函数 1270* 1271* @return 成功返回0,失败返回-1 1272*/ 1273int uv_fs_open(uv_loop_t* loop, 1274 uv_fs_t* req, 1275 const char* path, 1276 int flags, 1277 int mode, 1278 uv_fs_cb cb); 1279 1280/** 1281* @brief 异步发送文件 1282* 1283* @param loop 事件循环 1284* @param req 文件操作请求 1285* @param out_fd 输出文件描述符 1286* @param in_fd 输入文件描述符 1287* @param off 文件的偏移量 1288* @param len 发送的长度 1289* @param cb 完成后的回调函数 1290* 1291* @return 成功返回0,失败返回-1 1292*/ 1293int uv_fs_sendfile(uv_loop_t* loop, 1294 uv_fs_t* req, 1295 uv_file out_fd, 1296 uv_file in_fd, 1297 int64_t off, 1298 size_t len, 1299 uv_fs_cb cb); 1300 1301/** 1302* @brief 异步写入文件 1303* 1304* @param loop 事件循环 1305* @param req 文件操作请求 1306* @param file 文件描述符 1307* @param bufs 要写入的数据 1308* @param nbufs 数据的数量 1309* @param off 文件的偏移量 1310* @param cb 完成后的回调函数 1311* 1312* @return 成功返回0,失败返回-1 1313*/ 1314int uv_fs_write(uv_loop_t* loop, 1315 uv_fs_t* req, 1316 uv_file file, 1317 const uv_buf_t bufs[], 1318 unsigned int nbufs, 1319 int64_t off, 1320 uv_fs_cb cb); 1321 1322/** 1323* @brief 异步复制文件 1324* 1325* @param loop 事件循环 1326* @param req 文件操作请求 1327* @param path 源文件路径 1328* @param new_path 目标文件路径 1329* @param flags 复制选项 1330* @param cb 完成后的回调函数 1331* 1332* @return 成功返回0,失败返回-1 1333*/ 1334int uv_fs_copyfile(uv_loop_t* loop, 1335 uv_fs_t* req, 1336 const char* path, 1337 const char* new_path 1338 int flags, 1339 uv_fs_cb cb); 1340``` 1341 1342- uv_getaddrinfo_t 1343 1344 函数原型: 1345 1346```cpp 1347/** 1348* @brief 异步获取地址信息 1349* 1350* @param loop 事件循环 1351* @param req 地址信息请求 1352* @param cb 完成后的回调函数 1353* @param hostname 主机名 1354* @param service 服务名 1355* @param hints 地址信息提示 1356* 1357* @return 成功返回0,失败返回-1 1358*/ 1359int uv_getaddrinfo(uv_loop_t* loop, 1360 uv_getaddrinfo_t* req, 1361 uv_getaddrinfo_cb cb, 1362 const char* hostname, 1363 const char* service, 1364 const struct addrinfo* hints); 1365``` 1366 1367- uv_getnameinfo_t 1368 1369 函数原型: 1370 1371```cpp 1372/** 1373* @brief 异步获取名称信息 1374* 1375* @param loop 事件循环 1376* @param req 名称信息请求 1377* @param getnameinfo_cb 完成后的回调函数 1378* @param addr 地址 1379* @param flags 标志 1380* 1381* @return 成功返回0,失败返回-1 1382*/ 1383int uv_getnameinfo(uv_loop_t* loop, 1384 uv_getnameinfo_t* req, 1385 uv_getnameinfo_cb getnameinfo_cb, 1386 const struct sockaddr* addr, 1387 int flags); 1388``` 1389 1390在应用主线程上不生效的接口主要包括: 1391 1392- idle句柄 1393- prepare句柄 1394- check句柄 1395- signal相关函数 1396- tcp及udp相关函数 1397 1398## 技术案例 1399 1400[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) 1401 1402[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) 1403 1404[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) 1405