1# FFRT 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### Programming Models 10 11 12| Item | Thread Programming Model | FFRT Programming Model | 13| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | 14| Degree of Parallelism (DOP) mining mode| Programmers create multiple threads and assign tasks to them for parallel execution to achieve the optimal runtime performance.| Programmers, with the help of compilers or programming language features, decompose the application into tasks and describe their data dependencies during static programming. The scheduler allocates tasks to worker threads for execution.| 15| Owner for creating threads| Programmers are responsible for creating threads. The maximum number of threads that can be created is not under control.| The scheduler is responsible for creating and managing worker threads. Programmers cannot directly create threads.| 16| Load balancing | Programmers map tasks to threads during static programming. Improper mapping or uncertain task execution time will cause a load imbalance among threads.| A ready task is automatically scheduled to an idle thread for execution, reducing the load imbalance among threads.| 17| Scheduling overhead | Thread scheduling is implemented by a kernel-mode scheduler, resulting in high scheduling overhead. | Thread scheduling is implemented by a user-mode coroutine scheduler, requiring less scheduling overhead. In addition, FFRT can further reduce the scheduling overhead through hardware-based scheduling offload.| 18| Dependency expression | A thread is in the executable state once it is created, and it is executed parallelly with other threads, causing frequent thread switching.| FFRT determines whether a task can be executed based on the input and output dependencies explicitly expressed during task creation. If the input dependencies do not meet the requirements, the task is not scheduled.| 19 20## Basic Concepts 21 22### Function Flow 23 24The Function Flow programming model allows you to develop an application by creating tasks and describing their dependencies. Its most outstanding features are task-based and data-driven. 25 26#### Task-based 27 28Task-based means that you can use tasks to express an application and schedule the tasks at runtime. 29 30A task is defined as a developer-oriented programming clue and a runtime-oriented execution object. It usually contains a set of sequential instructions and a data context environment to run the instructions. 31 32Tasks in the Function Flow programming model have the following features: 33 34- The dependency between tasks can be specified in data-driven form. 35- Tasks can be nested. That is, when a task is being executed, a new task can be generated and delivered to that task to form a parent-child relationship. 36- Simultaneous operations, such as wait, lock, and condition variables, are supported. 37 38> **NOTE** 39> 40> The task granularity determines the number of concurrent tasks and therefore affects the application execution performance. A small granularity increases the scheduling overhead, whereas a large granularity decreases the DOP. The minimum task granularity allowed in the Function Flow programming model is 100 μs. 41 42#### Data-driven 43 44Data-driven means that the dependency between tasks is expressed through data dependencies. 45 46Data objects associated with a task are read and written during task execution. In the Function Flow programming model, a data object is abstracted as a data signature. They are in one-to-one mapping. 47 48Data dependencies, consisting of **in_deps** and **out_deps**, are abstracted as a list of data signatures mapping to the data objects manipulated by the task. When the signature of a data object appears in **in_deps** of a task, the task is a consumer task of the data object. The execution of a consumer task does not change the content of the input data object. When the signature of a data object appears in **out_deps** of a task, the task is a producer task of the data object. The execution of a producer task changes the content of the output data object and generates a new version of the data object. 49 50A data object may have multiple versions. Each version corresponds to one producer task and zero, one, or more consumer tasks. A sequence of the data object versions and the version-specific producer task and consumer tasks are defined according to the delivery sequence of the producer task and consumer tasks. 51 52When all producer tasks and consumer tasks of the data object of all the available versions are executed, the data dependency is removed. In this case, the task enters the ready state and can be scheduled for execution. 53 54With the data-driven dependency expression, FFRT can dynamically build different types of data dependencies between tasks and schedule the tasks based on the data dependency status at runtime. The following data dependency types are available: 55 56- Producer-Consumer dependency 57 58 A dependency formed between the producer task of a data object of a specific version and a consumer task of the data object of the same version. It is also referred to as a read-after-write dependency. 59 60- Consumer-Producer dependency 61 62 A dependency formed between a consumer task of a data object of a specific version and the producer task of the data object of the next version. It is also referred to as a write-after-read dependency. 63 64- Producer-Producer dependency 65 66 A dependency formed between the producer task of a data object of a specific version and a producer task of the data object of the next version. It is also referred to as a write-after-write dependency. 67 68 69Assume that the relationship between some tasks and data A is as follows: 70```{.c} 71task1(OUT A); 72task2(IN A); 73task3(IN A); 74task4(OUT A); 75task5(OUT A); 76``` 77 78<img src="figures/ffrtfigure3.png" style="zoom:40%" /> 79 80> **NOTE** 81> 82> For ease of description, circles are used to represent tasks and squares are used to represent data. 83 84The following conclusions can be drawn: 85- task1 and task2/task3 form a producer-consumer dependency. This means that task2/task3 can read data A only after task1 writes data A. 86- task2/task3 and task4 form a consumer-producer dependency. This means that task4 can write data A only after task2/task3 reads data A. 87- task 4 and task 5 form a producer-producer dependency. This means that task 5 can write data A only after task 4 writes data A. 88 89## Available APIs 90 91| API | Description | 92| ------------------------------------------------------------ | ------------------------------------------------------------ | 93| ffrt_condattr_init (ffrt_condattr_t* attr) | Initializes a condition variable attribute.| 94| ffrt_condattr_destroy(ffrt_condattr_t* attr) | Destroys a condition variable attribute.| 95| ffrt_condattr_setclock(ffrt_condattr_t* attr, ffrt_clockid_t clock) | Sets the clock of a condition variable attribute.| 96| ffrt_condattr_getclock(const ffrt_condattr_t* attr, ffrt_clockid_t* clock) | Obtains the clock of a condition variable attribute. | 97| ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr) | Initializes a condition variable. | 98| ffrt_cond_signal(ffrt_cond_t* cond) | Unblocks at least one of the threads that are blocked on a condition variable.| 99| ffrt_cond_broadcast(ffrt_cond_t* cond) | Unblocks all threads currently blocked on a condition variable.| 100| ffrt_cond_wait(ffrt_cond_t* cond, ffrt_mutex_t* mutex) | Blocks the calling thread on a condition variable.| 101| 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.| 102| ffrt_cond_destroy(ffrt_cond_t* cond) | Destroys a condition variable.| 103| ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr) | Initializes a mutex.| 104| ffrt_mutex_lock(ffrt_mutex_t* mutex) | Locks a mutex.| 105| ffrt_mutex_unlock(ffrt_mutex_t* mutex) | Unlocks a mutex.| 106| ffrt_mutex_trylock(ffrt_mutex_t* mutex) | Attempts to lock a mutex.| 107| ffrt_mutex_destroy(ffrt_mutex_t* mutex) | Destroys a mutex.| 108| ffrt_queue_attr_init(ffrt_queue_attr_t* attr) | Initializes a queue attribute.| 109| ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr) | Destroys a queue attribute.| 110| ffrt_queue_attr_set_qos(ffrt_queue_attr_t* attr, ffrt_qos_t qos) | Sets the QoS for a queue attribute.| 111| ffrt_queue_attr_get_qos(const ffrt_queue_attr_t* attr) | Obtains the QoS of a queue attribute.| 112| ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr) | Creates a queue.| 113| ffrt_queue_destroy(ffrt_queue_t queue) | Destroys a queue.| 114| ffrt_queue_submit(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr) | Submits a task to a queue.| 115| 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 a task handle.| 116| ffrt_queue_wait(ffrt_task_handle_t handle) | Waits until a task in the queue is complete.| 117| ffrt_queue_cancel(ffrt_task_handle_t handle) | Cancels a task in the queue.| 118| ffrt_usleep(uint64_t usec) | Suspends the calling thread for a given duration.| 119| ffrt_yield(void) | Passes control to other tasks so that they can be executed.| 120| ffrt_task_attr_init(ffrt_task_attr_t* attr) | Initializes a task attribute.| 121| ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name) | Sets a task name.| 122| ffrt_task_attr_get_name(const ffrt_task_attr_t* attr) | Obtains a task name.| 123| ffrt_task_attr_destroy(ffrt_task_attr_t* attr) | Destroys a task attribute.| 124| ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos) | Sets the QoS for a task attribute.| 125| ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr) | Obtains the QoS of a task attribute.| 126| ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us) | Sets the task delay time.| 127| ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr) | Obtains the task delay time.| 128| ffrt_this_task_update_qos(ffrt_qos_t qos) | Updates the QoS of this task.| 129| ffrt_this_task_get_id(void) | Obtains the ID of this task.| 130| ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind) | Applies for memory for the function execution structure.| 131| 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.| 132| 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 a task handle.| 133| ffrt_task_handle_destroy(ffrt_task_handle_t handle) | Destroys a task handle.| 134| ffrt_skip(ffrt_task_handle_t handle) | Skips a task.| 135| ffrt_wait_deps(const ffrt_deps_t* deps) | Waits until the dependent tasks are complete.| 136 137 138 139## API Introduction 140 141 142### Task Management APIs 143 144#### ffrt_submit_base 145 146Exports an FFRT dynamic library. You can encapsulate this API into the C API **ffrt_submit** for binary compatibility. 147 148##### Declaration 149 150```{.c} 151const int ffrt_auto_managed_function_storage_size = 64 + sizeof(ffrt_function_header_t); 152typedef enum { 153 ffrt_function_kind_general, 154 ffrt_function_kind_queue 155} ffrt_function_kind_t; 156 157void* ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind); 158 159typedef void(*ffrt_function_t)(void*); 160typedef struct { 161 ffrt_function_t exec; 162 ffrt_function_t destroy; 163 uint64_t reserve[2]; 164} ffrt_function_header_t; 165 166void 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); 167``` 168 169##### Parameters 170 171`kind` 172 173Subtype of **function**. It is used to optimize the internal data structure. The default value is **ffrt_function_kind_general**. 174 175`func` 176 177Pointer 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. 178 179`in_deps` 180 181* Optional. 182* Input dependencies of the task. FFRT establishes the dependency by using the virtual address of the data as the data signature. 183 184`out_deps` 185 186* Optional. 187 188* Output dependencies of the task. 189 190 **NOTE** 191 192 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. 193 194`attr` 195 196* Optional. 197* Task attribute, such as QoS. For details, see [ffrt_task_attr_t](#ffrt_task_attr_t). 198 199##### Return value 200 201N/A 202 203##### Use guide 204* You are advised to encapsulate **ffrt_submit_base** first. For details, see **Example** below. 205* As an underlying capability, **ffrt_submit_base** must meet the following requirements: 206 * 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**). 207 * 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. 208* The following two function pointers are defined in **ffrt_function_header_t**: 209 * **exec**: describes how the task is executed. It is called by FFRT to execute the task. 210 * **destroy**: describes how a task is destroyed. It is called by FFRT to destroy the task. 211 212##### Example 213 214 215```{.c} 216template<class T> 217struct function { 218 template<class CT> 219 function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {} 220 ffrt_function_header_t header; 221 T closure; 222}; 223 224template<class T> 225void exec_function_wrapper(void* t) 226{ 227 auto f = (function<std::decay_t<T>>*)t; 228 f->closure(); 229} 230 231template<class T> 232void destroy_function_wrapper(void* t) 233{ 234 auto f = (function<std::decay_t<T>>*)t; 235 f->closure = nullptr; 236} 237 238template<class T> 239inline ffrt_function_header_t* create_function_wrapper(T&& func) 240{ 241 using function_type = function<std::decay_t<T>>; 242 static_assert(sizeof(function_type) <= ffrt_auto_managed_function_storage_size, 243 "size of function must be less than ffrt_auto_managed_function_storage_size"); 244 245 auto p = ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 246 auto f = new (p) function_type( 247 {exec_function_wrapper<T>, destroy_function_wrapper<T>}, 248 std::forward<T>(func)); 249 return (ffrt_function_header_t*)f; 250} 251 252static inline void submit(std::function<void()>&& func) 253{ 254 return ffrt_submit_base(create_function_wrapper(std::move(func)), NULL, NULL, NULL); 255} 256``` 257 258#### ffrt_wait 259 260- Used together with **ffrt_submit_base**. 261- Waits by suspending the current execution context, until the specified data is produced or all subtasks of the current task are complete. 262 263##### Declaration 264 265```{.c} 266void ffrt_wait_deps(ffrt_deps_t* deps); 267void ffrt_wait(); 268``` 269 270##### Parameters 271 272`deps` 273 274Virtual 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. 275 276##### Return value 277 278N/A 279 280##### Use guide 281* **ffrt_wait_deps(deps)** is used to suspend code execution before the data specified by **deps** is produced. 282* **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. 283* This API can be called inside or outside an FFRT task. 284* **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. 285 286##### Example 287 288**Recursive Fibonacci** 289 290The Fibonacci Sequence implemented in serial mode is as follows: 291 292```{.c} 293#include <stdio.h> 294 295void fib(int x, int* y) { 296 if (x <= 1) { 297 *y = x; 298 } else { 299 int y1, y2; 300 fib(x - 1, &y1); 301 fib(x - 2, &y2); 302 *y = y1 + y2; 303 } 304} 305int main(int narg, char** argv) 306{ 307 int r; 308 fib(10, &r); 309 printf("fibonacci 10: %d\n", r); 310 return 0; 311} 312``` 313 314Use 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.) 315 316```{.c} 317#include <stdio.h> 318#include "ffrt.h" // All header files related to FFRT are included. 319 320typedef struct { 321 int x; 322 int* y; 323} fib_ffrt_s; 324 325typedef struct { 326 ffrt_function_header_t header; 327 ffrt_function_t func; 328 ffrt_function_t after_func; 329 void* arg; 330} c_function; 331 332static void ffrt_exec_function_wrapper(void* t) 333{ 334 c_function* f = (c_function*)t; 335 if (f->func) { 336 f->func(f->arg); 337 } 338} 339 340static void ffrt_destroy_function_wrapper(void* t) 341{ 342 c_function* f = (c_function*)t; 343 if (f->after_func) { 344 f->after_func(f->arg); 345 } 346} 347 348#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 349static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 350 const ffrt_function_t after_func, void* arg) 351{ 352 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 353 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 354 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 355 f->header.exec = ffrt_exec_function_wrapper; 356 f->header.destroy = ffrt_destroy_function_wrapper; 357 f->func = func; 358 f->after_func = after_func; 359 f->arg = arg; 360 return (ffrt_function_header_t*)f; 361} 362 363static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 364 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 365{ 366 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 367} 368 369void fib_ffrt(void* arg) 370{ 371 fib_ffrt_s* p = (fib_ffrt_s*)arg; 372 int x = p->x; 373 int* y = p->y; 374 375 if (x <= 1) { 376 *y = x; 377 } else { 378 int y1, y2; 379 fib_ffrt_s s1 = {x - 1, &y1}; 380 fib_ffrt_s s2 = {x - 2, &y2}; 381 const std::vector<ffrt_dependence_t> dx_deps = {{ffrt_dependence_data, &x}}; 382 ffrt_deps_t dx{static_cast<uint32_t>(dx_deps.size()), dx_deps.data()}; 383 const std::vector<ffrt_dependence_t> dy1_deps = {{ffrt_dependence_data, &y1}}; 384 ffrt_deps_t dy1{static_cast<uint32_t>(dy1_deps.size()), dy1_deps.data()}; 385 const std::vector<ffrt_dependence_t> dy2_deps = {{ffrt_dependence_data, &y2}}; 386 ffrt_deps_t dy2{static_cast<uint32_t>(dy2_deps.size()), dy2_deps.data()}; 387 const std::vector<ffrt_dependence_t> dy12_deps = {{ffrt_dependence_data, &y1}, {ffrt_dependence_data, &y2}}; 388 ffrt_deps_t dy12{static_cast<uint32_t>(dy12_deps.size()), dy12_deps.data()}; 389 ffrt_submit_c(fib_ffrt, NULL, &s1, &dx, &dy1, NULL); 390 ffrt_submit_c(fib_ffrt, NULL, &s2, &dx, &dy2, NULL); 391 ffrt_wait_deps(&dy12); 392 *y = y1 + y2; 393 } 394} 395 396int main(int narg, char** argv) 397{ 398 int r; 399 fib_ffrt_s s = {10, &r}; 400 const std::vector<ffrt_dependence_t> dr_deps = {{ffrt_dependence_data, &r}}; 401 ffrt_deps_t dr{static_cast<uint32_t>(dr_deps.size()), dr_deps.data()}; 402 ffrt_submit_c(fib_ffrt, NULL, &s, NULL, &dr, NULL); 403 ffrt_wait_deps(&dr); 404 printf("fibonacci 10: %d\n", r); 405 return 0; 406} 407``` 408 409**NOTE** 410 411(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. 412 413(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. 414 415<img src="figures/ffrtfigure2.png" style="zoom:100%" /> 416 417> **NOTE** 418> 419> The preceding implementation requires you to explicitly manage the data lifecycle and encapsulate input parameters, making the code complex. 420 421#### ffrt_deps_t 422 423Abstraction of dependency arrays in C code, logically equivalent to **std::vector<void*>** in C++ code. 424 425##### Declaration 426 427```{.c} 428typedef enum { 429 ffrt_dependence_data, 430 ffrt_dependence_task, 431} ffrt_dependence_type_t; 432 433typedef struct { 434 ffrt_dependence_type_t type; 435 const void* ptr; 436} ffrt_dependence_t; 437 438typedef struct { 439 uint32_t len; 440 const ffrt_dependence_t* items; 441} ffrt_deps_t; 442``` 443 444##### Parameters 445 446`len` 447 448Number of dependent signatures. The value must be greater than or equal to 0. 449 450`item` 451 452Pointer to the start address of each signature. 453 454`type` 455 456Dependency type, which can be data dependency or task dependency. 457 458`ptr` 459 460Actual address of the dependent signature content. 461 462##### Return value 463 464N/A 465 466##### Use guide 467 468**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). 469 470##### Example 471 472Create a data dependency or task dependency. 473 474```{.c} 475// Create ffrt_deps_t on which the data depends. 476int x = 0; 477const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}}; 478ffrt_deps_t in{static_cast<uint32_t>(in_deps.size()), in_deps.data()}; 479 480// Submit a task, and obtain a task handle. 481ffrt_task_handle_t task = ffrt_submit_h_base( 482 ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 483// Create ffrt_deps_t on which the task depends. 484const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}}; 485ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 486``` 487 488#### ffrt_task_attr_t 489 490Auxiliary class for defining task attributes. It is used together with **ffrt_submit_base**. 491 492##### Declaration 493 494```{.c} 495typedef enum { 496 ffrt_qos_inherent = -1, 497 ffrt_qos_background, 498 ffrt_qos_utility, 499 ffrt_qos_default, 500 ffrt_qos_user_initiated, 501} ffrt_qos_default_t; 502 503typedef int ffrt_qos_t; 504 505typedef struct { 506 uint32_t storage[(ffrt_task_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; 507} ffrt_task_attr_t; 508typedef void* ffrt_task_handle_t; 509 510int ffrt_task_attr_init(ffrt_task_attr_t* attr); 511void ffrt_task_attr_destroy(ffrt_task_attr_t* attr); 512void ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos); 513ffrt_qos_t ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr); 514void ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name); 515const char* ffrt_task_attr_get_name(const ffrt_task_attr_t* attr); 516void ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us); 517uint64_t ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr); 518``` 519 520##### Parameters 521 522`attr` 523 524Handle of the target task attribute. 525 526`qos` 527 528* Enumerated type of QoS. 529* **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. 530 531`delay_us` 532 533Delay for executing the task, in μs. 534 535##### Return value 536 537N/A 538 539##### Use guide 540* 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**. 541* Conventions: 542 * If **task_attr** is not used for QoS setting during task submission, the QoS of the task is **ffrt_qos_default**. 543 * 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**. 544 * In other cases, the QoS value passed in is used. 545* 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. 546* If **task_attr** is accessed after **ffrt_task_attr_destroy** is called, undefined behavior may occur. 547 548##### Example 549 550Submit a task with the QoS set to **ffrt_qos_background**: 551 552```{.c} 553#include <stdio.h> 554#include "ffrt.h" 555 556void my_print(void* arg) 557{ 558 printf("hello ffrt\n"); 559} 560 561typedef struct { 562 ffrt_function_header_t header; 563 ffrt_function_t func; 564 ffrt_function_t after_func; 565 void* arg; 566} c_function; 567 568static void ffrt_exec_function_wrapper(void* t) 569{ 570 c_function* f = (c_function*)t; 571 if (f->func) { 572 f->func(f->arg); 573 } 574} 575 576static void ffrt_destroy_function_wrapper(void* t) 577{ 578 c_function* f = (c_function*)t; 579 if (f->after_func) { 580 f->after_func(f->arg); 581 } 582} 583 584#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 585static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 586 const ffrt_function_t after_func, void* arg) 587{ 588 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 589 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 590 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 591 f->header.exec = ffrt_exec_function_wrapper; 592 f->header.destroy = ffrt_destroy_function_wrapper; 593 f->func = func; 594 f->after_func = after_func; 595 f->arg = arg; 596 return (ffrt_function_header_t*)f; 597} 598 599static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 600 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 601{ 602 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 603} 604 605int main(int narg, char** argv) 606{ 607 ffrt_task_attr_t attr; 608 ffrt_task_attr_init(&attr); 609 ffrt_task_attr_set_qos(&attr, ffrt_qos_background); 610 ffrt_task_attr_set_delay(&attr, 10000); 611 ffrt_submit_c(my_print, NULL, NULL, NULL, NULL, &attr); 612 ffrt_task_attr_destroy(&attr); 613 ffrt_wait(); 614 return 0; 615} 616``` 617 618 619 620 621#### ffrt_submit_h_base 622 623Submits 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. 624 625##### Declaration 626 627```{.c} 628typedef void* ffrt_task_handle_t; 629 630ffrt_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); 631void ffrt_task_handle_destroy(ffrt_task_handle_t handle); 632``` 633 634##### Parameters 635 636`func` 637 638Pointer 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. 639 640`in_deps` 641 642* Optional. 643* Input dependencies of the task. FFRT establishes the dependency by using the virtual address of the data as the data signature. 644 645`out_deps` 646 647* Optional. 648 649* Output dependencies of the task. 650 651 **NOTE** 652 653 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. 654 655`attr` 656 657* Optional. 658* Task attribute, such as QoS. For details, see [ffrt_task_attr_t](#ffrt_task_attr_t). 659 660##### Return value 661 662Take handle. The handle can be used to establish the dependency between tasks or implement synchronization in the wait statements. 663 664##### Use guide 665 666* **ffrt_task_handle_t** in the C code must be explicitly destroyed by calling **ffrt_task_handle_destroy**. 667* 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. 668* If **ffrt_task_handle_t** is accessed after **ffrt_task_handle_destroy** is called, undefined behavior may occur. 669 670##### Example 671 672```{.c} 673#include <stdio.h> 674#include "ffrt.h" 675 676void func0(void* arg) 677{ 678 printf("hello "); 679} 680 681void func1(void* arg) 682{ 683 (*(int*)arg)++; 684} 685 686void func2(void* arg) 687{ 688 printf("world, x = %d\n", *(int*)arg); 689} 690 691void func3(void* arg) 692{ 693 printf("handle wait"); 694 (*(int*)arg)++; 695} 696 697typedef struct { 698 ffrt_function_header_t header; 699 ffrt_function_t func; 700 ffrt_function_t after_func; 701 void* arg; 702} c_function; 703 704static void ffrt_exec_function_wrapper(void* t) 705{ 706 c_function* f = (c_function*)t; 707 if (f->func) { 708 f->func(f->arg); 709 } 710} 711 712static void ffrt_destroy_function_wrapper(void* t) 713{ 714 c_function* f = (c_function*)t; 715 if (f->after_func) { 716 f->after_func(f->arg); 717 } 718} 719 720#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 721static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 722 const ffrt_function_t after_func, void* arg) 723{ 724 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 725 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 726 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 727 f->header.exec = ffrt_exec_function_wrapper; 728 f->header.destroy = ffrt_destroy_function_wrapper; 729 f->func = func; 730 f->after_func = after_func; 731 f->arg = arg; 732 return (ffrt_function_header_t*)f; 733} 734 735static inline ffrt_task_handle_t ffrt_submit_h_c(ffrt_function_t func, const ffrt_function_t after_func, 736 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 737{ 738 return ffrt_submit_h_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 739} 740 741static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 742 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 743{ 744 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 745} 746 747 748int main(int narg, char** argv) 749{ 750 // Handle the work with submit. 751 ffrt_task_handle_t h = ffrt_submit_h_c(func0, NULL, NULL, NULL, NULL, NULL); // not need some data in this task 752 int x = 1; 753 const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}}; 754 ffrt_deps_t d2{static_cast<uint32_t>(in_deps.size()), in_deps.data()}; 755 756 const std::vector<ffrt_dependence_t> out_deps = {{ffrt_dependence_data, &x}}; 757 ffrt_deps_t d1{static_cast<uint32_t>(out_deps.size()), out_deps.data()}; 758 759 ffrt_submit_c(func1, NULL, &x, NULL, &d1, NULL); 760 ffrt_submit_c(func2, NULL, &x, &d2, NULL, NULL); // this task depend x and h 761 ffrt_task_handle_destroy(h); 762 763 // Handle the work with wait. 764 ffrt_task_handle_t h2 = ffrt_submit_h_c(func3, NULL, &x, NULL, NULL, NULL); 765 766 const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, h2}}; 767 ffrt_deps_t d3{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 768 ffrt_wait_deps(&d3); 769 ffrt_task_handle_destroy(h2); 770 printf("x = %d", x); 771 ffrt_wait(); 772 return 0; 773} 774``` 775 776Expected output: 777 778``` 779hello 780handle wait 781x = 2 782world, x = 3 783``` 784 785 786 787#### ffrt_this_task_get_id 788 789Obtains 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.) 790 791##### Declaration 792 793```{.c} 794uint64_t ffrt_this_task_get_id(); 795``` 796 797##### Parameters 798 799N/A 800 801##### Return value 802 803ID of the task being executed. 804 805##### Use guide 806 807* 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. 808* You can determine whether the function runs on an FFRT or a non-FFRT worker thread based on the return value. 809* 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. 810 811##### Example 812 813N/A 814 815#### ffrt_this_task_update_qos 816 817Updates the QoS of the task being executed. 818 819##### Declaration 820 821```{.c} 822int ffrt_this_task_update_qos(ffrt_qos_t qos); 823``` 824 825##### Parameters 826 827`qos` 828 829New QoS. 830 831##### Return value 832 833Returns **0** if the operation is successful; returns a non-zero value otherwise. 834 835##### Use guide 836 837* The QoS update takes effect immediately. 838* If the new QoS is different from the current QoS, the task is blocked and then resumed based on the new QoS. 839* If the new QoS is the same as the current QoS, the API returns **0** immediately without any processing. 840* If this API is called not inside a task, a non-zero value is returned. You can ignore the value or perform other operations. 841 842##### Example 843 844N/A 845 846### Serial Queue 847 848FFRT provides **queue** to implement capabilities similar to **WorkQueue** in Android. It can deliver excellent performance if being used properly. 849 850#### ffrt_queue_attr_t 851 852##### Declaration 853```{.c} 854typedef struct { 855 uint32_t storage[(ffrt_queue_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; 856} ffrt_queue_attr_t; 857 858int ffrt_queue_attr_init(ffrt_queue_attr_t* attr); 859void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr); 860``` 861 862##### Parameters 863 864`attr` 865 866Pointer to the uninitialized **ffrt_queue_attr_t** object. 867 868##### Return value 869Returns **0** if the API is called successfully; returns **-1** otherwise. 870 871##### Use guide 872* An **ffrt_queue_attr_t** object must be created prior to an **ffrt_queue_t** object. 873* 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. 874* If **ffrt_queue_attr_t** is accessed after **ffrt_queue_attr_destroy** is called, undefined behavior may occur. 875 876##### Example 877See the example provided in **ffrt_queue_t**. 878 879#### ffrt_queue_t 880 881##### Declaration 882```{.c} 883typedef enum { ffrt_queue_serial, ffrt_queue_max } ffrt_queue_type_t; 884typedef void* ffrt_queue_t; 885 886ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr) 887void ffrt_queue_destroy(ffrt_queue_t queue) 888``` 889 890##### Parameters 891 892`type` 893 894Queue type. 895 896`name` 897 898Pointer to the queue name. 899 900`attr` 901 902Pointer to the queue attribute. For details, see **ffrt_queue_attr_t**. 903 904##### Return value 905Returns the queue created if the API is called successfully; returns a null pointer otherwise. 906 907##### Use guide 908* Tasks submitted to the queue are executed in sequence. If a task is blocked, the execution sequence of the task cannot be ensured. 909* 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. 910* If **ffrt_queue_t** is accessed after **ffrt_queue_destroy** is called, undefined behavior may occur. 911 912##### Example 913``` 914#include <stdio.h> 915#include "ffrt.h" 916 917using namespace std; 918 919template<class T> 920struct Function { 921 ffrt_function_header_t header; 922 T closure; 923}; 924 925template<class T> 926void ExecFunctionWrapper(void* t) 927{ 928 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 929 f->closure(); 930} 931 932template<class T> 933void DestroyFunctionWrapper(void* t) 934{ 935 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 936 f = nullptr; 937} 938 939template<class T> 940static inline ffrt_function_header_t* create_function_wrapper(T&& func, 941 ffrt_function_kind_t kind = ffrt_function_kind_general) 942{ 943 using function_type = Function<std::decay_t<T>>; 944 auto p = ffrt_alloc_auto_managed_function_storage_base(kind); 945 auto f = new (p)function_type; 946 f->header.exec = ExecFunctionWrapper<T>; 947 f->header.destroy = DestroyFunctionWrapper<T>; 948 f->closure = std::forward<T>(func); 949 return reinterpret_cast<ffrt_function_header_t*>(f); 950} 951 952int main(int narg, char** argv) 953{ 954 ffrt_queue_attr_t queue_attr; 955 (void)ffrt_queue_attr_init(&queue_attr); 956 ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr); 957 std::function<void()>&& queueFunc = [] () {printf("Task done.\n");}; 958 ffrt_function_header_t* queueFunc_t = create_function_wrapper((queueFunc), ffrt_function_kind_queue); 959 ffrt_queue_submit(queue_handle, queueFunc_t, nullptr); 960 961 ffrt_queue_attr_destroy(&queue_attr); 962 ffrt_queue_destroy(queue_handle); 963} 964``` 965### Synchronization Primitive 966 967#### ffrt_mutex_t 968 969Provides performance implementation similar to pthread mutex. 970 971##### Declaration 972 973```{.c} 974typedef enum { 975 ffrt_error = -1, 976 ffrt_success = 0, 977 ffrt_error_nomem = ENOMEM, 978 ffrt_error_timedout = ETIMEDOUT, 979 ffrt_error_busy = EBUSY, 980 ffrt_error_inval = EINVAL 981} ffrt_error_t; 982 983struct ffrt_mutex_t; 984 985int ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr); 986int ffrt_mutex_lock(ffrt_mutex_t* mutex); 987int ffrt_mutex_unlock(ffrt_mutex_t* mutex); 988int ffrt_mutex_trylock(ffrt_mutex_t* mutex); 989int ffrt_mutex_destroy(ffrt_mutex_t* mutex); 990``` 991 992##### Parameters 993 994`attr` 995 996Attribute of the mutex. Set it to a null pointer. This is because FFRT supports only mutex of the basic type currently. 997 998`mutex` 999 1000Pointer to the target mutex. 1001 1002##### Return value 1003 1004Returns **ffrt_success** if the API is called successfully; returns an error code otherwise. 1005 1006##### Use guide 1007* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1008* 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. 1009* Currently, recursion and timing are not supported. 1010* **ffrt_mutex_t** in the C code must be explicitly created and destroyed by calling **ffrt_mutex_init** and **ffrt_mutex_destroy**, respectively. 1011* 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. 1012* If **ffrt_mutex_t** is accessed after **ffrt_mutex_destroy** is called, undefined behavior may occur. 1013 1014##### Example 1015 1016```{.c} 1017#include <stdio.h> 1018#include "ffrt.h" 1019 1020typedef struct { 1021 int* sum; 1022 ffrt_mutex_t* mtx; 1023} tuple; 1024 1025void func(void* arg) 1026{ 1027 tuple* t = (tuple*)arg; 1028 1029 int ret = ffrt_mutex_lock(t->mtx); 1030 if (ret != ffrt_success) { 1031 printf("error\n"); 1032 } 1033 (*t->sum)++; 1034 ret = ffrt_mutex_unlock(t->mtx); 1035 if (ret != ffrt_success) { 1036 printf("error\n"); 1037 } 1038} 1039 1040typedef struct { 1041 ffrt_function_header_t header; 1042 ffrt_function_t func; 1043 ffrt_function_t after_func; 1044 void* arg; 1045} c_function; 1046 1047static void ffrt_exec_function_wrapper(void* t) 1048{ 1049 c_function* f = (c_function*)t; 1050 if (f->func) { 1051 f->func(f->arg); 1052 } 1053} 1054 1055static void ffrt_destroy_function_wrapper(void* t) 1056{ 1057 c_function* f = (c_function*)t; 1058 if (f->after_func) { 1059 f->after_func(f->arg); 1060 } 1061} 1062 1063#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1064static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1065 const ffrt_function_t after_func, void* arg) 1066{ 1067 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1068 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1069 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1070 f->header.exec = ffrt_exec_function_wrapper; 1071 f->header.destroy = ffrt_destroy_function_wrapper; 1072 f->func = func; 1073 f->after_func = after_func; 1074 f->arg = arg; 1075 return (ffrt_function_header_t*)f; 1076} 1077 1078static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1079 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1080{ 1081 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1082} 1083 1084void ffrt_mutex_task(void *) 1085{ 1086 int sum = 0; 1087 ffrt_mutex_t mtx; 1088 tuple t = {&sum, &mtx}; 1089 int ret = ffrt_mutex_init(&mtx, NULL); 1090 if (ret != ffrt_success) { 1091 printf("error\n"); 1092 } 1093 for (int i = 0; i < 10; i++) { 1094 ffrt_submit_c(func, NULL, &t, NULL, NULL, NULL); 1095 } 1096 ffrt_mutex_destroy(&mtx); 1097 ffrt_wait(); 1098 printf("sum = %d\n", sum); 1099} 1100 1101int main(int narg, char** argv) 1102{ 1103 int r; 1104 ffrt_submit_c(ffrt_mutex_task, NULL, NULL, NULL, NULL, NULL); 1105 ffrt_wait(); 1106 return 0; 1107} 1108``` 1109 1110Expected output: 1111 1112``` 1113sum=10 1114``` 1115 1116This example is for reference only and is not encouraged in practice. 1117 1118 1119#### ffrt_cond_t 1120 1121Provides performance implementation similar to pthread semaphore. 1122 1123##### Declaration 1124 1125```{.c} 1126typedef enum { 1127 ffrt_error = -1, 1128 ffrt_success = 0, 1129 ffrt_error_nomem = ENOMEM, 1130 ffrt_error_timedout = ETIMEDOUT, 1131 ffrt_error_busy = EBUSY, 1132 ffrt_error_inval = EINVAL 1133} ffrt_error_t; 1134 1135struct ffrt_cond_t; 1136 1137int ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr); 1138int ffrt_cond_signal(ffrt_cond_t* cond); 1139int ffrt_cond_broadcast(ffrt_cond_t* cond); 1140int ffrt_cond_wait(ffrt_cond_t*cond, ffrt_mutex_t* mutex); 1141int ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point); 1142int ffrt_cond_destroy(ffrt_cond_t* cond); 1143``` 1144 1145##### Parameters 1146 1147`cond` 1148 1149Pointer to the target semaphore. 1150 1151`attr` 1152 1153Pointer to the attribute. A null pointer indicates that the default attribute is used. 1154 1155`mutex` 1156 1157Pointer to the target mutex. 1158 1159`time_point` 1160 1161Pointer to the maximum duration during which the thread is blocked. 1162 1163 1164##### Return value 1165 1166Returns **ffrt_success** if the API is successfully called; returns **ffrt_error_timedout** if the maximum duration is reached before the mutex is locked. 1167 1168##### Use guide 1169* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1170* 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. 1171* **ffrt_cond_t** in the C code must be explicitly created and destroyed by calling **ffrt_cond_init** and **ffrt_cond_destroy**, respectively. 1172* 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. 1173* If **ffrt_cond_t** is accessed after **ffrt_cond_destroy** is called, undefined behavior may occur. 1174 1175##### Example 1176 1177```{.c} 1178#include <stdio.h> 1179#include "ffrt.h" 1180 1181typedef struct { 1182 ffrt_cond_t* cond; 1183 int* a; 1184 ffrt_mutex_t* lock_; 1185} tuple; 1186 1187void func1(void* arg) 1188{ 1189 tuple* t = (tuple*)arg; 1190 int ret = ffrt_mutex_lock(t->lock_); 1191 if (ret != ffrt_success) { 1192 printf("error\n"); 1193 } 1194 while (*t->a != 1) { 1195 ret = ffrt_cond_wait(t->cond, t->lock_); 1196 if (ret != ffrt_success) { 1197 printf("error\n"); 1198 } 1199 } 1200 ret = ffrt_mutex_unlock(t->lock_); 1201 if (ret != ffrt_success) { 1202 printf("error\n"); 1203 } 1204 printf("a = %d\n", *(t->a)); 1205} 1206 1207void func2(void* arg) 1208{ 1209 tuple* t = (tuple*)arg; 1210 int ret = ffrt_mutex_lock(t->lock_); 1211 if (ret != ffrt_success) { 1212 printf("error\n"); 1213 } 1214 *(t->a) = 1; 1215 ret = ffrt_cond_signal(t->cond); 1216 if (ret != ffrt_success) { 1217 printf("error\n"); 1218 } 1219 ret = ffrt_mutex_unlock(t->lock_); 1220 if (ret != ffrt_success) { 1221 printf("error\n"); 1222 } 1223} 1224 1225typedef struct { 1226 ffrt_function_header_t header; 1227 ffrt_function_t func; 1228 ffrt_function_t after_func; 1229 void* arg; 1230} c_function; 1231 1232static void ffrt_exec_function_wrapper(void* t) 1233{ 1234 c_function* f = (c_function*)t; 1235 if (f->func) { 1236 f->func(f->arg); 1237 } 1238} 1239 1240static void ffrt_destroy_function_wrapper(void* t) 1241{ 1242 c_function* f = (c_function*)t; 1243 if (f->after_func) { 1244 f->after_func(f->arg); 1245 } 1246} 1247 1248#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1249static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1250 const ffrt_function_t after_func, void* arg) 1251{ 1252 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1253 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1254 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1255 f->header.exec = ffrt_exec_function_wrapper; 1256 f->header.destroy = ffrt_destroy_function_wrapper; 1257 f->func = func; 1258 f->after_func = after_func; 1259 f->arg = arg; 1260 return (ffrt_function_header_t*)f; 1261} 1262 1263static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1264 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1265{ 1266 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1267} 1268 1269void ffrt_cv_task(void *) 1270{ 1271 ffrt_cond_t cond; 1272 int ret = ffrt_cond_init(&cond, NULL); 1273 if (ret != ffrt_success) { 1274 printf("error\n"); 1275 } 1276 int a = 0; 1277 ffrt_mutex_t lock_; 1278 tuple t = {&cond, &a, &lock_}; 1279 ret = ffrt_mutex_init(&lock_, NULL); 1280 if (ret != ffrt_success) { 1281 printf("error\n"); 1282 } 1283 ffrt_submit_c(func1, NULL, &t, NULL, NULL, NULL); 1284 ffrt_submit_c(func2, NULL, &t, NULL, NULL, NULL); 1285 ffrt_wait(); 1286 ffrt_cond_destroy(&cond); 1287 ffrt_mutex_destroy(&lock_); 1288} 1289 1290int main(int narg, char** argv) 1291{ 1292 ffrt_submit_c(ffrt_cv_task, NULL, NULL, NULL, NULL, NULL); 1293 ffrt_wait(); 1294 return 0; 1295} 1296``` 1297 1298Expected output: 1299 1300``` 1301a=1 1302``` 1303 1304This example is for reference only and is not encouraged in practice. 1305 1306### Miscellaneous 1307 1308#### ffrt_usleep 1309 1310Provides performance implementation similar to C11 sleep and Linux usleep. 1311 1312##### Declaration 1313 1314```{.c} 1315int ffrt_usleep(uint64_t usec); 1316``` 1317 1318##### Parameters 1319 1320`usec` 1321 1322Duration that the calling thread is suspended, in μs. 1323 1324##### Return value 1325 1326N/A 1327 1328##### Use guide 1329* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1330* The traditional function **sleep** may cause unexpected kernel mode trap. **ffrt_usleep** solves this problem and therefore provides better performance if used properly. 1331 1332##### Example 1333 1334```{.c} 1335#include <time.h> 1336#include <stdio.h> 1337#include "ffrt.h" 1338 1339void func(void* arg) 1340{ 1341 time_t current_time = time(NULL); 1342 printf("Time: %s", ctime(¤t_time)); 1343 ffrt_usleep(2000000); // Suspend for 2 seconds 1344 current_time = time(NULL); 1345 printf("Time: %s", ctime(¤t_time)); 1346} 1347 1348typedef struct { 1349 ffrt_function_header_t header; 1350 ffrt_function_t func; 1351 ffrt_function_t after_func; 1352 void* arg; 1353} c_function; 1354 1355static void ffrt_exec_function_wrapper(void* t) 1356{ 1357 c_function* f = (c_function*)t; 1358 if (f->func) { 1359 f->func(f->arg); 1360 } 1361} 1362 1363static void ffrt_destroy_function_wrapper(void* t) 1364{ 1365 c_function* f = (c_function*)t; 1366 if (f->after_func) { 1367 f->after_func(f->arg); 1368 } 1369} 1370 1371#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1372static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1373 const ffrt_function_t after_func, void* arg) 1374{ 1375 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1376 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1377 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1378 f->header.exec = ffrt_exec_function_wrapper; 1379 f->header.destroy = ffrt_destroy_function_wrapper; 1380 f->func = func; 1381 f->after_func = after_func; 1382 f->arg = arg; 1383 return (ffrt_function_header_t*)f; 1384} 1385 1386static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1387 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1388{ 1389 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1390} 1391 1392int main(int narg, char** argv) 1393{ 1394 ffrt_submit_c(func, NULL, NULL, NULL, NULL, NULL); 1395 ffrt_wait(); 1396 return 0; 1397} 1398``` 1399 1400An output case is as follows: 1401 1402``` 1403Time: Tue Aug 13 15:45:30 2024 1404Time: Tue Aug 13 15:45:32 2024 1405``` 1406 1407#### ffrt_yield 1408 1409Passes control to other tasks so that they can be executed. If there is no other task that can be executed, this API is invalid. 1410 1411##### Declaration 1412 1413```{.c} 1414void ffrt_yield(); 1415``` 1416 1417##### Parameters 1418 1419N/A 1420 1421##### Return value 1422 1423N/A 1424 1425##### Use guide 1426* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1427* The exact behavior of this API depends on the implementation, especially the mechanism and system state of the FFRT scheduler in use. 1428 1429##### Example 1430 1431N/A 1432 1433 1434## How to Develop 1435 1436The following describes how to use the native APIs provided by FFRT to create parallel tasks and serial queue tasks and destroy corresponding resources. 1437 1438**Adding Dynamic Link Libraries** 1439 1440Add the following libraries to **CMakeLists.txt**. 1441```txt 1442libffrt.z.so 1443``` 1444 1445**Including Header Files** 1446```c++ 1447#include "ffrt/task.h" 1448#include "ffrt/type_def.h" 1449#include "ffrt/condition_variable.h" 1450#include "ffrt/mutex.h" 1451#include "ffrt/queue.h" 1452#include "ffrt/sleep.h" 1453``` 1454 14551. **Encapsulate the function to be executed.** 1456 ```c++ 1457 // Method 1: Use the template. C++ is supported. 1458 template<class T> 1459 struct Function { 1460 ffrt_function_header_t header; 1461 T closure; 1462 }; 1463 1464 template<class T> 1465 void ExecFunctionWrapper(void* t) 1466 { 1467 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 1468 f->closure(); 1469 } 1470 1471 template<class T> 1472 void DestroyFunctionWrapper(void* t) 1473 { 1474 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 1475 f->closure = nullptr; 1476 } 1477 1478 template<class T> 1479 static inline ffrt_function_header_t* create_function_wrapper(T&& func, 1480 ffrt_function_kind_t kind = ffrt_function_kind_general) 1481 { 1482 using function_type = Function<std::decay_t<T>>; 1483 auto p = ffrt_alloc_auto_managed_function_storage_base(kind); 1484 auto f = new (p)function_type; 1485 f->header.exec = ExecFunctionWrapper<T>; 1486 f->header.destroy = DestroyFunctionWrapper<T>; 1487 f->closure = std::forward<T>(func); 1488 return reinterpret_cast<ffrt_function_header_t*>(f); 1489 } 1490 1491 // Method 2 1492 typedef struct { 1493 ffrt_function_header_t header; 1494 ffrt_function_t func; 1495 ffrt_function_t after_func; 1496 void* arg; 1497 } CFunction; 1498 1499 static void FfrtExecFunctionWrapper(void* t) 1500 { 1501 CFunction* f = static_cast<CFunction*>(t); 1502 if (f->func) { 1503 f->func(f->arg); 1504 } 1505 } 1506 1507 static void FfrtDestroyFunctionWrapper(void* t) 1508 { 1509 CFunction* f = static_cast<CFunction*>(t); 1510 if (f->after_func) { 1511 f->after_func(f->arg); 1512 } 1513 } 1514 1515 #define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1516 static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1517 const ffrt_function_t after_func, void* arg, ffrt_function_kind_t kind_t = ffrt_function_kind_general) 1518 { 1519 FFRT_STATIC_ASSERT(sizeof(CFunction) <= ffrt_auto_managed_function_storage_size, 1520 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1521 CFunction* f = static_cast<CFunction*>(ffrt_alloc_auto_managed_function_storage_base(kind_t)); 1522 f->header.exec = FfrtExecFunctionWrapper; 1523 f->header.destroy = FfrtDestroyFunctionWrapper; 1524 f->func = func; 1525 f->after_func = after_func; 1526 f->arg = arg; 1527 return reinterpret_cast<ffrt_function_header_t*>(f); 1528 } 1529 1530 // Example: function to be submitted for execution. 1531 void OnePlusForTest(void* arg) 1532 { 1533 (*static_cast<int*>(arg)) += 1; 1534 } 1535 ``` 1536 15372. **Set the task attributes.** 1538 1539 Set the task attributes, including the QoS and task name, before submitting the task. 1540 ```c++ 1541 // ******Initialize the attributes of the parallel task****** 1542 ffrt_task_attr_t attr; 1543 ffrt_task_attr_init(&attr); 1544 1545 // ******Create a serial queue****** 1546 1547 // Create the attributes of the serial queue. 1548 ffrt_queue_attr_t queue_attr; 1549 // Create the handle of the serial queue. 1550 ffrt_queue_t queue_handle; 1551 1552 // Initialize the queue attribute. 1553 (void)ffrt_queue_attr_init(&queue_attr); 1554 1555 // Set the QoS if necessary. 1556 ffrt_queue_attr_set_qos(&queue_attr, static_cast<ffrt_qos_t>(ffrt_qos_inherit)); 1557 // Set the timeout period (ms) if necessary. 1558 ffrt_queue_attr_set_timeout(&queue_attr, 10000); 1559 // Set the timeout callback if necessary. 1560 int x = 0; 1561 ffrt_queue_attr_set_callback(&queue_attr, ffrt_create_function_wrapper(OnePlusForTest, NULL, &x, 1562 ffrt_function_kind_queue)); 1563 1564 // Initialize the queue based on the attributes. 1565 queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr); 1566 ``` 1567 15683. **Submit the task.** 1569 ```c++ 1570 int a = 0; 1571 // ******Parallel task****** 1572 // Submit the parallel task without obtaining a handle. 1573 ffrt_submit_base(ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 1574 // Submit the parallel task and obtain a handle. 1575 ffrt_task_handle_t task = ffrt_submit_h_base( 1576 ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 1577 1578 // ******Serial queue task****** 1579 // Submit the serial queue task without obtaining a handle. 1580 ffrt_queue_submit(queue_handle, ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, 1581 ffrt_function_kind_queue), nullptr); 1582 // Submit the serial queue task and obtain a handle. 1583 ffrt_task_handle_t handle = ffrt_queue_submit_h(queue_handle, 1584 ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, ffrt_function_kind_queue), nullptr); 1585 1586 // Call wait if you need to wait for the execution result. 1587 const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}}; 1588 ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 1589 ffrt_wait_deps(&wait); 1590 1591 ffrt_queue_wait(handle); 1592 ``` 1593 15944. **Destroy the resources after the task is submitted.** 1595 ```c++ 1596 // ******Destroy the parallel task****** 1597 ffrt_task_attr_destroy(&attr); 1598 ffrt_task_handle_destroy(task); 1599 1600 // ******Destroy the serial queue task****** 1601 // Destroy the task handle and then the queue. 1602 ffrt_queue_attr_destroy(&queue_attr); 1603 ffrt_task_handle_destroy(handle); 1604 ffrt_queue_destroy(queue_handle); 1605 ``` 1606 1607## Suggestions 1608 1609### Suggestion 1: Functional programming 1610 1611Basic idea: Use functional programming for the calculation process. 1612 1613* Use pure functions and encapsulate them to express each step of the process. 1614* There is no global data access. 1615* There is no internal state reserved. 1616* Use **ffrt_submit_base()** to submit a function in asynchronous mode for execution. 1617* 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. 1618* Use **inDeps** and **outDeps** to specify the dependency between tasks to ensure the correctness of program execution. 1619 1620> **NOTE** 1621> 1622> Using pure functions helps you maximize the parallelism and avoid data races and lock abuse. 1623 1624In practice, you may not use pure functions in certain scenarios, with the following prerequisites: 1625 1626* **in_deps** and **out_deps** can ensure the correctness of program execution. 1627* The lock mechanism provided by FFRT is used to protect access to global variables. 1628 1629 1630### Suggestion 2: Use FFRT APIs 1631 1632* 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. 1633* Use the lock, condition variable, sleep, and I/O APIs provided by FFRT to replace the APIs of the system thread library. 1634* Using the APIs of the system thread library may block worker threads and result in extra performance overhead. 1635 1636### Suggestion 3: Deadline mechanism 1637 1638* Use FFRT APIs in processing flows that feature periodic/repeated execution. 1639* Use FFRT APIs in processing flows with clear time constraints and is performance critical. 1640* Use FFRT APIs in relatively large-granularity processing flows, such as the frame processing flow with the 16.6 ms time constraint. 1641 1642### Suggestion 4: Migration from the thread model 1643 1644* Create a thread instead of creating an FFRT task. 1645* A thread is logically similar to a task without **in_deps**. 1646* Identify the dependency between threads and express the dependencies in **in_deps** or **out_deps** of the task. 1647* Decompose an intra-thread computing process into asynchronous tasks for invoking. 1648* Use the task dependency and lock mechanism to avoid data races of concurrent tasks. 1649 1650## Restrictions 1651 1652After an FFRT object is initialized in the C code, you are responsible for setting the object to null or destroying the object. 1653 1654To 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. 1655 1656Noncompliant example 1: Repeated calling of **destroy()** may cause unpredictable data damage. 1657 1658```{.c} 1659#include "ffrt.h" 1660void abnormal_case_1() 1661{ 1662 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1663 ... 1664 ffrt_task_handle_destroy(h); 1665 ffrt_task_handle_destroy(h); // double free 1666} 1667``` 1668 1669Noncompliant example 2: A memory leak occurs if **destroy()** is not called. 1670 1671```{.c} 1672#include "ffrt.h" 1673void abnormal_case_2() 1674{ 1675 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1676 ... 1677 // Memory leak 1678} 1679``` 1680 1681Recommended example: Call **destroy()** only once; set the object to null if necessary. 1682 1683```{.c} 1684#include "ffrt.h" 1685void normal_case() 1686{ 1687 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1688 ... 1689 ffrt_task_handle_destroy(h); 1690 h = nullptr; // if necessary 1691} 1692``` 1693