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 369#define ffrt_deps_define(name, dep1, ...) const void* __v_##name[] = {dep1, ##__VA_ARGS__}; \ 370 ffrt_deps_t name = {sizeof(__v_##name) / sizeof(void*), __v_##name} 371 372void fib_ffrt(void* arg) 373{ 374 fib_ffrt_s* p = (fib_ffrt_s*)arg; 375 int x = p->x; 376 int* y = p->y; 377 378 if (x <= 1) { 379 *y = x; 380 } else { 381 int y1, y2; 382 fib_ffrt_s s1 = {x - 1, &y1}; 383 fib_ffrt_s s2 = {x - 2, &y2}; 384 ffrt_deps_define(dx, &x); 385 ffrt_deps_define(dy1, &y1); 386 ffrt_deps_define(dy2, &y2); 387 ffrt_deps_define(dy12, &y1, &y2); 388 ffrt_submit_c(fib_ffrt, NULL, &s1, &dx, &dy1, NULL); 389 ffrt_submit_c(fib_ffrt, NULL, &s2, &dx, &dy2, NULL); 390 ffrt_wait_deps(&dy12); 391 *y = y1 + y2; 392 } 393} 394 395int main(int narg, char** argv) 396{ 397 int r; 398 fib_ffrt_s s = {10, &r}; 399 ffrt_deps_define(dr, &r); 400 ffrt_submit_c(fib_ffrt, NULL, &s, NULL, &dr, NULL); 401 ffrt_wait_deps(&dr); 402 printf("fibonacci 10: %d\n", r); 403 return 0; 404} 405``` 406 407**NOTE** 408 409(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. 410 411(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. 412 413<img src="figures/ffrtfigure2.png" style="zoom:100%" /> 414 415> **NOTE** 416> 417> The preceding implementation requires you to explicitly manage the data lifecycle and encapsulate input parameters, making the code complex. 418 419#### ffrt_deps_t 420 421Abstraction of dependency arrays in C code, logically equivalent to **std::vector<void*>** in C++ code. 422 423##### Declaration 424 425```{.c} 426typedef enum { 427 ffrt_dependence_data, 428 ffrt_dependence_task, 429} ffrt_dependence_type_t; 430 431typedef struct { 432 ffrt_dependence_type_t type; 433 const void* ptr; 434} ffrt_dependence_t; 435 436typedef struct { 437 uint32_t len; 438 const ffrt_dependence_t* items; 439} ffrt_deps_t; 440``` 441 442##### Parameters 443 444`len` 445 446Number of dependent signatures. The value must be greater than or equal to 0. 447 448`item` 449 450Pointer to the start address of each signature. 451 452`type` 453 454Dependency type, which can be data dependency or task dependency. 455 456`ptr` 457 458Actual address of the dependent signature content. 459 460##### Return value 461 462N/A 463 464##### Use guide 465 466**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). 467 468##### Example 469 470Create a data dependency or task dependency. 471 472```{.c} 473// Create ffrt_deps_t on which the data depends. 474int x = 0; 475const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}}; 476ffrt_deps_t in{static_cast<uint32_t>(in_deps.size()), in_deps.data()}; 477 478// Submit a task, and obtain a task handle. 479ffrt_task_handle_t task = ffrt_submit_h_base( 480 ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 481// Create ffrt_deps_t on which the task depends. 482const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}}; 483ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 484``` 485 486#### ffrt_task_attr_t 487 488Auxiliary class for defining task attributes. It is used together with **ffrt_submit_base**. 489 490##### Declaration 491 492```{.c} 493typedef enum { 494 ffrt_qos_inherent = -1, 495 ffrt_qos_background, 496 ffrt_qos_utility, 497 ffrt_qos_default, 498 ffrt_qos_user_initiated, 499} ffrt_qos_default_t; 500 501typedef int ffrt_qos_t; 502 503typedef struct { 504 uint32_t storage[(ffrt_task_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; 505} ffrt_task_attr_t; 506typedef void* ffrt_task_handle_t; 507 508int ffrt_task_attr_init(ffrt_task_attr_t* attr); 509void ffrt_task_attr_destroy(ffrt_task_attr_t* attr); 510void ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos); 511ffrt_qos_t ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr); 512void ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name); 513const char* ffrt_task_attr_get_name(const ffrt_task_attr_t* attr); 514void ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us); 515uint64_t ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr); 516``` 517 518##### Parameters 519 520`attr` 521 522Handle of the target task attribute. 523 524`qos` 525 526* Enumerated type of QoS. 527* **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. 528 529`delay_us` 530 531Delay for executing the task, in μs. 532 533##### Return value 534 535N/A 536 537##### Use guide 538* 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**. 539* Conventions: 540 * If **task_attr** is not used for QoS setting during task submission, the QoS of the task is **ffrt_qos_default**. 541 * 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**. 542 * In other cases, the QoS value passed in is used. 543* 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. 544* If **task_attr** is accessed after **ffrt_task_attr_destroy** is called, undefined behavior may occur. 545 546##### Example 547 548Submit a task with the QoS set to **ffrt_qos_background**: 549 550```{.c} 551#include <stdio.h> 552#include "ffrt.h" 553 554void my_print(void* arg) 555{ 556 printf("hello ffrt\n"); 557} 558 559typedef struct { 560 ffrt_function_header_t header; 561 ffrt_function_t func; 562 ffrt_function_t after_func; 563 void* arg; 564} c_function; 565 566static void ffrt_exec_function_wrapper(void* t) 567{ 568 c_function* f = (c_function*)t; 569 if (f->func) { 570 f->func(f->arg); 571 } 572} 573 574static void ffrt_destroy_function_wrapper(void* t) 575{ 576 c_function* f = (c_function*)t; 577 if (f->after_func) { 578 f->after_func(f->arg); 579 } 580} 581 582#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 583static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 584 const ffrt_function_t after_func, void* arg) 585{ 586 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 587 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 588 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 589 f->header.exec = ffrt_exec_function_wrapper; 590 f->header.destroy = ffrt_destroy_function_wrapper; 591 f->func = func; 592 f->after_func = after_func; 593 f->arg = arg; 594 return (ffrt_function_header_t*)f; 595} 596 597static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 598 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 599{ 600 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 601} 602 603int main(int narg, char** argv) 604{ 605 ffrt_task_attr_t attr; 606 ffrt_task_attr_init(&attr); 607 ffrt_task_attr_set_qos(&attr, ffrt_qos_background); 608 ffrt_task_attr_set_delay(&attr, 10000); 609 ffrt_submit_c(my_print, NULL, NULL, NULL, NULL, &attr); 610 ffrt_task_attr_destroy(&attr); 611 ffrt_wait(); 612 return 0; 613} 614``` 615 616 617 618 619#### ffrt_submit_h_base 620 621Submits 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. 622 623##### Declaration 624 625```{.c} 626typedef void* ffrt_task_handle_t; 627 628ffrt_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); 629void ffrt_task_handle_destroy(ffrt_task_handle_t handle); 630``` 631 632##### Parameters 633 634`func` 635 636Pointer 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. 637 638`in_deps` 639 640* Optional. 641* Input dependencies of the task. FFRT establishes the dependency by using the virtual address of the data as the data signature. 642 643`out_deps` 644 645* Optional. 646 647* Output dependencies of the task. 648 649 **NOTE** 650 651 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. 652 653`attr` 654 655* Optional. 656* Task attribute, such as QoS. For details, see [ffrt_task_attr_t](#ffrt_task_attr_t). 657 658##### Return value 659 660Take handle. The handle can be used to establish the dependency between tasks or implement synchronization in the wait statements. 661 662##### Use guide 663 664* **ffrt_task_handle_t** in the C code must be explicitly destroyed by calling **ffrt_task_handle_destroy**. 665* 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. 666* If **ffrt_task_handle_t** is accessed after **ffrt_task_handle_destroy** is called, undefined behavior may occur. 667 668##### Example 669 670```{.c} 671#include <stdio.h> 672#include "ffrt.h" 673 674void func0(void* arg) 675{ 676 printf("hello "); 677} 678 679void func1(void* arg) 680{ 681 (*(int*)arg)++; 682} 683 684void func2(void* arg) 685{ 686 printf("world, x = %d\n", *(int*)arg); 687} 688 689void func3(void* arg) 690{ 691 printf("handle wait"); 692 (*(int*)arg)++; 693} 694 695typedef struct { 696 ffrt_function_header_t header; 697 ffrt_function_t func; 698 ffrt_function_t after_func; 699 void* arg; 700} c_function; 701 702static void ffrt_exec_function_wrapper(void* t) 703{ 704 c_function* f = (c_function*)t; 705 if (f->func) { 706 f->func(f->arg); 707 } 708} 709 710static void ffrt_destroy_function_wrapper(void* t) 711{ 712 c_function* f = (c_function*)t; 713 if (f->after_func) { 714 f->after_func(f->arg); 715 } 716} 717 718#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 719static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 720 const ffrt_function_t after_func, void* arg) 721{ 722 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 723 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 724 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 725 f->header.exec = ffrt_exec_function_wrapper; 726 f->header.destroy = ffrt_destroy_function_wrapper; 727 f->func = func; 728 f->after_func = after_func; 729 f->arg = arg; 730 return (ffrt_function_header_t*)f; 731} 732 733static inline ffrt_task_handle_t ffrt_submit_h_c(ffrt_function_t func, const ffrt_function_t after_func, 734 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 735{ 736 return ffrt_submit_h_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 737} 738 739static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 740 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 741{ 742 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 743} 744 745 746int main(int narg, char** argv) 747{ 748 // Handle the work with submit. 749 ffrt_task_handle_t h = ffrt_submit_h_c(func0, NULL, NULL, NULL, NULL, NULL); // not need some data in this task 750 int x = 1; 751 const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}}; 752 ffrt_deps_t d2{static_cast<uint32_t>(in_deps.size()), in_deps.data()}; 753 754 const std::vector<ffrt_dependence_t> out_deps = {{ffrt_dependence_data, &x}}; 755 ffrt_deps_t d1{static_cast<uint32_t>(out_deps.size()), out_deps.data()}; 756 757 ffrt_submit_c(func1, NULL, &x, NULL, &d1, NULL); 758 ffrt_submit_c(func2, NULL, &x, &d2, NULL, NULL); // this task depend x and h 759 ffrt_task_handle_destroy(h); 760 761 // Handle the work with wait. 762 ffrt_task_handle_t h2 = ffrt_submit_h_c(func3, NULL, &x, NULL, NULL, NULL); 763 764 const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, h2}}; 765 ffrt_deps_t d3{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 766 ffrt_wait_deps(&d3); 767 ffrt_task_handle_destroy(h2); 768 printf("x = %d", x); 769 ffrt_wait(); 770 return 0; 771} 772``` 773 774Expected output: 775 776``` 777hello world, x = 2 778handle wait 779x = 3 780``` 781 782 783 784#### ffrt_this_task_get_id 785 786Obtains 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.) 787 788##### Declaration 789 790```{.c} 791uint64_t ffrt_this_task_get_id(); 792``` 793 794##### Parameters 795 796N/A 797 798##### Return value 799 800ID of the task being executed. 801 802##### Use guide 803 804* 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. 805* You can determine whether the function runs on an FFRT or a non-FFRT worker thread based on the return value. 806* 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. 807 808##### Example 809 810N/A 811 812#### ffrt_this_task_update_qos 813 814Updates the QoS of the task being executed. 815 816##### Declaration 817 818```{.c} 819int ffrt_this_task_update_qos(ffrt_qos_t qos); 820``` 821 822##### Parameters 823 824`qos` 825 826New QoS. 827 828##### Return value 829 830Returns **0** if the operation is successful; returns a non-zero value otherwise. 831 832##### Use guide 833 834* The QoS update takes effect immediately. 835* If the new QoS is different from the current QoS, the task is blocked and then resumed based on the new QoS. 836* If the new QoS is the same as the current QoS, the API returns **0** immediately without any processing. 837* If this API is called not inside a task, a non-zero value is returned. You can ignore the value or perform other operations. 838 839##### Example 840 841N/A 842 843### Serial Queue 844 845FFRT provides **queue** to implement capabilities similar to **WorkQueue** in Android. It can deliver excellent performance if being used properly. 846 847#### ffrt_queue_attr_t 848 849##### Declaration 850```{.c} 851typedef struct { 852 uint32_t storage[(ffrt_queue_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; 853} ffrt_queue_attr_t; 854 855int ffrt_queue_attr_init(ffrt_queue_attr_t* attr); 856void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr); 857``` 858 859##### Parameters 860 861`attr` 862 863Pointer to the uninitialized **ffrt_queue_attr_t** object. 864 865##### Return value 866Returns **0** if the API is called successfully; returns **-1** otherwise. 867 868##### Use guide 869* An **ffrt_queue_attr_t** object must be created prior to an **ffrt_queue_t** object. 870* 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. 871* If **ffrt_queue_attr_t** is accessed after **ffrt_queue_attr_destroy** is called, undefined behavior may occur. 872 873##### Example 874See the example provided in **ffrt_queue_t**. 875 876#### ffrt_queue_t 877 878##### Declaration 879```{.c} 880typedef enum { ffrt_queue_serial, ffrt_queue_max } ffrt_queue_type_t; 881typedef void* ffrt_queue_t; 882 883ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr) 884void ffrt_queue_destroy(ffrt_queue_t queue) 885``` 886 887##### Parameters 888 889`type` 890 891Queue type. 892 893`name` 894 895Pointer to the queue name. 896 897`attr` 898 899Pointer to the queue attribute. For details, see **ffrt_queue_attr_t**. 900 901##### Return value 902Returns the queue created if the API is called successfully; returns a null pointer otherwise. 903 904##### Use guide 905* Tasks submitted to the queue are executed in sequence. If a task is blocked, the execution sequence of the task cannot be ensured. 906* 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. 907* If **ffrt_queue_t** is accessed after **ffrt_queue_destroy** is called, undefined behavior may occur. 908 909##### Example 910``` 911#include <stdio.h> 912#include "ffrt.h" 913 914using namespace std; 915 916template<class T> 917struct Function { 918 template<class CT> 919 Function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {} 920 ffrt_function_header_t header; 921 T closure; 922}; 923 924template<class T> 925void ExecFunctionWrapper(void* t) 926{ 927 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 928 f->closure(); 929} 930 931template<class T> 932void DestroyFunctionWrapper(void* t) 933{ 934 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 935 f->closure = nullptr; 936} 937 938template<class T> 939static inline ffrt_function_header_t* create_function_wrapper(T&& func, 940 ffrt_function_kind_t kind = ffrt_function_kind_general) 941{ 942 using function_type = Function<std::decay_t<T>>; 943 auto p = ffrt_alloc_auto_managed_function_storage_base(kind); 944 auto f = 945 new (p)function_type({ ExecFunctionWrapper<T>, DestroyFunctionWrapper<T>, { 0 } }, std::forward<T>(func)); 946 return reinterpret_cast<ffrt_function_header_t*>(f); 947} 948 949int main(int narg, char** argv) 950{ 951 ffrt_queue_attr_t queue_attr; 952 (void)ffrt_queue_attr_init(&queue_attr); 953 ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr); 954 955 ffrt_queue_submit(queue_handle, create_function_wrapper([]() {printf("Task done.\n");}, ffrt_function_kind_queue), nullptr); 956 957 ffrt_queue_attr_destroy(&queue_attr); 958 ffrt_queue_destroy(queue_handle); 959} 960``` 961### Synchronization Primitive 962 963#### ffrt_mutex_t 964 965Provides performance implementation similar to pthread mutex. 966 967##### Declaration 968 969```{.c} 970typedef enum { 971 ffrt_error = -1, 972 ffrt_success = 0, 973 ffrt_error_nomem = ENOMEM, 974 ffrt_error_timedout = ETIMEDOUT, 975 ffrt_error_busy = EBUSY, 976 ffrt_error_inval = EINVAL 977} ffrt_error_t; 978 979struct ffrt_mutex_t; 980 981int ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr); 982int ffrt_mutex_lock(ffrt_mutex_t* mutex); 983int ffrt_mutex_unlock(ffrt_mutex_t* mutex); 984int ffrt_mutex_trylock(ffrt_mutex_t* mutex); 985int ffrt_mutex_destroy(ffrt_mutex_t* mutex); 986``` 987 988##### Parameters 989 990`attr` 991 992Attribute of the mutex. Set it to a null pointer. This is because FFRT supports only mutex of the basic type currently. 993 994`mutex` 995 996Pointer to the target mutex. 997 998##### Return value 999 1000Returns **ffrt_success** if the API is called successfully; returns an error code otherwise. 1001 1002##### Use guide 1003* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1004* 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. 1005* Currently, recursion and timing are not supported. 1006* **ffrt_mutex_t** in the C code must be explicitly created and destroyed by calling **ffrt_mutex_init** and **ffrt_mutex_destroy**, respectively. 1007* 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. 1008* If **ffrt_mutex_t** is accessed after **ffrt_mutex_destroy** is called, undefined behavior may occur. 1009 1010##### Example 1011 1012```{.c} 1013#include <stdio.h> 1014#include "ffrt.h" 1015 1016typedef struct { 1017 int* sum; 1018 ffrt_mutex_t* mtx; 1019} tuple; 1020 1021void func(void* arg) 1022{ 1023 tuple* t = (tuple*)arg; 1024 1025 int ret = ffrt_mutex_lock(t->mtx); 1026 if (ret != ffrt_success) { 1027 printf("error\n"); 1028 } 1029 (*t->sum)++; 1030 ret = ffrt_mutex_unlock(t->mtx); 1031 if (ret != ffrt_success) { 1032 printf("error\n"); 1033 } 1034} 1035 1036typedef struct { 1037 ffrt_function_header_t header; 1038 ffrt_function_t func; 1039 ffrt_function_t after_func; 1040 void* arg; 1041} c_function; 1042 1043static void ffrt_exec_function_wrapper(void* t) 1044{ 1045 c_function* f = (c_function*)t; 1046 if (f->func) { 1047 f->func(f->arg); 1048 } 1049} 1050 1051static void ffrt_destroy_function_wrapper(void* t) 1052{ 1053 c_function* f = (c_function*)t; 1054 if (f->after_func) { 1055 f->after_func(f->arg); 1056 } 1057} 1058 1059#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1060static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1061 const ffrt_function_t after_func, void* arg) 1062{ 1063 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1064 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1065 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1066 f->header.exec = ffrt_exec_function_wrapper; 1067 f->header.destroy = ffrt_destroy_function_wrapper; 1068 f->func = func; 1069 f->after_func = after_func; 1070 f->arg = arg; 1071 return (ffrt_function_header_t*)f; 1072} 1073 1074static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1075 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1076{ 1077 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1078} 1079 1080void ffrt_mutex_task() 1081{ 1082 int sum = 0; 1083 ffrt_mutex_t mtx; 1084 tuple t = {&sum, &mtx}; 1085 int ret = ffrt_mutex_init(&mtx, NULL); 1086 if (ret != ffrt_success) { 1087 printf("error\n"); 1088 } 1089 for (int i = 0; i < 10; i++) { 1090 ffrt_submit_c(func, NULL, &t, NULL, NULL, NULL); 1091 } 1092 ffrt_mutex_destroy(&mtx); 1093 ffrt_wait(); 1094 printf("sum = %d", sum); 1095} 1096 1097int main(int narg, char** argv) 1098{ 1099 int r; 1100 ffrt_submit_c(ffrt_mutex_task, NULL, NULL, NULL, NULL, NULL); 1101 ffrt_wait(); 1102 return 0; 1103} 1104``` 1105 1106Expected output: 1107 1108``` 1109sum=10 1110``` 1111 1112This example is for reference only and is not encouraged in practice. 1113 1114 1115#### ffrt_cond_t 1116 1117Provides performance implementation similar to pthread semaphore. 1118 1119##### Declaration 1120 1121```{.c} 1122typedef enum { 1123 ffrt_error = -1, 1124 ffrt_success = 0, 1125 ffrt_error_nomem = ENOMEM, 1126 ffrt_error_timedout = ETIMEDOUT, 1127 ffrt_error_busy = EBUSY, 1128 ffrt_error_inval = EINVAL 1129} ffrt_error_t; 1130 1131struct ffrt_cond_t; 1132 1133int ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr); 1134int ffrt_cond_signal(ffrt_cond_t* cond); 1135int ffrt_cond_broadcast(ffrt_cond_t* cond); 1136int ffrt_cond_wait(ffrt_cond_t*cond, ffrt_mutex_t* mutex); 1137int ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point); 1138int ffrt_cond_destroy(ffrt_cond_t* cond); 1139``` 1140 1141##### Parameters 1142 1143`cond` 1144 1145Pointer to the target semaphore. 1146 1147`attr` 1148 1149Pointer to the attribute. A null pointer indicates that the default attribute is used. 1150 1151`mutex` 1152 1153Pointer to the target mutex. 1154 1155`time_point` 1156 1157Pointer to the maximum duration during which the thread is blocked. 1158 1159 1160##### Return value 1161 1162Returns **ffrt_success** if the API is successfully called; returns **ffrt_error_timedout** if the maximum duration is reached before the mutex is locked. 1163 1164##### Use guide 1165* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1166* 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. 1167* **ffrt_cond_t** in the C code must be explicitly created and destroyed by calling **ffrt_cond_init** and **ffrt_cond_destroy**, respectively. 1168* 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. 1169* If **ffrt_cond_t** is accessed after **ffrt_cond_destroy** is called, undefined behavior may occur. 1170 1171##### Example 1172 1173```{.c} 1174#include <stdio.h> 1175#include "ffrt.h" 1176 1177typedef struct { 1178 ffrt_cond_t* cond; 1179 int* a; 1180 ffrt_mutex_t* lock_; 1181} tuple; 1182 1183void func1(void* arg) 1184{ 1185 tuple* t = (tuple*)arg; 1186 int ret = ffrt_mutex_lock(t->lock_); 1187 if (ret != ffrt_success) { 1188 printf("error\n"); 1189 } 1190 while (*t->a != 1) { 1191 ret = ffrt_cond_wait(t->cond, t->lock_); 1192 if (ret != ffrt_success) { 1193 printf("error\n"); 1194 } 1195 } 1196 ret = ffrt_mutex_unlock(t->lock_); 1197 if (ret != ffrt_success) { 1198 printf("error\n"); 1199 } 1200 printf("a = %d", *(t->a)); 1201} 1202 1203void func2(void* arg) 1204{ 1205 tuple* t = (tuple*)arg; 1206 int ret = ffrt_mutex_lock(t->lock_); 1207 if (ret != ffrt_success) { 1208 printf("error\n"); 1209 } 1210 *(t->a) = 1; 1211 ret = ffrt_cond_signal(t->cond); 1212 if (ret != ffrt_success) { 1213 printf("error\n"); 1214 } 1215 ret = ffrt_mutex_unlock(t->lock_); 1216 if (ret != ffrt_success) { 1217 printf("error\n"); 1218 } 1219} 1220 1221typedef struct { 1222 ffrt_function_header_t header; 1223 ffrt_function_t func; 1224 ffrt_function_t after_func; 1225 void* arg; 1226} c_function; 1227 1228static void ffrt_exec_function_wrapper(void* t) 1229{ 1230 c_function* f = (c_function*)t; 1231 if (f->func) { 1232 f->func(f->arg); 1233 } 1234} 1235 1236static void ffrt_destroy_function_wrapper(void* t) 1237{ 1238 c_function* f = (c_function*)t; 1239 if (f->after_func) { 1240 f->after_func(f->arg); 1241 } 1242} 1243 1244#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1245static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1246 const ffrt_function_t after_func, void* arg) 1247{ 1248 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1249 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1250 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1251 f->header.exec = ffrt_exec_function_wrapper; 1252 f->header.destroy = ffrt_destroy_function_wrapper; 1253 f->func = func; 1254 f->after_func = after_func; 1255 f->arg = arg; 1256 return (ffrt_function_header_t*)f; 1257} 1258 1259static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1260 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1261{ 1262 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1263} 1264 1265void ffrt_cv_task() 1266{ 1267 ffrt_cond_t cond; 1268 int ret = ffrt_cond_init(&cond, NULL); 1269 if (ret != ffrt_success) { 1270 printf("error\n"); 1271 } 1272 int a = 0; 1273 ffrt_mutex_t lock_; 1274 tuple t = {&cond, &a, &lock_}; 1275 ret = ffrt_mutex_init(&lock_, NULL); 1276 if (ret != ffrt_success) { 1277 printf("error\n"); 1278 } 1279 ffrt_submit_c(func1, NULL, &t, NULL, NULL, NULL); 1280 ffrt_submit_c(func2, NULL, &t, NULL, NULL, NULL); 1281 ffrt_wait(); 1282 ffrt_cond_destroy(&cond); 1283 ffrt_mutex_destroy(&lock_); 1284} 1285 1286int main(int narg, char** argv) 1287{ 1288 ffrt_submit_c(ffrt_cv_task, NULL, NULL, NULL, NULL, NULL); 1289 ffrt_wait(); 1290 return 0; 1291} 1292``` 1293 1294Expected output: 1295 1296``` 1297a=1 1298``` 1299 1300This example is for reference only and is not encouraged in practice. 1301 1302### Miscellaneous 1303 1304#### ffrt_usleep 1305 1306Provides performance implementation similar to C11 sleep and Linux usleep. 1307 1308##### Declaration 1309 1310```{.c} 1311int ffrt_usleep(uint64_t usec); 1312``` 1313 1314##### Parameters 1315 1316`usec` 1317 1318Duration that the calling thread is suspended, in μs. 1319 1320##### Return value 1321 1322N/A 1323 1324##### Use guide 1325* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1326* The traditional function **sleep** may cause unexpected kernel mode trap. **ffrt_usleep** solves this problem and therefore provides better performance if used properly. 1327 1328##### Example 1329 1330```{.c} 1331#include <time.h> 1332#include <stdio.h> 1333#include "ffrt.h" 1334 1335void func(void* arg) 1336{ 1337 printf("Time: %s", ctime(&(time_t){time(NULL)})); 1338 ffrt_usleep(2000000); // Suspend for 2 seconds 1339 printf("Time: %s", ctime(&(time_t){time(NULL)})); 1340} 1341 1342typedef struct { 1343 ffrt_function_header_t header; 1344 ffrt_function_t func; 1345 ffrt_function_t after_func; 1346 void* arg; 1347} c_function; 1348 1349static void ffrt_exec_function_wrapper(void* t) 1350{ 1351 c_function* f = (c_function*)t; 1352 if (f->func) { 1353 f->func(f->arg); 1354 } 1355} 1356 1357static void ffrt_destroy_function_wrapper(void* t) 1358{ 1359 c_function* f = (c_function*)t; 1360 if (f->after_func) { 1361 f->after_func(f->arg); 1362 } 1363} 1364 1365#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1366static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1367 const ffrt_function_t after_func, void* arg) 1368{ 1369 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1370 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1371 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1372 f->header.exec = ffrt_exec_function_wrapper; 1373 f->header.destroy = ffrt_destroy_function_wrapper; 1374 f->func = func; 1375 f->after_func = after_func; 1376 f->arg = arg; 1377 return (ffrt_function_header_t*)f; 1378} 1379 1380static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1381 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1382{ 1383 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1384} 1385 1386int main(int narg, char** argv) 1387{ 1388 ffrt_submit_c(func, NULL, NULL, NULL, NULL, NULL); 1389 ffrt_wait(); 1390 return 0; 1391} 1392``` 1393 1394#### ffrt_yield 1395 1396Passes control to other tasks so that they can be executed. If there is no other task that can be executed, this API is invalid. 1397 1398##### Declaration 1399 1400```{.c} 1401void ffrt_yield(); 1402``` 1403 1404##### Parameters 1405 1406N/A 1407 1408##### Return value 1409 1410N/A 1411 1412##### Use guide 1413* This API can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur. 1414* The exact behavior of this API depends on the implementation, especially the mechanism and system state of the FFRT scheduler in use. 1415 1416##### Example 1417 1418N/A 1419 1420 1421## How to Develop 1422 1423The following describes how to use the native APIs provided by FFRT to create parallel tasks and serial queue tasks and destroy corresponding resources. 1424 1425**Adding Dynamic Link Libraries** 1426 1427Add the following libraries to **CMakeLists.txt**. 1428```txt 1429libffrt.z.so 1430``` 1431 1432**Including Header Files** 1433```c++ 1434#include "ffrt/task.h" 1435#include "ffrt/type_def.h" 1436#include "ffrt/condition_variable.h" 1437#include "ffrt/mutex.h" 1438#include "ffrt/queue.h" 1439#include "ffrt/sleep.h" 1440``` 1441 14421. **Encapsulate the function to be executed.** 1443 ```c++ 1444 // Method 1: Use the template. C++ is supported. 1445 template<class T> 1446 struct Function { 1447 template<class CT> 1448 Function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {} 1449 ffrt_function_header_t header; 1450 T closure; 1451 }; 1452 1453 template<class T> 1454 void ExecFunctionWrapper(void* t) 1455 { 1456 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 1457 f->closure(); 1458 } 1459 1460 template<class T> 1461 void DestroyFunctionWrapper(void* t) 1462 { 1463 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 1464 f->closure = nullptr; 1465 } 1466 1467 template<class T> 1468 static inline ffrt_function_header_t* create_function_wrapper(T&& func, 1469 ffrt_function_kind_t kind = ffrt_function_kind_general) 1470 { 1471 using function_type = Function<std::decay_t<T>>; 1472 auto p = ffrt_alloc_auto_managed_function_storage_base(kind); 1473 auto f = 1474 new (p)function_type({ ExecFunctionWrapper<T>, DestroyFunctionWrapper<T>, { 0 } }, std::forward<T>(func)); 1475 return reinterpret_cast<ffrt_function_header_t*>(f); 1476 } 1477 1478 // Method 2 1479 typedef struct { 1480 ffrt_function_header_t header; 1481 ffrt_function_t func; 1482 ffrt_function_t after_func; 1483 void* arg; 1484 } CFunction; 1485 1486 static void FfrtExecFunctionWrapper(void* t) 1487 { 1488 CFunction* f = static_cast<CFunction*>(t); 1489 if (f->func) { 1490 f->func(f->arg); 1491 } 1492 } 1493 1494 static void FfrtDestroyFunctionWrapper(void* t) 1495 { 1496 CFunction* f = static_cast<CFunction*>(t); 1497 if (f->after_func) { 1498 f->after_func(f->arg); 1499 } 1500 } 1501 1502 #define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1503 static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1504 const ffrt_function_t after_func, void* arg, ffrt_function_kind_t kind_t = ffrt_function_kind_general) 1505 { 1506 FFRT_STATIC_ASSERT(sizeof(CFunction) <= ffrt_auto_managed_function_storage_size, 1507 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1508 CFunction* f = static_cast<CFunction*>(ffrt_alloc_auto_managed_function_storage_base(kind_t)); 1509 f->header.exec = FfrtExecFunctionWrapper; 1510 f->header.destroy = FfrtDestroyFunctionWrapper; 1511 f->func = func; 1512 f->after_func = after_func; 1513 f->arg = arg; 1514 return reinterpret_cast<ffrt_function_header_t*>(f); 1515 } 1516 1517 // Example: function to be submitted for execution. 1518 void OnePlusForTest(void* arg) 1519 { 1520 (*static_cast<int*>(arg)) += 1; 1521 } 1522 ``` 1523 15242. **Set the task attributes.** 1525 1526 Set the task attributes, including the QoS and task name, before submitting the task. 1527 ```c++ 1528 // ******Initialize the attributes of the parallel task****** 1529 ffrt_task_attr_t attr; 1530 ffrt_task_attr_init(&attr); 1531 1532 // ******Create a serial queue****** 1533 1534 // Create the attributes of the serial queue. 1535 ffrt_queue_attr_t queue_attr; 1536 // Create the handle of the serial queue. 1537 ffrt_queue_t queue_handle; 1538 1539 // Initialize the queue attribute. 1540 (void)ffrt_queue_attr_init(&queue_attr); 1541 1542 // Set the QoS if necessary. 1543 ffrt_queue_attr_set_qos(&queue_attr, static_cast<ffrt_qos_t>(ffrt_qos_inherit)); 1544 // Set the timeout period (ms) if necessary. 1545 ffrt_queue_attr_set_timeout(&queue_attr, 10000); 1546 // Set the timeout callback if necessary. 1547 int x = 0; 1548 ffrt_queue_attr_set_callback(&queue_attr, ffrt_create_function_wrapper(OnePlusForTest, NULL, &x, 1549 ffrt_function_kind_queue)); 1550 1551 // Initialize the queue based on the attributes. 1552 queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr); 1553 ``` 1554 15553. **Submit the task.** 1556 ```c++ 1557 int a = 0; 1558 // ******Parallel task****** 1559 // Submit the parallel task without obtaining a handle. 1560 ffrt_submit_base(ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 1561 // Submit the parallel task and obtain a handle. 1562 ffrt_task_handle_t task = ffrt_submit_h_base( 1563 ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 1564 1565 // ******Serial queue task****** 1566 // Submit the serial queue task without obtaining a handle. 1567 ffrt_queue_submit(queue_handle, ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, 1568 ffrt_function_kind_queue), nullptr); 1569 // Submit the serial queue task and obtain a handle. 1570 ffrt_task_handle_t handle = ffrt_queue_submit_h(queue_handle, 1571 ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, ffrt_function_kind_queue), nullptr); 1572 1573 // Call wait if you need to wait for the execution result. 1574 const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}}; 1575 ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 1576 ffrt_wait_deps(&wait); 1577 1578 ffrt_queue_wait(handle); 1579 ``` 1580 15814. **Destroy the resources after the task is submitted.** 1582 ```c++ 1583 // ******Destroy the parallel task****** 1584 ffrt_task_attr_destroy(&attr); 1585 ffrt_task_handle_destroy(task); 1586 1587 // ******Destroy the serial queue task****** 1588 // Destroy the task handle and then the queue. 1589 ffrt_queue_attr_destroy(&queue_attr); 1590 ffrt_task_handle_destroy(handle); 1591 ffrt_queue_destroy(queue_handle); 1592 ``` 1593 1594## Suggestions 1595 1596### Suggestion 1: Functional programming 1597 1598Basic idea: Use functional programming for the calculation process. 1599 1600* Use pure functions and encapsulate them to express each step of the process. 1601* There is no global data access. 1602* There is no internal state reserved. 1603* Use **ffrt_submit_base()** to submit a function in asynchronous mode for execution. 1604* 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. 1605* Use **inDeps** and **outDeps** to specify the dependency between tasks to ensure the correctness of program execution. 1606 1607> **NOTE** 1608> 1609> Using pure functions helps you maximize the parallelism and avoid data races and lock abuse. 1610 1611In practice, you may not use pure functions in certain scenarios, with the following prerequisites: 1612 1613* **in_deps** and **out_deps** can ensure the correctness of program execution. 1614* The lock mechanism provided by FFRT is used to protect access to global variables. 1615 1616 1617### Suggestion 2: Use FFRT APIs 1618 1619* 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. 1620* Use the lock, condition variable, sleep, and I/O APIs provided by FFRT to replace the APIs of the system thread library. 1621* Using the APIs of the system thread library may block worker threads and result in extra performance overhead. 1622 1623### Suggestion 3: Deadline mechanism 1624 1625* Use FFRT APIs in processing flows that feature periodic/repeated execution. 1626* Use FFRT APIs in processing flows with clear time constraints and is performance critical. 1627* Use FFRT APIs in relatively large-granularity processing flows, such as the frame processing flow with the 16.6 ms time constraint. 1628 1629### Suggestion 4: Migration from the thread model 1630 1631* Create a thread instead of creating an FFRT task. 1632* A thread is logically similar to a task without **in_deps**. 1633* Identify the dependency between threads and express the dependencies in **in_deps** or **out_deps** of the task. 1634* Decompose an intra-thread computing process into asynchronous tasks for invoking. 1635* Use the task dependency and lock mechanism to avoid data races of concurrent tasks. 1636 1637## Restrictions 1638 1639After an FFRT object is initialized in the C code, you are responsible for setting the object to null or destroying the object. 1640 1641To 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. 1642 1643Noncompliant example 1: Repeated calling of **destroy()** may cause unpredictable data damage. 1644 1645```{.c} 1646#include "ffrt.h" 1647void abnormal_case_1() 1648{ 1649 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1650 ... 1651 ffrt_task_handle_destroy(h); 1652 ffrt_task_handle_destroy(h); // double free 1653} 1654``` 1655 1656Noncompliant example 2: A memory leak occurs if **destroy()** is not called. 1657 1658```{.c} 1659#include "ffrt.h" 1660void abnormal_case_2() 1661{ 1662 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1663 ... 1664 // Memory leak 1665} 1666``` 1667 1668Recommended example: Call **destroy()** only once; set the object to null if necessary. 1669 1670```{.c} 1671#include "ffrt.h" 1672void normal_case() 1673{ 1674 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1675 ... 1676 ffrt_task_handle_destroy(h); 1677 h = nullptr; // if necessary 1678} 1679``` 1680