• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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