• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# FFRT 开发指导
2
3## 场景介绍
4
5Function Flow编程模型是一种基于任务和数据驱动的并发编程模型,允许开发者通过任务及其依赖关系描述的方式进行应用开发。FFRT(Function Flow运行时)是支持Function Flow编程模型的软件运行时库,用于调度执行开发者基于Function Flow编程模型开发的应用。通过Function Flow编程模型和FFRT,开发者可专注于应用功能开发,由FFRT在运行时根据任务依赖状态和可用执行资源自动并发调度和执行任务。
6
7本文用于指导开发者基于Function Flow编程模型和FFRT实现并行编程。
8
9### 两种编程模型
10
11
12|                | 线程编程模型                                                 | FFRT任务编程模型                                             |
13| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
14| 并行度挖掘方式 | 程序员通过创建多线程并把任务分配到每个线程中执行来挖掘运行时的并行度。 | 程序员(编译器工具或语言特性配合)静态编程时将应用分解成任务及其数据依赖关系,运行时调度器分配任务到工作线程执行。 |
15| 谁负责线程创建 | 程序员负责创建线程,线程编程模型无法约束线程的创建,滥用可能造成系统中大量线程。| 由调度器负责工作线程池的创建和管理,程序员无法直接创建线程。 |
16| 负载均衡       | 程序员静态编程时将任务映射到线程,映射不合理或任务执行时间不确定造成线程负载不均。 | FFRT运行时根据线程执行状态调度就绪任务到空闲线程执行,减轻了线程负载不均问题。 |
17| 调度开销       | 线程调度由内核态调度器完成,调度开销大。                       | FFRT运行时在用户态以协程方式调度执行,相比内核线程调度机制更为轻量,减小调度的开销,并可通过硬化调度卸载进一步减小调度开销。 |
18| 依赖表达       | 线程创建时即处于可执行状态,执行时与其他线程同步操作,增加线程切换。 | FFRT运行时根据任务创建时显式表达的输入依赖和输出依赖关系判断任务可执行状态,当输入依赖不满足时,任务不被调度执行。 |
19
20## 基本概念
21
22### Function Flow 任务编程模型
23
24Function Flow编程模型允许开发者通过任务及其依赖关系描述的方式进行应用开发,其主要特性包括`Task-Based` 和 `Data-Driven` 。
25
26#### Task-Based 特性
27
28`Task-Based` 指在Function Flow编程模型中开发者以任务方式来组织应用程序表达,运行时以任务粒度执行调度。
29
30任务定义为一种面向开发者的编程线索和面向运行时的执行对象,通常包含一组指令序列及其操作的数据上下文环境。
31
32Function Flow编程模型中的任务包含以下主要特征:
33
34- 任务之间可指定依赖关系,依赖关系通过`Data-Driven`方式表达。
35- 任务可支持嵌套,即任务在执行过程中可生成新的任务下发给运行时,形成父子任务关系。
36- 多任务支持互同步操作,例如等待、锁、条件变量等。
37
38> 注意
39>
40> 任务颗粒度影响应用执行性能,颗粒度过小增加调度开销,颗粒度过大降低并行度。Function Flow编程模型中任务的目标颗粒度最小为100us量级,开发者应注意合理控制任务颗粒度。
41
42#### Data-Driven 特性
43
44`Data-Driven`指任务之间的依赖关系通过数据依赖表达。
45
46任务执行过程中对其关联的数据对象进行读写操作。在Function Flow编程模型中,数据对象表达抽象为数据签名,每个数据签名唯一对应一个数据对象。
47
48数据依赖抽象为任务所操作的数据对象的数据签名列表,包括输入数据依赖`in_deps`和输出数据依赖`out_deps`。数据对象的签名出现在一个任务的`in_deps`中时,该任务称为数据对象的消费者任务,消费者任务执行不改变其输入数据对象的内容;数据对象的签名出现在任务的`out_deps`中时,该任务称为数据对象的生产者任务,生产者任务执行改变其输出数据对象的内容,从而生成该数据对象的一个新的版本。
49
50一个数据对象可能存在多个版本,每个版本对应一个生产者任务和零个,一个或多个消费者任务,根据生产者任务和消费者任务的下发顺序定义数据对象的多个版本的顺序以及每个版本所对应的生产者和消费者任务。
51
52数据依赖解除的任务进入就绪状态允许被调度执行,依赖解除状态指任务所有输入数据对象版本的生产者任务执行完成,且所有输出数据对象版本的所有消费者任务执行完成的状态。
53
54通过上述`Data-Driven`的数据依赖表达,FFRT在运行时可动态构建任务之间的基于生产者/消费者的数据依赖关系并遵循任务数据依赖状态执行调度,包括:
55
56- Producer-Consumer 依赖
57
58  一个数据对象版本的生产者任务和该数据对象版本的消费者任务之间形成的依赖关系,也称为Read-after-Write依赖。
59
60- Consumer-Producer 依赖
61
62  一个数据对象版本的消费者任务和该数据对象的下一个版本的生产者任务之间形成的依赖关系,也称为Write-after-Read依赖。
63
64- Producer-Producer 依赖
65
66  一个数据对象版本的生产者任务和该数据对象的下一个版本的生产者任务之间形成的依赖关系,也称为Write-after-Write依赖。
67
68
69例如,如果有这么一些任务,与数据A的关系表述为:
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> 为表述方便,本文中的数据流图均以圆圈表示 Task,方块表示数据。
81
82可以得出以下结论:
83- task1 与task2/task3 构成Producer-Consumer 依赖,即:task2/task3 需要等到task1 写完A之后才能读A。
84- task2/task3 与task4 构成Consumer-Producer 依赖,即:task4 需要等到task2/task3 读完A之后才能写A。
85- task4 与task5 构成Producer-Producer 依赖,即:task5 需要等到task4 写完A之后才能写A。
86
87## 接口说明
88
89| 接口名                                                       | 描述                                                         |
90| ------------------------------------------------------------ | ------------------------------------------------------------ |
91| ffrt_condattr_init (ffrt_condattr_t* attr) | 初始化条件变量属性。 |
92| ffrt_condattr_destroy(ffrt_condattr_t* attr)  | 销毁条件变量属性。 |
93| ffrt_condattr_setclock(ffrt_condattr_t* attr, ffrt_clockid_t clock) | 设置条件变量的时钟属性。 |
94| ffrt_condattr_getclock(const ffrt_condattr_t* attr, ffrt_clockid_t* clock)        | 获取条件变量的时钟属性。              |
95| ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr)   | 初始化条件变量。      |
96| ffrt_cond_signal(ffrt_cond_t* cond)         | 唤醒阻塞在条件变量上的一个任务。 |
97| ffrt_cond_broadcast(ffrt_cond_t* cond) | 唤醒阻塞在条件变量上的所有任务。 |
98| ffrt_cond_wait(ffrt_cond_t* cond, ffrt_mutex_t* mutex)            | 条件变量等待函数,条件变量不满足时阻塞当前任务。 |
99| ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point)            | 条件变量超时等待函数,条件变量不满足时阻塞当前任务,超时等待返回。 |
100| ffrt_cond_destroy(ffrt_cond_t* cond)            | 销毁条件变量。 |
101| ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr) | 初始化mutex。 |
102| ffrt_mutex_lock(ffrt_mutex_t* mutex)   | 获取mutex。 |
103| ffrt_mutex_unlock(ffrt_mutex_t* mutex)  | 释放mutex。 |
104| ffrt_mutex_trylock(ffrt_mutex_t* mutex)   | 尝试获取mutex。 |
105| ffrt_mutex_destroy(ffrt_mutex_t* mutex)   | 销毁mutex。 |
106| ffrt_queue_attr_init(ffrt_queue_attr_t* attr)    | 初始化串行队列属性。 |
107| ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr)    | 销毁串行队列属性。 |
108| ffrt_queue_attr_set_qos(ffrt_queue_attr_t* attr, ffrt_qos_t qos)    | 设置串行队列qos属性。 |
109| ffrt_queue_attr_get_qos(const ffrt_queue_attr_t* attr)      | 获取串行队列qos属性。 |
110| ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr)   | 创建队列。 |
111| ffrt_queue_destroy(ffrt_queue_t queue)   | 销毁队列。 |
112| ffrt_queue_submit(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr)   | 提交一个任务到队列中调度执行。 |
113| ffrt_queue_submit_h(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr)  | 提交一个任务到队列中调度执行,并返回任务句柄。 |
114| ffrt_queue_wait(ffrt_task_handle_t handle)    | 等待队列中一个任务执行完成。 |
115| ffrt_queue_cancel(ffrt_task_handle_t handle)     | 取消队列中一个任务。 |
116| ffrt_usleep(uint64_t usec)   | 延迟usec微秒。 |
117| ffrt_yield(void)     | 当前任务主动放权,让其他任务有机会调度执行。 |
118| ffrt_task_attr_init(ffrt_task_attr_t* attr)     | 初始化任务属性。 |
119| ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name)   | 设置任务名字。 |
120| ffrt_task_attr_get_name(const ffrt_task_attr_t* attr)   | 获取任务名字。 |
121| ffrt_task_attr_destroy(ffrt_task_attr_t* attr)    | 销毁任务属性。 |
122| ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos)    | 设置任务qos。 |
123| ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr)      | 获取任务qos。 |
124| ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us)    | 设置任务延迟时间。 |
125| ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr)      | 获取任务延迟时间。 |
126| ffrt_this_task_update_qos(ffrt_qos_t qos)    | 更新任务qos。 |
127| ffrt_this_task_get_id(void)    | 获取任务id。 |
128| ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind)     | 申请函数执行结构的内存。 |
129| 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)   | 提交任务调度执行。 |
130| 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)    | 提交任务调度执行并返回任务句柄。 |
131| ffrt_task_handle_destroy(ffrt_task_handle_t handle)    | 销毁任务句柄。 |
132| ffrt_skip(ffrt_task_handle_t handle)     | 跳过指定任务。 |
133| ffrt_wait_deps(const ffrt_deps_t* deps)    | 等待依赖的任务完成,当前任务开始执行。 |
134
135
136
137## 函数介绍
138
139
140### 任务管理
141
142#### ffrt_submit_base
143
144* 该接口为ffrt动态库的导出接口,基于此可以封装出C API ffrt_submit,满足二进制兼容。
145
146##### 声明
147
148```{.c}
149const int ffrt_auto_managed_function_storage_size = 64 + sizeof(ffrt_function_header_t);
150typedef enum {
151    ffrt_function_kind_general,
152    ffrt_function_kind_queue
153} ffrt_function_kind_t;
154
155void* ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind);
156
157typedef void(*ffrt_function_t)(void*);
158typedef struct {
159    ffrt_function_t exec;
160    ffrt_function_t destroy;
161    uint64_t reserve[2];
162} ffrt_function_header_t;
163
164void 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);
165```
166
167##### 参数
168
169`kind`
170
171* function子类型,用于优化内部数据结构,默认使用ffrt_function_kind_general类型。
172
173`func`
174
175* CPU Function的指针,该指针执行的数据结构,按照`ffrt_function_header_t`定义的描述了该CPU Task如何执行和销毁的函数指针,FFRT通过这两个函数指针完成Task的执行和销毁。
176
177`in_deps`
178
179* 该参数是可选的。
180* 该参数用于描述该任务的输入依赖,FFRT 通过数据的虚拟地址作为数据的Signature 来建立依赖。
181
182`out_deps`
183
184* 该参数是可选的。
185* 该参数用于描述该任务的输出依赖。
186* `注意`:该依赖值本质上是一个数值,ffrt没办法区分该值是合理的还是不合理的,会假定输入的值是合理的进行处理;但不建议采用NULL,1, 2 等值来建立依赖关系,建议采用实际的内存地址,因为前者使用不当会建立起不必要的依赖,影响并发。
187
188`attr`
189
190* 该参数是可选的。
191* 该参数用于描述Task 的属性,比如qos 等,详见 [ffrt_task_attr_t](#ffrt_task_attr_t)章节。
192
193##### 返回值
194
195* 不涉及。
196
197##### 描述
198* 建议用户对ffrt_submit_base进行封装后调用,具体可参考样例。
199* **ffrt_submit_base作为底层能力,使用时需要满足以下限制:**
200  * ffrt_submit_base入参中的func指针只能通过ffrt_alloc_auto_managed_function_storage_base申请,且二者的调用需一一对应。
201  * ffrt_alloc_auto_managed_function_storage_base申请的内存为ffrt_auto_managed_function_storage_size字节,其生命周期归ffrt管理,在该task结束时,由FFRT自动释放,用户无需释放。
202* ffrt_function_header_t 中定义了两个函数指针:
203  * exec:用于描述该Task如何被执行,当FFRT需要执行该Task时由FFRT调用。
204  * destroy:用于描述该Task如何被销毁,当FFRT需要销毁该Task时由FFRT调用。
205
206##### 样例
207
208
209```{.c}
210template<class T>
211struct function {
212    template<class CT>
213    function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {}
214    ffrt_function_header_t header;
215    T closure;
216};
217
218template<class T>
219void exec_function_wrapper(void* t)
220{
221    auto f = (function<std::decay_t<T>>*)t;
222    f->closure();
223}
224
225template<class T>
226void destroy_function_wrapper(void* t)
227{
228    auto f = (function<std::decay_t<T>>*)t;
229    f->closure = nullptr;
230}
231
232template<class T>
233inline ffrt_function_header_t* create_function_wrapper(T&& func)
234{
235    using function_type = function<std::decay_t<T>>;
236    static_assert(sizeof(function_type) <= ffrt_auto_managed_function_storage_size,
237        "size of function must be less than ffrt_auto_managed_function_storage_size");
238
239    auto p = ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
240    auto f = new (p) function_type(
241        {exec_function_wrapper<T>, destroy_function_wrapper<T>},
242        std::forward<T>(func));
243    return (ffrt_function_header_t*)f;
244}
245
246static inline void submit(std::function<void()>&& func)
247{
248    return ffrt_submit_base(create_function_wrapper(std::move(func)), NULL, NULL, NULL);
249}
250```
251
252#### ffrt_wait
253
254<hr/>
255* 同步等待,与ffrt_submit_base 配合使用。
256* 等待指定的数据被生产完成,或等待当前任务的所有子任务完成,在不满足条件之前,当前的执行上下文被suspend,在满足条件后恢复执行。
257
258##### 声明
259
260```{.c}
261void ffrt_wait_deps(ffrt_deps_t* deps);
262void ffrt_wait();
263```
264
265##### 参数
266
267`deps`
268
269* 需要等待被生产完成的数据的虚拟地址,这些地址可能作为某些任务在submit 时的out_deps,该依赖的生成见ffrt_deps_t章节,空指针表示无依赖。
270
271##### 返回值
272
273* 不涉及。
274
275##### 描述
276* ffrt_wait_deps(deps) 用于等待deps指代的数据被生产完成才能执行后面的代码。
277* ffrt_wait() 用于等待当前上下文提交的所有子任务(`注意:不包括孙任务和下级子任务`)都完成才能执行后面的代码。
278* 该接口支持在FFRT task 内部调用,也支持在FFRT task 外部调用。
279* 在FFRT task 外部调用的wait 是OS 能够感知的等待,相对于FFRT task 内部调用的wait 是更加昂贵的,因此我们希望尽可能让更多的wait 发生在FFRT task 内部 ,而不是FFRT task 外部。
280
281##### 样例
282
283**recursive fibonacci**
284
285串行版的fibonacci 可以实现为:
286
287```{.c}
288#include <stdio.h>
289
290void fib(int x, int* y) {
291    if (x <= 1) {
292        *y = x;
293    } else {
294        int y1, y2;
295        fib(x - 1, &y1);
296        fib(x - 2, &y2);
297        *y = y1 + y2;
298    }
299}
300int main(int narg, char** argv)
301{
302    int r;
303    fib(10, &r);
304    printf("fibonacci 10: %d\n", r);
305    return 0;
306}
307```
308
309若要使用 FFRT 实现并行(注,对于单纯的fibonacci,单个 Task 的计算量极小,不具有并行加速的意义,但这种调用pattern 对并行编程模型的灵活性考验是非常高的),其中1种可行的实现为:
310
311```{.c}
312#include <stdio.h>
313#include "ffrt.h"  //包含所有ffrt涉及的头文件
314
315typedef struct {
316    int x;
317    int* y;
318} fib_ffrt_s;
319
320typedef struct {
321    ffrt_function_header_t header;
322    ffrt_function_t func;
323    ffrt_function_t after_func;
324    void* arg;
325} c_function;
326
327static void ffrt_exec_function_wrapper(void* t)
328{
329    c_function* f = (c_function*)t;
330    if (f->func) {
331        f->func(f->arg);
332    }
333}
334
335static void ffrt_destroy_function_wrapper(void* t)
336{
337    c_function* f = (c_function*)t;
338    if (f->after_func) {
339        f->after_func(f->arg);
340    }
341}
342
343#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
344static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
345    const ffrt_function_t after_func, void* arg)
346{
347    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
348        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
349    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
350    f->header.exec = ffrt_exec_function_wrapper;
351    f->header.destroy = ffrt_destroy_function_wrapper;
352    f->func = func;
353    f->after_func = after_func;
354    f->arg = arg;
355    return (ffrt_function_header_t*)f;
356}
357
358static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
359    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
360{
361    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
362}
363
364#define ffrt_deps_define(name, dep1, ...) const void* __v_##name[] = {dep1, ##__VA_ARGS__}; \
365    ffrt_deps_t name = {sizeof(__v_##name) / sizeof(void*), __v_##name}
366
367void fib_ffrt(void* arg)
368{
369    fib_ffrt_s* p = (fib_ffrt_s*)arg;
370    int x = p->x;
371    int* y = p->y;
372
373    if (x <= 1) {
374        *y = x;
375    } else {
376        int y1, y2;
377        fib_ffrt_s s1 = {x - 1, &y1};
378        fib_ffrt_s s2 = {x - 2, &y2};
379        ffrt_deps_define(dx, &x);
380        ffrt_deps_define(dy1, &y1);
381        ffrt_deps_define(dy2, &y2);
382        ffrt_deps_define(dy12, &y1, &y2);
383        ffrt_submit_c(fib_ffrt, NULL, &s1, &dx, &dy1, NULL);
384        ffrt_submit_c(fib_ffrt, NULL, &s2, &dx, &dy2, NULL);
385        ffrt_wait_deps(&dy12);
386        *y = y1 + y2;
387    }
388}
389
390int main(int narg, char** argv)
391{
392    int r;
393    fib_ffrt_s s = {10, &r};
394    ffrt_deps_define(dr, &r);
395    ffrt_submit_c(fib_ffrt, NULL, &s, NULL, &dr, NULL);
396    ffrt_wait_deps(&dr);
397    printf("fibonacci 10: %d\n", r);
398    return 0;
399}
400```
401
402`解析`:
403
4041)   将fibonacci (x-1)和fibonacci (x-2) 作为2个Task 提交给FFRT,在两个Task 完成之后将结果累加;
405
4062)   虽然单个Task 只能拆分成2个SubTask 但是子Task 可以继续拆分,因此,整个计算图的并行度是非常高的,Task 之间在FFRT 内部形成了一颗调用树;
407
408<img src="figures/ffrtfigure2.png" style="zoom:100%" />
409
410> 以上实现,因为需要用户显式管理数据生命周期和函数入参打包两个因素,所以使得代码实现异常复杂。
411
412
413
414#### ffrt_deps_t
415
416* C API中对依赖数组的抽象,逻辑上等同于C++ API中的`std::vector<void*>`
417
418##### 声明
419
420```{.c}
421typedef enum {
422    ffrt_dependence_data,
423    ffrt_dependence_task,
424} ffrt_dependence_type_t;
425
426typedef struct {
427    ffrt_dependence_type_t type;
428    const void* ptr;
429} ffrt_dependence_t;
430
431typedef struct {
432    uint32_t len;
433    const ffrt_dependence_t* items;
434} ffrt_deps_t;
435```
436
437##### 参数
438
439`len`
440
441* 所依赖的Signature的个数,取值大于等于0。
442
443`item`
444
445* len个Signature的起始地址指针。
446
447`type`
448
449* 当前依赖为数据依赖还是任务依赖。
450
451`ptr`
452
453* 所依赖对应Signature内容的实际地址。
454
455##### 返回值
456
457* 不涉及。
458
459##### 描述
460
461* item为len个Signature的起始指针,该指针可以指向堆空间,也可以指向栈空间,但是要求分配的空间大于等于len * sizeof(ffrt_dependence_t)。
462
463##### 样例
464
465* 创建数据依赖或者任务依赖:
466
467```{.c}
468// 创建数据依赖的ffrt_deps_t
469int x = 0;
470const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}};
471ffrt_deps_t in{static_cast<uint32_t>(in_deps.size()), in_deps.data()};
472
473// 提交某个返回handle任务
474ffrt_task_handle_t task = ffrt_submit_h_base(
475        ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr);
476// 创建任务依赖的ffrt_deps_t
477const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}};
478ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()};
479```
480
481#### ffrt_task_attr_t
482
483<hr/>
484* 定义task 的属性的辅助类,与ffrt_submit_base 配合使用。
485
486##### 声明
487
488```{.c}
489typedef enum {
490    ffrt_qos_inherent = -1,
491    ffrt_qos_background,
492    ffrt_qos_utility,
493    ffrt_qos_default,
494    ffrt_qos_user_initiated,
495} ffrt_qos_default_t;
496
497typedef int ffrt_qos_t;
498
499typedef struct {
500    uint32_t storage[(ffrt_task_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
501} ffrt_task_attr_t;
502typedef void* ffrt_task_handle_t;
503
504int ffrt_task_attr_init(ffrt_task_attr_t* attr);
505void ffrt_task_attr_destroy(ffrt_task_attr_t* attr);
506void ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos);
507ffrt_qos_t ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr);
508void ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name);
509const char* ffrt_task_attr_get_name(const ffrt_task_attr_t* attr);
510void ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us);
511uint64_t ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr);
512```
513
514##### 参数
515
516`attr`
517
518* 创建的tasks属性的句柄。
519
520`qos`
521
522* qos 设定的枚举类型。
523* inherent 是一个qos 设定策略,代表即将ffrt_submit 的task 的qos 继承当前task 的qos。
524
525`delay_us`
526
527* 任务延迟执行的时间,单位为us。
528
529##### 返回值
530
531* 不涉及。
532
533##### 描述
534* `attr`所传递的内容会在ffrt_submit内部完成取存,ffrt_submit返回后用户即可销毁。
535* 约定:
536  * 在submit 时,如果不通过task_attr 设定qos,那么默认该提交的task的qos 为`ffrt_qos_default`。
537  * 在submit 时,如果通过task_attr 设定qos 为`ffrt_qos_inherent`,表示将该提交的task 的qos 与当前task 的qos 相同,在FFRT task 外部提交的属性为`ffrt_qos_inherent` 的task,其qos 为`ffrt_qos_default`。
538  * 其他情况下,该提交的task 的qos 被设定为指定的值。
539* ffrt_task_attr_t对象的置空和销毁由用户完成,对同一个ffrt_task_attr_t仅能调用一次`ffrt_task_attr_destroy`,重复对同一个ffrt_task_attr_t调用`ffrt_task_attr_destroy`,其行为是未定义的。
540* 在`ffrt_task_attr_destroy`之后再对task_attr进行访问,其行为是未定义的。
541
542##### 样例
543
544* 提交一个qos 级别为background 的任务:
545
546```{.c}
547#include <stdio.h>
548#include "ffrt.h"
549
550void my_print(void* arg)
551{
552    printf("hello ffrt\n");
553}
554
555typedef struct {
556    ffrt_function_header_t header;
557    ffrt_function_t func;
558    ffrt_function_t after_func;
559    void* arg;
560} c_function;
561
562static void ffrt_exec_function_wrapper(void* t)
563{
564    c_function* f = (c_function*)t;
565    if (f->func) {
566        f->func(f->arg);
567    }
568}
569
570static void ffrt_destroy_function_wrapper(void* t)
571{
572    c_function* f = (c_function*)t;
573    if (f->after_func) {
574        f->after_func(f->arg);
575    }
576}
577
578#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
579static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
580    const ffrt_function_t after_func, void* arg)
581{
582    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
583        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
584    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
585    f->header.exec = ffrt_exec_function_wrapper;
586    f->header.destroy = ffrt_destroy_function_wrapper;
587    f->func = func;
588    f->after_func = after_func;
589    f->arg = arg;
590    return (ffrt_function_header_t*)f;
591}
592
593static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
594    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
595{
596    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
597}
598
599int main(int narg, char** argv)
600{
601    ffrt_task_attr_t attr;
602    ffrt_task_attr_init(&attr);
603    ffrt_task_attr_set_qos(&attr, ffrt_qos_background);
604    ffrt_task_attr_set_delay(&attr, 10000);
605    ffrt_submit_c(my_print, NULL, NULL, NULL, NULL, &attr);
606    ffrt_task_attr_destroy(&attr);
607    ffrt_wait();
608    return 0;
609}
610```
611
612
613
614
615#### ffrt_submit_h_base
616
617<hr/>
618
619* 向调度器提交一个task,与ffrt_submit_base 的差别在于返回task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步。
620
621##### 声明
622
623```{.c}
624typedef void* ffrt_task_handle_t;
625
626ffrt_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);
627void ffrt_task_handle_destroy(ffrt_task_handle_t handle);
628```
629
630##### 参数
631
632`func`
633
634* CPU Function的指针,该指针执行的数据结构,按照`ffrt_function_header_t`定义的描述了该CPU Task如何执行和销毁的函数指针,FFRT通过这两个函数指针完成Task的执行和销毁。
635
636`in_deps`
637
638* 该参数是可选的。
639* 该参数用于描述该任务的输入依赖,FFRT 通过数据的虚拟地址作为数据的Signature 来建立依赖。
640
641`out_deps`
642
643* 该参数是可选的。
644* 该参数用于描述该任务的输出依赖。
645* `注意`:该依赖值本质上是一个数值,ffrt没办法区分该值是合理的还是不合理的,会假定输入的值是合理的进行处理;但不建议采用NULL,1, 2 等值来建立依赖关系,建议采用实际的内存地址,因为前者使用不当会建立起不必要的依赖,影响并发。
646
647`attr`
648
649* 该参数是可选的。
650* 该参数用于描述Task 的属性,比如qos 等,详见 [ffrt_task_attr_t](#ffrt_task_attr_t)章节。
651
652##### 返回值
653
654* task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步。
655
656##### 描述
657
658* **C API中的ffrt_task_handle_t需要用户调用`ffrt_task_handle_destroy`显式销毁。**
659* C API中的task_handle_t对象的置空和销毁由用户完成,对同一个ffrt_task_handle_t仅能调用一次`ffrt_task_handle_destroy`,重复对同一个ffrt_task_handle_t调用`ffrt_task_handle_destroy`,其行为是未定义的。
660* 在`ffrt_task_handle_destroy`之后再对ffrt_task_handle_t进行访问,其行为是未定义的。
661
662##### 样例
663
664```{.c}
665#include <stdio.h>
666#include "ffrt.h"
667
668void func0(void* arg)
669{
670    printf("hello ");
671}
672
673void func1(void* arg)
674{
675    (*(int*)arg)++;
676}
677
678void func2(void* arg)
679{
680    printf("world, x = %d\n", *(int*)arg);
681}
682
683void func3(void* arg)
684{
685    printf("handle wait");
686    (*(int*)arg)++;
687}
688
689typedef struct {
690    ffrt_function_header_t header;
691    ffrt_function_t func;
692    ffrt_function_t after_func;
693    void* arg;
694} c_function;
695
696static void ffrt_exec_function_wrapper(void* t)
697{
698    c_function* f = (c_function*)t;
699    if (f->func) {
700        f->func(f->arg);
701    }
702}
703
704static void ffrt_destroy_function_wrapper(void* t)
705{
706    c_function* f = (c_function*)t;
707    if (f->after_func) {
708        f->after_func(f->arg);
709    }
710}
711
712#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
713static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
714    const ffrt_function_t after_func, void* arg)
715{
716    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
717        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
718    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
719    f->header.exec = ffrt_exec_function_wrapper;
720    f->header.destroy = ffrt_destroy_function_wrapper;
721    f->func = func;
722    f->after_func = after_func;
723    f->arg = arg;
724    return (ffrt_function_header_t*)f;
725}
726
727static inline ffrt_task_handle_t ffrt_submit_h_c(ffrt_function_t func, const ffrt_function_t after_func,
728    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
729{
730    return ffrt_submit_h_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
731}
732
733static inline void ffrt_submit_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    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
737}
738
739
740int main(int narg, char** argv)
741{
742    // handle work with submit
743    ffrt_task_handle_t h = ffrt_submit_h_c(func0, NULL, NULL, NULL, NULL, NULL); // not need some data in this task
744    int x = 1;
745    const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}};
746    ffrt_deps_t d2{static_cast<uint32_t>(in_deps.size()), in_deps.data()};
747
748    const std::vector<ffrt_dependence_t> out_deps = {{ffrt_dependence_data, &x}};
749    ffrt_deps_t d1{static_cast<uint32_t>(out_deps.size()), out_deps.data()};
750
751    ffrt_submit_c(func1, NULL, &x, NULL, &d1, NULL);
752    ffrt_submit_c(func2, NULL, &x, &d2, NULL, NULL); // this task depend x and h
753    ffrt_task_handle_destroy(h);
754
755    // handle work with wait
756    ffrt_task_handle_t h2 = ffrt_submit_h_c(func3, NULL, &x, NULL, NULL, NULL);
757
758    const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, h2}};
759    ffrt_deps_t d3{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()};
760    ffrt_wait_deps(&d3);
761    ffrt_task_handle_destroy(h2);
762    printf("x = %d", x);
763    ffrt_wait();
764    return 0;
765}
766```
767
768* 预期的输出为:
769
770```
771hello world, x = 2
772handle wait
773x = 3
774```
775
776
777
778#### ffrt_this_task_get_id
779
780<hr/>
781
782* 返回当前task的id标识,更多使用用于维测(原因是task name可能重名)。
783
784##### 声明
785
786```{.c}
787uint64_t ffrt_this_task_get_id();
788```
789
790##### 参数
791
792* 不涉及。
793
794##### 返回值
795
796* 当前task的id。
797
798##### 描述
799
800* 该接口在task内部调用将返回当前task的id标识,在task外部调用将返回0。
801* 可以基于该接口在task外部调用返回0的特性来区分函数是运行在FFRT 工作线程上还是非FFRT工作线程上。
802* task id为从1开始编码,每提交一个task便增加1,被设计成64bit,即便是每秒百万次提交,也需要292471.2年才会发生翻转。
803
804##### 样例
805
806* 忽略。
807
808
809
810#### ffrt_this_task_update_qos
811
812<hr/>
813
814* 更新当前正在执行的task的优先级。
815
816##### 声明
817
818```{.c}
819int ffrt_this_task_update_qos(ffrt_qos_t qos);
820```
821
822##### 参数
823
824* `qos` 新的优先级。
825
826##### 返回值
827
828* 0表示成功,非0表示失败。
829
830##### 描述
831
832* 该接口对当前task的qos调整会立即生效。
833* 如果新设定的qos与当前的qos不一致,则会block当前task的执行,再按照新的qos恢复执行。
834* 如果新设定的qos与当前的qos一致,则接口会立即返回0,不做任何处理。
835* **如果在非task内部调用该接口,则返回非0值,用户可以选择忽略或其他处理。**
836
837##### 样例
838
839* 忽略。
840
841### 串行队列
842<hr />
843* FFRT提供queue来实现Andorid中类似WorkQueue能力,且在使用得当的情况下将有更好的性能。
844
845#### ffrt_queue_attr_t
846
847##### 声明
848```{.c}
849typedef struct {
850    uint32_t storage[(ffrt_queue_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
851} ffrt_queue_attr_t;
852
853int ffrt_queue_attr_init(ffrt_queue_attr_t* attr);
854void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr);
855```
856
857##### 参数
858
859`attr`
860* 该参数是指向未初始化的ffrt_queue_attr_t。
861
862##### 返回值
863* 若成功返回0,否则返回-1。
864
865##### 描述
866* ffrt_queue_attr_t用于创建ffrt_queue_t且不单独使用,因此必须在创建队列前先创建好队列属性。
867* ffrt_queue_attr_t对象的置空和销毁由用户完成,对同一个ffrt_queue_attr_t仅能调用一次`ffrt_queue_attr_destroy`,重复对同一个ffrt_queue_attr_t调用`ffrt_queue_attr_destroy`,其行为是未定义的。
868* 在`ffrt_queue_attr_destroy`之后再对ffrt_queue_attr_t进行访问,其行为是未定义的。
869
870##### 样例
871参考ffrt_queue_t章节的样例。
872
873#### ffrt_queue_t
874
875##### 声明
876```{.c}
877typedef enum { ffrt_queue_serial, ffrt_queue_max } ffrt_queue_type_t;
878typedef void* ffrt_queue_t;
879
880ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr)
881void ffrt_queue_destroy(ffrt_queue_t queue)
882```
883
884##### 参数
885
886`type`
887* 该参数用于描述创建的队列类型。
888
889`name`
890* 该参数用于描述创建队列的名字。
891
892`attr`
893* 该参数用于描述queue的属性,详见ffrt_queue_attr_t章节。
894
895##### 返回值
896* 若成功则返回新创建的队列,否则返回空指针。
897
898##### 描述
899* 提交至该队列的任务将按照顺序执行,如果某个提交的任务中发生阻塞,则无法保证该任务的执行顺序。
900* ffrt_queue_t对象的置空和销毁由用户完成,对同一个ffrt_queue_t仅能调用一次`ffrt_queue_destroy`,重复对同一个ffrt_queue_t调用`ffrt_queue_destroy`,其行为是未定义的。
901* 在`ffrt_queue_destroy`之后再对ffrt_queue_t进行访问,其行为是未定义的。
902
903##### 样例
904```
905#include <stdio.h>
906#include "ffrt.h"
907
908using namespace std;
909
910template<class T>
911struct Function {
912    template<class CT>
913    Function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {}
914    ffrt_function_header_t header;
915    T closure;
916};
917
918template<class T>
919void ExecFunctionWrapper(void* t)
920{
921    auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t);
922    f->closure();
923}
924
925template<class T>
926void DestroyFunctionWrapper(void* t)
927{
928    auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t);
929    f->closure = nullptr;
930}
931
932template<class T>
933static inline ffrt_function_header_t* create_function_wrapper(T&& func,
934    ffrt_function_kind_t kind = ffrt_function_kind_general)
935{
936    using function_type = Function<std::decay_t<T>>;
937    auto p = ffrt_alloc_auto_managed_function_storage_base(kind);
938    auto f =
939        new (p)function_type({ ExecFunctionWrapper<T>, DestroyFunctionWrapper<T>, { 0 } }, std::forward<T>(func));
940    return reinterpret_cast<ffrt_function_header_t*>(f);
941}
942
943int main(int narg, char** argv)
944{
945    ffrt_queue_attr_t queue_attr;
946    (void)ffrt_queue_attr_init(&queue_attr);
947    ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);
948
949    ffrt_queue_submit(queue_handle, create_function_wrapper([]() {printf("Task done.\n");}, ffrt_function_kind_queue), nullptr);
950
951    ffrt_queue_attr_destroy(&queue_attr);
952    ffrt_queue_destroy(queue_handle);
953}
954```
955### 同步原语
956
957#### ffrt_mutex_t
958<hr/>
959* FFRT提供的类似pthread mutex 的性能实现。
960
961##### 声明
962
963```{.c}
964typedef enum {
965    ffrt_error = -1,
966    ffrt_success = 0,
967    ffrt_error_nomem = ENOMEM,
968    ffrt_error_timedout = ETIMEDOUT,
969    ffrt_error_busy = EBUSY,
970    ffrt_error_inval = EINVAL
971} ffrt_error_t;
972
973struct ffrt_mutex_t;
974
975int ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr);
976int ffrt_mutex_lock(ffrt_mutex_t* mutex);
977int ffrt_mutex_unlock(ffrt_mutex_t* mutex);
978int ffrt_mutex_trylock(ffrt_mutex_t* mutex);
979int ffrt_mutex_destroy(ffrt_mutex_t* mutex);
980```
981
982##### 参数
983
984`attr`
985
986* 当前FFRT只支持基础类型的mutex,因此attr必须为空指针。
987
988`mutex`
989
990* 指向所操作的互斥锁的指针。
991
992##### 返回值
993
994* 若成功则为 ffrt_success ,否则发生错误。
995
996##### 描述
997* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。
998* 该功能能够避免pthread传统的pthread_mutex_t 在抢不到锁时陷入内核的问题,在使用得当的条件下将会有更好的性能。
999* **注意:目前暂不支持递归和定时功能。**
1000* **注意:C API中的ffrt_mutex_t需要用户调用`ffrt_mutex_init`和`ffrt_mutex_destroy`显式创建和销毁。**
1001* **注意:C API中的ffrt_mutex_t对象的置空和销毁由用户完成,对同一个ffrt_mutex_t仅能调用一次`ffrt_mutex_destroy`,重复对同一个ffrt_mutex_t调用`ffrt_mutex_destroy`,其行为是未定义的。**
1002* **注意:在`ffrt_mutex_destroy`之后再对ffrt_mutex_t进行访问,其行为是未定义的。**
1003
1004##### 样例
1005
1006```{.c}
1007#include <stdio.h>
1008#include "ffrt.h"
1009
1010typedef struct {
1011    int* sum;
1012    ffrt_mutex_t* mtx;
1013} tuple;
1014
1015void func(void* arg)
1016{
1017    tuple* t = (tuple*)arg;
1018
1019    int ret = ffrt_mutex_lock(t->mtx);
1020    if (ret != ffrt_success) {
1021        printf("error\n");
1022    }
1023    (*t->sum)++;
1024    ret = ffrt_mutex_unlock(t->mtx);
1025    if (ret != ffrt_success) {
1026        printf("error\n");
1027    }
1028}
1029
1030typedef struct {
1031    ffrt_function_header_t header;
1032    ffrt_function_t func;
1033    ffrt_function_t after_func;
1034    void* arg;
1035} c_function;
1036
1037static void ffrt_exec_function_wrapper(void* t)
1038{
1039    c_function* f = (c_function*)t;
1040    if (f->func) {
1041        f->func(f->arg);
1042    }
1043}
1044
1045static void ffrt_destroy_function_wrapper(void* t)
1046{
1047    c_function* f = (c_function*)t;
1048    if (f->after_func) {
1049        f->after_func(f->arg);
1050    }
1051}
1052
1053#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
1054static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
1055    const ffrt_function_t after_func, void* arg)
1056{
1057    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
1058        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
1059    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1060    f->header.exec = ffrt_exec_function_wrapper;
1061    f->header.destroy = ffrt_destroy_function_wrapper;
1062    f->func = func;
1063    f->after_func = after_func;
1064    f->arg = arg;
1065    return (ffrt_function_header_t*)f;
1066}
1067
1068static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
1069    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1070{
1071    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1072}
1073
1074void ffrt_mutex_task()
1075{
1076    int sum = 0;
1077    ffrt_mutex_t mtx;
1078    tuple t = {&sum, &mtx};
1079    int ret = ffrt_mutex_init(&mtx, NULL);
1080    if (ret != ffrt_success) {
1081        printf("error\n");
1082    }
1083    for (int i = 0; i < 10; i++) {
1084        ffrt_submit_c(func, NULL, &t, NULL, NULL, NULL);
1085    }
1086    ffrt_mutex_destroy(&mtx);
1087    ffrt_wait();
1088    printf("sum = %d", sum);
1089}
1090
1091int main(int narg, char** argv)
1092{
1093    int r;
1094    ffrt_submit_c(ffrt_mutex_task, NULL, NULL, NULL, NULL, NULL);
1095    ffrt_wait();
1096    return 0;
1097}
1098```
1099
1100预期输出为:
1101
1102```
1103sum=10
1104```
1105
1106* 该例子为功能示例,实际中并不鼓励这样使用。
1107
1108
1109#### ffrt_cond_t
1110<hr/>
1111
1112* FFRT提供的类似pthread 信号量的性能实现。
1113
1114##### 声明
1115
1116```{.c}
1117typedef enum {
1118    ffrt_error = -1,
1119    ffrt_success = 0,
1120    ffrt_error_nomem = ENOMEM,
1121    ffrt_error_timedout = ETIMEDOUT,
1122    ffrt_error_busy = EBUSY,
1123    ffrt_error_inval = EINVAL
1124} ffrt_error_t;
1125
1126struct ffrt_cond_t;
1127
1128int ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr);
1129int ffrt_cond_signal(ffrt_cond_t* cond);
1130int ffrt_cond_broadcast(ffrt_cond_t* cond);
1131int ffrt_cond_wait(ffrt_cond_t*cond, ffrt_mutex_t* mutex);
1132int ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point);
1133int ffrt_cond_destroy(ffrt_cond_t* cond);
1134```
1135
1136##### 参数
1137
1138`cond`
1139
1140* 指向所操作的信号量的指针。
1141
1142`attr`
1143
1144* 属性设定,空指针表示使用默认属性。
1145
1146`mutex`
1147
1148* 指向要在阻塞期间解锁的互斥锁的指针。
1149
1150`time_point`
1151
1152* 指向指定等待时限时间的对象的指针。
1153
1154
1155##### 返回值
1156
1157* 若成功则为 ffrt_success,若在锁定互斥前抵达时限则为 ffrt_error_timedout。
1158
1159##### 描述
1160* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。
1161* 该功能能够避免传统的pthread_cond_t在条件不满足时陷入内核的问题,在使用得当的条件下将会有更好的性能。
1162* **注意:C API中的ffrt_cond_t需要用户调用`ffrt_cond_init`和`ffrt_cond_destroy`显式创建和销毁。**
1163* **注意:C API中的ffrt_cond_t对象的置空和销毁由用户完成,对同一个ffrt_cond_t仅能调用一次`ffrt_cond_destroy`,重复对同一个ffrt_cond_t调用`ffrt_cond_destroy`,其行为是未定义的。**
1164* **注意:在`ffrt_cond_destroy`之后再对ffrt_cond_t进行访问,其行为是未定义的。**
1165
1166##### 样例
1167
1168```{.c}
1169#include <stdio.h>
1170#include "ffrt.h"
1171
1172typedef struct {
1173    ffrt_cond_t* cond;
1174    int* a;
1175    ffrt_mutex_t* lock_;
1176} tuple;
1177
1178void func1(void* arg)
1179{
1180    tuple* t = (tuple*)arg;
1181    int ret = ffrt_mutex_lock(t->lock_);
1182    if (ret != ffrt_success) {
1183        printf("error\n");
1184    }
1185    while (*t->a != 1) {
1186        ret = ffrt_cond_wait(t->cond, t->lock_);
1187        if (ret != ffrt_success) {
1188            printf("error\n");
1189        }
1190    }
1191    ret = ffrt_mutex_unlock(t->lock_);
1192    if (ret != ffrt_success) {
1193        printf("error\n");
1194    }
1195    printf("a = %d", *(t->a));
1196}
1197
1198void func2(void* arg)
1199{
1200    tuple* t = (tuple*)arg;
1201    int ret = ffrt_mutex_lock(t->lock_);
1202    if (ret != ffrt_success) {
1203        printf("error\n");
1204    }
1205    *(t->a) = 1;
1206    ret = ffrt_cond_signal(t->cond);
1207    if (ret != ffrt_success) {
1208        printf("error\n");
1209    }
1210    ret = ffrt_mutex_unlock(t->lock_);
1211    if (ret != ffrt_success) {
1212        printf("error\n");
1213    }
1214}
1215
1216typedef struct {
1217    ffrt_function_header_t header;
1218    ffrt_function_t func;
1219    ffrt_function_t after_func;
1220    void* arg;
1221} c_function;
1222
1223static void ffrt_exec_function_wrapper(void* t)
1224{
1225    c_function* f = (c_function*)t;
1226    if (f->func) {
1227        f->func(f->arg);
1228    }
1229}
1230
1231static void ffrt_destroy_function_wrapper(void* t)
1232{
1233    c_function* f = (c_function*)t;
1234    if (f->after_func) {
1235        f->after_func(f->arg);
1236    }
1237}
1238
1239#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
1240static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
1241    const ffrt_function_t after_func, void* arg)
1242{
1243    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
1244        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
1245    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1246    f->header.exec = ffrt_exec_function_wrapper;
1247    f->header.destroy = ffrt_destroy_function_wrapper;
1248    f->func = func;
1249    f->after_func = after_func;
1250    f->arg = arg;
1251    return (ffrt_function_header_t*)f;
1252}
1253
1254static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
1255    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1256{
1257    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1258}
1259
1260void ffrt_cv_task()
1261{
1262    ffrt_cond_t cond;
1263    int ret = ffrt_cond_init(&cond, NULL);
1264    if (ret != ffrt_success) {
1265        printf("error\n");
1266    }
1267    int a = 0;
1268    ffrt_mutex_t lock_;
1269    tuple t = {&cond, &a, &lock_};
1270    ret = ffrt_mutex_init(&lock_, NULL);
1271    if (ret != ffrt_success) {
1272        printf("error\n");
1273    }
1274    ffrt_submit_c(func1, NULL, &t, NULL, NULL, NULL);
1275    ffrt_submit_c(func2, NULL, &t, NULL, NULL, NULL);
1276    ffrt_wait();
1277    ffrt_cond_destroy(&cond);
1278    ffrt_mutex_destroy(&lock_);
1279}
1280
1281int main(int narg, char** argv)
1282{
1283    ffrt_submit_c(ffrt_cv_task, NULL, NULL, NULL, NULL, NULL);
1284    ffrt_wait();
1285    return 0;
1286}
1287```
1288
1289预期输出为:
1290
1291```
1292a=1
1293```
1294
1295* 该例子为功能示例,实际中并不鼓励这样使用。
1296
1297### 杂项
1298
1299#### ffrt_usleep
1300
1301<hr/>
1302* FFRT提供的类似C11 sleep和linux usleep的性能实现。
1303
1304##### 声明
1305
1306```{.c}
1307int ffrt_usleep(uint64_t usec);
1308```
1309
1310##### 参数
1311
1312`usec`
1313
1314* 睡眠的us数。
1315
1316##### 返回值
1317
1318* 不涉及。
1319
1320##### 描述
1321* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。
1322* 该功能能够避免传统的sleep 睡眠时陷入内核的问题,在使用得当的条件下将会有更好的性能。
1323
1324##### 样例
1325
1326```{.c}
1327#include <time.h>
1328#include <stdio.h>
1329#include "ffrt.h"
1330
1331void func(void* arg)
1332{
1333    printf("Time: %s", ctime(&(time_t){time(NULL)}));
1334    ffrt_usleep(2000000); // 睡眠 2 秒
1335    printf("Time: %s", ctime(&(time_t){time(NULL)}));
1336}
1337
1338typedef struct {
1339    ffrt_function_header_t header;
1340    ffrt_function_t func;
1341    ffrt_function_t after_func;
1342    void* arg;
1343} c_function;
1344
1345static void ffrt_exec_function_wrapper(void* t)
1346{
1347    c_function* f = (c_function*)t;
1348    if (f->func) {
1349        f->func(f->arg);
1350    }
1351}
1352
1353static void ffrt_destroy_function_wrapper(void* t)
1354{
1355    c_function* f = (c_function*)t;
1356    if (f->after_func) {
1357        f->after_func(f->arg);
1358    }
1359}
1360
1361#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
1362static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
1363    const ffrt_function_t after_func, void* arg)
1364{
1365    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
1366        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
1367    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1368    f->header.exec = ffrt_exec_function_wrapper;
1369    f->header.destroy = ffrt_destroy_function_wrapper;
1370    f->func = func;
1371    f->after_func = after_func;
1372    f->arg = arg;
1373    return (ffrt_function_header_t*)f;
1374}
1375
1376static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
1377    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1378{
1379    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1380}
1381
1382int main(int narg, char** argv)
1383{
1384    ffrt_submit_c(func, NULL, NULL, NULL, NULL, NULL);
1385    ffrt_wait();
1386    return 0;
1387}
1388```
1389
1390#### ffrt_yield
1391<hr/>
1392* 当前task 主动让出CPU 执行资源,让其他可以被执行的task 先执行,如果没有其他可被执行的task,yield 无效。
1393
1394##### 声明
1395
1396```{.c}
1397void ffrt_yield();
1398```
1399
1400##### 参数
1401
1402* 不涉及。
1403
1404##### 返回值
1405
1406* 不涉及。
1407
1408##### 描述
1409* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。
1410* 此函数的确切行为取决于实现,特别是使用中的FFRT 调度程序的机制和系统状态。
1411
1412##### 样例
1413
1414* 省略。
1415
1416
1417## 开发步骤
1418
1419以下步骤描述了如何使用`FFRT`提供的Native API接口,创建并行任务和串行队列任务以及销毁相应资源。
1420
1421**添加动态链接库**
1422
1423CMakeLists.txt中添加以下lib:
1424```txt
1425libffrt.z.so
1426```
1427
1428**头文件**
1429```c++
1430#include "ffrt/task.h"
1431#include "ffrt/type_def.h"
1432#include "ffrt/condition_variable.h"
1433#include "ffrt/mutex.h"
1434#include "ffrt/queue.h"
1435#include "ffrt/sleep.h"
1436```
1437
14381. **首先需要对执行的函数进行封装**。
1439    ```c++
1440    // 第一种使用模板,支持C++
1441    template<class T>
1442    struct Function {
1443        template<class CT>
1444        Function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {}
1445        ffrt_function_header_t header;
1446        T closure;
1447    };
1448
1449    template<class T>
1450    void ExecFunctionWrapper(void* t)
1451    {
1452        auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t);
1453        f->closure();
1454    }
1455
1456    template<class T>
1457    void DestroyFunctionWrapper(void* t)
1458    {
1459        auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t);
1460        f->closure = nullptr;
1461    }
1462
1463    template<class T>
1464    static inline ffrt_function_header_t* create_function_wrapper(T&& func,
1465        ffrt_function_kind_t kind = ffrt_function_kind_general)
1466    {
1467        using function_type = Function<std::decay_t<T>>;
1468        auto p = ffrt_alloc_auto_managed_function_storage_base(kind);
1469        auto f =
1470            new (p)function_type({ ExecFunctionWrapper<T>, DestroyFunctionWrapper<T>, { 0 } }, std::forward<T>(func));
1471        return reinterpret_cast<ffrt_function_header_t*>(f);
1472    }
1473
1474    // 第二种创建方式
1475    typedef struct {
1476        ffrt_function_header_t header;
1477        ffrt_function_t func;
1478        ffrt_function_t after_func;
1479        void* arg;
1480    } CFunction;
1481
1482    static void FfrtExecFunctionWrapper(void* t)
1483    {
1484        CFunction* f = static_cast<CFunction*>(t);
1485        if (f->func) {
1486            f->func(f->arg);
1487        }
1488    }
1489
1490    static void FfrtDestroyFunctionWrapper(void* t)
1491    {
1492        CFunction* f = static_cast<CFunction*>(t);
1493        if (f->after_func) {
1494            f->after_func(f->arg);
1495        }
1496    }
1497
1498    #define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
1499    static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
1500        const ffrt_function_t after_func, void* arg, ffrt_function_kind_t kind_t = ffrt_function_kind_general)
1501    {
1502        FFRT_STATIC_ASSERT(sizeof(CFunction) <= ffrt_auto_managed_function_storage_size,
1503            size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
1504        CFunction* f = static_cast<CFunction*>(ffrt_alloc_auto_managed_function_storage_base(kind_t));
1505        f->header.exec = FfrtExecFunctionWrapper;
1506        f->header.destroy = FfrtDestroyFunctionWrapper;
1507        f->func = func;
1508        f->after_func = after_func;
1509        f->arg = arg;
1510        return reinterpret_cast<ffrt_function_header_t*>(f);
1511    }
1512
1513    // 样例:待提交执行的函数
1514    void OnePlusForTest(void* arg)
1515    {
1516        (*static_cast<int*>(arg)) += 1;
1517    }
1518    ```
1519
15202. **设置task属性值**。
1521
1522    用户提交任务时可以设置任务属性,包括qos优先级,名称等,具体可参考接口文档。
1523    ```c++
1524    // ******初始化并行任务属性******
1525    ffrt_task_attr_t attr;
1526    ffrt_task_attr_init(&attr);
1527
1528    // ******创建串行队列******
1529
1530    // 创建串行队列的属性
1531    ffrt_queue_attr_t queue_attr;
1532    // 创建串行队列的handle
1533    ffrt_queue_t queue_handle;
1534
1535    // 初始化队列属性
1536    (void)ffrt_queue_attr_init(&queue_attr);
1537
1538    // 如有需要,设置指定优先级
1539    ffrt_queue_attr_set_qos(&queue_attr, static_cast<ffrt_qos_t>(ffrt_qos_inherit));
1540    // 如有需要,设置超时时间(ms)
1541    ffrt_queue_attr_set_timeout(&queue_attr, 10000);
1542    // 如有需要,设置超时回调
1543    int x = 0;
1544    ffrt_queue_attr_set_callback(&queue_attr, ffrt_create_function_wrapper(OnePlusForTest, NULL, &x,
1545        ffrt_function_kind_queue));
1546
1547    // 基于属性,初始化队列
1548    queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);
1549    ```
1550
15513. **提交任务**。
1552    ```c++
1553    int a = 0;
1554    // ******并行任务******
1555    // 提交不带handle返回值的并行任务
1556    ffrt_submit_base(ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr);
1557    // 提交带handle返回值的并行任务
1558    ffrt_task_handle_t task = ffrt_submit_h_base(
1559        ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr);
1560
1561    // ******串行任务******
1562    // 提交不返回handle的串行队列任务
1563    ffrt_queue_submit(queue_handle, ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a,
1564        ffrt_function_kind_queue), nullptr);
1565    // 提交带handle的串行队列任务
1566    ffrt_task_handle_t handle = ffrt_queue_submit_h(queue_handle,
1567        ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, ffrt_function_kind_queue), nullptr);
1568
1569    // 如果需要等待执行结果,则调用wait
1570    const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}};
1571    ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()};
1572    ffrt_wait_deps(&wait);
1573
1574    ffrt_queue_wait(handle);
1575    ```
1576
15774. **任务提交完成后销毁相应资源**。
1578    ```c++
1579    // ******销毁并行任务******
1580    ffrt_task_attr_destroy(&attr);
1581    ffrt_task_handle_destroy(task);
1582
1583    // ******销毁串行队列任务******
1584    // 先销毁任务handle,再销毁队列
1585    ffrt_queue_attr_destroy(&queue_attr);
1586    ffrt_task_handle_destroy(handle);
1587    ffrt_queue_destroy(queue_handle);
1588    ```
1589
1590## 使用建议
1591
1592### 建议1: 函数化
1593
1594**基本思想:计算过程函数化**
1595
1596* 程序过程各步骤以函数封装表达,函数满足类纯函数特性。
1597* 无全局数据访问。
1598* 无内部状态保留。
1599* 通过ffrt_submit_base()接口以异步任务方式提交函数执行。
1600* 将函数访问的数据对象以及访问方式在ffrt_submit_base()接口中的in_deps/out_deps参数表达。
1601* 程序员通过inDeps/outDeps参数表达任务间依赖关系以保证程序执行的正确性。
1602
1603> 做到纯函数的好处在于:1. 能够最大化挖掘并行度,2.避免DataRace和锁的问题。
1604
1605
1606
1607**在实际中,可以根据场景放松纯函数的约束,但前提是:**
1608
1609* 确定添加的in_deps/out_deps可确保程序正确执行。
1610* 通过FFRT提供的锁机制保护对全局变量的访问。
1611
1612
1613### 建议2: 使用FFRT提供的替代API
1614
1615* 禁止在FFRT任务中使用系统线程库API创建线程,使用submit提交任务。
1616* 使用FFRT提供的锁,条件变量,睡眠,IO等API代替系统线程库API。
1617  * 使用系统线程库API可能造成工作线程阻塞,引起额外性能开销。
1618
1619
1620
1621### 建议3: Deadline机制
1622
1623* **必须用于具备周期/重复执行特征的处理流程。**
1624* 在有明确时间约束和性能关键的处理流程中使用,避免滥用。
1625* 在相对大颗粒度的处理流程中使用,例如具有16.6ms时间约束的帧处理流程。
1626
1627
1628
1629### 建议4: 从线程模型迁移
1630
1631* 创建线程替代为创建FFRT任务。
1632  * 线程从逻辑上类似无in_deps的任务。
1633* 识别线程间的依赖关系,并将其表达在任务的依赖关系in_deps/out_deps上。
1634* 线程内计算过程分解为异步任务调用。
1635* 通过任务依赖关系和锁机制避免并发任务数据竞争问题。
1636
1637
1638
1639## 已知限制
1640
1641
1642### C API中初始化ffrt对象后,对象的置空与销毁由用户负责
1643
1644* 为保证较高的性能,ffrt的C API中内部不包含对对象的销毁状态的标记,用户需要合理地进行资源的释放,重复调用各个对象的destroy操作,其结果是未定义的。
1645* 错误示例1,重复调用destroy可能造成不可预知的数据损坏。
1646
1647```{.c}
1648#include "ffrt.h"
1649void abnormal_case_1()
1650{
1651    ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
1652    ...
1653    ffrt_task_handle_destroy(h);
1654    ffrt_task_handle_destroy(h); // double free
1655}
1656```
1657
1658* 错误示例2,未调用destroy会造成内存泄漏
1659
1660```{.c}
1661#include "ffrt.h"
1662void abnormal_case_2()
1663{
1664    ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
1665    ...
1666    // memory leak
1667}
1668```
1669
1670* 建议示例,仅调用一次destroy,如有必要可进行置空
1671
1672```{.c}
1673#include "ffrt.h"
1674void normal_case()
1675{
1676    ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
1677    ...
1678    ffrt_task_handle_destroy(h);
1679    h = nullptr; // if necessary
1680}
1681```
1682