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