1e41f4b71Sopenharmony_ci# Introduction to Function Flow Runtime Kit
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ci## Introduction
4e41f4b71Sopenharmony_ci
5e41f4b71Sopenharmony_ciFunction Flow Runtime Kit provides a concurrent programming framework that allows you to develop an application by creating tasks and describing their dependencies. It supports data dependency management, task executor, and system event processing. It adopts coroutine-based task execution to support more concurrent tasks and improve the thread usage with fewer system threads. It leverages the computing resources of the multi-core platform to ensure intensive resource management. It solves the problem of thread resource abuse and provides the ultimate user experience.
6e41f4b71Sopenharmony_ci
7e41f4b71Sopenharmony_ci## Basic Concepts
8e41f4b71Sopenharmony_ci
9e41f4b71Sopenharmony_ciFunction 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.
10e41f4b71Sopenharmony_ci
11e41f4b71Sopenharmony_ci### Programming Models
12e41f4b71Sopenharmony_ci
13e41f4b71Sopenharmony_ci| Item | Thread Programming Model                                                | FFRT Programming Model                                            |
14e41f4b71Sopenharmony_ci| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
15e41f4b71Sopenharmony_ci| 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.|
16e41f4b71Sopenharmony_ci| 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.|
17e41f4b71Sopenharmony_ci| 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.|
18e41f4b71Sopenharmony_ci| 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.|
19e41f4b71Sopenharmony_ci| 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.|
20e41f4b71Sopenharmony_ci
21e41f4b71Sopenharmony_ci
22e41f4b71Sopenharmony_ci
23e41f4b71Sopenharmony_ci### Function Flow
24e41f4b71Sopenharmony_ci
25e41f4b71Sopenharmony_ciThe 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.
26e41f4b71Sopenharmony_ci
27e41f4b71Sopenharmony_ci#### Task-based
28e41f4b71Sopenharmony_ci
29e41f4b71Sopenharmony_ciTask-based means that you can use tasks to express an application and schedule the tasks at runtime.
30e41f4b71Sopenharmony_ci
31e41f4b71Sopenharmony_ciA 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.
32e41f4b71Sopenharmony_ci
33e41f4b71Sopenharmony_ciTasks in the Function Flow programming model have the following features:
34e41f4b71Sopenharmony_ci
35e41f4b71Sopenharmony_ci- The dependency between tasks can be specified in data-driven form.
36e41f4b71Sopenharmony_ci- 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.
37e41f4b71Sopenharmony_ci- Simultaneous operations, such as wait, lock, and condition variables, are supported.
38e41f4b71Sopenharmony_ci
39e41f4b71Sopenharmony_ci> **NOTE**
40e41f4b71Sopenharmony_ci>
41e41f4b71Sopenharmony_ci> 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.
42e41f4b71Sopenharmony_ci
43e41f4b71Sopenharmony_ci#### Data-driven
44e41f4b71Sopenharmony_ci
45e41f4b71Sopenharmony_ciData-driven means that the dependency between tasks is expressed through data dependencies.
46e41f4b71Sopenharmony_ci
47e41f4b71Sopenharmony_ciData 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.
48e41f4b71Sopenharmony_ci
49e41f4b71Sopenharmony_ciData 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.
50e41f4b71Sopenharmony_ci
51e41f4b71Sopenharmony_ciA 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.
52e41f4b71Sopenharmony_ci
53e41f4b71Sopenharmony_ciWhen 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.
54e41f4b71Sopenharmony_ci
55e41f4b71Sopenharmony_ciWith 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:
56e41f4b71Sopenharmony_ci
57e41f4b71Sopenharmony_ci- Producer-Consumer dependency
58e41f4b71Sopenharmony_ci
59e41f4b71Sopenharmony_ci  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.
60e41f4b71Sopenharmony_ci
61e41f4b71Sopenharmony_ci- Consumer-Producer dependency
62e41f4b71Sopenharmony_ci
63e41f4b71Sopenharmony_ci  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.
64e41f4b71Sopenharmony_ci
65e41f4b71Sopenharmony_ci- Producer-Producer dependency
66e41f4b71Sopenharmony_ci
67e41f4b71Sopenharmony_ci  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.
68e41f4b71Sopenharmony_ci
69e41f4b71Sopenharmony_ci
70e41f4b71Sopenharmony_ciAssume that the relationship between some tasks and data A is as follows:
71e41f4b71Sopenharmony_ci```{.cpp}
72e41f4b71Sopenharmony_citask1(OUT A);
73e41f4b71Sopenharmony_citask2(IN A);
74e41f4b71Sopenharmony_citask3(IN A);
75e41f4b71Sopenharmony_citask4(OUT A);
76e41f4b71Sopenharmony_citask5(OUT A);
77e41f4b71Sopenharmony_ci```
78e41f4b71Sopenharmony_ci
79e41f4b71Sopenharmony_ci<img src="figures/ffrtfigure3.png" style="zoom:60%" />
80e41f4b71Sopenharmony_ci
81e41f4b71Sopenharmony_ci> **NOTE**
82e41f4b71Sopenharmony_ci>
83e41f4b71Sopenharmony_ci> For ease of description, circles are used to represent tasks and squares are used to represent data.
84e41f4b71Sopenharmony_ci
85e41f4b71Sopenharmony_ciThe following conclusions can be drawn:
86e41f4b71Sopenharmony_ci- task1 and task2/task3 form a producer-consumer dependency. This means that task2/task3 can read data A only after task1 writes data A.
87e41f4b71Sopenharmony_ci- task2/task3 and task4 form a consumer-producer dependency. This means that task4 can write data A only after task2/task3 reads data A.
88e41f4b71Sopenharmony_ci- 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.
89e41f4b71Sopenharmony_ci
90e41f4b71Sopenharmony_ci## Constraints
91e41f4b71Sopenharmony_ci
92e41f4b71Sopenharmony_ci### The **thread_local** variable is not supported.
93e41f4b71Sopenharmony_ci
94e41f4b71Sopenharmony_ciThe behavior of creating the **thread_local** variable in a task or transferring it between tasks is uncertain. The reason is that FFRT does not have the concept of thread. It only has the concept of task.
95e41f4b71Sopenharmony_ci
96e41f4b71Sopenharmony_ciIn C++ semantics, the **thread_local** variable can be compiled, but it is uncertain the thread on which the task that uses the variable is executed.
97e41f4b71Sopenharmony_ci
98e41f4b71Sopenharmony_ciFor non-worker threads in FFRT processes, the behavior of the **thread_local** variable is not affected by FFRT.
99e41f4b71Sopenharmony_ci
100e41f4b71Sopenharmony_ci> Similar problems occur for thread-bound features such as thread_idx, pthread_specific, recursive lock, thread priority, thread affinity, and recursive lock.
101e41f4b71Sopenharmony_ci>
102e41f4b71Sopenharmony_ci> Do not use these features. If these features are required, use **task local** of FFRT instead.
103e41f4b71Sopenharmony_ci
104e41f4b71Sopenharmony_ci### Do not use FFRT in forked subprocesses.
105e41f4b71Sopenharmony_ci
106e41f4b71Sopenharmony_ci### Deploy FFRT in dynamic library mode.
107e41f4b71Sopenharmony_ci
108e41f4b71Sopenharmony_ciStatic library deployment may cause multi-instance problems. For example, when multiple .so files loaded by the same process use FFRT in static library mode, FFRT is instantiated into multiple copies, and their behavior is unknown.
109e41f4b71Sopenharmony_ci
110e41f4b71Sopenharmony_ci### After an FFRT object is initialized in the C code, you are responsible for setting the object to null or destroying the object.
111e41f4b71Sopenharmony_ci
112e41f4b71Sopenharmony_ciTo 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.
113e41f4b71Sopenharmony_ci
114e41f4b71Sopenharmony_ciNoncompliant example 1: Repeated calling of **destroy()** may cause unpredictable data damage.
115e41f4b71Sopenharmony_ci
116e41f4b71Sopenharmony_ci```{.cpp}
117e41f4b71Sopenharmony_ci#include "ffrt.h"
118e41f4b71Sopenharmony_civoid abnormal_case_1()
119e41f4b71Sopenharmony_ci{
120e41f4b71Sopenharmony_ci    ffrt_task_handle_t h = ffrt_submit_h([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
121e41f4b71Sopenharmony_ci    ...
122e41f4b71Sopenharmony_ci    ffrt_task_handle_destroy(h);
123e41f4b71Sopenharmony_ci    ffrt_task_handle_destroy(h); // double free
124e41f4b71Sopenharmony_ci}
125e41f4b71Sopenharmony_ci```
126e41f4b71Sopenharmony_ci
127e41f4b71Sopenharmony_ciNoncompliant example 2: A memory leak occurs if **destroy()** is not called.
128e41f4b71Sopenharmony_ci
129e41f4b71Sopenharmony_ci```{.cpp}
130e41f4b71Sopenharmony_ci#include "ffrt.h"
131e41f4b71Sopenharmony_civoid abnormal_case_2()
132e41f4b71Sopenharmony_ci{
133e41f4b71Sopenharmony_ci    ffrt_task_handle_t h = ffrt_submit_h([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
134e41f4b71Sopenharmony_ci    ...
135e41f4b71Sopenharmony_ci    // Memory leak
136e41f4b71Sopenharmony_ci}
137e41f4b71Sopenharmony_ci```
138e41f4b71Sopenharmony_ci
139e41f4b71Sopenharmony_ciRecommended example: Call **destroy()** only once; set the object to null if necessary.
140e41f4b71Sopenharmony_ci
141e41f4b71Sopenharmony_ci```{.cpp}
142e41f4b71Sopenharmony_ci#include "ffrt.h"
143e41f4b71Sopenharmony_civoid normal_case()
144e41f4b71Sopenharmony_ci{
145e41f4b71Sopenharmony_ci    ffrt_task_handle_t h = ffrt_submit_h([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
146e41f4b71Sopenharmony_ci    ...
147e41f4b71Sopenharmony_ci    ffrt_task_handle_destroy(h);
148e41f4b71Sopenharmony_ci    h = nullptr; // if necessary
149e41f4b71Sopenharmony_ci}
150e41f4b71Sopenharmony_ci```
151e41f4b71Sopenharmony_ci
152e41f4b71Sopenharmony_ci### The number of input and output dependencies is limited.
153e41f4b71Sopenharmony_ci
154e41f4b71Sopenharmony_ciFor **submit()**, the total number of input dependencies and output dependencies of each task cannot exceed 8.
155e41f4b71Sopenharmony_ci
156e41f4b71Sopenharmony_ciFor **submit_h()**, the total number of input dependencies and output dependencies of each task cannot exceed 7.
157e41f4b71Sopenharmony_ci
158e41f4b71Sopenharmony_ciWhen a parameter is used as both an input dependency and an output dependency, it is counted as one dependency. For example, if the input dependency is {&x} and the output dependency is also {&x}, then the number of dependencies is 1.
159e41f4b71Sopenharmony_ci
160e41f4b71Sopenharmony_ci### It is recommended that FFRT mutex be used in FFRT task context.
161e41f4b71Sopenharmony_ciFFRT provides performance implementation similar to **std::mutex**. FFRT mutex can be called only inside an FFRT task. If it is called outside an FFRT task, undefined behavior may occur.
162e41f4b71Sopenharmony_ci
163e41f4b71Sopenharmony_ci**std::mutex** may cause unexpected kernel mode trap when it fails to lock a mutex. FFRT mutex solves this problem and therefore provides better performance if used properly.
164e41f4b71Sopenharmony_ci
165e41f4b71Sopenharmony_ciFFRT supports a maximum of eight worker threads. When eight tasks use **std::mutex** at the same time, deadlock occurs on all threads during coroutine switchover. Therefore, exercise caution when using **std::mutex**.