1# Function Flow Runtime Development 2 3## When to Use 4 5Function Flow is a task-based and data-driven concurrent programming model that allows you to develop an application by creating tasks and describing their dependencies. Function Flow Runtime (FFRT) is a software runtime library that works with the Function Flow programming model. It is used to schedule and execute tasks of an application developed on the Function Flow programming model. Specifically, FFRT automatically and concurrently schedules and executes tasks of the application based on the task dependency status and available resources, so that you can focus on feature development. 6 7This topic walks you through how to implement parallel programming based on the Function Flow programming model and FFRT. 8 9## Available APIs 10 11| API | Description | 12| ------------------------------------------------------------ | ------------------------------------------------------------ | 13| ffrt_condattr_init (ffrt_condattr_t* attr) | Initializes a condition variable attribute.| 14| ffrt_condattr_destroy(ffrt_condattr_t* attr) | Destroys a condition variable attribute.| 15| ffrt_condattr_setclock(ffrt_condattr_t* attr, ffrt_clockid_t clock) | Sets the clock of a condition variable attribute.| 16| ffrt_condattr_getclock(const ffrt_condattr_t* attr, ffrt_clockid_t* clock) | Obtains the clock of a condition variable attribute. | 17| ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr) | Initializes a condition variable. | 18| ffrt_cond_signal(ffrt_cond_t* cond) | Unblocks at least one of the threads that are blocked on a condition variable.| 19| ffrt_cond_broadcast(ffrt_cond_t* cond) | Unblocks all threads currently blocked on a condition variable.| 20| ffrt_cond_wait(ffrt_cond_t* cond, ffrt_mutex_t* mutex) | Blocks the calling thread on a condition variable.| 21| ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point) | Blocks the calling thread on a condition variable for a given duration.| 22| ffrt_cond_destroy(ffrt_cond_t* cond) | Destroys a condition variable.| 23| ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr) | Initializes a mutex.| 24| ffrt_mutex_lock(ffrt_mutex_t* mutex) | Locks a mutex.| 25| ffrt_mutex_unlock(ffrt_mutex_t* mutex) | Unlocks a mutex.| 26| ffrt_mutex_trylock(ffrt_mutex_t* mutex) | Attempts to lock a mutex.| 27| ffrt_mutex_destroy(ffrt_mutex_t* mutex) | Destroys a mutex.| 28| ffrt_queue_attr_init(ffrt_queue_attr_t* attr) | Initializes a queue attribute.| 29| ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr) | Destroys a queue attribute.| 30| ffrt_queue_attr_set_qos(ffrt_queue_attr_t* attr, ffrt_qos_t qos) | Sets the queue QoS.| 31| ffrt_queue_attr_get_qos(const ffrt_queue_attr_t* attr) | Obtains the queue QoS.| 32| ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr) | Creates a queue.| 33| ffrt_queue_destroy(ffrt_queue_t queue) | Destroys a queue.| 34| ffrt_queue_submit(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr) | Submits a task to a queue.| 35| ffrt_queue_submit_h(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr) | Submits a task to a queue, and obtains the task handle.| 36| ffrt_queue_wait(ffrt_task_handle_t handle) | Waits until a task in the queue is complete.| 37| ffrt_queue_cancel(ffrt_task_handle_t handle) | Cancels a task in the queue.| 38| ffrt_queue_attr_set_max_concurrency(ffrt_queue_attr_t* attr, const int max_concurrency) | Sets the maximum concurrency for a queue, which must be a concurrent queue.| 39| ffrt_queue_attr_get_max_concurrency(ffrt_queue_attr_t* attr) | Obtains the maximum concurrency of a queue, which must be a concurrent queue.| 40| ffrt_get_main_queue() | Obtains the main thread queue.| 41| ffrt_get_current_queue() | Obtains the ArkTS Worker thread queue.| 42| ffrt_usleep(uint64_t usec) | Suspends the calling thread for a given duration.| 43| ffrt_yield(void) | Passes control to other tasks so that they can be executed.| 44| ffrt_task_attr_init(ffrt_task_attr_t* attr) | Initializes a task attribute.| 45| ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name) | Sets a task name.| 46| ffrt_task_attr_get_name(const ffrt_task_attr_t* attr) | Obtains a task name.| 47| ffrt_task_attr_destroy(ffrt_task_attr_t* attr) | Destroys a task attribute.| 48| ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos) | Sets the task QoS.| 49| ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr) | Obtains the task QoS.| 50| ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us) | Sets the task delay time.| 51| ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr) | Obtains the task delay time.| 52| ffrt_task_attr_set_queue_priority(ffrt_task_attr_t* attr, ffrt_queue_priority_t priority) | Sets the task priority in the queue.| 53| ffrt_task_attr_get_queue_priority(const ffrt_task_attr_t* attr) | Obtains the task priority in the queue.| 54| ffrt_this_task_get_qos() | Obtains the QoS of this task.| 55| ffrt_this_task_update_qos(ffrt_qos_t qos) | Updates the QoS of this task.| 56| ffrt_this_task_get_id(void) | Obtains the ID of this task.| 57| ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind) | Applies for memory for the function execution structure.| 58| ffrt_submit_base(ffrt_function_header_t* f, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) | Submits a task.| 59| ffrt_submit_h_base(ffrt_function_header_t* f, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) | Submits a task, and obtains the task handle.| 60| ffrt_task_handle_destroy(ffrt_task_handle_t handle) | Destroys a task handle.| 61| ffrt_skip(ffrt_task_handle_t handle) | Skips a task.| 62| ffrt_wait_deps(const ffrt_deps_t* deps) | Waits until the dependent tasks are complete.| 63| ffrt_loop_create(ffrt_queue_t queue) | Creates a loop.| 64| ffrt_loop_destroy(ffrt_loop_t loop) | Destroys a loop.| 65| ffrt_loop_run(ffrt_loop_t loop) | Runs a loop.| 66| ffrt_loop_stop(ffrt_loop_t loop) | Stops a loop.| 67| ffrt_loop_epoll_ctl(ffrt_loop_t loop, int op, int fd, uint32_t events, void* data, ffrt_poller_cb cb) | Manages listening events on a loop.| 68| ffrt_loop_timer_start(ffrt_loop_t loop, uint64_t timeout, void* data, ffrt_timer_cb cb, bool repeat) | Starts the timer on a loop.| 69| ffrt_loop_timer_stop(ffrt_loop_t loop, ffrt_timer_t handle) | Stops the timer on a loop.| 70| ffrt_timer_start(ffrt_qos_t qos, uint64_t timeout, void* data, ffrt_timer_cb cb, bool repeat) | Starts the timer.| 71| ffrt_timer_stop(ffrt_qos_t qos, ffrt_timer_t handle); | Stops the timer.| 72 73## API Introduction 74 75 76### Task Management APIs 77 78#### ffrt_submit_base 79 80Exports an FFRT dynamic library. You can encapsulate this API into the C API **ffrt_submit** for binary compatibility. 81 82##### Declaration 83 84```{.c} 85const int ffrt_auto_managed_function_storage_size = 64 + sizeof(ffrt_function_header_t); 86typedef enum { 87 ffrt_function_kind_general, 88 ffrt_function_kind_queue 89} ffrt_function_kind_t; 90 91void* ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind); 92 93typedef void(*ffrt_function_t)(void*); 94typedef struct { 95 ffrt_function_t exec; 96 ffrt_function_t destroy; 97 uint64_t reserve[2]; 98} ffrt_function_header_t; 99 100void ffrt_submit_base(ffrt_function_header_t* func, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr); 101``` 102 103##### Parameters 104 105`kind` 106 107Subtype of **function**. It is used to optimize the internal data structure. The default value is **ffrt_function_kind_general**. 108 109`func` 110 111Pointer to the CPU function. The struct executed by the pointer describes two function pointers, namely, **exec** and **destroy**, according to the **ffrt_function_header_t** definition. FFRT executes and destroys the task by using the two function pointers. 112 113`in_deps` 114 115* Optional. 116* Input dependencies of the task. FFRT establishes the dependency by using the virtual address of the data as the data signature. 117 118`out_deps` 119 120* Optional. 121 122* Output dependencies of the task. 123 124 **NOTE** 125 126 The dependency is essentially a value. FFRT cannot determine whether the value is reasonable. It always treats the input value reasonable. However, you are not advised to use inappropriate values such as **NULL**, **1**, or **2** to establish dependencies because doing this will establish unnecessary dependencies and affect concurrency. Instead, use the actual memory address. 127 128`attr` 129 130* Optional. 131* Task attribute, such as QoS. For details, see [ffrt_task_attr_t](#ffrt_task_attr_t). 132 133##### Return value 134 135N/A 136 137##### Description 138* You are advised to encapsulate **ffrt_submit_base** first. For details, see **Example** below. 139* As an underlying capability, **ffrt_submit_base** must meet the following requirements: 140 * The **func** pointer can be allocated by calling **ffrt_alloc_auto_managed_function_storage_base**, and the two function pointers in the struct must be in the specified sequence (**exec** prior to **destroy**). 141 * The memory allocated by calling **ffrt_alloc_auto_managed_function_storage_base** is of the size specified by **ffrt_auto_managed_function_storage_size**. Its lifecycle is managed by FFRT. When the task is complete, FFRT automatically releases the memory. 142* The following two function pointers are defined in **ffrt_function_header_t**: 143 * **exec**: describes how the task is executed. It is called by FFRT to execute the task. 144 * **destroy**: describes how a task is destroyed. It is called by FFRT to destroy the task. 145 146##### Example 147 148 149```{.c} 150template<class T> 151struct function { 152 template<class CT> 153 function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {} 154 ffrt_function_header_t header; 155 T closure; 156}; 157 158template<class T> 159void exec_function_wrapper(void* t) 160{ 161 auto f = (function<std::decay_t<T>>*)t; 162 f->closure(); 163} 164 165template<class T> 166void destroy_function_wrapper(void* t) 167{ 168 auto f = (function<std::decay_t<T>>*)t; 169 f->closure = nullptr; 170} 171 172template<class T> 173inline ffrt_function_header_t* create_function_wrapper(T&& func) 174{ 175 using function_type = function<std::decay_t<T>>; 176 static_assert(sizeof(function_type) <= ffrt_auto_managed_function_storage_size, 177 "size of function must be less than ffrt_auto_managed_function_storage_size"); 178 179 auto p = ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 180 auto f = new (p) function_type( 181 {exec_function_wrapper<T>, destroy_function_wrapper<T>}, 182 std::forward<T>(func)); 183 return (ffrt_function_header_t*)f; 184} 185 186static inline void submit(std::function<void()>&& func) 187{ 188 return ffrt_submit_base(create_function_wrapper(std::move(func)), NULL, NULL, NULL); 189} 190``` 191 192#### ffrt_wait 193 194- Used together with **ffrt_submit_base**. 195- Waits by suspending the current execution context, until the specified data is produced or all subtasks of the current task are complete. 196 197##### Declaration 198 199```{.c} 200void ffrt_wait_deps(ffrt_deps_t* deps); 201void ffrt_wait(); 202``` 203 204##### Parameters 205 206`deps` 207 208Virtual addresses of the data to be produced. These addresses may be used as **out_deps** in **submit()** of some tasks. For details about how to generate the dependency, see **ffrt_deps_t**. Note that a null pointer indicates no dependency. 209 210##### Return value 211 212N/A 213 214##### Description 215* **ffrt_wait_deps(deps)** is used to suspend code execution before the data specified by **deps** is produced. 216* **ffrt_wait()** is used to suspend code execution before all subtasks (excluding grandchild tasks and lower-level subtasks) submitted by the current context are complete. 217* This API can be called inside or outside an FFRT task. 218* **ffrt_wait_deps(deps)** or **ffrt_wait()** called outside an FFRT task can be sensed by the OS, and therefore it is more expensive than that called inside an FFRT task. As such, you are advised to use **ffrt_wait()** inside an FFRT task whenever possible. 219 220##### Example 221 222**Recursive Fibonacci** 223 224The Fibonacci Sequence implemented in serial mode is as follows: 225 226```{.c} 227#include <stdio.h> 228 229void fib(int x, int* y) { 230 if (x <= 1) { 231 *y = x; 232 } else { 233 int y1, y2; 234 fib(x - 1, &y1); 235 fib(x - 2, &y2); 236 *y = y1 + y2; 237 } 238} 239int main(int narg, char** argv) 240{ 241 int r; 242 fib(10, &r); 243 printf("fibonacci 10: %d\n", r); 244 return 0; 245} 246``` 247 248Use FFRT to implement the Fibonacci Sequence in parallel mode: (For Fibonacci, the computing workload of a single task is small and parallel acceleration is not required. However, this pattern requires high flexibility of the parallel programming model.) 249 250```{.c} 251#include <stdio.h> 252#include "ffrt.h" // All header files related to FFRT are included. 253 254typedef struct { 255 int x; 256 int* y; 257} fib_ffrt_s; 258 259typedef struct { 260 ffrt_function_header_t header; 261 ffrt_function_t func; 262 ffrt_function_t after_func; 263 void* arg; 264} c_function; 265 266static void ffrt_exec_function_wrapper(void* t) 267{ 268 c_function* f = (c_function*)t; 269 if (f->func) { 270 f->func(f->arg); 271 } 272} 273 274static void ffrt_destroy_function_wrapper(void* t) 275{ 276 c_function* f = (c_function*)t; 277 if (f->after_func) { 278 f->after_func(f->arg); 279 } 280} 281 282#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 283static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 284 const ffrt_function_t after_func, void* arg) 285{ 286 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 287 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 288 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 289 f->header.exec = ffrt_exec_function_wrapper; 290 f->header.destroy = ffrt_destroy_function_wrapper; 291 f->func = func; 292 f->after_func = after_func; 293 f->arg = arg; 294 return (ffrt_function_header_t*)f; 295} 296 297static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 298 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 299{ 300 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 301} 302 303void fib_ffrt(void* arg) 304{ 305 fib_ffrt_s* p = (fib_ffrt_s*)arg; 306 int x = p->x; 307 int* y = p->y; 308 309 if (x <= 1) { 310 *y = x; 311 } else { 312 int y1, y2; 313 fib_ffrt_s s1 = {x - 1, &y1}; 314 fib_ffrt_s s2 = {x - 2, &y2}; 315 const std::vector<ffrt_dependence_t> dx_deps = {{ffrt_dependence_data, &x}}; 316 ffrt_deps_t dx{static_cast<uint32_t>(dx_deps.size()), dx_deps.data()}; 317 const std::vector<ffrt_dependence_t> dy1_deps = {{ffrt_dependence_data, &y1}}; 318 ffrt_deps_t dy1{static_cast<uint32_t>(dy1_deps.size()), dy1_deps.data()}; 319 const std::vector<ffrt_dependence_t> dy2_deps = {{ffrt_dependence_data, &y2}}; 320 ffrt_deps_t dy2{static_cast<uint32_t>(dy2_deps.size()), dy2_deps.data()}; 321 const std::vector<ffrt_dependence_t> dy12_deps = {{ffrt_dependence_data, &y1}, {ffrt_dependence_data, &y2}}; 322 ffrt_deps_t dy12{static_cast<uint32_t>(dy12_deps.size()), dy12_deps.data()}; 323 ffrt_submit_c(fib_ffrt, NULL, &s1, &dx, &dy1, NULL); 324 ffrt_submit_c(fib_ffrt, NULL, &s2, &dx, &dy2, NULL); 325 ffrt_wait_deps(&dy12); 326 *y = y1 + y2; 327 } 328} 329 330int main(int narg, char** argv) 331{ 332 int r; 333 fib_ffrt_s s = {10, &r}; 334 const std::vector<ffrt_dependence_t> dr_deps = {{ffrt_dependence_data, &r}}; 335 ffrt_deps_t dr{static_cast<uint32_t>(dr_deps.size()), dr_deps.data()}; 336 ffrt_submit_c(fib_ffrt, NULL, &s, NULL, &dr, NULL); 337 ffrt_wait_deps(&dr); 338 printf("fibonacci 10: %d\n", r); 339 return 0; 340} 341``` 342 343**NOTE** 344 345(1) fibonacci (x-1) and fibonacci (x-2) are submitted to FFRT as two tasks. After the two tasks are complete, the results are accumulated. 346 347(2) A single task can be split into only two subtasks, but the subtasks can be further split. Therefore, the entire computing graph delivers a high DOP, and a call tree is formed between tasks in FFRT. 348 349<img src="figures/ffrtfigure2.png" style="zoom:100%" /> 350 351> **NOTE** 352> 353> The preceding implementation requires you to explicitly manage the data lifecycle and encapsulate input parameters, making the code complex. 354 355#### ffrt_deps_t 356 357Abstraction of dependency arrays in C code, logically equivalent to **std::vector<void*>** in C++ code. 358 359##### Declaration 360 361```{.c} 362typedef enum { 363 ffrt_dependence_data, 364 ffrt_dependence_task, 365} ffrt_dependence_type_t; 366 367typedef struct { 368 ffrt_dependence_type_t type; 369 const void* ptr; 370} ffrt_dependence_t; 371 372typedef struct { 373 uint32_t len; 374 const ffrt_dependence_t* items; 375} ffrt_deps_t; 376``` 377 378##### Parameters 379 380`len` 381 382Number of dependent signatures. The value must be greater than or equal to 0. 383 384`item` 385 386Pointer to the start address of each signature. 387 388`type` 389 390Dependency type, which can be data dependency or task dependency. 391 392`ptr` 393 394Actual address of the dependent signature content. 395 396##### Return value 397 398N/A 399 400##### Description 401 402**item** is the start address pointer of each signature. The pointer can point to the heap space or stack space, but the allocated space must be greater than or equal to len * sizeof(ffrt_dependence_t). 403 404##### Example 405 406Create a data dependency or task dependency. 407 408```{.c} 409// Create ffrt_deps_t on which the data depends. 410int x = 0; 411const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}}; 412ffrt_deps_t in{static_cast<uint32_t>(in_deps.size()), in_deps.data()}; 413 414// Submit a task, and obtain a task handle. 415ffrt_task_handle_t task = ffrt_submit_h_base( 416 ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 417// Create ffrt_deps_t on which the task depends. 418const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}}; 419ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 420``` 421 422#### ffrt_task_attr_t 423 424Auxiliary class for defining task attributes. It is used together with **ffrt_submit_base**. 425 426##### Declaration 427 428```{.c} 429typedef enum { 430 ffrt_qos_inherent = -1, 431 ffrt_qos_background, 432 ffrt_qos_utility, 433 ffrt_qos_default, 434 ffrt_qos_user_initiated, 435} ffrt_qos_default_t; 436 437typedef int ffrt_qos_t; 438 439typedef struct { 440 uint32_t storage[(ffrt_task_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; 441} ffrt_task_attr_t; 442typedef void* ffrt_task_handle_t; 443 444int ffrt_task_attr_init(ffrt_task_attr_t* attr); 445void ffrt_task_attr_destroy(ffrt_task_attr_t* attr); 446void ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos); 447ffrt_qos_t ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr); 448void ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name); 449const char* ffrt_task_attr_get_name(const ffrt_task_attr_t* attr); 450void ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us); 451uint64_t ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr); 452``` 453 454##### Parameters 455 456`attr` 457 458Handle of the target task attribute. 459 460`qos` 461 462* QoS. 463* **ffrt_qos_inherent** is a QoS type, indicating that the QoS of the task to be submitted by **ffrt_submit** inherits the QoS of the current task. 464 465`delay_us` 466 467Delay for executing the task, in μs. 468 469##### Return value 470 471N/A 472 473##### Description 474* The content passed by **attr** is fetched and stored when **ffrt_submit** is being executed. You can destroy the content on receiving the return value of **ffrt_submit**. 475* Conventions: 476 * If **task_attr** is not used for QoS setting during task submission, the QoS of the task is **ffrt_qos_default**. 477 * If **task_attr** is set to **ffrt_qos_inherent** during task submission, the QoS of the task to be submitted is the same as that of the current task. If a task with the **ffrt_qos_inherent** attribute is submitted outside an FFRT task, its QoS is **ffrt_qos_default**. 478 * In other cases, the QoS value passed in is used. 479* You need to set the **ffrt_task_attr_t** object to null or destroy the object. For the same **ffrt_task_attr_t** object, **ffrt_task_attr_destroy** can be called only once. Otherwise, undefined behavior may occur. 480* If **task_attr** is accessed after **ffrt_task_attr_destroy** is called, undefined behavior may occur. 481 482##### Example 483 484Submit a task with the QoS set to **ffrt_qos_background**: 485 486```{.c} 487#include <stdio.h> 488#include "ffrt.h" 489 490void my_print(void* arg) 491{ 492 printf("hello ffrt\n"); 493} 494 495typedef struct { 496 ffrt_function_header_t header; 497 ffrt_function_t func; 498 ffrt_function_t after_func; 499 void* arg; 500} c_function; 501 502static void ffrt_exec_function_wrapper(void* t) 503{ 504 c_function* f = (c_function*)t; 505 if (f->func) { 506 f->func(f->arg); 507 } 508} 509 510static void ffrt_destroy_function_wrapper(void* t) 511{ 512 c_function* f = (c_function*)t; 513 if (f->after_func) { 514 f->after_func(f->arg); 515 } 516} 517 518#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 519static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 520 const ffrt_function_t after_func, void* arg) 521{ 522 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 523 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 524 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 525 f->header.exec = ffrt_exec_function_wrapper; 526 f->header.destroy = ffrt_destroy_function_wrapper; 527 f->func = func; 528 f->after_func = after_func; 529 f->arg = arg; 530 return (ffrt_function_header_t*)f; 531} 532 533static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 534 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 535{ 536 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 537} 538 539int main(int narg, char** argv) 540{ 541 ffrt_task_attr_t attr; 542 ffrt_task_attr_init(&attr); 543 ffrt_task_attr_set_qos(&attr, ffrt_qos_background); 544 ffrt_task_attr_set_delay(&attr, 10000); 545 ffrt_submit_c(my_print, NULL, NULL, NULL, NULL, &attr); 546 ffrt_task_attr_destroy(&attr); 547 ffrt_wait(); 548 return 0; 549} 550``` 551 552 553 554 555#### ffrt_submit_h_base 556 557Submits a task to the scheduler. Different from **ffrt_submit_base**, **ffrt_submit_h_base** returns a task handle. The handle can be used to establish the dependency between tasks or implement synchronization in the **wait** statements. 558 559##### Declaration 560 561```{.c} 562typedef void* ffrt_task_handle_t; 563 564ffrt_task_handle_t ffrt_submit_h_base(ffrt_function_t func, void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr); 565void ffrt_task_handle_destroy(ffrt_task_handle_t handle); 566``` 567 568##### Parameters 569 570`func` 571 572Pointer to the CPU function. The struct executed by the pointer describes two function pointers, namely, **exec** and **destroy**, according to the **ffrt_function_header_t** definition. FFRT executes and destroys the task by using the two function pointers. 573 574`in_deps` 575 576* Optional. 577* Input dependencies of the task. FFRT establishes the dependency by using the virtual address of the data as the data signature. 578 579`out_deps` 580 581* Optional. 582 583* Output dependencies of the task. 584 585 **NOTE** 586 587 The dependency is essentially a value. FFRT cannot determine whether the value is reasonable. It always treats the input value reasonable. However, you are not advised to use inappropriate values such as **NULL**, **1**, or **2** to establish dependencies. Instead, use the actual memory address because inappropriate values will establish unnecessary dependencies and affect concurrency. 588 589`attr` 590 591* Optional. 592* Task attribute, such as QoS. For details, see [ffrt_task_attr_t](#ffrt_task_attr_t). 593 594##### Return value 595 596Take handle. The handle can be used to establish the dependency between tasks or implement synchronization in the wait statements. 597 598##### Description 599 600* **ffrt_task_handle_t** in the C code must be explicitly destroyed by calling **ffrt_task_handle_destroy**. 601* You need to set the **ffrt_task_handle_t** object in the C code to null or destroy the object. For the same **ffrt_task_handle_t** object, **ffrt_task_handle_destroy** can be called only once. Otherwise, undefined behavior may occur. 602* If **ffrt_task_handle_t** is accessed after **ffrt_task_handle_destroy** is called, undefined behavior may occur. 603 604##### Example 605 606```{.c} 607#include <stdio.h> 608#include "ffrt.h" 609 610void func0(void* arg) 611{ 612 printf("hello "); 613} 614 615void func1(void* arg) 616{ 617 (*(int*)arg)++; 618} 619 620void func2(void* arg) 621{ 622 printf("world, x = %d\n", *(int*)arg); 623} 624 625void func3(void* arg) 626{ 627 printf("handle wait"); 628 (*(int*)arg)++; 629} 630 631typedef struct { 632 ffrt_function_header_t header; 633 ffrt_function_t func; 634 ffrt_function_t after_func; 635 void* arg; 636} c_function; 637 638static void ffrt_exec_function_wrapper(void* t) 639{ 640 c_function* f = (c_function*)t; 641 if (f->func) { 642 f->func(f->arg); 643 } 644} 645 646static void ffrt_destroy_function_wrapper(void* t) 647{ 648 c_function* f = (c_function*)t; 649 if (f->after_func) { 650 f->after_func(f->arg); 651 } 652} 653 654#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 655static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 656 const ffrt_function_t after_func, void* arg) 657{ 658 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 659 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 660 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 661 f->header.exec = ffrt_exec_function_wrapper; 662 f->header.destroy = ffrt_destroy_function_wrapper; 663 f->func = func; 664 f->after_func = after_func; 665 f->arg = arg; 666 return (ffrt_function_header_t*)f; 667} 668 669static inline ffrt_task_handle_t ffrt_submit_h_c(ffrt_function_t func, const ffrt_function_t after_func, 670 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 671{ 672 return ffrt_submit_h_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 673} 674 675static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 676 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 677{ 678 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 679} 680 681 682int main(int narg, char** argv) 683{ 684 // Handle the work with submit. 685 ffrt_task_handle_t h = ffrt_submit_h_c(func0, NULL, NULL, NULL, NULL, NULL); // not need some data in this task 686 int x = 1; 687 const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}}; 688 ffrt_deps_t d2{static_cast<uint32_t>(in_deps.size()), in_deps.data()}; 689 690 const std::vector<ffrt_dependence_t> out_deps = {{ffrt_dependence_data, &x}}; 691 ffrt_deps_t d1{static_cast<uint32_t>(out_deps.size()), out_deps.data()}; 692 693 ffrt_submit_c(func1, NULL, &x, NULL, &d1, NULL); 694 ffrt_submit_c(func2, NULL, &x, &d2, NULL, NULL); // this task depend x and h 695 ffrt_task_handle_destroy(h); 696 697 // Handle the work with wait. 698 ffrt_task_handle_t h2 = ffrt_submit_h_c(func3, NULL, &x, NULL, NULL, NULL); 699 700 const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, h2}}; 701 ffrt_deps_t d3{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 702 ffrt_wait_deps(&d3); 703 ffrt_task_handle_destroy(h2); 704 printf("x = %d", x); 705 ffrt_wait(); 706 return 0; 707} 708``` 709 710Expected output 711 712``` 713hello 714handle wait 715x = 2 716world, x = 3 717``` 718 719 720 721#### ffrt_this_task_get_id 722 723Obtains the ID of this task. This API is used for maintenance and testing. (The task ID is unique, but the task name may be duplicate.) 724 725##### Declaration 726 727```{.c} 728uint64_t ffrt_this_task_get_id(); 729``` 730 731##### Parameters 732 733N/A 734 735##### Return value 736 737ID of the task being executed. 738 739##### Description 740 741* If this API is called inside a task, the ID of this task is returned. If this API is called outside a task, **0** is returned. 742* You can determine whether the function runs on an FFRT or a non-FFRT worker thread based on the return value. 743* The task ID starts from 1 and is incremented by 1 each time a task is submitted. The task ID contains 64 bits. Even if one million tasks are submitted per second, it takes 292471.2 years to finish one loop. 744 745##### Example 746 747N/A 748 749#### ffrt_this_task_update_qos 750 751Updates the QoS of the task being executed. 752 753##### Declaration 754 755```{.c} 756int ffrt_this_task_update_qos(ffrt_qos_t qos); 757``` 758 759##### Parameters 760 761`qos` 762 763New QoS. 764 765##### Return value 766 767Returns **0** if the operation is successful; returns a non-zero value otherwise. 768 769##### Description 770 771* The QoS update takes effect immediately. 772* If the new QoS is different from the current QoS, the task is blocked and then resumed based on the new QoS. 773* If the new QoS is the same as the current QoS, the API returns **0** immediately without any processing. 774* If this API is called not inside a task, a non-zero value is returned. You can ignore the value or perform other operations. 775 776##### Example 777 778N/A 779 780#### ffrt_this_task_get_qos 781 782Obtains the QoS of the task being executed. 783 784##### Declaration 785 786```{.c} 787ffrt_qos_t ffrt_this_task_get_qos(); 788``` 789 790##### Parameters 791 792NA 793 794##### Return value 795 796QoS. 797 798##### Description 799 800N/A 801 802##### Example 803 804```{.c} 805#include "ffrt.h" 806 807int main(int narg, char** argv) 808{ 809 static int x = 0; 810 int* xf = &x; 811 void* data = xf; 812 uint64_t timeout1 = 20; 813 814 ffrt::submit([=]() { 815 ffrt_qos_t taskQos = ffrt_this_task_get_qos(); 816 ffrt_timer_cb cb; 817 ffrt_timer_start(taskQos, timeout1, data, cb, false); 818 ffrt_usleep(200); 819 }, {}, {}); 820 ffrt::wait(); 821 return 0; 822} 823 824``` 825 826### Serial Queue 827 828Based on the FFRT coroutine scheduling model, the serial queue implements a message queue. Serial tasks are executed in FFRT Worker threads. You do not need to maintain a dedicated thread, making the scheduling overhead lightweight. 829 830#### ffrt_queue_attr_t 831 832##### Declaration 833```{.c} 834typedef struct { 835 uint32_t storage[(ffrt_queue_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; 836} ffrt_queue_attr_t; 837 838int ffrt_queue_attr_init(ffrt_queue_attr_t* attr); 839void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr); 840``` 841 842##### Parameters 843 844`attr` 845Pointer to the uninitialized **ffrt_queue_attr_t** object. 846 847##### Return value 848Returns **0** if the API is called successfully; returns **-1** otherwise. 849 850##### Description 851* An **ffrt_queue_attr_t** object must be created prior to an **ffrt_queue_t** object. 852* You need to set the **ffrt_queue_attr_t** object to null or destroy the object. For the same **ffrt_queue_attr_t** object, **ffrt_queue_attr_destroy** can be called only once. Otherwise, undefined behavior may occur. 853* If **ffrt_queue_attr_t** is accessed after **ffrt_queue_attr_destroy** is called, undefined behavior may occur. 854 855##### Example 856See the example provided in **ffrt_queue_t**. 857 858#### ffrt_queue_t 859 860##### Declaration 861```{.c} 862typedef enum { ffrt_queue_serial, ffrt_queue_concurrent, ffrt_queue_max } ffrt_queue_type_t; 863typedef void* ffrt_queue_t; 864 865ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr) 866void ffrt_queue_destroy(ffrt_queue_t queue) 867``` 868 869##### Parameters 870 871`type` 872 873Queue type. 874 875`name` 876 877Pointer to the queue name. 878 879`attr` 880 881Pointer to the queue attribute. For details, see **ffrt_queue_attr_t**. 882 883##### Return value 884Returns the queue created if the API is called successfully; returns a null pointer otherwise. 885 886##### Description 887* Tasks submitted to the queue are executed in sequence. If a task is blocked, the execution sequence of the task cannot be ensured. 888* You need to set the **ffrt_queue_t** object to null or destroy the object. For the same **ffrt_queue_t** object, **ffrt_queue_destroy** can be called only once. Otherwise, undefined behavior may occur. 889* If **ffrt_queue_t** is accessed after **ffrt_queue_destroy** is called, undefined behavior may occur. 890 891##### Example 892``` 893#include <stdio.h> 894#include "ffrt.h" 895 896using namespace std; 897 898template<class T> 899struct Function { 900 ffrt_function_header_t header; 901 T closure; 902}; 903 904template<class T> 905void ExecFunctionWrapper(void* t) 906{ 907 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 908 f->closure(); 909} 910 911template<class T> 912void DestroyFunctionWrapper(void* t) 913{ 914 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 915 f = nullptr; 916} 917 918template<class T> 919static inline ffrt_function_header_t* create_function_wrapper(T&& func, 920 ffrt_function_kind_t kind = ffrt_function_kind_general) 921{ 922 using function_type = Function<std::decay_t<T>>; 923 auto p = ffrt_alloc_auto_managed_function_storage_base(kind); 924 auto f = new (p)function_type; 925 f->header.exec = ExecFunctionWrapper<T>; 926 f->header.destroy = DestroyFunctionWrapper<T>; 927 f->closure = std::forward<T>(func); 928 return reinterpret_cast<ffrt_function_header_t*>(f); 929} 930 931int main(int narg, char** argv) 932{ 933 ffrt_queue_attr_t queue_attr; 934 (void)ffrt_queue_attr_init(&queue_attr); 935 ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr); 936 std::function<void()>&& queueFunc = [] () {printf("Task done.\n");}; 937 ffrt_function_header_t* queueFunc_t = create_function_wrapper((queueFunc), ffrt_function_kind_queue); 938 ffrt_queue_submit(queue_handle, queueFunc_t, nullptr); 939 940 ffrt_queue_attr_destroy(&queue_attr); 941 ffrt_queue_destroy(queue_handle); 942} 943``` 944 945#### ffrt_get_main_queue 946 947Obtains the main thread queue. 948 949##### Declaration 950```{.c} 951ffrt_queue_t ffrt_get_main_queue(); 952``` 953 954##### Parameters 955 956NA 957 958##### Return value 959 960Main thread queue. 961 962##### Description 963 964The main thread queue is obtained for the FFRT thread to communicate with the main thread. 965 966##### Example 967```{.c} 968This test case must be executed on HarmonyOS. 969#include "ffrt.h" 970 971inline void OnePlusForTest(void* data) 972{ 973 *(int*)data += 1; 974} 975 976int main(int narg, char** argv) 977{ 978 ffrt::queue *serialQueue = new ffrt::queue("ffrt_normal_queue", {}); 979 ffrt_queue_t mainQueue = ffrt_get_main_queue(); 980 ffrt_task_attr_t attr; 981 ffrt_task_attr_init(&attr); 982 ffrt_task_attr_set_qos(&attr, ffrt_qos_user_initiated); 983 int result = 0; 984 std::function<void()>&& basicFunc = [&result]() { 985 OnePlusForTest(static_cast<void*>(&result)); 986 OnePlusForTest(static_cast<void*>(&result)); 987 ffrt_usleep(3000); 988 }; 989 990 ffrt::task_handle handle = serialQueue->submit_h( 991 [&] { 992 result = result + 1; 993 ffrt_queue_submit(mainQueue, ffrt::create_function_wrapper(basicFunc, ffrt_function_kind_queue), 994 &attr); 995 }, 996 ffrt::task_attr().qos(3).name("ffrt main_queue.")); 997 998 serialQueue->wait(handle); 999 return 0; 1000} 1001``` 1002 1003#### ffrt_get_current_queue 1004 1005Obtains the ArkTS Worker thread queue. 1006 1007##### Declaration 1008```{.c} 1009ffrt_queue_t ffrt_get_current_queue(); 1010``` 1011 1012##### Parameters 1013 1014NA 1015 1016##### Return value 1017 1018ArkTS Worker thread queue. 1019 1020##### Description 1021 1022The ArkTS Worker thread queue is obtained for the FFRT thread to communicate with the ArkTS Worker thread. 1023 1024##### Example 1025```{.c} 1026// This test case must be executed on HarmonyOS. 1027#include "ffrt.h" 1028 1029inline void OnePlusForTest(void* data) 1030{ 1031 *(int*)data += 1; 1032} 1033 1034int main(int narg, char** argv) 1035{ 1036 ffrt::queue *serialQueue = new ffrt::queue("ffrt_normal_queue", {}); 1037 ffrt_queue_t currentQueue = ffrt_get_current_queue(); 1038 ffrt_task_attr_t attr; 1039 ffrt_task_attr_init(&attr); 1040 ffrt_task_attr_set_qos(&attr, ffrt_qos_user_initiated); 1041 int result = 0; 1042 std::function<void()>&& basicFunc = [&result]() { 1043 OnePlusForTest(static_cast<void*>(&result)); 1044 OnePlusForTest(static_cast<void*>(&result)); 1045 ffrt_usleep(3000); 1046 }; 1047 1048 ffrt::task_handle handle = serialQueue->submit_h( 1049 [&] { 1050 result = result + 1; 1051 ffrt_queue_submit(currentQueue, ffrt::create_function_wrapper(basicFunc, ffrt_function_kind_queue), 1052 &attr); 1053 }, 1054 ffrt::task_attr().qos(3).name("ffrt current_queue.")); 1055 1056 serialQueue->wait(handle); 1057 return 0; 1058} 1059``` 1060 1061 1062### Concurrent Queue 1063 1064FFRT supports concurrent queues and allows you to set the concurrency and task priority of a concurrent queue. 1065 1066#### ffrt_queue_attr_set_max_concurrency 1067 1068Sets the maximum concurrency for a queue, which must be a concurrent queue. 1069 1070##### Declaration 1071```{.c} 1072void ffrt_queue_attr_set_max_concurrency(ffrt_queue_attr_t* attr, const int max_concurrency); 1073``` 1074 1075##### Parameters 1076 1077`attr` 1078 1079Pointer to the queue attribute. For details, see **ffrt_queue_attr_t**. 1080 1081`max_concurrency` 1082 1083Maximum concurrency. If the parameter is set to a value less than or equal to 0, the concurrency 1 is used. 1084 1085##### Return value 1086 1087NA 1088 1089##### Description 1090 1091If the concurrency is set to a large value, for example, 100, the actual concurrency may fail to reach the set value due to hardware capability limitations. 1092 1093##### Example 1094```{.c} 1095#include "ffrt.h" 1096 1097int main(int narg, char** argv) 1098{ 1099 ffrt_queue_attr_t queue_attr; 1100 (void)ffrt_queue_attr_init(&queue_attr); 1101 uint64_t concurrency = 4; 1102 ffrt_queue_attr_set_max_concurrency(&queue_attr, concurrency); 1103 concurrency = ffrt_queue_attr_get_max_concurrency(&queue_attr); 1104 ffrt_queue_attr_destroy(&queue_attr); 1105 printf("concurrency=%lu\n", concurrency); 1106 return 0; 1107} 1108``` 1109 1110Expected output 1111 1112``` 1113concurrency=4 1114``` 1115 1116#### ffrt_queue_attr_get_max_concurrency 1117 1118Obtains the maximum concurrency of a queue, which must be a concurrent queue. 1119 1120##### Declaration 1121```{.c} 1122int ffrt_queue_attr_get_max_concurrency(const ffrt_queue_attr_t* attr); 1123``` 1124 1125##### Parameters 1126 1127`attr` 1128 1129Pointer to the queue attribute. For details, see **ffrt_queue_attr_t**. 1130 1131##### Return value 1132 1133Maximum concurrency. 1134 1135##### Description 1136N/A 1137 1138##### Example 1139```{.c} 1140#include "ffrt.h" 1141 1142int main(int narg, char** argv) 1143{ 1144 ffrt_queue_attr_t queue_attr; 1145 (void)ffrt_queue_attr_init(&queue_attr); 1146 uint64_t concurrency = 4; 1147 ffrt_queue_attr_set_max_concurrency(&queue_attr, concurrency); 1148 concurrency = ffrt_queue_attr_get_max_concurrency(&queue_attr); 1149 ffrt_queue_attr_destroy(&queue_attr); 1150 printf("concurrency=%lu\n", concurrency); 1151 return 0; 1152} 1153``` 1154 1155Expected output 1156 1157``` 1158concurrency=4 1159``` 1160 1161#### ffrt_task_attr_set_queue_priority 1162<hr/> 1163Sets the task priority in the queue. 1164 1165##### Declaration 1166```{.c} 1167/* Task priority */ 1168typedef enum { 1169ffrt_queue_priority_immediate = 0, 1170ffrt_queue_priority_high, 1171ffrt_queue_priority_low, 1172ffrt_queue_priority_idle, 1173} ffrt_queue_priority_t; 1174 1175void ffrt_task_attr_set_queue_priority(ffrt_task_attr_t* attr, ffrt_queue_priority_t priority); 1176``` 1177 1178##### Parameters 1179 1180`attr` 1181 1182Pointer to the task attribute. For details, see **ffrt_task_attr_t**. 1183 1184`priority` 1185 1186Task priority. Four priorities are supported. For details, see **ffrt_queue_priority_t**. 1187 1188##### Return value 1189 1190NA 1191 1192##### Description 1193N/A 1194 1195##### Example 1196```{.c} 1197#include "ffrt.h" 1198 1199int main(int narg, char** argv) 1200{ 1201 ffrt_task_attr_t task_attr; 1202 (void)ffrt_task_attr_init(&task_attr); 1203 ffrt_queue_priority_t priority = ffrt_queue_priority_idle; 1204 ffrt_task_attr_set_queue_priority(&task_attr, priority); 1205 priority = ffrt_task_attr_get_queue_priority(&task_attr); 1206 ffrt_task_attr_destroy(&task_attr); 1207 printf("priority=%d\n", priority); 1208 return 0; 1209} 1210``` 1211 1212Expected output 1213 1214``` 1215priority=3 1216``` 1217 1218#### ffrt_task_attr_get_queue_priority 1219 1220Obtains the task priority in a queue. 1221 1222##### Declaration 1223```{.c} 1224ffrt_queue_priority_t ffrt_task_attr_get_queue_priority(const ffrt_task_attr_t* attr); 1225``` 1226 1227##### Parameters 1228 1229`attr` 1230 1231Pointer to the queue attribute. For details, see **ffrt_queue_attr_t**. 1232 1233##### Return value 1234 1235Task priority. 1236 1237##### Description 1238N/A 1239 1240##### Example 1241```{.c} 1242#include "ffrt.h" 1243 1244int main(int narg, char** argv) 1245{ 1246 ffrt_task_attr_t task_attr; 1247 (void)ffrt_task_attr_init(&task_attr); 1248 ffrt_queue_priority_t priority = ffrt_queue_priority_idle; 1249 ffrt_task_attr_set_queue_priority(&task_attr, priority); 1250 priority = ffrt_task_attr_get_queue_priority(&task_attr); 1251 ffrt_task_attr_destroy(&task_attr); 1252 printf("priority=%d\n", priority); 1253 return 0; 1254} 1255``` 1256 1257Expected output 1258 1259``` 1260priority=3 1261``` 1262 1263### Synchronization Primitive 1264 1265#### ffrt_mutex_t 1266 1267Provides performance implementation similar to pthread mutex. 1268 1269##### Declaration 1270 1271```{.c} 1272typedef enum { 1273 ffrt_error = -1, 1274 ffrt_success = 0, 1275 ffrt_error_nomem = ENOMEM, 1276 ffrt_error_timedout = ETIMEDOUT, 1277 ffrt_error_busy = EBUSY, 1278 ffrt_error_inval = EINVAL 1279} ffrt_error_t; 1280 1281struct ffrt_mutex_t; 1282 1283int ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr); 1284int ffrt_mutex_lock(ffrt_mutex_t* mutex); 1285int ffrt_mutex_unlock(ffrt_mutex_t* mutex); 1286int ffrt_mutex_trylock(ffrt_mutex_t* mutex); 1287int ffrt_mutex_destroy(ffrt_mutex_t* mutex); 1288``` 1289 1290##### Parameters 1291 1292`attr` 1293 1294Attribute of the mutex. Set it to a null pointer. This is because FFRT supports only mutex of the basic type currently. 1295 1296`mutex` 1297 1298Pointer to the target mutex. 1299 1300##### Return value 1301 1302Returns **ffrt_success** if the API is called successfully; returns an error code otherwise. 1303 1304##### Description 1305* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1306* The traditional function **pthread_mutex_t** may cause unexpected kernel mode trap when it fails to lock a mutex. **ffrt_mutex_t** solves this problem and therefore provides better performance if used properly. 1307* Currently, recursion and timing are not supported. 1308* **ffrt_mutex_t** in the C code must be explicitly created and destroyed by calling **ffrt_mutex_init** and **ffrt_mutex_destroy**, respectively. 1309* You need to set the **ffrt_mutex_t** object in the C code to null or destroy the object. For the same **ffrt_mutex_t** object, **ffrt_mutex_destroy** can be called only once. Otherwise, undefined behavior may occur. 1310* If **ffrt_mutex_t** is accessed after **ffrt_mutex_destroy** is called, undefined behavior may occur. 1311 1312##### Example 1313 1314```{.c} 1315#include <stdio.h> 1316#include "ffrt.h" 1317 1318typedef struct { 1319 int* sum; 1320 ffrt_mutex_t* mtx; 1321} tuple; 1322 1323void func(void* arg) 1324{ 1325 tuple* t = (tuple*)arg; 1326 1327 int ret = ffrt_mutex_lock(t->mtx); 1328 if (ret != ffrt_success) { 1329 printf("error\n"); 1330 } 1331 (*t->sum)++; 1332 ret = ffrt_mutex_unlock(t->mtx); 1333 if (ret != ffrt_success) { 1334 printf("error\n"); 1335 } 1336} 1337 1338typedef struct { 1339 ffrt_function_header_t header; 1340 ffrt_function_t func; 1341 ffrt_function_t after_func; 1342 void* arg; 1343} c_function; 1344 1345static void ffrt_exec_function_wrapper(void* t) 1346{ 1347 c_function* f = (c_function*)t; 1348 if (f->func) { 1349 f->func(f->arg); 1350 } 1351} 1352 1353static void ffrt_destroy_function_wrapper(void* t) 1354{ 1355 c_function* f = (c_function*)t; 1356 if (f->after_func) { 1357 f->after_func(f->arg); 1358 } 1359} 1360 1361#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1362static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1363 const ffrt_function_t after_func, void* arg) 1364{ 1365 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1366 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1367 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1368 f->header.exec = ffrt_exec_function_wrapper; 1369 f->header.destroy = ffrt_destroy_function_wrapper; 1370 f->func = func; 1371 f->after_func = after_func; 1372 f->arg = arg; 1373 return (ffrt_function_header_t*)f; 1374} 1375 1376static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1377 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1378{ 1379 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1380} 1381 1382void ffrt_mutex_task(void *) 1383{ 1384 int sum = 0; 1385 ffrt_mutex_t mtx; 1386 tuple t = {&sum, &mtx}; 1387 int ret = ffrt_mutex_init(&mtx, NULL); 1388 if (ret != ffrt_success) { 1389 printf("error\n"); 1390 } 1391 for (int i = 0; i < 10; i++) { 1392 ffrt_submit_c(func, NULL, &t, NULL, NULL, NULL); 1393 } 1394 ffrt_mutex_destroy(&mtx); 1395 ffrt_wait(); 1396 printf("sum = %d\n", sum); 1397} 1398 1399int main(int narg, char** argv) 1400{ 1401 int r; 1402 ffrt_submit_c(ffrt_mutex_task, NULL, NULL, NULL, NULL, NULL); 1403 ffrt_wait(); 1404 return 0; 1405} 1406``` 1407 1408Expected output 1409 1410``` 1411sum=10 1412``` 1413 1414This example is for reference only and is not encouraged in practice. 1415 1416 1417#### ffrt_cond_t 1418 1419Provides performance implementation similar to pthread semaphore. 1420 1421##### Declaration 1422 1423```{.c} 1424typedef enum { 1425 ffrt_error = -1, 1426 ffrt_success = 0, 1427 ffrt_error_nomem = ENOMEM, 1428 ffrt_error_timedout = ETIMEDOUT, 1429 ffrt_error_busy = EBUSY, 1430 ffrt_error_inval = EINVAL 1431} ffrt_error_t; 1432 1433struct ffrt_cond_t; 1434 1435int ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr); 1436int ffrt_cond_signal(ffrt_cond_t* cond); 1437int ffrt_cond_broadcast(ffrt_cond_t* cond); 1438int ffrt_cond_wait(ffrt_cond_t*cond, ffrt_mutex_t* mutex); 1439int ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point); 1440int ffrt_cond_destroy(ffrt_cond_t* cond); 1441``` 1442 1443##### Parameters 1444 1445`cond` 1446 1447Pointer to the target semaphore. 1448 1449`attr` 1450 1451Pointer to the attribute. A null pointer indicates that the default attribute is used. 1452 1453`mutex` 1454 1455Pointer to the target mutex. 1456 1457`time_point` 1458 1459Pointer to the maximum duration during which the thread is blocked. 1460 1461 1462##### Return value 1463 1464Returns **ffrt_success** if the API is successfully called; returns **ffrt_error_timedout** if the maximum duration is reached before the mutex is locked. 1465 1466##### Description 1467* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1468* The traditional function **pthread_cond_t** may cause unexpected kernel mode trap when the conditions are not met. **ffrt_cond_t** solves this problem and therefore provides better performance if being used properly. 1469* **ffrt_cond_t** in the C code must be explicitly created and destroyed by calling **ffrt_cond_init** and **ffrt_cond_destroy**, respectively. 1470* You need to set the **ffrt_cond_t** object in the C code to null or destroy the object. For the same **ffrt_cond_t** object, **ffrt_cond_destroy** can be called only once. Otherwise, undefined behavior may occur. 1471* If **ffrt_cond_t** is accessed after **ffrt_cond_destroy** is called, undefined behavior may occur. 1472 1473##### Example 1474 1475```{.c} 1476#include <stdio.h> 1477#include "ffrt.h" 1478 1479typedef struct { 1480 ffrt_cond_t* cond; 1481 int* a; 1482 ffrt_mutex_t* lock_; 1483} tuple; 1484 1485void func1(void* arg) 1486{ 1487 tuple* t = (tuple*)arg; 1488 int ret = ffrt_mutex_lock(t->lock_); 1489 if (ret != ffrt_success) { 1490 printf("error\n"); 1491 } 1492 while (*t->a != 1) { 1493 ret = ffrt_cond_wait(t->cond, t->lock_); 1494 if (ret != ffrt_success) { 1495 printf("error\n"); 1496 } 1497 } 1498 ret = ffrt_mutex_unlock(t->lock_); 1499 if (ret != ffrt_success) { 1500 printf("error\n"); 1501 } 1502 printf("a = %d\n", *(t->a)); 1503} 1504 1505void func2(void* arg) 1506{ 1507 tuple* t = (tuple*)arg; 1508 int ret = ffrt_mutex_lock(t->lock_); 1509 if (ret != ffrt_success) { 1510 printf("error\n"); 1511 } 1512 *(t->a) = 1; 1513 ret = ffrt_cond_signal(t->cond); 1514 if (ret != ffrt_success) { 1515 printf("error\n"); 1516 } 1517 ret = ffrt_mutex_unlock(t->lock_); 1518 if (ret != ffrt_success) { 1519 printf("error\n"); 1520 } 1521} 1522 1523typedef struct { 1524 ffrt_function_header_t header; 1525 ffrt_function_t func; 1526 ffrt_function_t after_func; 1527 void* arg; 1528} c_function; 1529 1530static void ffrt_exec_function_wrapper(void* t) 1531{ 1532 c_function* f = (c_function*)t; 1533 if (f->func) { 1534 f->func(f->arg); 1535 } 1536} 1537 1538static void ffrt_destroy_function_wrapper(void* t) 1539{ 1540 c_function* f = (c_function*)t; 1541 if (f->after_func) { 1542 f->after_func(f->arg); 1543 } 1544} 1545 1546#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1547static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1548 const ffrt_function_t after_func, void* arg) 1549{ 1550 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1551 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1552 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1553 f->header.exec = ffrt_exec_function_wrapper; 1554 f->header.destroy = ffrt_destroy_function_wrapper; 1555 f->func = func; 1556 f->after_func = after_func; 1557 f->arg = arg; 1558 return (ffrt_function_header_t*)f; 1559} 1560 1561static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1562 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1563{ 1564 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1565} 1566 1567void ffrt_cv_task(void *) 1568{ 1569 ffrt_cond_t cond; 1570 int ret = ffrt_cond_init(&cond, NULL); 1571 if (ret != ffrt_success) { 1572 printf("error\n"); 1573 } 1574 int a = 0; 1575 ffrt_mutex_t lock_; 1576 tuple t = {&cond, &a, &lock_}; 1577 ret = ffrt_mutex_init(&lock_, NULL); 1578 if (ret != ffrt_success) { 1579 printf("error\n"); 1580 } 1581 ffrt_submit_c(func1, NULL, &t, NULL, NULL, NULL); 1582 ffrt_submit_c(func2, NULL, &t, NULL, NULL, NULL); 1583 ffrt_wait(); 1584 ffrt_cond_destroy(&cond); 1585 ffrt_mutex_destroy(&lock_); 1586} 1587 1588int main(int narg, char** argv) 1589{ 1590 ffrt_submit_c(ffrt_cv_task, NULL, NULL, NULL, NULL, NULL); 1591 ffrt_wait(); 1592 return 0; 1593} 1594``` 1595 1596Expected output 1597 1598``` 1599a=1 1600``` 1601 1602This example is for reference only and is not encouraged in practice. 1603 1604#### ffrt_usleep 1605 1606Provides performance implementation similar to C11 sleep and Linux usleep. 1607 1608##### Declaration 1609 1610```{.c} 1611int ffrt_usleep(uint64_t usec); 1612``` 1613 1614##### Parameters 1615 1616`usec` 1617 1618Duration that the calling thread is suspended, in μs. 1619 1620##### Return value 1621 1622N/A 1623 1624##### Description 1625* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1626* The traditional function **sleep** may cause unexpected kernel mode trap. **ffrt_usleep** solves this problem and therefore provides better performance if used properly. 1627 1628##### Example 1629 1630```{.c} 1631#include <time.h> 1632#include <stdio.h> 1633#include "ffrt.h" 1634 1635void func(void* arg) 1636{ 1637 time_t current_time = time(NULL); 1638 printf("Time: %s", ctime(¤t_time)); 1639 ffrt_usleep(2000000); // Suspend for 2 seconds 1640 current_time = time(NULL); 1641 printf("Time: %s", ctime(¤t_time)); 1642} 1643 1644typedef struct { 1645 ffrt_function_header_t header; 1646 ffrt_function_t func; 1647 ffrt_function_t after_func; 1648 void* arg; 1649} c_function; 1650 1651static void ffrt_exec_function_wrapper(void* t) 1652{ 1653 c_function* f = (c_function*)t; 1654 if (f->func) { 1655 f->func(f->arg); 1656 } 1657} 1658 1659static void ffrt_destroy_function_wrapper(void* t) 1660{ 1661 c_function* f = (c_function*)t; 1662 if (f->after_func) { 1663 f->after_func(f->arg); 1664 } 1665} 1666 1667#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1668static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1669 const ffrt_function_t after_func, void* arg) 1670{ 1671 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1672 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1673 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1674 f->header.exec = ffrt_exec_function_wrapper; 1675 f->header.destroy = ffrt_destroy_function_wrapper; 1676 f->func = func; 1677 f->after_func = after_func; 1678 f->arg = arg; 1679 return (ffrt_function_header_t*)f; 1680} 1681 1682static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1683 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1684{ 1685 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1686} 1687 1688int main(int narg, char** argv) 1689{ 1690 ffrt_submit_c(func, NULL, NULL, NULL, NULL, NULL); 1691 ffrt_wait(); 1692 return 0; 1693} 1694``` 1695 1696An output case is as follows: 1697 1698``` 1699Time: Tue Aug 13 15:45:30 2024 1700Time: Tue Aug 13 15:45:32 2024 1701``` 1702 1703#### ffrt_yield 1704 1705Passes control to other tasks so that they can be executed. If there is no other task that can be executed, this API is invalid. 1706 1707##### Declaration 1708 1709```{.c} 1710void ffrt_yield(); 1711``` 1712 1713##### Parameters 1714 1715N/A 1716 1717##### Return value 1718 1719N/A 1720 1721##### Description 1722* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1723* The exact behavior of this API depends on the implementation, especially the mechanism and system state of the FFRT scheduler in use. 1724 1725##### Example 1726 1727N/A 1728 1729### ffrt timer 1730 1731FFRT supports the capability of starting and stopping the timer. 1732 1733#### ffrt_timer_start 1734 1735Starts a timer. 1736 1737##### Declaration 1738```{.c} 1739typedef int ffrt_timer_t; 1740typedef void (*ffrt_timer_cb)(void* data); 1741 1742ffrt_timer_t ffrt_timer_start(ffrt_qos_t qos, uint64_t timeout, void* data, ffrt_timer_cb cb, bool repeat); 1743``` 1744 1745##### Parameters 1746 1747`qos` 1748 1749QoS. 1750 1751`timeout` 1752 1753Timeout of the timer. 1754 1755`data` 1756 1757Pointer to the input parameter in the callback function invoked upon a timeout. 1758 1759`cb` 1760 1761Callback function invoked upon a timeout. 1762 1763`repeat` 1764 1765Whether to repeat the timer. 1766 1767##### Return value 1768 1769Handle to the timer. 1770 1771##### Description 1772N/A 1773 1774##### Example 1775```{.c} 1776#include <stdint.h> 1777#include <unistd.h> 1778#include "ffrt.h" 1779 1780static void testfun(void *data) 1781{ 1782 *(int *)data += 1; 1783} 1784 1785void (*cb)(void *) = testfun; 1786 1787int main(int narg, char** argv) 1788{ 1789 static int x = 0; 1790 int *xf = &x; 1791 void *data = xf; 1792 uint64_t timeout = 200; 1793 int handle = ffrt_timer_start(ffrt_qos_default, timeout, data, cb, false); 1794 usleep(300000); 1795 ffrt_timer_stop(ffrt_qos_default, handle); 1796 printf("data: %d\n", x); 1797 return 0; 1798} 1799``` 1800 1801Expected output 1802 1803``` 1804data: 1 1805``` 1806 1807#### ffrt_timer_stop 1808 1809Stops the timer. 1810 1811##### Declaration 1812```{.c} 1813int ffrt_timer_stop(ffrt_qos_t qos, ffrt_timer_t handle); 1814``` 1815 1816##### Parameters 1817 1818`qos` 1819 1820QoS. 1821 1822`handle` 1823 1824Handle to the timer. 1825 1826##### Return value 1827 1828**0** if the call is successful; **-1** if the call fails. 1829 1830##### Description 1831N/A 1832 1833##### Example 1834```{.c} 1835#include <stdint.h> 1836#include <unistd.h> 1837#include "ffrt.h" 1838 1839static void testfun(void *data) 1840{ 1841 *(int *)data += 1; 1842} 1843 1844void (*cb)(void *) = testfun; 1845 1846int main(int narg, char** argv) 1847{ 1848 static int x = 0; 1849 int *xf = &x; 1850 void *data = xf; 1851 uint64_t timeout = 200; 1852 int handle = ffrt_timer_start(ffrt_qos_default, timeout, data, cb, false); 1853 usleep(300000); 1854 ffrt_timer_stop(ffrt_qos_default, handle); 1855 printf("data: %d\n", x); 1856 return 0; 1857} 1858``` 1859 1860Expected output 1861 1862``` 1863data: 1 1864``` 1865 1866### ffrt looper 1867 1868FFRT provides the looper mechanism. The looper supports task submission, event listening, and timer. The looper runs in the user thread. 1869 1870#### ffrt_loop_create 1871 1872Creates a loop. 1873 1874##### Declaration 1875```{.c} 1876typedef void* ffrt_loop_t; 1877 1878ffrt_loop_t ffrt_loop_create(ffrt_queue_t queue); 1879``` 1880 1881##### Parameters 1882 1883`queue` 1884 1885Queue, which must be a concurrent queue. 1886 1887##### Return value 1888 1889Loop object. 1890 1891##### Description 1892N/A 1893 1894##### Example 1895```{.c} 1896#include <stdint.h> 1897#include <unistd.h> 1898#include <stdio.h> 1899#include "c/loop.h" 1900 1901int main(int narg, char** argv) 1902{ 1903 ffrt_queue_attr_t queue_attr; 1904 (void)ffrt_queue_attr_init(&queue_attr); 1905 ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_concurrent, "test_queue", &queue_attr); 1906 1907 auto loop = ffrt_loop_create(queue_handle); 1908 1909 if (loop != NULL) { 1910 printf("loop is not null.\n"); 1911 } 1912 1913 int ret = ffrt_loop_destroy(loop); 1914 1915 ffrt_queue_attr_destroy(&queue_attr); 1916 ffrt_queue_destroy(queue_handle); 1917 return 0; 1918} 1919``` 1920 1921Expected output 1922 1923``` 1924loop is not null. 1925``` 1926 1927#### ffrt_loop_destroy 1928 1929Destroys a loop. 1930 1931##### Declaration 1932```{.c} 1933int ffrt_loop_destroy(ffrt_loop_t loop); 1934``` 1935 1936##### Parameters 1937 1938`loop` 1939 1940Loop object. 1941 1942##### Return value 1943 1944**0** if the call is successful; **-1** if the call fails. 1945 1946##### Description 1947N/A 1948 1949##### Example 1950```{.c} 1951#include <stdint.h> 1952#include <unistd.h> 1953#include <stdio.h> 1954#include "c/loop.h" 1955 1956int main(int narg, char** argv) 1957{ 1958 ffrt_queue_attr_t queue_attr; 1959 (void)ffrt_queue_attr_init(&queue_attr); 1960 ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_concurrent, "test_queue", &queue_attr); 1961 1962 auto loop = ffrt_loop_create(queue_handle); 1963 1964 int ret = ffrt_loop_destroy(loop); 1965 1966 if (ret == 0) { 1967 printf("loop normal destruction."); 1968 } 1969 1970 ffrt_queue_attr_destroy(&queue_attr); 1971 ffrt_queue_destroy(queue_handle); 1972 return 0; 1973} 1974``` 1975 1976Expected output 1977 1978``` 1979loop normal destruction. 1980``` 1981 1982#### ffrt_loop_run 1983 1984Runs a loop. 1985 1986##### Declaration 1987```{.c} 1988int ffrt_loop_run(ffrt_loop_t loop); 1989``` 1990 1991##### Parameters 1992 1993`loop` 1994 1995Loop object. 1996 1997##### Return value 1998 1999**0** if the call is successful; **-1** if the call fails. 2000 2001##### Description 2002N/A 2003 2004##### Example 2005```{.c} 2006#include <pthread.h> 2007#include <unistd.h> 2008#include <stdio.h> 2009#include "c/loop.h" 2010 2011void* ThreadFunc(void* p) 2012{ 2013 int ret = ffrt_loop_run(p); 2014 if (ret == 0) { 2015 printf("loop normal operation."); 2016 } 2017 return nullptr; 2018} 2019int main(int narg, char** argv) 2020{ 2021 ffrt_queue_attr_t queue_attr; 2022 (void)ffrt_queue_attr_init(&queue_attr); 2023 ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_concurrent, "test_queue", &queue_attr); 2024 2025 auto loop = ffrt_loop_create(queue_handle); 2026 pthread_t thread; 2027 pthread_create(&thread, 0, ThreadFunc, loop); 2028 2029 ffrt_loop_stop(loop); 2030 int ret = ffrt_loop_destroy(loop); 2031 2032 ffrt_queue_attr_destroy(&queue_attr); 2033 ffrt_queue_destroy(queue_handle); 2034 return 0; 2035} 2036``` 2037 2038Expected output 2039 2040``` 2041loop normal operation. 2042``` 2043 2044#### ffrt_loop_stop 2045 2046Stops a loop. 2047 2048##### Declaration 2049```{.c} 2050void ffrt_loop_stop(ffrt_loop_t loop); 2051``` 2052 2053##### Parameters 2054 2055`loop` 2056 2057Loop object. 2058 2059##### Return value 2060 2061N/A. 2062 2063##### Description 2064N/A 2065 2066##### Example 2067```{.c} 2068#include <pthread.h> 2069#include <unistd.h> 2070#include <stdio.h> 2071#include "c/loop.h" 2072 2073void* ThreadFunc(void* p) 2074{ 2075 int ret = ffrt_loop_run(p); 2076 return nullptr; 2077} 2078int main(int narg, char** argv) 2079{ 2080 ffrt_queue_attr_t queue_attr; 2081 (void)ffrt_queue_attr_init(&queue_attr); 2082 ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_concurrent, "test_queue", &queue_attr); 2083 2084 auto loop = ffrt_loop_create(queue_handle); 2085 pthread_t thread; 2086 pthread_create(&thread, 0, ThreadFunc, loop); 2087 2088 ffrt_loop_stop(loop); 2089 int ret = ffrt_loop_destroy(loop); 2090 2091 ffrt_queue_attr_destroy(&queue_attr); 2092 ffrt_queue_destroy(queue_handle); 2093 return 0; 2094} 2095``` 2096 2097Expected output 2098 2099``` 2100**0** if the loop object is stopped normally. 2101``` 2102 2103#### ffrt_loop_epoll_ctl 2104 2105Manages listening events on a loop. 2106 2107##### Declaration 2108```{.c} 2109int ffrt_loop_epoll_ctl(ffrt_loop_t loop, int op, int fd, uint32_t events, void *data, ffrt_poller_cb cb); 2110``` 2111 2112##### Parameters 2113 2114`loop` 2115 2116Loop object. 2117 2118`op` 2119 2120Operation to be performed, such as **EPOLL_CTL_ADD** and **EPOLL_CLT_DEL**. 2121 2122`fd` 2123 2124File descriptor. 2125 2126`events` 2127 2128Events linked to the file descriptor. 2129 2130`data` 2131 2132Pointer to the input parameter in the callback function invoked upon event changes. 2133 2134`cb` 2135 2136Callback function invoked upon event changes. 2137 2138##### Return value 2139 2140**0** if the call is successful; **-1** if the call fails. 2141 2142##### Description 2143N/A 2144 2145##### Example 2146```{.c} 2147#include <pthread.h> 2148#include <unistd.h> 2149#include <stdio.h> 2150#include <functional> 2151#include <sys/epoll.h> 2152#include <sys/eventfd.h> 2153#include "c/loop.h" 2154#include "ffrt.h" 2155 2156void* ThreadFunc(void* p) 2157{ 2158 int ret = ffrt_loop_run(p); 2159 return nullptr; 2160} 2161 2162static void testfun(void* data) 2163{ 2164 *(int*)data += 1; 2165} 2166 2167static void (*cb)(void*) = testfun; 2168 2169void testCallBack(void *data, unsigned int events) {} 2170 2171struct TestData { 2172 int fd; 2173 uint64_t expected; 2174}; 2175 2176int main(int narg, char** argv) 2177{ 2178 ffrt_queue_attr_t queue_attr; 2179 (void)ffrt_queue_attr_init(&queue_attr); 2180 ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_concurrent, "test_queue", &queue_attr); 2181 2182 auto loop = ffrt_loop_create(queue_handle); 2183 int result1 = 0; 2184 std::function<void()> &&basicFunc1 = [&result1]() {result1 += 10;}; 2185 ffrt_task_handle_t task1 = ffrt_queue_submit_h(queue_handle, ffrt::create_function_wrapper(basicFunc1, ffrt_function_kind_queue), nullptr); 2186 2187 pthread_t thread; 2188 pthread_create(&thread, 0, ThreadFunc, loop); 2189 2190 static int x = 0; 2191 int* xf = &x; 2192 void* data = xf; 2193 uint64_t timeout1 = 20; 2194 uint64_t timeout2 = 10; 2195 uint64_t expected = 0xabacadae; 2196 2197 int testFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 2198 struct TestData testData {.fd = testFd, .expected = expected}; 2199 ffrt_timer_t timeHandle = ffrt_loop_timer_start(loop, timeout1, data, cb, false); 2200 2201 int ret = ffrt_loop_epoll_ctl(loop, EPOLL_CTL_ADD, testFd, EPOLLIN, (void*)(&testData), testCallBack); 2202 if (ret == 0) { 2203 printf("ffrt_loop_epoll_ctl executed successfully.\n"); 2204 } 2205 ssize_t n = write(testFd, &expected, sizeof(uint64_t)); 2206 usleep(25000); 2207 ffrt_loop_epoll_ctl(loop, EPOLL_CTL_DEL, testFd, 0, nullptr, nullptr); 2208 2209 ffrt_loop_stop(loop); 2210 pthread_join(thread, nullptr); 2211 ffrt_loop_timer_stop(loop, timeHandle); 2212 ret = ffrt_loop_destroy(loop); 2213 2214 ffrt_queue_attr_destroy(&queue_attr); 2215 ffrt_queue_destroy(queue_handle); 2216 return 0; 2217} 2218``` 2219 2220Expected output 2221 2222``` 2223ffrt_loop_epoll_ctl if the operation is successful. 2224``` 2225 2226#### ffrt_loop_timer_start 2227 2228Starts the timer on a loop. 2229 2230##### Declaration 2231```{.c} 2232ffrt_timer_t ffrt_loop_timer_start(ffrt_loop_t loop, uint64_t timeout, void* data, ffrt_timer_cb cb, bool repeat); 2233``` 2234 2235##### Parameters 2236 2237`loop` 2238 2239Loop object. 2240 2241`timeout` 2242 2243Timeout of the timer. 2244 2245`data` 2246 2247Pointer to the input parameter in the callback function invoked upon event changes. 2248 2249`cb` 2250 2251Callback function invoked upon event changes. 2252 2253`repeat` 2254 2255* Whether to repeat the timer. 2256 2257##### Return value 2258 2259Handle to the timer. 2260 2261##### Description 2262N/A 2263 2264##### Example 2265For details, see the **ffrt_loop_epoll_ctl** interface example. 2266 2267#### ffrt_loop_timer_stop 2268 2269Stops the timer. 2270 2271##### Declaration 2272```{.c} 2273int ffrt_loop_timer_stop(ffrt_loop_t loop, ffrt_timer_t handle) 2274``` 2275 2276##### Parameters 2277 2278`loop` 2279 2280Loop object. 2281 2282`handle` 2283 2284Handle to the timer. 2285 2286##### Return value 2287 2288**0** if the call is successful; **-1** if the call fails. 2289 2290##### Description 2291N/A 2292 2293##### Example 2294For details, see the **ffrt_loop_epoll_ctl** interface example. 2295 2296## Long-Time Task Monitoring 2297 2298### Mechanism 2299* When the task execution reaches one second, stack printing is triggered. The stack printing interval is then changed to one minute. After 10 prints, the interval is changed to 10 minutes. After another 10 prints, the interval is changed to and fixed at 30 minutes. 2300* The **GetBacktraceStringByTid** interface of the DFX is called for the stack printing. This interface sends stack capture signals to the blocked thread to trigger interrupts and capture the call stack return. 2301 2302### Example 2303Search for the keyword **RecordSymbolAndBacktrace** in the corresponding process log. The following is an example of the corresponding log: 2304 2305``` 2306W C01719/ffrt: 60500:RecordSymbolAndBacktrace:159 Tid[16579] function occupies worker for more than [1]s. 2307W C01719/ffrt: 60501:RecordSymbolAndBacktrace:164 Backtrace: 2308W C01719/ffrt: #00 pc 00000000000075f0 /system/lib64/module/file/libhash.z.so 2309W C01719/ffrt: #01 pc 0000000000008758 /system/lib64/module/file/libhash.z.so 2310W C01719/ffrt: #02 pc 0000000000012b98 /system/lib64/module/file/libhash.z.so 2311W C01719/ffrt: #03 pc 000000000002aaa0 /system/lib64/platformsdk/libfilemgmt_libn.z.so 2312W C01719/ffrt: #04 pc 0000000000054b2c /system/lib64/platformsdk/libace_napi.z.so 2313W C01719/ffrt: #05 pc 00000000000133a8 /system/lib64/platformsdk/libuv.so 2314W C01719/ffrt: #06 pc 00000000000461a0 /system/lib64/chipset-sdk/libffrt.so 2315W C01719/ffrt: #07 pc 0000000000046d44 /system/lib64/chipset-sdk/libffrt.so 2316W C01719/ffrt: #08 pc 0000000000046a6c /system/lib64/chipset-sdk/libffrt.so 2317W C01719/ffrt: #09 pc 00000000000467b0 /system/lib64/chipset-sdk/libffrt.so 2318``` 2319The log prints the task stack, Worker thread ID, and execution time of the long-time task. Find the corresponding component based on the stack to determine the blocking cause. 2320 2321 2322### Precautions 2323During long-time task monitoring, an interrupt signal is sent. If your code contains **sleep** or a blocked thread that will be woken up by the interrupt, you should receive the return value of the blocked thread and call the code again. 2324The following is an example: 2325``` 2326unsigned int leftTime = sleep(10); 2327while (leftTime != 0) { 2328 leftTime = sleep(leftTime); 2329} 2330``` 2331 2332## How to Develop 2333 2334The following describes how to use the native APIs provided by FFRT to create parallel tasks and serial queue tasks and destroy corresponding resources. 2335 2336**Adding Dynamic Link Libraries** 2337 2338Add the following libraries to **CMakeLists.txt**. 2339```txt 2340libffrt.z.so 2341``` 2342 2343**Including Header Files** 2344```c++ 2345#include "ffrt/task.h" 2346#include "ffrt/type_def.h" 2347#include "ffrt/condition_variable.h" 2348#include "ffrt/mutex.h" 2349#include "ffrt/queue.h" 2350#include "ffrt/sleep.h" 2351``` 2352 23531. **Encapsulate the function to be executed.** 2354 ```c++ 2355 // Method 1: Use the template. C++ is supported. 2356 template<class T> 2357 struct Function { 2358 ffrt_function_header_t header; 2359 T closure; 2360 }; 2361 2362 template<class T> 2363 void ExecFunctionWrapper(void* t) 2364 { 2365 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 2366 f->closure(); 2367 } 2368 2369 template<class T> 2370 void DestroyFunctionWrapper(void* t) 2371 { 2372 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 2373 f->closure = nullptr; 2374 } 2375 2376 template<class T> 2377 static inline ffrt_function_header_t* create_function_wrapper(T&& func, 2378 ffrt_function_kind_t kind = ffrt_function_kind_general) 2379 { 2380 using function_type = Function<std::decay_t<T>>; 2381 auto p = ffrt_alloc_auto_managed_function_storage_base(kind); 2382 auto f = new (p)function_type; 2383 f->header.exec = ExecFunctionWrapper<T>; 2384 f->header.destroy = DestroyFunctionWrapper<T>; 2385 f->closure = std::forward<T>(func); 2386 return reinterpret_cast<ffrt_function_header_t*>(f); 2387 } 2388 2389 // Method 2 2390 typedef struct { 2391 ffrt_function_header_t header; 2392 ffrt_function_t func; 2393 ffrt_function_t after_func; 2394 void* arg; 2395 } CFunction; 2396 2397 static void FfrtExecFunctionWrapper(void* t) 2398 { 2399 CFunction* f = static_cast<CFunction*>(t); 2400 if (f->func) { 2401 f->func(f->arg); 2402 } 2403 } 2404 2405 static void FfrtDestroyFunctionWrapper(void* t) 2406 { 2407 CFunction* f = static_cast<CFunction*>(t); 2408 if (f->after_func) { 2409 f->after_func(f->arg); 2410 } 2411 } 2412 2413 #define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 2414 static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 2415 const ffrt_function_t after_func, void* arg, ffrt_function_kind_t kind_t = ffrt_function_kind_general) 2416 { 2417 FFRT_STATIC_ASSERT(sizeof(CFunction) <= ffrt_auto_managed_function_storage_size, 2418 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 2419 CFunction* f = static_cast<CFunction*>(ffrt_alloc_auto_managed_function_storage_base(kind_t)); 2420 f->header.exec = FfrtExecFunctionWrapper; 2421 f->header.destroy = FfrtDestroyFunctionWrapper; 2422 f->func = func; 2423 f->after_func = after_func; 2424 f->arg = arg; 2425 return reinterpret_cast<ffrt_function_header_t*>(f); 2426 } 2427 2428 // Example: function to be submitted for execution. 2429 void OnePlusForTest(void* arg) 2430 { 2431 (*static_cast<int*>(arg)) += 1; 2432 } 2433 ``` 2434 24352. **Set the task attributes.** 2436 2437 Set the task attributes, including the QoS and task name, before submitting the task. 2438 ```c++ 2439 // ******Initialize the attributes of the parallel task****** 2440 ffrt_task_attr_t attr; 2441 ffrt_task_attr_init(&attr); 2442 2443 // ******Create a serial queue****** 2444 2445 // Create the attributes of the serial queue. 2446 ffrt_queue_attr_t queue_attr; 2447 // Create the handle of the serial queue. 2448 ffrt_queue_t queue_handle; 2449 2450 // Initialize the queue attribute. 2451 (void)ffrt_queue_attr_init(&queue_attr); 2452 2453 // Set the QoS if necessary. 2454 ffrt_queue_attr_set_qos(&queue_attr, static_cast<ffrt_qos_t>(ffrt_qos_inherit)); 2455 // Set the timeout period (ms) if necessary. 2456 ffrt_queue_attr_set_timeout(&queue_attr, 10000); 2457 // Set the timeout callback if necessary. 2458 int x = 0; 2459 ffrt_queue_attr_set_callback(&queue_attr, ffrt_create_function_wrapper(OnePlusForTest, NULL, &x, 2460 ffrt_function_kind_queue)); 2461 2462 // Initialize the queue based on the attributes. 2463 queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr); 2464 ``` 2465 24663. **Submit the task.** 2467 ```c++ 2468 int a = 0; 2469 // ******Parallel task****** 2470 // Submit the parallel task without obtaining a handle. 2471 ffrt_submit_base(ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 2472 // Submit the parallel task and obtain a handle. 2473 ffrt_task_handle_t task = ffrt_submit_h_base( 2474 ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 2475 2476 // ******Serial queue task****** 2477 // Submit the serial queue task without obtaining a handle. 2478 ffrt_queue_submit(queue_handle, ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, 2479 ffrt_function_kind_queue), nullptr); 2480 // Submit the serial queue task and obtain a handle. 2481 ffrt_task_handle_t handle = ffrt_queue_submit_h(queue_handle, 2482 ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, ffrt_function_kind_queue), nullptr); 2483 2484 // Call wait if you need to wait for the execution result. 2485 const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}}; 2486 ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 2487 ffrt_wait_deps(&wait); 2488 2489 ffrt_queue_wait(handle); 2490 ``` 2491 24924. **Destroy the resources after the task is submitted.** 2493 ```c++ 2494 // ******Destroy the parallel task****** 2495 ffrt_task_attr_destroy(&attr); 2496 ffrt_task_handle_destroy(task); 2497 2498 // ******Destroy the serial queue task****** 2499 // Destroy the task handle and then the queue. 2500 ffrt_queue_attr_destroy(&queue_attr); 2501 ffrt_task_handle_destroy(handle); 2502 ffrt_queue_destroy(queue_handle); 2503 ``` 2504 2505## Suggestions 2506 2507### Suggestion 1: Functional programming 2508 2509Basic idea: Use functional programming for the calculation process. 2510 2511* Use pure functions and encapsulate them to express each step of the process. 2512* There is no global data access. 2513* There is no internal state reserved. 2514* Use **ffrt_submit_base()** to submit a function in asynchronous mode for execution. 2515* Use **in_deps** and **out_deps** of **ffrt_submit_base()** to specify the data objects to be accessed by the function and the access mode. 2516* Use **inDeps** and **outDeps** to specify the dependency between tasks to ensure the correctness of program execution. 2517 2518> **NOTE** 2519> 2520> Using pure functions helps you maximize the parallelism and avoid data races and lock abuse. 2521 2522In practice, you may not use pure functions in certain scenarios, with the following prerequisites: 2523 2524* **in_deps** and **out_deps** can ensure the correctness of program execution. 2525* The lock mechanism provided by FFRT is used to protect access to global variables. 2526 2527 2528### Suggestion 2: Use FFRT APIs 2529 2530* Do not use the APIs of the system thread library to create threads in FFRT tasks. Instead, use **ffrt_submit_base** or **ffrt_submit_h_base** to submit tasks. 2531* Use the lock, condition variable, sleep, and I/O APIs provided by FFRT to replace the APIs of the system thread library. 2532* Using the APIs of the system thread library may block worker threads and result in extra performance overhead. 2533 2534### Suggestion 3: Deadline mechanism 2535 2536* Use FFRT APIs in processing flows that feature periodic/repeated execution. 2537* Use FFRT APIs in processing flows with clear time constraints and is performance critical. 2538* Use FFRT APIs in relatively large-granularity processing flows, such as the frame processing flow with the 16.6 ms time constraint. 2539 2540### Suggestion 4: Migration from the thread model 2541 2542* Create a thread instead of creating an FFRT task. 2543* A thread is logically similar to a task without **in_deps**. 2544* Identify the dependency between threads and express the dependencies in **in_deps** or **out_deps** of the task. 2545* Decompose an intra-thread computing process into asynchronous tasks for invoking. 2546* Use the task dependency and lock mechanism to avoid data races of concurrent tasks. 2547 2548### Suggestion 5: C++ APIs recommended 2549 2550* The FFRT C++ APIs are implemented based on the C APIs. Before using the APIs, you can manually add the C++ header file. 2551* For details about how to download C++ APIs, see [FFRT C++ APIs](https://gitee.com/openharmony/resourceschedule_ffrt/tree/master/interfaces/kits). 2552 2553## Constraints 2554 2555 2556After an FFRT object is initialized in the C code, you are responsible for setting the object to null or destroying the object. 2557 2558To ensure high performance, the C APIs of FFRT do not use a flag to indicate the object destruction status. You need to release resources properly. Repeatedly destroying an object will cause undefined behavior. 2559 2560Noncompliant example 1: Repeated calling of **destroy()** may cause unpredictable data damage. 2561 2562```{.c} 2563#include "ffrt.h" 2564void abnormal_case_1() 2565{ 2566 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 2567 ... 2568 ffrt_task_handle_destroy(h); 2569 ffrt_task_handle_destroy(h); // double free 2570} 2571``` 2572 2573Noncompliant example 2: A memory leak occurs if **destroy()** is not called. 2574 2575```{.c} 2576#include "ffrt.h" 2577void abnormal_case_2() 2578{ 2579 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 2580 ... 2581 // Memory leak 2582} 2583``` 2584 2585Recommended example: Call **destroy()** only once; set the object to null if necessary. 2586 2587```{.c} 2588#include "ffrt.h" 2589void normal_case() 2590{ 2591 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 2592 ... 2593 ffrt_task_handle_destroy(h); 2594 h = nullptr; // if necessary 2595} 2596``` 2597