• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1## FFRT 用户编程指南
2
3> Function Flow编程模型是一种基于任务和数据驱动的并发编程模型,允许开发者通过任务及其依赖关系描述的方式进行应用开发。FFRT(Function Flow运行时)是支持Function Flow编程模型的软件运行时库,用于调度执行开发者基于Function Flow编程模型开发的应用。通过Function Flow编程模型和FFRT,开发者可专注于应用功能开发,由FFRT在运行时根据任务依赖状态和可用执行资源自动并发调度和执行任务。
4>
5> 本文用于指导开发者基于Function Flow编程模型和FFRT实现并行编程。
6
7<hr/>
8# 版本
9
10| 版本 | 编辑                                                         | 主要变更                                                     | 日期       |
11| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ---------- |
12| V0.1 | linjiashu <br />zhangguowei <br />huangyouzhong  | 发布以下API:<br />1. task 管理,包括:submit,wait,task_attr, task_handle/submit_h<br />2. 同步原语,包括:mutex,condition_variable<br />3. Deadline 调度<br />4. 杂项:sleep,yield<br /> | 2022/09/26 |
13| V0.1.1 | shengxia  | 部分描述更新 | 2023/08/24 |
14| V0.1.2 | wanghuachen  | 新增串行队列相关接口以及说明,增加规范以避免double free问题 | 2023/10/07 |
15| V0.1.3 | shengxia  | 优化串行队列内容描述 | 2024/01/12 |
16
17<br/>
18
19<hr/>
20# 缩写
21
22| 缩略语        | 英文全名                        | 中文解释                                                     |
23| ------------- | ------------------------------- | ------------------------------------------------------------ |
24| FFRT          | Function Flow Run Time          | 软件实现Function Flow运行时用于任务调度和执行                |
25| Function Flow | Function Flow Programming Model | Function Flow编程模型                                        |
26| Pure Function | Pure Function                   | 纯函数,注意本文中定义的纯函数指的是通过表达相互间数据依赖即可由调度系统保证正确执行的任务。 |
27
28
29<br/>
30<hr/>
31# 编程模型
32## 两种编程模型
33
34|                | 线程编程模型                                                 | FFRT任务编程模型                                             |
35| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
36| 并行度挖掘方式 | 程序员通过创建多线程并把任务分配到每个线程中执行来挖掘运行时的并行度 | 程序员(编译器工具或语言特性配合)静态编程时将应用分解成任务及其数据依赖关系,运行时调度器分配任务到工作线程执行 |
37| 谁负责线程创建 | 程序员负责创建线程,线程编程模型无法约束线程的创建,滥用可能造成系统中大量线程 | FFRT运行时负责工作线程池的创建和管理由调度器负责,程序员无法直接创建线程 |
38| 负载均衡       | 程序员静态编程时将任务映射到线程,映射不合理或任务执行时间不确定造成线程负载不均 | FFRT运行时根据线程执行状态调度就绪任务到空闲线程执行,减轻了线程负载不均问题 |
39| 调度开销       | 线程调度由内核态调度器完成,调度开销大                       | FFRT运行时在用户态以协程方式调度执行,相比内核线程调度机制更为轻量,减小调度的开销,并可通过硬化调度卸载进一步减小调度开销 |
40| 依赖表达       | 线程创建时即处于可执行状态,执行时与其他线程同步操作,增加线程切换 | FFRT运行时根据任务创建时显式表达的输入依赖和输出依赖关系判断任务可执行状态,当输入依赖不满足时,任务不被调度执行 |
41
42
43
44## Function Flow 任务编程模型
45
46Function Flow编程模型允许开发者通过任务及其依赖关系描述的方式进行应用开发,其主要特性包括`Task-Based` 和 `Data-Driven` 。
47
48### Task-Based 特性
49
50`Task-Based` 指在Function Flow编程模型中开发者以任务方式来组织应用程序表达,运行时以任务粒度执行调度。
51
52任务定义为一种面向开发者的编程线索和面向运行时的执行对象,通常包含一组指令序列及其操作的数据上下文环境。
53
54Function Flow编程模型中的任务包含以下主要特征:
55
56- 任务之间可指定依赖关系,依赖关系通过`Data-Driven`方式表达。
57- 任务可支持嵌套,即任务在执行过程中可生成新的任务下发给运行时,形成父子任务关系。
58- 多任务支持互同步操作,例如等待,锁,条件变量等。
59
60> 注意
61>
62> 任务颗粒度影响应用执行性能,颗粒度过小增加调度开销,颗粒度过大降低并行度。Function Flow编程模型中任务的目标颗粒度最小为100us量级,开发者应注意合理控制任务颗粒度。
63
64### Data-Driven 特性
65
66`Data-Driven`指任务之间的依赖关系通过数据依赖表达。
67
68任务执行过程中对其关联的数据对象进行读写操作。在Function Flow编程模型中,数据对象表达抽象为数据签名,每个数据签名唯一对应一个数据对象。
69
70数据依赖抽象为任务所操作的数据对象的数据签名列表,包括输入数据依赖`in_deps`和输出数据依赖`out_deps`。数据对象的签名出现在一个任务的`in_deps`中时,该任务称为数据对象的消费者任务,消费者任务执行不改变其输入数据对象的内容;数据对象的签名出现在任务的`out_deps`中时,该任务称为数据对象的生产者任务,生产者任务执行改变其输出数据对象的内容,从而生成该数据对象的一个新的版本。
71
72一个数据对象可能存在多个版本,每个版本对应一个生产者任务和零个,一个或多个消费者任务,根据生产者任务和消费者任务的下发顺序定义数据对象的多个版本的顺序以及每个版本所对应的生产者和消费者任务。
73
74数据依赖解除的任务进入就绪状态允许被调度执行,依赖解除状态指任务所有输入数据对象版本的生产者任务执行完成,且所有输出数据对象版本的所有消费者任务执行完成的状态。
75
76通过上述`Data-Driven`的数据依赖表达,FFRT在运行时可动态构建任务之间的基于生产者/消费者的数据依赖关系并遵循任务数据依赖状态执行调度,包括:
77
78- Producer-Consumer 依赖
79
80  一个数据对象版本的生产者任务和该数据对象版本的消费者任务之间形成的依赖关系,也称为Read-after-Write依赖。
81
82- Consumer-Producer 依赖
83
84  一个数据对象版本的消费者任务和该数据对象的下一个版本的生产者任务之间形成的依赖关系,也称为Write-after-Read依赖。
85
86- Producer-Producer 依赖
87
88  一个数据对象版本的生产者任务和该数据对象的下一个版本的生产者任务之间形成的依赖关系,也称为Write-after-Write依赖。
89
90
91例如,如果有这么一些任务,与数据A的关系表述为:
92```{.cpp}
93task1(OUT A);
94task2(IN A);
95task3(IN A);
96task4(OUT A);
97task5(OUT A);
98```
99
100<img src="images/image-20220926150341102.png" style="zoom:60%" />
101
102> 为表述方便,本文中的数据流图均以圆圈表示 Task,方块表示数据。
103
104可以得出以下结论:
105- task1 与task2/task3 构成Producer-Consumer 依赖,即:task2/task3 需要等到task1 写完A之后才能读A
106- task2/task3 与task4 构成Consumer-Producer 依赖,即:task4 需要等到task2/task3 读完A之后才能写A
107- task4 与task5 构成Producer-Producer 依赖,即:task5 需要等到task4 写完A之后才能写A
108
109
110
111# C++ API
112
113> C++ API采用接近C++11的命名风格,以`ffrt`命名空间替代`std`命名空间
114> 需编译使用-std=c++17
115
116## 任务管理
117### submit
118<hr/>
119* 向调度器提交一个task
120* 该接口是异步的,即该接口不等到task完成即可返回,因此,通常与[wait](#wait) 配合使用
121
122#### 声明
123
124```{.cpp}
125namespace ffrt {
126void submit(std::function<void()>&& func, const std::vector<const void*>& in_deps = {}, const std::vector<const void*>& out_deps = {}, const task_attr& attr = {});
127void submit(const std::function<void()>& func, const std::vector<const void*>& in_deps = {}, const std::vector<const void*>& out_deps = {}, const task_attr& attr = {});
128}
129```
130
131#### 参数
132
133`func`
134
135* 可被std::function 接收的一切CPU 可执行体,可以为C++ 定义的Lambda 函数闭包,函数指针,甚至是函数对象
136
137`in_deps`
138
139* 该参数是可选的
140* 该参数用于描述该任务的输入依赖,FFRT 通过数据的虚拟地址作为数据的Signature 来建立依赖
141
142`out_deps`
143
144* 该参数是可选的
145* 该参数用于描述该任务的输出依赖
146* `注意`:该依赖值本质上是一个数值,ffrt没办法区分该值是合理的还是不合理的,会假定输入的值是合理的进行处理;但不建议采用NULL,1, 2 等值来建立依赖关系,建议采用实际的内存地址,因为前者使用不当会建立起不必要的依赖,影响并发
147
148`attr`
149
150* 该参数是可选的
151* 该参数用于描述Task 的属性,比如qos 等,详见 [task_attr](#task_attr)章节
152
153#### 返回值
154
155* 不涉及
156
157#### 描述
158* 该接口支持在FFRT task 内部调用,也支持在FFRT task 外部调用
159
160* 该接口支持嵌套调用,即任务中可以继续提交子任务
161
162* 该接口在实现上使用多个重载版本以优化性能(基于移动语义,初始化列表等),用户只需按上述原型使用,编译时会自动选择最佳版本,支持的重载版本有:
163
164  ```{.cpp}
165  void submit(std::function<void()>&& func);
166  void submit(std::function<void()>&& func, std::initializer_list<const void*> in_deps);
167  void submit(std::function<void()>&& func, std::initializer_list<const void*> in_deps, std::initializer_list<const void*> out_deps);
168  void submit(std::function<void()>&& func, std::initializer_list<const void*> in_deps, std::initializer_list<const void*> out_deps, const task_attr& attr);
169
170  void submit(std::function<void()>&& func, const std::vector<const void*>& in_deps);
171  void submit(std::function<void()>&& func, const std::vector<const void*>& in_deps, const std::vector<const void*>& out_deps);
172  void submit(std::function<void()>&& func, const std::vector<const void*>& in_deps, const std::vector<const void*>& out_deps, const task_attr& attr);
173
174  void submit(const std::function<void()>& func);
175  void submit(const std::function<void()>& func, std::initializer_list<const void*> in_deps);
176  void submit(const std::function<void()>& func, std::initializer_list<const void*> in_deps, std::initializer_list<const void*> out_deps);
177  void submit(const std::function<void()>& func, std::initializer_list<const void*> in_deps, std::initializer_list<const void*> out_deps, const task_attr& attr);
178
179  void submit(const std::function<void()>& func, const std::vector<const void*>& in_deps);
180  void submit(const std::function<void()>& func, const std::vector<const void*>& in_deps, const std::vector<const void*>& out_deps);
181  void submit(const std::function<void()>& func, const std::vector<const void*>& in_deps, const std::vector<const void*>& out_deps, const task_attr& attr);
182  ```
183
184
185#### 样例
186
187**submit and wait**
188
189```{.cpp}
190#include <iostream>
191#include "ffrt.h"
192
193int main(int narg, char** argv)
194{
195    int i = 0;
196    for (i = 0; i < 3; i++) {
197        ffrt::submit([i] { std::cout << "num: " << i << std::endl; });
198    }
199    ffrt::wait();
200    return 0;
201}
202```
203
204`解析`:
205
2061)   该示例中连续下发了3个Task,Task 使用C++ 11 Lambda 来描述(实际中Task 还可以使用函数指针,函数对象来描述),这些 Task 都会读取i,但是不会写任何变量;
207
2082)   ffrt::submit 为异步下发,因此,Task2 并不会等到 Task1 执行完成之后再下发;
209
2103)   ffrt::wait 用于实现待所有Task 都执行完成之后 main 函数再退出;
211
2124)   由于3个Task 在数据依赖关系上没有生产者-消费者或生产者-生产者依赖关系,因此3个 Task 是可以并行的,1种可能的输出是:
213
214```{.cpp}
215num: 0
216num: 2
217num: 1
218```
219
220`注意`:
221
222如果将Lambda 表达式中的值捕获设置成引用捕获(即`[&i] { std::cout << "num: " << i << std::endl; }`),可能得到的输出为:
223
224```{.cpp}
225num: 2
226num: 2
227num: 2
228```
229
230这是因为FFRT 是异步编程模型,在第一个task 真正开始执行的时候,i 的值可能已经被修改为1或者2
231
232
233
234**data verison**
235
236<img src="images/image-20220926150341102.png" style="zoom:60%" />
237
238```{.cpp}
239#include <iostream>
240#include "ffrt.h"
241
242int main(int narg, char** argv)
243{
244    int x = 1;
245    ffrt::submit([&] {x = 100; std::cout << "x:" << x << std::endl;}, {}, {&x});
246    ffrt::submit([&] {std::cout << "x:" << x << std::endl;}, {&x}, {});
247    ffrt::submit([&] {std::cout << "x:" << x << std::endl;}, {&x}, {});
248    ffrt::submit([&] {x++; std::cout << "x:" << x << std::endl;}, {}, {&x});
249    ffrt::submit([&] {x++; std::cout << "x:" << x << std::endl;}, {}, {&x});
250
251    ffrt::wait();
252    return 0;
253}
254```
255
256 `解析`:
257
2581)   按上一章节[Data-Driven 特性](#Data-Driven 特性)的描述,输出一定为:
259
260```{.cpp}
261x:100
262x:100
263x:100
264x:101
265x:102
266```
267
268**nested task**
269
270```{.cpp}
271#include <iostream>
272#include "ffrt.h"
273
274int main(int narg, char** argv)
275{
276    ffrt::submit([&] {
277        std::cout << "task 1" << std::endl;
278        ffrt::submit([&] {std::cout << "nested task 1.1" << std::endl;}, {}, {});
279        ffrt::submit([&] {std::cout << "nested task 1.2" << std::endl;}, {}, {});
280        ffrt::wait();
281    }, {}, {});
282
283    ffrt::submit([&] {
284        std::cout << "task 2" << std::endl;
285        ffrt::submit([&] {std::cout << "nested task 2.1" << std::endl;}, {}, {});
286        ffrt::submit([&] {std::cout << "nested task 2.2" << std::endl;}, {}, {});
287        ffrt::wait();
288    }, {}, {});
289    ffrt::wait();
290    return 0;
291}
292```
293
294 `解析`:
295
2961)   FFRT允许在 Task 内部继续提交多个SubTask,这样 Task 之间可以建立起一颗调用树;
297
2982)   Task1 和Task2 可以并行,Task 1.1/1.2/2.1/2.2 之间也可以并行,因此1种可行的输出为:
299
300```
301task 1
302nested task  1.1
303task 2
304nested task 1.2
305nested task 2.2
306nested task 2.1
307```
308
309### wait
310<hr/>
311* 同步等待,与[submit](#submit) 配合使用
312* 等待指定的数据被生产完成,或等待当前任务的所有子任务完成,在不满足条件之前,当前的执行上下文被suspend,在满足条件后恢复执行
313
314#### 声明
315
316```{.cpp}
317namespace ffrt {
318void wait(const std::vector<const void*>& deps);
319void wait();
320}
321```
322
323#### 参数
324
325`deps`
326
327* 需要等待被生产完成的数据的虚拟地址,这些地址可能作为某些任务在submit 时的out_deps
328
329#### 返回值
330
331* 不涉及
332
333#### 描述
334* wait(deps) 用于等待deps指代的数据被生产完成才能执行后面的代码
335* wait() 用于等待当前上下文提交的所有子任务(`注意:不包括孙子任务`)都完成才能执行后面的代码
336* 该接口支持在FFRT task 内部调用,也支持在FFRT task 外部调用
337* 在FFRT task 外部调用的wait 是OS 能够感知的等待,相对于FFRT task 内部调用的wait 是更加昂贵的,因此我们希望尽可能让更多的wait 发生在FFRT task 内部 ,而不是FFRT task 外部
338
339#### 样例
340
341**recursive fibonacci**
342
343串行版的fibonacci 可以实现为:
344
345```{.cpp}
346#include <iostream>
347
348void fib(int x, int& y) {
349    if (x <= 1) {
350        y = x;
351    } else {
352        int y1, y2;
353        fib(x - 1, y1);
354        fib(x - 2, y2);
355        y = y1 + y2;
356    }
357}
358
359int main(int narg, char** argv)
360{
361    int r;
362    fib(10, r);
363    std::cout << "fibonacci 10: " << r << std::endl;
364    return 0;
365}
366```
367
368若要使用 FFRT 实现并行(注,对于单纯的fibonacci,单个 Task 的计算量极小,不具有并行加速的意义,但这种调用pattern 对并行编程模型的灵活性考验是非常高的),其中1种可行的实现为:
369
370```{.cpp}
371#include <iostream>
372
373#include "ffrt.h"
374
375void fib_ffrt(int x, int& y)
376{
377    if (x <= 1) {
378        y = x;
379    } else {
380        int y1, y2;
381        ffrt::submit([&] {fib_ffrt(x - 1, y1);}, {&x}, {&y1} );
382        ffrt::submit([&] {fib_ffrt(x - 2, y2);}, {&x}, {&y2} );
383        ffrt::wait({&y1, &y2});
384        y = y1 + y2;
385    }
386}
387
388int main(int narg, char** argv)
389{
390    int r;
391    ffrt::submit([&] { fib_ffrt(10, r); }, {}, {&r});
392    ffrt::wait({&r});
393    std::cout << "fibonacci 10: " << r << std::endl;
394    return 0;
395}
396```
397
398`解析`:
399
4001)   将fibonacci (x-1)和fibonacci (x-2) 作为2个Task 提交给FFRT,在两个Task 完成之后将结果累加;
401
4022)   虽然单个Task 只能拆分成2个SubTask 但是子Task 可以继续拆分,因此,整个计算图的并行度是非常高的,Task 之间在FFRT 内部形成了一颗调用树;
403
404<img src="images/image-20220926152331554.png" style="zoom:100%" />
405
406
407### task_attr
408<hr/>
409* 定义task 的属性的辅助类,与[submit](#submit) 配合使用
410
411#### 声明
412
413```{.cpp}
414namespace ffrt {
415enum qos {
416    qos_inherit = -1,
417    qos_background,
418    qos_utility,
419    qos_default,
420    qos_user_initiated,
421};
422
423class task_attr {
424public:
425    task_attr& qos(enum qos qos); // set qos
426    enum qos qos() const; // get qos
427    task_attr& name(const char* name); // set name
428    const char* name() const; // get name
429};
430}
431```
432
433#### 参数
434
435`qos`
436
437* qos 设定的枚举类型
438* inherent 是一个qos 设定策略,代表即将submit 的task 的qos 继承当前task 的qos
439
440#### 返回值
441
442* 不涉及
443
444#### 描述
445* 约定
446  * 在submit 时,如果不通过task_attr 设定qos,那么默认该提交的task的qos 为`qos_default`
447  * 在submit 时,如果通过task_attr 设定qos 为`qos_inherent`,表示将该提交的task 的qos 与当前task 的qos 相同,在FFRT task 外部提交的属性为`qos_inherent` 的task,其qos 为`qos_default`
448  * 其他情况下,该提交的task 的qos 被设定为指定的值
449* qos 级别从上到下依次递增,qos_user_interactive拥有最高优先级
450
451#### 样例
452
453```{.cpp}
454#include <iostream>
455#include "ffrt.h"
456
457int main(int narg, char** argv)
458{
459    ffrt::submit([] { std::cout << "hello ffrt" << std::endl; }, {}, {},
460        ffrt::task_attr().qos(ffrt::qos_background));
461    ffrt::wait();
462    return 0;
463}
464```
465
466* 提交一个qos 级别为background 的任务
467
468
469
470### submit_h
471
472<hr/>
473
474* 向调度器提交一个task,与[submit](#submit) 的差别在于返回task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步
475
476#### 声明
477
478```{.cpp}
479namespace ffrt {
480class task_handle {
481public:
482    task_handle();
483    task_handle(ffrt_task_handle_t p);
484
485    task_handle(task_handle const&) = delete;
486    void operator=(task_handle const&) = delete;
487
488    task_handle(task_handle&& h);
489    task_handle& operator=(task_handle&& h);
490
491    operator void* () const;
492};
493
494task_handle submit_h(std::function<void()>&& func, const std::vector<const void*>& in_deps = {}, const std::vector<const void*>& out_deps = {}, const task_attr& attr = {});
495task_handle submit_h(const std::function<void()>& func, const std::vector<const void*>& in_deps = {}, const std::vector<const void*>& out_deps = {}, const task_attr& attr = {});
496}
497```
498
499#### 参数
500
501`func`
502
503* 同submit,详见[submit](#submit) 定义
504
505`in_deps`
506
507* 同submit,详见[submit](#submit) 定义
508
509`out_deps`
510
511* 同submit,详见[submit](#submit) 定义
512
513`attr`
514
515* 同submit,详见[submit](#submit) 定义
516
517#### 返回值
518
519* task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步
520
521#### 描述
522
523* 该接口与submit 使用基本相同,从性能的角度,在不需要返回task handle 的场景,可以调用submit 接口相对于submit_h 有更好的性能
524* task_handle 可以和其他的数据depends 同时作为某个task 的in_deps,表示该task 的执行依赖task_handle 对应的task 执行完成
525* task_handle 可以和其他的数据depends 同时作为wait 的deps,表示当前任务将被suspend,直到task_handle 对应的task 执行完成后将被恢复
526* task_handle 不建议作为某个task 的out_deps,其行为是未定义的
527
528
529#### 样例
530
531```{.cpp}
532#include <iostream>
533#include "ffrt.h"
534
535int main(int narg, char** argv)
536{
537    // handle work with submit
538    ffrt::task_handle h = ffrt::submit_h([] { std::cout << "hello "; }); // not need some data in this task
539    int x = 1;
540    ffrt::submit([&] { x++; }, {}, {&x});
541    ffrt::submit([&] { std::cout << "world, x = " << x << std::endl; }, {&x, h}); // this task depend x and h
542
543    // handle work with wait
544    ffrt::task_handle h2 = ffrt::submit_h([&] { std::cout << "handle wait" << std::endl; x++; });
545    ffrt::wait({h2});
546    std::cout << "x = " << x << std::endl;
547    ffrt::wait();
548    return 0;
549}
550```
551
552* 预期的输出为
553
554```
555hello world, x = 2
556handle wait
557x = 3
558```
559
560### get_id
561
562<hr/>
563
564* 返回当前task的id标识,更多使用用于维测(原因是task name可能重名)
565
566#### 声明
567
568```{.cpp}
569namespace ffrt {
570namespace this_task {
571uint64_t get_id();
572}
573}
574```
575
576#### 参数
577
578* 不涉及
579
580#### 返回值
581
582* 当前task的id
583
584#### 描述
585
586* 该接口在task内部调用将返回当前task的id标识,在task外部调用将返回0
587* 可以基于该接口在task外部调用返回0的特性来区分函数是运行在FFRT 工作线程上还是非FFRT工作线程上
588* task id为从1开始编码,每提交一个task便增加1,被设计成64bit,即便是每秒百万次提交,也需要292471.2年才会发生翻转
589
590#### 样例
591
592```{.cpp}
593#include <iostream>
594#include "ffrt.h"
595
596int main(int narg, char** argv)
597{
598    ffrt::submit([] { std::cout << "task id: " << ffrt::this_task::get_id() << std::endl; });
599    ffrt::submit([] { std::cout <<"task id: " << ffrt::this_task::get_id() << std::endl; });
600    ffrt::wait();
601    std::cout << "task id: " << ffrt::this_task::get_id() << std::endl;
602    return 0;
603}
604```
605
606* 可能的输出为:
607
608```
609task id: 1
610task id: 2
611task id: 0
612```
613
614### update_qos
615
616<hr/>
617
618* 更新当前正在执行的task的优先级
619
620#### 声明
621
622```{.cpp}
623namespace ffrt {
624namespace this_task {
625int update_qos(enum qos qos);
626}
627}
628```
629
630#### 参数
631
632`qos`
633* 新的qos等级
634
635#### 返回值
636
637* 0表示成功,非0表示失败
638
639#### 描述
640
641* 该接口对当前task的qos调整会立即生效
642* 如果新设定的qos与当前的qos不一致,则会block当前task的执行,再按照新的qos恢复执行
643* 如果新设定的qos与当前的qos一致,则接口会立即返回,不做任何处理
644* **如果在非task内部调用该接口,则返回非0值,用户可以选择忽略或其他处理**
645
646#### 样例
647
648```{.cpp}
649#include <iostream>
650#include <thread>
651#include "ffrt.h"
652
653int main(int narg, char** argv)
654{
655    ffrt::submit([] {
656        std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
657        std::cout << "return " << ffrt::this_task::update_qos(ffrt::qos_user_initiated) << std::endl;
658        std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
659    });
660    ffrt::wait();
661    std::cout << "return " << ffrt::this_task::update_qos(ffrt::qos_user_initiated) << std::endl;
662    return 0;
663}
664```
665
666* 可能的输出为:
667
668```
669thread id: 1024
670return 0
671thread id: 2222
672return 1
673```
674
675
676
677## 串行队列
678<hr />
679* FFRT提供queue来实现Andorid中类似WorkQueue能力,且在使用得当的情况下将有更好的性能
680
681* 串行队列支持提交延时任务,提交后会等待大于等于设定的延时时间再执行。当系统资源充分时,满足ms级精度要求;当系统资源紧张时,串行任务只保证计算顺序
682
683* 串行队列的watchdog机制,串行任务执行超过30s会上报超时,创建串行任务时注意不要提交会执行超过30s的任务,例如死循环任务
684
685### submit
686<hr/>
687
688#### 声明
689```{.c}
690namespace ffrt {
691void submit(std::function<void()>& func, const task_attr& attr);
692void submit(std::function<void()>&& func);
693void submit(std::function<void()>&& func, const task_attr& attr);
694}
695```
696
697#### 参数
698`func`
699* 可被std::function接收的一切CPU可执行体,可以为C++定义的Lambda函数闭包,函数指针,甚至是函数对象
700
701`attr`
702* 该参数是可选的
703* 该参数用于描述Task的属性,比如qos等,详见task_attr章节
704
705#### 返回值
706* 不涉及
707
708#### 描述
709* 提交一个任务到队列中调度执行
710
711#### 样例
712```
713#include <stdio.h>
714#include "ffrt.h"
715
716using namespace ffrt;
717using namespace std;
718
719int main(int narg, char** argv)
720{
721    queue* testQueue = new queue("test_queue");
722
723    int x = 0;
724    testQueue->submit([&x] { x += 10; });
725    usleep(100);
726
727    delete testQueue;
728}
729```
730
731### submit_h
732<hr/>
733
734#### 声明
735```{.c}
736namespace ffrt {
737task_handle submit_h(std::function<void()>& func);
738task_handle submit_h(std::function<void()>& func, const task_attr& attr);
739task_handle submit_h(std::function<void()>&& func);
740task_handle submit_h(std::function<void()>&& func, const task_attr& attr);
741}
742```
743
744#### 参数
745`func`
746* 可被std::function接收的一切CPU可执行体,可以为C++定义的Lambda函数闭包,函数指针,甚至是函数对象
747
748`attr`
749* 该参数是可选的
750* 该参数用于描述Task的属性,比如qos等,详见task_attr章节
751
752#### 返回值
753`task_handle`
754* task的句柄,该句柄可以用于建立task之间的依赖
755
756#### 描述
757* 提交一个任务到队列中调度执行,并返回一个句柄
758
759#### 样例
760```
761#include <stdio.h>
762#include "ffrt.h"
763
764using namespace ffrt;
765using namespace std;
766
767int main(int narg, char** argv)
768{
769    queue* testQueue = new queue("test_queue");
770
771    int x = 0;
772    testQueue->submit([&x] { x += 10; });
773    task_handle task = testQueue->submit_h([&x] { x += 10; });
774    testQueue->wait(task);
775
776    delete testQueue;
777}
778```
779
780### cancel
781<hr/>
782
783#### 声明
784```{.c}
785namespace ffrt {
786int cancel(task_handle& handle);
787}
788```
789
790#### 参数
791`handle`
792* 任务的句柄
793
794#### 返回值
795* 若成功返回0,否则返回-1
796
797#### 描述
798* 根据句柄取消对应的任务。该任务若为延时任务且未到期望执行的时间则可以正常被取消;若欲取消无延时任务,则有概率取消失败。
799
800#### 样例
801```
802#include <stdio.h>
803#include "ffrt.h"
804
805using namespace ffrt;
806using namespace std;
807
808int main(int narg, char** argv)
809{
810    queue* testQueue = new queue("test_queue", queue_attr().qos(qos_utility));
811
812    int x = 0;
813    task_handle t1 = testQueue->submit_h([&x] { x += 10; });
814    task_handle t2 = testQueue->submit_h([&x] { x += 10; }, task_attr().delay(1000));
815    testQueue->wait(t1);
816    int ret = testQueue->cancel(t2);
817
818    delete testQueue;
819}
820```
821
822### wait
823<hr/>
824
825#### 声明
826```{.c}
827namespace ffrt {
828void wait(task_handle& handle);
829}
830```
831
832#### 参数
833`handle`
834* 任务的句柄
835
836#### 返回值
837* 不涉及
838
839#### 描述
840* 等待句柄对应的任务执行完成
841
842#### 样例
843* 见[submit_h](#submit_h)章节样例
844
845### qos
846<hr/>
847
848#### 声明
849```{.c}
850namespace ffrt {
851queue_attr& qos(qos qos_){
852    ffrt_queue_attr_set_qos(this, qos_);
853    return *this;
854}
855}
856```
857
858#### 参数
859`qos_`
860* 见[ffrt_queue_attr_set_qos](#ffrt_queue_attr_set_qos)
861
862#### 返回值
863* 见[ffrt_queue_attr_set_qos](#ffrt_queue_attr_set_qos)
864
865#### 描述
866* 设置串行队列qos属性
867
868#### 样例
869* 见[ffrt_queue_t](#ffrt_queue_t)章节样例
870
871
872### qos
873<hr/>
874
875#### 声明
876```{.c}
877namespace ffrt {
878int qos() const {
879    return ffrt_queue_attr_get_qos(this);
880}
881}
882```
883
884#### 参数
885* 无
886
887#### 返回值
888* 同ffrt_queue_attr_get_qos,详见[ffrt_queue_attr_get_qos](#ffrt_queue_attr_get_qos)
889
890#### 描述
891* 获取串行队列qos属性,默认为default等级
892
893#### 样例
894* 见[ffrt_queue_t](#ffrt_queue_t)章节样例
895
896
897### timeout
898<hr/>
899
900#### 声明
901```{.c}
902namespace ffrt {
903queue_attr& timeout(uint64_t timeout_us) {
904    ffrt_queue_attr_set_timeout(this, timeout_us);
905    return *this;
906}
907}
908```
909
910#### 参数
911`timeout_us`
912* 见[ffrt_queue_attr_set_timeout](#ffrt_queue_attr_set_timeout)
913
914#### 返回值
915* 见[ffrt_queue_attr_set_timeout](#ffrt_queue_attr_set_timeout)
916
917#### 描述
918* 设置串行队列任务执行超时时间
919
920#### 样例
921* 见[ffrt_queue_attr_set_timeout](#ffrt_queue_attr_set_timeout)章节样例
922
923
924### timeout
925<hr/>
926
927#### 声明
928```{.c}
929namespace ffrt {
930uint64_t timeout() const {
931    return ffrt_queue_attr_get_timeout(this);
932}
933}
934```
935
936#### 参数
937* 无
938
939#### 返回值
940* 见[ffrt_queue_attr_get_timeout](#ffrt_queue_attr_get_timeout)
941
942#### 描述
943* 获取所设的串行队列任务执行超时时间
944
945#### 样例
946* 见[ffrt_queue_attr_set_timeout](#ffrt_queue_attr_set_timeout)章节样例
947
948
949### callback
950<hr/>
951
952#### 声明
953```{.c}
954namespace ffrt {
955queue_attr& callback(std::function<void()>& func) {
956    ffrt_queue_attr_set_callback(this, create_function_wrapper(func, ffrt_function_kind_queue));
957    return *this;
958}
959}
960```
961
962#### 参数
963`func`
964* 见[ffrt_queue_attr_set_callback](#ffrt_queue_attr_set_callback)
965
966#### 返回值
967* 见[ffrt_queue_attr_set_callback](#ffrt_queue_attr_set_callback)
968
969#### 描述
970* 设置所设的串行队列超时回调函数
971
972#### 样例
973* 见[ffrt_queue_attr_set_callback](#ffrt_queue_attr_set_callback)章节样例
974
975
976### callback
977<hr/>
978
979#### 声明
980```{.c}
981namespace ffrt {
982ffrt_function_header_t* callback() const {
983    return ffrt_queue_attr_get_callback(this);
984}
985}
986```
987
988#### 参数
989* 无
990
991#### 返回值
992* 见[ffrt_queue_attr_get_callback](#ffrt_queue_attr_get_callback)
993
994#### 描述
995* 获取串行队列超时回调函数
996
997#### 样例
998* 见[ffrt_queue_attr_set_callback](#ffrt_queue_attr_set_callback)章节样例
999
1000
1001
1002
1003## 同步原语
1004
1005### mutex
1006<hr/>
1007* FFRT提供的类似std::mutex 的性能实现
1008
1009#### 声明
1010
1011```{.cpp}
1012namespace ffrt {
1013class mutex {
1014public:
1015    mutex(mutex const &) = delete;
1016    void operator =(mutex const &) = delete;
1017
1018    void lock();
1019    void unlock();
1020    bool try_lock();
1021};
1022}
1023```
1024
1025#### 参数
1026
1027* 不涉及
1028
1029#### 返回值
1030
1031* 不涉及
1032
1033#### 描述
1034* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
1035* 该功能能够避免传统的std::mutex 在抢不到锁时陷入内核的问题,在使用得当的条件下将会有更好的性能
1036
1037#### 样例
1038
1039```{.cpp}
1040#include <iostream>
1041#include "ffrt.h"
1042
1043void ffrt_mutex_task()
1044{
1045    int sum = 0;
1046    ffrt::mutex mtx;
1047    for (int i = 0; i < 10; i++) {
1048        ffrt::submit([&sum, i, &mtx] {
1049             mtx.lock();
1050             sum = sum + i;
1051             mtx.unlock();
1052        }, {}, {});
1053    }
1054    ffrt::wait();
1055    std::cout << "sum = " << sum << std::endl;
1056}
1057
1058int main(int narg, char** argv)
1059{
1060    int r;
1061    ffrt::submit(ffrt_mutex_task);
1062    ffrt::wait();
1063    return 0;
1064}
1065```
1066
1067预期输出为
1068
1069```
1070sum=45
1071```
1072
1073* 该例子为功能示例,实际中并不鼓励这样使用
1074
1075
1076### condition_variable
1077<hr/>
1078
1079* FFRT提供的类似std::condition_variable 的性能实现
1080
1081#### 声明
1082
1083```{.cpp}
1084namespace ffrt {
1085enum class cv_status {
1086    no_timeout,
1087    timeout
1088};
1089
1090class condition_variable {
1091public:
1092    using TimePoint = std::chrono::steady_clock::time_point;
1093    template<typename Clock, typename Duration, typename Pred>
1094    bool wait_until(std::unique_lock<mutex>& lk,
1095            const std::chrono::time_point<Clock, Duration>& tp,
1096            Pred&& pred) noexcept;
1097
1098    template<typename Clock, typename Duration>
1099    cv_status wait_until(std::unique_lock<mutex>& lk,
1100            const std::chrono::time_point<Clock, Duration>& tp) noexcept;
1101
1102    template<typename Rep, typename Period>
1103    cv_status wait_for(std::unique_lock<mutex>& lk,
1104            const std::chrono::duration<Rep, Period>& sleep_time) noexcept;
1105
1106    template<typename Rep, typename Period, typename Pred>
1107    bool wait_for(std::unique_lock<mutex>& lk,
1108            const std::chrono::duration<Rep, Period>& sleepTime,
1109            Pred&& pred) noexcept;
1110
1111    void wait(std::unique_lock<mutex>& lk);
1112
1113    template<typename Pred>
1114    void wait(std::unique_lock<mutex>& lk, Pred&& pred);
1115
1116    void notify_one() noexcept;
1117
1118    void notify_all() noexcept;
1119};
1120}
1121```
1122
1123#### 参数
1124
1125`lk`
1126* mutex互斥量
1127`tp`
1128* 等待时间
1129`sleep_time`
1130* 等待时间
1131`pred`
1132* 检查是否等待函数
1133#### 返回值
1134
1135* 不涉及
1136
1137#### 描述
1138* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
1139* 该功能能够避免传统的std::condition_variable  在条件不满足时陷入内核的问题,在使用得当的条件下将会有更好的性能
1140
1141#### 样例
1142
1143```{.cpp}
1144#include <iostream>
1145#include "ffrt.h"
1146
1147void ffrt_cv_task()
1148{
1149    ffrt::condition_variable cond;
1150    int a = 0;
1151    ffrt::mutex lock_;
1152    ffrt::submit([&] {
1153        std::unique_lock lck(lock_);
1154        cond.wait(lck, [&] { return a == 1; });
1155        std::cout << "a = " << a << std::endl;
1156    }, {}, {});
1157    ffrt::submit([&] {
1158        std::unique_lock lck(lock_);
1159        a = 1;
1160        cond.notify_one();
1161    }, {}, {});
1162
1163    ffrt::wait();
1164}
1165
1166int main(int narg, char** argv)
1167{
1168    int r;
1169    ffrt::submit(ffrt_cv_task);
1170    ffrt::wait();
1171    return 0;
1172}
1173
1174```
1175
1176预期输出为:
1177
1178```
1179a=1
1180```
1181
1182* 该例子为功能示例,实际中并不鼓励这样使用
1183
1184## 杂项
1185
1186### sleep
1187
1188<hr/>
1189* FFRT提供的类似std::this_thread::sleep_for / std::this_thread::sleep_until 的性能实现
1190
1191#### 声明
1192
1193```{.cpp}
1194namespace ffrt {
1195namespace this_task {
1196template<class _Rep, class _Period>
1197void sleep_for(const std::chrono::duration<_Rep, _Period>& sleep_duration);
1198
1199template<class _Clock, class _Duration>
1200void sleep_until(const std::chrono::time_point<_Clock, _Duration>& sleep_time);
1201}
1202}
1203```
1204
1205#### 参数
1206
1207`sleep_duration`
1208
1209* 睡眠的时长
1210
1211`sleep_time`
1212
1213* 睡眠到达的时间点
1214
1215#### 返回值
1216
1217* 不涉及
1218
1219#### 描述
1220* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
1221* 该功能能够避免传统的std::this_thread::sleep_for 睡眠时陷入内核的问题,在使用得当的条件下将会有更好的性能
1222* 该接口调用后实际睡眠时长不小于配置值
1223
1224#### 样例
1225
1226```{.cpp}
1227#include <chrono>
1228#include <iostream>
1229#include "ffrt.h"
1230
1231using namespace std::chrono_literals;
1232int main(int narg, char** argv)
1233{
1234    ffrt::submit([] {
1235        std::cout << "Hello waiter\n" << std::flush;
1236        auto start = std::chrono::high_resolution_clock::now();
1237        ffrt::this_task::sleep_for(2000ms);
1238        auto end = std::chrono::high_resolution_clock::now();
1239        std::chrono::duration<double, std::milli> elapsed = end-start;
1240        std::cout << "Waited " << elapsed.count() << " ms\n";
1241    });
1242    ffrt::wait();
1243    return 0;
1244}
1245```
1246
1247* 预期输出为
1248
1249```
1250Hello waiter
1251Waited 2000.12 ms
1252```
1253
1254### yield
1255<hr/>
1256* 当前task 主动让出CPU 执行资源,让其他可以被执行的task 先执行,如果没有其他可被执行的task,yield 无效
1257
1258#### 声明
1259
1260```{.cpp}
1261namespace ffrt {
1262namespace this_task {
1263void yield();
1264}
1265}
1266```
1267
1268#### 参数
1269
1270* 不涉及
1271
1272#### 返回值
1273
1274* 不涉及
1275
1276#### 描述
1277* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
1278* 此函数的确切行为取决于实现,特别是使用中的FFRT 调度程序的机制和系统状态
1279
1280#### 样例
1281
1282```{.cpp}
1283#include <chrono>
1284#include "ffrt.h"
1285
1286using namespace std::chrono_literals;
1287// "busy sleep" while suggesting that other tasks run
1288// for a small amount of time
1289void little_sleep(std::chrono::microseconds us)
1290{
1291    auto start = std::chrono::high_resolution_clock::now();
1292    auto end = start + us;
1293    do {
1294        ffrt::this_task::yield();
1295    } while (std::chrono::high_resolution_clock::now() < end);
1296}
1297
1298int main(int narg, char** argv)
1299{
1300    ffrt::submit([] { little_sleep(200us); });
1301    ffrt::wait();
1302    return 0;
1303}
1304```
1305
1306* 这是一个`busy sleep`,同时允许其他可以被执行的task 插入执行
1307
1308
1309# C API
1310
1311> C API采用接近C11/pthread (https://zh.cppreference.com/w/c) 的命名风格,并冠以`ffrt_`前缀,以`_base`为后缀的API是内部API,通常不被用户直接调用
1312>
1313> **出于易用性方面的考虑,除非必要,强烈建议你使用C++ API(亦满足二进制兼容要求),调用C API将会使你的代码非常臃肿**
1314
1315## 任务管理
1316
1317### ffrt_submit_base
1318
1319* 该接口为ffrt动态库的导出接口,基于此可以封装出不同的C++ API ffrt::submit和C API ffrt_submit,满足二进制兼容
1320
1321#### 声明
1322
1323```{.cpp}
1324const int ffrt_auto_managed_function_storage_size = 64 + sizeof(ffrt_function_header_t);
1325typedef enum {
1326    ffrt_function_kind_general,
1327    ffrt_function_kind_queue
1328} ffrt_function_kind_t;
1329
1330void* ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind);
1331
1332typedef void(*ffrt_function_t)(void*);
1333typedef struct {
1334    ffrt_function_t exec;
1335    ffrt_function_t destroy;
1336    uint64_t reserve[2];
1337} ffrt_function_header_t;
1338
1339void 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);
1340```
1341
1342#### 参数
1343
1344`kind`
1345
1346* function子类型,用于优化内部数据结构,默认使用ffrt_function_kind_general类型
1347
1348`func`
1349
1350* CPU Function的指针,该指针执行的数据结构,按照`ffrt_function_header_t`定义的描述了该CPU Task如何执行和销毁的函数指针,FFRT通过这两个函数指针完成Task的执行和销毁
1351
1352`in_deps`
1353
1354* 同ffrt_submit
1355
1356
1357`out_deps`
1358
1359* 同ffrt_submit
1360
1361`attr`
1362
1363* 同ffrt_submit
1364
1365#### 返回值
1366
1367* 不涉及
1368
1369#### 描述
1370
1371* ffrt_submit_base不建议用户直接调用,推荐使用基于此封装的C++接口(亦满足二进制兼容)
1372* **ffrt_submit_base作为底层能力,只有在用户需要自定义task类型时使用,使用时需要满足以下限制:**
1373  * ffrt_submit_base入参中的func指针只能通过ffrt_alloc_auto_managed_function_storage_base申请,且二者的调用需一一对应
1374  * ffrt_alloc_auto_managed_function_storage_base申请的内存为ffrt_auto_managed_function_storage_size字节,其生命周期归ffrt管理,在该task结束时,由FFRT自动释放,用户无需释放
1375* ffrt_function_header_t 中定义了两个函数指针:
1376  * exec:用于描述该Task如何被执行,当FFRT需要执行该Task时由FFRT调用
1377  * destroy:用于描述该Task如何被执行,当FFRT需要执行该Task时由FFRT调用
1378
1379#### 样例
1380
1381* 通过该接口提供C++11 Lambda表达式的支持(该代码已经在ffrr.h中提供,默认支持)
1382
1383```{.cpp}
1384template<class T>
1385struct function {
1386    template<class CT>
1387    function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {}
1388    ffrt_function_header_t header;
1389    T closure;
1390};
1391
1392template<class T>
1393void exec_function_wrapper(void* t)
1394{
1395    auto f = (function<std::decay_t<T>>*)t;
1396    f->closure();
1397}
1398
1399template<class T>
1400void destroy_function_wrapper(void* t)
1401{
1402    auto f = (function<std::decay_t<T>>*)t;
1403    f->closure = nullptr;
1404}
1405
1406template<class T>
1407inline ffrt_function_header_t* create_function_wrapper(T&& func)
1408{
1409    using function_type = function<std::decay_t<T>>;
1410    static_assert(sizeof(function_type) <= ffrt_auto_managed_function_storage_size,
1411        "size of function must be less than ffrt_auto_managed_function_storage_size");
1412
1413    auto p = ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1414    auto f = new (p) function_type(
1415        {exec_function_wrapper<T>, destroy_function_wrapper<T>},
1416        std::forward<T>(func));
1417    return (ffrt_function_header_t*)f;
1418}
1419
1420static inline void submit(std::function<void()>&& func)
1421{
1422    return ffrt_submit_base(create_function_wrapper(std::move(func)), NULL, NULL, NULL);
1423}
1424```
1425
1426### ffrt_wait
1427
1428<hr/>
1429* 同步等待,与ffrt_submit 配合使用
1430* 等待指定的数据被生产完成,或等待当前任务的所有子任务完成,在不满足条件之前,当前的执行上下文被suspend,在满足条件后恢复执行
1431
1432#### 声明
1433
1434```{.cpp}
1435void ffrt_wait_deps(ffrt_deps_t* deps);
1436void ffrt_wait();
1437```
1438
1439#### 参数
1440
1441`deps`
1442
1443* 需要等待被生产完成的数据的虚拟地址,这些地址可能作为某些任务在submit 时的out_deps,该依赖的生成见ffrt_deps_t章节,空指针表示无依赖
1444
1445#### 返回值
1446
1447* 不涉及
1448
1449#### 描述
1450* ffrt_wait_deps(deps) 用于等待deps指代的数据被生产完成才能执行后面的代码
1451* ffrt_wait() 用于等待当前上下文提交的所有子任务(`注意:不包括孙任务和下级子任务`)都完成才能执行后面的代码
1452* 该接口支持在FFRT task 内部调用,也支持在FFRT task 外部调用
1453* 在FFRT task 外部调用的wait 是OS 能够感知的等待,相对于FFRT task 内部调用的wait 是更加昂贵的,因此我们希望尽可能让更多的wait 发生在FFRT task 内部 ,而不是FFRT task 外部
1454
1455#### 样例
1456
1457**recursive fibonacci**
1458
1459串行版的fibonacci 可以实现为:
1460
1461```{.c}
1462#include <stdio.h>
1463
1464void fib(int x, int* y) {
1465    if (x <= 1) {
1466        *y = x;
1467    } else {
1468        int y1, y2;
1469        fib(x - 1, &y1);
1470        fib(x - 2, &y2);
1471        *y = y1 + y2;
1472    }
1473}
1474int main(int narg, char** argv)
1475{
1476    int r;
1477    fib(10, &r);
1478    printf("fibonacci 10: %d\n", r);
1479    return 0;
1480}
1481```
1482
1483若要使用 FFRT 实现并行(注,对于单纯的fibonacci,单个 Task 的计算量极小,不具有并行加速的意义,但这种调用pattern 对并行编程模型的灵活性考验是非常高的),其中1种可行的实现为:
1484
1485```{.c}
1486#include <stdio.h>
1487#include "ffrt.h"
1488
1489typedef struct {
1490    int x;
1491    int* y;
1492} fib_ffrt_s;
1493
1494typedef struct {
1495    ffrt_function_header_t header;
1496    ffrt_function_t func;
1497    ffrt_function_t after_func;
1498    void* arg;
1499} c_function;
1500
1501static void ffrt_exec_function_wrapper(void* t)
1502{
1503    c_function* f = (c_function*)t;
1504    if (f->func) {
1505        f->func(f->arg);
1506    }
1507}
1508
1509static void ffrt_destroy_function_wrapper(void* t)
1510{
1511    c_function* f = (c_function*)t;
1512    if (f->after_func) {
1513        f->after_func(f->arg);
1514    }
1515}
1516
1517#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
1518static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
1519    const ffrt_function_t after_func, void* arg)
1520{
1521    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
1522        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
1523    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1524    f->header.exec = ffrt_exec_function_wrapper;
1525    f->header.destroy = ffrt_destroy_function_wrapper;
1526    f->func = func;
1527    f->after_func = after_func;
1528    f->arg = arg;
1529    return (ffrt_function_header_t*)f;
1530}
1531
1532static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
1533    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1534{
1535    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1536}
1537
1538#define ffrt_deps_define(name, dep1, ...) const void* __v_##name[] = {dep1, ##__VA_ARGS__}; \
1539    ffrt_deps_t name = {sizeof(__v_##name) / sizeof(void*), __v_##name}
1540
1541void fib_ffrt(void* arg)
1542{
1543    fib_ffrt_s* p = (fib_ffrt_s*)arg;
1544    int x = p->x;
1545    int* y = p->y;
1546
1547    if (x <= 1) {
1548        *y = x;
1549    } else {
1550        int y1, y2;
1551        fib_ffrt_s s1 = {x - 1, &y1};
1552        fib_ffrt_s s2 = {x - 2, &y2};
1553        ffrt_deps_define(dx, &x);
1554        ffrt_deps_define(dy1, &y1);
1555        ffrt_deps_define(dy2, &y2);
1556        ffrt_deps_define(dy12, &y1, &y2);
1557        ffrt_submit_c(fib_ffrt, NULL, &s1, &dx, &dy1, NULL);
1558        ffrt_submit_c(fib_ffrt, NULL, &s2, &dx, &dy2, NULL);
1559        ffrt_wait_deps(&dy12);
1560        *y = y1 + y2;
1561    }
1562}
1563
1564int main(int narg, char** argv)
1565{
1566    int r;
1567    fib_ffrt_s s = {10, &r};
1568    ffrt_deps_define(dr, &r);
1569    ffrt_submit_c(fib_ffrt, NULL, &s, NULL, &dr, NULL);
1570    ffrt_wait_deps(&dr);
1571    printf("fibonacci 10: %d\n", r);
1572    return 0;
1573}
1574```
1575
1576`解析`:
1577
15781)   将fibonacci (x-1)和fibonacci (x-2) 作为2个Task 提交给FFRT,在两个Task 完成之后将结果累加;
1579
15802)   虽然单个Task 只能拆分成2个SubTask 但是子Task 可以继续拆分,因此,整个计算图的并行度是非常高的,Task 之间在FFRT 内部形成了一颗调用树;
1581
1582<img src="images/image-20220926152331554.png" style="zoom:100%" />
1583
1584> 以上实现,逻辑上虽与C++ API中的实现类似,但是用户显式管理数据生命周期和函数入参打包两个因素将使代码异常复杂
1585
1586
1587
1588### ffrt_deps_t
1589
1590* C API中对依赖数组的抽象,逻辑上等同于C++ API中的`std::vector<void*>`
1591
1592#### 声明
1593
1594```{.cpp}
1595typedef struct {
1596    uint32_t len;
1597    const void* const * items;
1598} ffrt_deps_t;
1599```
1600
1601#### 参数
1602
1603`len`
1604
1605* 所依赖的Signature的个数,取值大于等于0
1606
1607`item`
1608
1609* len个Signature的起始地址指针
1610
1611#### 返回值
1612
1613* 不涉及
1614
1615#### 描述
1616
1617* item为len个Signature的起始指针,该指针可以指向堆空间,也可以指向栈空间,但是要求分配的空间大于等于len * sizeof(void*)
1618
1619#### 样例
1620
1621* item指向栈空间的ffrt_deps_t
1622
1623```{.c}
1624#include "ffrt.h"
1625
1626int main(int narg, char** argv)
1627{
1628    int x1 = 1;
1629    int x2 = 2;
1630
1631    void *t[] = {&x1, &x2};
1632    ffrt_deps_t deps = {2, (const void* const *)&t};
1633    // some code use deps
1634    return 0;
1635}
1636```
1637
1638* item指向栈空间的ffrt_deps_t
1639
1640```{.c}
1641#include <stdlib.h>
1642#include "ffrt.h"
1643
1644int main(int narg, char** argv)
1645{
1646    int x1 = 1;
1647    int x2 = 2;
1648
1649    void** t = (void**)malloc(sizeof(void*) * 2);
1650    t[0]= &x1;
1651    t[1]= &x2;
1652    ffrt_deps_t deps = {2, t};
1653
1654    // some code use deps
1655    free(t);
1656    return 0;
1657}
1658```
1659
1660### ffrt_task_attr_t
1661
1662<hr/>
1663* 定义task 的属性的辅助类,与ffrt_submit 配合使用
1664
1665#### 声明
1666
1667```{.c}
1668typedef enum {
1669    ffrt_qos_inherent = -1,
1670    ffrt_qos_background,
1671    ffrt_qos_utility,
1672    ffrt_qos_default,
1673    ffrt_qos_user_initiated,
1674} ffrt_qos_t;
1675
1676typedef struct {
1677    char storage[ffrt_task_attr_storage_size];
1678} ffrt_task_attr_t;
1679typedef void* ffrt_task_handle_t;
1680
1681int ffrt_task_attr_init(ffrt_task_attr_t* attr);
1682void ffrt_task_attr_destroy(ffrt_task_attr_t* attr);
1683void ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos);
1684ffrt_qos_t ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr);
1685void ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name);
1686const char* ffrt_task_attr_get_name(const ffrt_task_attr_t* attr);
1687```
1688
1689#### 参数
1690
1691`attr`
1692
1693* 创建的tasks属性的句柄
1694
1695`qos`
1696
1697* qos 设定的枚举类型
1698* inherent 是一个qos 设定策略,代表即将ffrt_submit 的task 的qos 继承当前task 的qos
1699
1700#### 返回值
1701
1702* 不涉及
1703
1704#### 描述
1705* `attr`所传递的内容会在ffrt_submit内部完成取存,ffrt_submit返回后用户即可销毁
1706* 约定
1707  * 在submit 时,如果不通过task_attr 设定qos,那么默认该提交的task的qos 为`ffrt_qos_default`
1708  * 在submit 时,如果通过task_attr 设定qos 为`ffrt_qos_inherent`,表示将该提交的task 的qos 与当前task 的qos 相同,在FFRT task 外部提交的属性为`ffrt_qos_inherent` 的task,其qos 为`ffrt_qos_default`
1709  * 其他情况下,该提交的task 的qos 被设定为指定的值
1710* ffrt_task_attr_t对象的置空和销毁由用户完成,对同一个ffrt_task_attr_t仅能调用一次`ffrt_task_attr_destroy`,重复对同一个ffrt_task_attr_t调用`ffrt_task_attr_destroy`,其行为是未定义的
1711* 在`ffrt_task_attr_destroy`之后再对task_attr进行访问,其行为是未定义的
1712
1713#### 样例
1714
1715```{.c}
1716#include <stdio.h>
1717#include "ffrt.h"
1718
1719void my_print(void* arg)
1720{
1721    printf("hello ffrt\n");
1722}
1723
1724typedef struct {
1725    ffrt_function_header_t header;
1726    ffrt_function_t func;
1727    ffrt_function_t after_func;
1728    void* arg;
1729} c_function;
1730
1731static void ffrt_exec_function_wrapper(void* t)
1732{
1733    c_function* f = (c_function*)t;
1734    if (f->func) {
1735        f->func(f->arg);
1736    }
1737}
1738
1739static void ffrt_destroy_function_wrapper(void* t)
1740{
1741    c_function* f = (c_function*)t;
1742    if (f->after_func) {
1743        f->after_func(f->arg);
1744    }
1745}
1746
1747#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
1748static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
1749    const ffrt_function_t after_func, void* arg)
1750{
1751    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
1752        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
1753    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1754    f->header.exec = ffrt_exec_function_wrapper;
1755    f->header.destroy = ffrt_destroy_function_wrapper;
1756    f->func = func;
1757    f->after_func = after_func;
1758    f->arg = arg;
1759    return (ffrt_function_header_t*)f;
1760}
1761
1762static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
1763    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1764{
1765    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1766}
1767
1768int main(int narg, char** argv)
1769{
1770    ffrt_task_attr_t attr;
1771    ffrt_task_attr_init(&attr);
1772    ffrt_task_attr_set_qos(&attr, ffrt_qos_background);
1773    ffrt_submit_c(my_print, NULL, NULL, NULL, NULL, &attr);
1774    ffrt_task_attr_destroy(&attr);
1775    ffrt_wait();
1776    return 0;
1777}
1778```
1779
1780* 提交一个qos 级别为background 的任务
1781
1782
1783
1784### ffrt_submit_h
1785
1786<hr/>
1787
1788* 向调度器提交一个task,与ffrt_submit 的差别在于返回task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步
1789
1790#### 声明
1791
1792```{.cpp}
1793typedef void* ffrt_task_handle_t;
1794
1795ffrt_task_handle_t ffrt_submit_h(ffrt_function_t func, void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr);
1796void ffrt_task_handle_destroy(ffrt_task_handle_t handle);
1797```
1798
1799#### 参数
1800
1801`func`
1802
1803* 同ffrt_submit,详见[ffrt_submit](#ffrt_submit) 定义
1804
1805`in_deps`
1806
1807* 同ffrt_submit,详见[ffrt_submit](#ffrt_submit) 定义
1808
1809`out_deps`
1810
1811* 同ffrt_submit,详见[ffrt_submit](#ffrt_submit) 定义
1812
1813`attr`
1814
1815* 同ffrt_submit,详见[ffrt_submit](#ffrt_submit) 定义
1816
1817#### 返回值
1818
1819* task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步
1820
1821#### 描述
1822
1823* C API中的ffrt_task_handle_t的使用与C++ API中的ffrt::task_handle相同
1824* **差异在于:C API中的ffrt_task_handle_t需要用户调用`ffrt_task_handle_destroy`显式销毁,而C++ API无需该操作**
1825* C API中的task_handle_t对象的置空和销毁由用户完成,对同一个ffrt_task_handle_t仅能调用一次`ffrt_task_handle_destroy`,重复对同一个ffrt_task_handle_t调用`ffrt_task_handle_destroy`,其行为是未定义的
1826* 在`ffrt_task_handle_destroy`之后再对ffrt_task_handle_t进行访问,其行为是未定义的
1827
1828#### 样例
1829
1830```{.c}
1831#include <stdio.h>
1832#include "ffrt.h"
1833
1834void func0(void* arg)
1835{
1836    printf("hello ");
1837}
1838
1839void func1(void* arg)
1840{
1841    (*(int*)arg)++;
1842}
1843
1844void func2(void* arg)
1845{
1846    printf("world, x = %d\n", *(int*)arg);
1847}
1848
1849void func3(void* arg)
1850{
1851    printf("handle wait");
1852    (*(int*)arg)++;
1853}
1854
1855typedef struct {
1856    ffrt_function_header_t header;
1857    ffrt_function_t func;
1858    ffrt_function_t after_func;
1859    void* arg;
1860} c_function;
1861
1862static void ffrt_exec_function_wrapper(void* t)
1863{
1864    c_function* f = (c_function*)t;
1865    if (f->func) {
1866        f->func(f->arg);
1867    }
1868}
1869
1870static void ffrt_destroy_function_wrapper(void* t)
1871{
1872    c_function* f = (c_function*)t;
1873    if (f->after_func) {
1874        f->after_func(f->arg);
1875    }
1876}
1877
1878#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
1879static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
1880    const ffrt_function_t after_func, void* arg)
1881{
1882    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
1883        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
1884    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1885    f->header.exec = ffrt_exec_function_wrapper;
1886    f->header.destroy = ffrt_destroy_function_wrapper;
1887    f->func = func;
1888    f->after_func = after_func;
1889    f->arg = arg;
1890    return (ffrt_function_header_t*)f;
1891}
1892
1893static inline ffrt_task_handle_t ffrt_submit_h_c(ffrt_function_t func, const ffrt_function_t after_func,
1894    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1895{
1896    return ffrt_submit_h_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1897}
1898
1899static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
1900    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1901{
1902    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1903}
1904
1905#define ffrt_deps_define(name, dep1, ...) const void* __v_##name[] = {dep1, ##__VA_ARGS__}; \
1906    ffrt_deps_t name = {sizeof(__v_##name) / sizeof(void*), __v_##name}
1907
1908int main(int narg, char** argv)
1909{
1910    // handle work with submit
1911    ffrt_task_handle_t h = ffrt_submit_h_c(func0, NULL, NULL, NULL, NULL, NULL); // not need some data in this task
1912    int x = 1;
1913    ffrt_deps_define(d1, &x);
1914    ffrt_deps_define(d2, &x, h);
1915    ffrt_submit_c(func1, NULL, &x, NULL, &d1, NULL);
1916    ffrt_submit_c(func2, NULL, &x, &d2, NULL, NULL); // this task depend x and h
1917    ffrt_task_handle_destroy(h);
1918
1919    // handle work with wait
1920    ffrt_task_handle_t h2 = ffrt_submit_h_c(func3, NULL, &x, NULL, NULL, NULL);
1921    ffrt_deps_define(d3, h2);
1922    ffrt_wait_deps(&d3);
1923    ffrt_task_handle_destroy(h2);
1924    printf("x = %d", x);
1925    ffrt_wait();
1926    return 0;
1927}
1928```
1929
1930* 预期的输出为
1931
1932```
1933hello world, x = 2
1934handle wait
1935x = 3
1936```
1937
1938
1939
1940### ffrt_this_task_get_id
1941
1942<hr/>
1943
1944* 返回当前task的id标识,更多使用用于维测(原因是task name可能重名)
1945
1946#### 声明
1947
1948```{.c}
1949uint64_t ffrt_this_task_get_id();
1950```
1951
1952#### 参数
1953
1954* 不涉及
1955
1956#### 返回值
1957
1958* 当前task的id
1959
1960#### 描述
1961
1962* 该接口在task内部调用将返回当前task的id标识,在task外部调用将返回0
1963* 可以基于该接口在task外部调用返回0的特性来区分函数是运行在FFRT 工作线程上还是非FFRT工作线程上
1964* task id为从1开始编码,每提交一个task便增加1,被设计成64bit,即便是每秒百万次提交,也需要292471.2年才会发生翻转
1965
1966#### 样例
1967
1968* 忽略
1969
1970
1971
1972### ffrt_this_task_update_qos
1973
1974<hr/>
1975
1976* 更新当前正在执行的task的优先级
1977
1978#### 声明
1979
1980```{.cpp}
1981int ffrt_this_task_update_qos(ffrt_qos_t qos);
1982```
1983
1984#### 参数
1985
1986* `qos` 新的优先级
1987
1988#### 返回值
1989
1990* 0表示成功,非0表示失败
1991
1992#### 描述
1993
1994* 该接口对当前task的qos调整会立即生效
1995* 如果新设定的qos与当前的qos不一致,则会block当前task的执行,再按照新的qos恢复执行
1996* 如果新设定的qos与当前的qos一致,则接口会立即返回0,不做任何处理
1997* **如果在非task内部调用该接口,则返回非0值,用户可以选择忽略或其他处理**
1998
1999#### 样例
2000
2001* 忽略
2002
2003## 串行队列
2004<hr />
2005* FFRT提供queue来实现Andorid中类似WorkQueue能力,且在使用得当的情况下将有更好的性能
2006
2007### ffrt_queue_attr_t
2008
2009#### 声明
2010```{.c}
2011typedef struct {
2012    uint32_t storage[(ffrt_queue_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
2013} ffrt_queue_attr_t;
2014
2015int ffrt_queue_attr_init(ffrt_queue_attr_t* attr);
2016void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr);
2017```
2018
2019### 参数
2020
2021`attr`
2022* 该参数是指向未初始化的ffrt_queue_attr_t
2023
2024### 返回值
2025* 若成功返回0,否则返回-1
2026
2027### 描述
2028* ffrt_queue_attr_t用于创建ffrt_queue_t且不单独使用,因此必须在创建队列前先创建好队列属性
2029* ffrt_queue_attr_t对象的置空和销毁由用户完成,对同一个ffrt_queue_t仅能调用一次`ffrt_queue_attr_destroy`,重复对同一个ffrt_queue_t调用`ffrt_queue_attr_destroy`,其行为是未定义的
2030* 在`ffrt_queue_attr_destroy`之后再对ffrt_queue_t进行访问,其行为是未定义的
2031
2032### 样例
2033见[ffrt_queue_t](#ffrt_queue_t)章节的样例
2034
2035### ffrt_queue_t
2036<hr/>
2037
2038#### 声明
2039```{.c}
2040typedef enum { ffrt_queue_serial, ffrt_queue_max } ffrt_queue_type_t;
2041typedef void* ffrt_queue_t;
2042
2043ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr)
2044void ffrt_queue_destroy(ffrt_queue_t queue)
2045```
2046
2047### 参数
2048
2049`type`
2050* 该参数用于描述创建的队列类型
2051
2052`name`
2053* 该参数用于描述创建队列的名字
2054
2055`attr`
2056* 该参数用于描述queue的属性,详见[ffrt_queue_attr_t](#ffrt_queue_attr_t)章节
2057
2058### 返回值
2059* 若成功则返回新创建的队列,否则返回空指针
2060
2061### 描述
2062* 提交至该队列的任务将按照顺序执行,如果某个提交的任务中发生阻塞,则无法保证该任务的执行顺序
2063* ffrt_queue_t对象的置空和销毁由用户完成,对同一个ffrt_queue_t仅能调用一次`ffrt_queue_t`,重复对同一个ffrt_queue_t调用`ffrt_queue_destroy`,其行为是未定义的
2064* 在`ffrt_queue_destroy`之后再对ffrt_queue_t进行访问,其行为是未定义的
2065
2066### 样例
2067```
2068#include <stdio.h>
2069#include "ffrt.h"
2070
2071using namespace ffrt;
2072using namespace std;
2073
2074int main(int narg, char** argv)
2075{
2076    ffrt_queue_attr_t queue_attr;
2077    (void)ffrt_queue_attr_init(&queue_attr);
2078    ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);
2079    ffrt_queue_attr_set_qos(&queue_attr, static_cast<int>(ffrt_qos_default));
2080    int ret = ffrt_queue_attr_get_qos(&queue_attr);
2081
2082    ffrt_queue_submit(queue_handle, ffrt::create_function_wrapper([]() {printf("Task done.\n");}, ffrt_function_kind_queue), nullptr);
2083
2084    ffrt_queue_attr_destroy(&queue_attr);
2085    ffrt_queue_destroy(queue_handle);
2086}
2087```
2088
2089### ffrt_queue_attr_init
2090<hr/>
2091
2092#### 声明
2093```{.c}
2094int ffrt_queue_attr_init(ffrt_queue_attr_t* attr);
2095```
2096
2097### 参数
2098`attr`
2099* 该参数为已初始化的queue属性
2100
2101### 返回值
2102* 若成功返回0,否则返回-1
2103
2104### 描述
2105* 初始化串行队列的属性
2106
2107### 样例
2108* 见[ffrt_queue_t](#ffrt_queue_t)章节样例
2109
2110### ffrt_queue_attr_destroy
2111<hr/>
2112
2113#### 声明
2114```{.c}
2115void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr);
2116```
2117
2118### 参数
2119`attr`
2120* 该参数为所创建的queue属性
2121
2122### 返回值
2123* 无返回值
2124
2125### 描述
2126* 销毁串行队列的属性
2127* 见[ffrt_queue_t](#ffrt_queue_t)章节的描述
2128
2129### 样例
2130* 见[ffrt_queue_t](#ffrt_queue_t)章节的样例
2131
2132
2133### ffrt_queue_attr_set_qos
2134<hr/>
2135
2136#### 声明
2137```{.c}
2138void ffrt_queue_attr_set_qos(ffrt_queue_attr_t* attr, ffrt_qos_t qos);
2139```
2140
2141### 参数
2142`attr`
2143* 该参数为所创建的queue属性
2144
2145`qos`
2146* 该参数指向优先级QoS
2147
2148### 返回值
2149* 无返回值
2150
2151### 描述
2152* 设置串行队列qos属性
2153
2154### 样例
2155* 见[ffrt_queue_t](#ffrt_queue_t)章节的样例
2156
2157
2158### ffrt_queue_attr_get_qos
2159<hr/>
2160
2161#### 声明
2162```{.c}
2163ffrt_qos_t ffrt_queue_attr_get_qos(const ffrt_queue_attr_t* attr);
2164```
2165
2166### 参数
2167`attr`
2168* 该参数为所创建的queue属性
2169
2170### 返回值
2171* 所设置的串行队列的qos等级,默认为default等级
2172
2173### 描述
2174* 获取串行队列qos属性
2175
2176### 样例
2177* 见[ffrt_queue_t](#ffrt_queue_t)章节的样例
2178
2179
2180### ffrt_queue_attr_set_timeout
2181<hr/>
2182
2183#### 声明
2184```{.c}
2185void ffrt_queue_attr_set_timeout(ffrt_queue_attr_t* attr, uint64_t timeout_us);
2186```
2187
2188### 参数
2189`attr`
2190* 该参数为所创建的queue属性
2191
2192`timeout_us`
2193* 该参数用于描述串行队列任务执行超时时间,单位为us
2194
2195### 返回值
2196* 无返回值
2197
2198### 描述
2199* 设置串行队列任务执行超时时间
2200
2201### 样例
2202```
2203#include <stdio.h>
2204#include "ffrt.h"
2205
2206using namespace ffrt;
2207using namespace std;
2208
2209int main(int narg, char** argv)
2210{
2211    ffrt_queue_attr_t queue_attr;
2212    (void)ffrt_queue_attr_init(&queue_attr);
2213    ffrt_queue_attr_set_timeout(&queue_attr, 10000);
2214    uint64_t time = ffrt_queue_attr_get_timeout(&queue_attr);
2215    ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);
2216
2217    ffrt_queue_attr_destroy(&queue_attr);
2218    ffrt_queue_destroy(queue_handle);
2219}
2220```
2221
2222
2223### ffrt_queue_attr_get_timeout
2224<hr/>
2225
2226#### 声明
2227```{.c}
2228uint64_t ffrt_queue_attr_get_timeout(const ffrt_queue_attr_t* attr);
2229```
2230
2231### 参数
2232`attr`
2233* 该参数为所创建的queue属性
2234
2235### 返回值
2236* 串行队列任务执行超时时间,单位为us
2237
2238### 描述
2239* 获取所设的串行队列任务执行超时时间
2240
2241### 样例
2242* 见[ffrt_queue_attr_set_timeout](#ffrt_queue_attr_set_timeout)章节的样例
2243
2244
2245### ffrt_queue_attr_set_callback
2246<hr/>
2247
2248#### 声明
2249```{.c}
2250void ffrt_queue_attr_set_callback(ffrt_queue_attr_t* attr, ffrt_function_header_t* f);
2251```
2252
2253### 参数
2254`attr`
2255* 该参数为所创建的queue属性
2256
2257`f`
2258* 该参数为串行队列超时回调函数
2259
2260### 返回值
2261* 无返回值
2262
2263### 描述
2264* 设置串行队列超时回调函数
2265
2266### 样例
2267```
2268#include <stdio.h>
2269#include "ffrt.h"
2270
2271using namespace ffrt;
2272using namespace std;
2273
2274int main(int narg, char** argv)
2275{
2276    ffrt_queue_attr_t queue_attr;
2277    (void)ffrt_queue_attr_init(&queue_attr);
2278    ffrt_queue_attr_set_callback(&queue_attr, ffrt::create_function_wrapper(cbOne, ffrt_function_kind_queue));
2279    ffrt_function_header_t* func = ffrt_queue_attr_get_callback(nullptr);
2280    ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);
2281
2282    ffrt_queue_attr_destroy(&queue_attr);
2283    ffrt_queue_destroy(queue_handle);
2284}
2285```
2286
2287
2288### ffrt_queue_attr_get_callback
2289<hr/>
2290
2291#### 声明
2292```{.c}
2293ffrt_function_header_t* ffrt_queue_attr_get_callback(const ffrt_queue_attr_t* attr);
2294```
2295
2296### 参数
2297`attr`
2298* 该参数为所创建的queue属性
2299
2300### 返回值
2301* 串行队列任务超时回调函数
2302
2303### 描述
2304* 获取串行队列超时回调函数
2305
2306### 样例
2307* 见[ffrt_queue_attr_set_callback](#ffrt_queue_attr_set_callback)章节的样例
2308
2309
2310### ffrt_queue_create
2311<hr/>
2312
2313#### 声明
2314```{.c}
2315ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr);
2316```
2317
2318### 参数
2319`type`
2320* 该参数用于描述创建的队列类型,串行队列type须为ffrt_queue_serial
2321
2322`name`
2323* 该参数用于描述创建的队列名称,若未设置则会默认设置为unnamed_...
2324
2325`attr`
2326* 该参数为所创建的queue属性,若未设定则会使用默认值
2327
2328### 返回值
2329* 如果成功创建了队列,则返回一个非空的队列句柄;否则返回空指针。
2330
2331### 描述
2332* 创建串行队列
2333
2334### 样例
2335* 见[ffrt_queue_t](#ffrt_queue_t)章节的样例
2336
2337
2338### ffrt_queue_destroy
2339<hr/>
2340
2341#### 声明
2342```{.c}
2343void ffrt_queue_destroy(ffrt_queue_t queue);
2344```
2345
2346### 参数
2347`queue`
2348* 该参数为想要销毁的队列的句柄
2349
2350### 返回值
2351* 无
2352
2353### 描述
2354* 销毁串行队列
2355
2356### 样例
2357* 见[ffrt_queue_t](#ffrt_queue_t)章节的样例
2358
2359
2360### ffrt_queue_submit
2361<hr/>
2362
2363#### 声明
2364```{.c}
2365void ffrt_queue_submit(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr);
2366```
2367
2368### 参数
2369`queue`
2370* 该参数为队列的句柄
2371
2372`f`
2373* 该参数为任务执行器指针
2374
2375`attr`
2376* 该参数为所创建的queue属性
2377
2378### 返回值
2379* 无
2380
2381### 描述
2382* 提交一个任务到队列中调度执行
2383
2384### 样例
2385* 见[ffrt_queue_t](#ffrt_queue_t)章节的样例
2386
2387
2388### ffrt_queue_submit_h
2389<hr/>
2390
2391#### 声明
2392```{.c}
2393ffrt_task_handle_t ffrt_queue_submit_h(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr);
2394```
2395
2396### 参数
2397`queue`
2398* 该参数为队列的句柄
2399
2400`f`
2401* 该参数为任务执行器指针
2402
2403`attr`
2404* 该参数为所创建的queue属性
2405
2406### 返回值
2407* 如果任务被提交,则返回一个非空的任务句柄;否则返回空指针。
2408
2409### 描述
2410* 提交一个任务到队列中调度执行,并返回任务句柄
2411
2412### 样例
2413* 见[ffrt_queue_wait](#ffrt_queue_wait)章节的样例
2414
2415
2416### ffrt_queue_wait
2417<hr/>
2418
2419#### 声明
2420```{.c}
2421void ffrt_queue_wait(ffrt_task_handle_t handle);
2422```
2423
2424### 参数
2425`handle`
2426* 该参数为任务的句柄
2427
2428### 返回值
2429* 无返回值
2430
2431### 描述
2432* 等待队列中一个任务执行完成
2433
2434### 样例
2435```
2436#include <stdio.h>
2437#include "ffrt.h"
2438
2439using namespace ffrt;
2440using namespace std;
2441
2442int main(int narg, char** argv)
2443{
2444    ffrt_queue_attr_t queue_attr;
2445    (void)ffrt_queue_attr_init(&queue_attr);
2446    ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);
2447
2448    std::function<void()>&& OnePlusFunc = [&result]() { OnePlusForTest((void *)(&result)); };
2449    ffrt_task_handle_t task = ffrt_queue_submit_h(queue_handle,
2450        ffrt::create_function_wrapper(OnePlusFunc, ffrt_function_kind_queue), nullptr);
2451    ffrt_queue_wait(task);
2452
2453    ffrt_task_handle_destroy(task);
2454    ffrt_queue_attr_destroy(&queue_attr);
2455    ffrt_queue_destroy(queue_handle);
2456}
2457```
2458
2459
2460
2461### ffrt_queue_cancel
2462<hr/>
2463
2464#### 声明
2465```{.c}
2466int ffrt_queue_cancel(ffrt_task_handle_t handle);
2467```
2468
2469### 参数
2470`handle`
2471* 该参数为任务的句柄
2472
2473### 返回值
2474* 若成功返回0,否则返回-1
2475
2476### 描述
2477* 取消队列中一个任务
2478* 任务开始执行后则无法取消,仅能成功取消未开始执行的任务,若任务已开始执行会返回-1
2479* 不能通过队列名称取消任务,必须使用submit_h后拿到的task_handle,否则会报异常
2480
2481### 样例
2482```
2483#include <stdio.h>
2484#include "ffrt.h"
2485
2486using namespace ffrt;
2487using namespace std;
2488
2489int main(int narg, char** argv)
2490{
2491    ffrt_queue_attr_t queue_attr;
2492    (void)ffrt_queue_attr_init(&queue_attr);
2493    ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);
2494
2495    std::function<void()>&& OnePlusFunc = [&result]() { OnePlusForTest((void *)(&result)); };
2496    ffrt_task_handle_t task = ffrt_queue_submit_h(queue_handle,
2497        ffrt::create_function_wrapper(OnePlusFunc, ffrt_function_kind_queue), nullptr);
2498    int ret = ffrt_queue_cancel(task);
2499
2500    ffrt_task_attr_destroy(&task_attr);
2501    ffrt_task_handle_destroy(task);
2502    ffrt_queue_attr_destroy(&queue_attr);
2503    ffrt_queue_destroy(queue_handle);
2504}
2505```
2506
2507
2508## 同步原语
2509
2510### ffrt_mutex_t
2511<hr/>
2512* FFRT提供的类似pthread mutex 的性能实现
2513
2514#### 声明
2515
2516```{.cpp}
2517typedef enum {
2518    ffrt_error = -1,
2519    ffrt_success = 0,
2520    ffrt_error_nomem = ENOMEM,
2521    ffrt_error_timedout = ETIMEDOUT,
2522    ffrt_error_busy = EBUSY,
2523    ffrt_error_inval = EINVAL
2524} ffrt_error_t;
2525
2526struct ffrt_mutex_t;
2527
2528int ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr);
2529int ffrt_mutex_lock(ffrt_mutex_t* mutex);
2530int ffrt_mutex_unlock(ffrt_mutex_t* mutex);
2531int ffrt_mutex_trylock(ffrt_mutex_t* mutex);
2532int ffrt_mutex_destroy(ffrt_mutex_t* mutex);
2533```
2534
2535#### 参数
2536
2537`attr`
2538
2539* 当前FFRT只支持基础类型的mutex,因此attr必须为空指针
2540
2541`mutex`
2542
2543* 指向所操作的互斥锁的指针
2544
2545#### 返回值
2546
2547* 若成功则为 ffrt_success ,否则发生错误
2548
2549#### 描述
2550* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
2551* 该功能能够避免pthread传统的pthread_mutex_t 在抢不到锁时陷入内核的问题,在使用得当的条件下将会有更好的性能
2552* **注意:目前暂不支持递归和定时功能**
2553* **注意:C API中的ffrt_mutex_t需要用户调用`ffrt_mutex_init`和`ffrt_mutex_destroy`显式创建和销毁,而C++ API无需该操作**
2554* **注意:C API中的ffrt_mutex_t对象的置空和销毁由用户完成,对同一个ffrt_mutex_t仅能调用一次`ffrt_mutex_destroy`,重复对同一个ffrt_mutex_t调用`ffrt_mutex_destroy`,其行为是未定义的**
2555* **注意:在`ffrt_mutex_destroy`之后再对ffrt_mutex_t进行访问,其行为是未定义的**
2556
2557#### 样例
2558
2559```{.c}
2560#include <stdio.h>
2561#include "ffrt.h"
2562
2563typedef struct {
2564    int* sum;
2565    ffrt_mutex_t* mtx;
2566} tuple;
2567
2568void func(void* arg)
2569{
2570    tuple* t = (tuple*)arg;
2571
2572    int ret = ffrt_mutex_lock(t->mtx);
2573    if (ret != ffrt_success) {
2574        printf("error\n");
2575    }
2576    (*t->sum)++;
2577    ret = ffrt_mutex_unlock(t->mtx);
2578    if (ret != ffrt_success) {
2579        printf("error\n");
2580    }
2581}
2582
2583typedef struct {
2584    ffrt_function_header_t header;
2585    ffrt_function_t func;
2586    ffrt_function_t after_func;
2587    void* arg;
2588} c_function;
2589
2590static void ffrt_exec_function_wrapper(void* t)
2591{
2592    c_function* f = (c_function*)t;
2593    if (f->func) {
2594        f->func(f->arg);
2595    }
2596}
2597
2598static void ffrt_destroy_function_wrapper(void* t)
2599{
2600    c_function* f = (c_function*)t;
2601    if (f->after_func) {
2602        f->after_func(f->arg);
2603    }
2604}
2605
2606#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
2607static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
2608    const ffrt_function_t after_func, void* arg)
2609{
2610    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
2611        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
2612    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
2613    f->header.exec = ffrt_exec_function_wrapper;
2614    f->header.destroy = ffrt_destroy_function_wrapper;
2615    f->func = func;
2616    f->after_func = after_func;
2617    f->arg = arg;
2618    return (ffrt_function_header_t*)f;
2619}
2620
2621static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
2622    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
2623{
2624    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
2625}
2626
2627void ffrt_mutex_task()
2628{
2629    int sum = 0;
2630    ffrt_mutex_t mtx;
2631    tuple t = {&sum, &mtx};
2632    int ret = ffrt_mutex_init(&mtx, NULL);
2633    if (ret != ffrt_success) {
2634        printf("error\n");
2635    }
2636    for (int i = 0; i < 10; i++) {
2637        ffrt_submit_c(func, NULL, &t, NULL, NULL, NULL);
2638    }
2639    ffrt_mutex_destroy(&mtx);
2640    ffrt_wait();
2641    printf("sum = %d", sum);
2642}
2643
2644int main(int narg, char** argv)
2645{
2646    int r;
2647    ffrt_submit_c(ffrt_mutex_task, NULL, NULL, NULL, NULL, NULL);
2648    ffrt_wait();
2649    return 0;
2650}
2651```
2652
2653预期输出为
2654
2655```
2656sum=10
2657```
2658
2659* 该例子为功能示例,实际中并不鼓励这样使用
2660
2661
2662### ffrt_cond_t
2663<hr/>
2664
2665* FFRT提供的类似pthread 信号量的性能实现
2666
2667#### 声明
2668
2669```{.c}
2670typedef enum {
2671    ffrt_error = -1,
2672    ffrt_success = 0,
2673    ffrt_error_nomem = ENOMEM,
2674    ffrt_error_timedout = ETIMEDOUT,
2675    ffrt_error_busy = EBUSY,
2676    ffrt_error_inval = EINVAL
2677} ffrt_error_t;
2678
2679struct ffrt_cond_t;
2680typedef enum {
2681    ffrt_clock_realtime = CLOCK_REALTIME,
2682    ffrt_clock_monotonic = CLOCK_MONOTONIC
2683} ffrt_clockid_t;
2684
2685int ffrt_condattr_init(ffrt_condattr_t* attr);
2686int ffrt_condattr_destroy(ffrt_condattr_t* attr);
2687int ffrt_condattr_setclock(ffrt_condattr_t* attr, ffrt_clockid_t clock);
2688int ffrt_condattr_getclock(const ffrt_condattr_t* attr, ffrt_clockid_t* clock);
2689
2690int ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr);
2691int ffrt_cond_signal(ffrt_cond_t* cond);
2692int ffrt_cond_broadcast(ffrt_cond_t* cond);
2693int ffrt_cond_wait(ffrt_cond_t*cond, ffrt_mutex_t* mutex);
2694int ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point);
2695int ffrt_cond_destroy(ffrt_cond_t* cond);
2696```
2697
2698#### 参数
2699
2700`cond`
2701
2702* 指向所操作的信号量的指针
2703
2704`attr`
2705
2706* 属性设定,空指针表示使用默认属性
2707
2708`mutex`
2709
2710* 指向要在阻塞期间解锁的互斥锁的指针
2711
2712`time_point`
2713
2714* 指向指定等待时限时间的对象的指针
2715
2716
2717#### 返回值
2718
2719* 若成功则为 ffrt_success,若在锁定互斥前抵达时限则为 ffrt_error_timedout
2720
2721#### 描述
2722* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
2723* 该功能能够避免传统的pthread_cond_t在条件不满足时陷入内核的问题,在使用得当的条件下将会有更好的性能
2724* **注意:C API中的ffrt_cond_t需要用户调用`ffrt_cond_init`和`ffrt_cond_destroy`显式创建和销毁,而C++ API中依赖构造和析构自动完成**
2725* **注意:C API中的ffrt_cond_t对象的置空和销毁由用户完成,对同一个ffrt_cond_t仅能调用一次`ffrt_cond_destroy`,重复对同一个ffrt_cond_t调用`ffrt_cond_destroy`,其行为是未定义的**
2726* **注意:在`ffrt_cond_destroy`之后再对ffrt_cond_t进行访问,其行为是未定义的**
2727
2728#### 样例
2729
2730```{.c}
2731#include <stdio.h>
2732#include "ffrt.h"
2733
2734typedef struct {
2735    ffrt_cond_t* cond;
2736    int* a;
2737    ffrt_mutex_t* lock_;
2738} tuple;
2739
2740void func1(void* arg)
2741{
2742    tuple* t = (tuple*)arg;
2743    int ret = ffrt_mutex_lock(t->lock_);
2744    if (ret != ffrt_success) {
2745        printf("error\n");
2746    }
2747    while (*t->a != 1) {
2748        ret = ffrt_cond_wait(t->cond, t->lock_);
2749        if (ret != ffrt_success) {
2750            printf("error\n");
2751        }
2752    }
2753    ret = ffrt_mutex_unlock(t->lock_);
2754    if (ret != ffrt_success) {
2755        printf("error\n");
2756    }
2757    printf("a = %d", *(t->a));
2758}
2759
2760void func2(void* arg)
2761{
2762    tuple* t = (tuple*)arg;
2763    int ret = ffrt_mutex_lock(t->lock_);
2764    if (ret != ffrt_success) {
2765        printf("error\n");
2766    }
2767    *(t->a) = 1;
2768    ret = ffrt_cond_signal(t->cond);
2769    if (ret != ffrt_success) {
2770        printf("error\n");
2771    }
2772    ret = ffrt_mutex_unlock(t->lock_);
2773    if (ret != ffrt_success) {
2774        printf("error\n");
2775    }
2776}
2777
2778typedef struct {
2779    ffrt_function_header_t header;
2780    ffrt_function_t func;
2781    ffrt_function_t after_func;
2782    void* arg;
2783} c_function;
2784
2785static void ffrt_exec_function_wrapper(void* t)
2786{
2787    c_function* f = (c_function*)t;
2788    if (f->func) {
2789        f->func(f->arg);
2790    }
2791}
2792
2793static void ffrt_destroy_function_wrapper(void* t)
2794{
2795    c_function* f = (c_function*)t;
2796    if (f->after_func) {
2797        f->after_func(f->arg);
2798    }
2799}
2800
2801#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
2802static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
2803    const ffrt_function_t after_func, void* arg)
2804{
2805    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
2806        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
2807    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
2808    f->header.exec = ffrt_exec_function_wrapper;
2809    f->header.destroy = ffrt_destroy_function_wrapper;
2810    f->func = func;
2811    f->after_func = after_func;
2812    f->arg = arg;
2813    return (ffrt_function_header_t*)f;
2814}
2815
2816static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
2817    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
2818{
2819    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
2820}
2821
2822void ffrt_cv_task()
2823{
2824    ffrt_cond_t cond;
2825    int ret = ffrt_cond_init(&cond, NULL);
2826    if (ret != ffrt_success) {
2827        printf("error\n");
2828    }
2829    int a = 0;
2830    ffrt_mutex_t lock_;
2831    tuple t = {&cond, &a, &lock_};
2832    ret = ffrt_mutex_init(&lock_, NULL);
2833    if (ret != ffrt_success) {
2834        printf("error\n");
2835    }
2836    ffrt_submit_c(func1, NULL, &t, NULL, NULL, NULL);
2837    ffrt_submit_c(func2, NULL, &t, NULL, NULL, NULL);
2838    ffrt_wait();
2839    ffrt_cond_destroy(&cond);
2840    ffrt_mutex_destroy(&lock_);
2841}
2842
2843int main(int narg, char** argv)
2844{
2845    ffrt_submit_c(ffrt_cv_task, NULL, NULL, NULL, NULL, NULL);
2846    ffrt_wait();
2847    return 0;
2848}
2849```
2850
2851预期输出为:
2852
2853```
2854a=1
2855```
2856
2857* 该例子为功能示例,实际中并不鼓励这样使用
2858
2859## 杂项
2860
2861### ffrt_usleep
2862
2863<hr/>
2864* FFRT提供的类似C11 sleep和linux usleep的性能实现
2865
2866#### 声明
2867
2868```{.c}
2869int ffrt_usleep(uint64_t usec);
2870```
2871
2872#### 参数
2873
2874`usec`
2875
2876* 睡眠的us数
2877
2878#### 返回值
2879
2880* 不涉及
2881
2882#### 描述
2883* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
2884* 该功能能够避免传统的sleep 睡眠时陷入内核的问题,在使用得当的条件下将会有更好的性能
2885
2886#### 样例
2887
2888```{.c}
2889#include <time.h>
2890#include <stdio.h>
2891#include "ffrt.h"
2892
2893void func(void* arg)
2894{
2895    printf("Time: %s", ctime(&(time_t){time(NULL)}));
2896    ffrt_usleep(2000000); // 睡眠 2 秒
2897    printf("Time: %s", ctime(&(time_t){time(NULL)}));
2898}
2899
2900typedef struct {
2901    ffrt_function_header_t header;
2902    ffrt_function_t func;
2903    ffrt_function_t after_func;
2904    void* arg;
2905} c_function;
2906
2907static void ffrt_exec_function_wrapper(void* t)
2908{
2909    c_function* f = (c_function*)t;
2910    if (f->func) {
2911        f->func(f->arg);
2912    }
2913}
2914
2915static void ffrt_destroy_function_wrapper(void* t)
2916{
2917    c_function* f = (c_function*)t;
2918    if (f->after_func) {
2919        f->after_func(f->arg);
2920    }
2921}
2922
2923#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
2924static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
2925    const ffrt_function_t after_func, void* arg)
2926{
2927    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
2928        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
2929    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
2930    f->header.exec = ffrt_exec_function_wrapper;
2931    f->header.destroy = ffrt_destroy_function_wrapper;
2932    f->func = func;
2933    f->after_func = after_func;
2934    f->arg = arg;
2935    return (ffrt_function_header_t*)f;
2936}
2937
2938static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
2939    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
2940{
2941    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
2942}
2943
2944int main(int narg, char** argv)
2945{
2946    ffrt_submit_c(func, NULL, NULL, NULL, NULL, NULL);
2947    ffrt_wait();
2948    return 0;
2949}
2950```
2951
2952### ffrt_yield
2953<hr/>
2954* 当前task 主动让出CPU 执行资源,让其他可以被执行的task 先执行,如果没有其他可被执行的task,yield 无效
2955
2956#### 声明
2957
2958```{.cpp}
2959void ffrt_yield();
2960```
2961
2962#### 参数
2963
2964* 不涉及
2965
2966#### 返回值
2967
2968* 不涉及
2969
2970#### 描述
2971* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
2972* 此函数的确切行为取决于实现,特别是使用中的FFRT 调度程序的机制和系统状态
2973
2974#### 样例
2975
2976* 省略
2977
2978
2979# 部署
2980
2981## 部署方式
2982<img src="images/image-20230120153923679.png" alt="image-20230120153923679" style="zoom:67%;" />
2983
2984* FFRT的部署依赖FFRT动态库libffrt.so和一组header头文件
2985
2986* FFRT的头文件为`ffrt.h`,内部包含了C++ API,C API和C base API
2987  * ffrt.h 定义为:
2988  ```{.cpp}
2989  #ifndef FFRT_API_FFRT_H
2990  #define FFRT_API_FFRT_H
2991  #ifdef __cplusplus
2992  #include "cpp/task.h"
2993  #include "cpp/deadline.h"
2994  #include "cpp/sys_event.h"
2995  #include "cpp/mutex.h"
2996  #include "cpp/condition_variable.h"
2997  #include "cpp/sleep.h"
2998  #include "cpp/thread.h"
2999  #include "cpp/config.h"
3000  #include "cpp/future.h"
3001  #else
3002  #include "c/task.h"
3003  #include "c/deadline.h"
3004  #include "c/sys_event.h"
3005  #include "c/mutex.h"
3006  #include "c/condition_variable.h"
3007  #include "c/sleep.h"
3008  #include "c/thread.h"
3009  #include "c/config.h"
3010  #endif
3011  #endif
3012  ```
3013  * C base API定义示例:
3014  ```{.cpp}
3015  void ffrt_submit_base(ffrt_function_header_t* func, ...);
3016  int ffrt_mutex_init(...);
3017  ```
3018  * C API定义示例:
3019  ```{.cpp}
3020  static inline void ffrt_submit(ffrt_function_t func, void* arg, ...)
3021  {
3022      ffrt_submit_base(ffrt_create_function_wrapper(func, arg), ...);
3023  }
3024  ```
3025  * C++ API定义示例:
3026  ```{.cpp}
3027  namespace ffrt {
3028  static inline void submit(std::function& func, ...)
3029  {
3030      ffrt_submit_base(ffrt_create_function_wrapper(func), ...);
3031  }
3032  struct mutex {
3033      mutex() {
3034          ffrt_mutex_init(...);
3035          ...
3036      };
3037  }
3038  ```
3039
3040* **出于易用性方面的考虑,除非必要,强烈建议你使用C++ API,调用C API将会使你的代码非常臃肿或者更容易产生资源未释放问题**
3041
3042| 需求列表                                                     |
3043| ------------------------------------------------------------ |
3044| 需求1:ABI兼容性,在NDK场景中由于用户的编译环境与FFRT的编译环境不同,使用C++接口可能存在ABI兼容性问题,要有解决方案 |
3045| 需求2:用户的编译环境为纯C编译环境,不想因为引入FFRT而引入C++元素的场景,要有解决方案 |
3046| 需求3:易用性,尽可能让接口简单易用,用户少出错              |
3047
3048* 对于需求1,通过在用户调用的C++接口和FFRT的实现之间增加一个C base API层,并基于头文件方式将API中的C++的元素编译到用户的so,从而解决ABI兼容的问题
3049* 对于需求2,可以通过C Base API解决
3050* 对于需求3,建议用户尽可能使用C++ API,以避免C API固有的资源未初始化/释放、参数冗长等问题,对于不得不使用C API的场景,FFRT仍然支持用户使用C API和C base API
3051
3052
3053
3054<br/>
3055<br/>
3056
3057<hr/>
3058# 实战指南
3059
3060## 步骤1: 分析应用
3061
3062使用 FFRT 并行编程的第一步便是您需要了解你的应用。
3063
3064【建议1】:使用 Task 梳理应用的流程。
3065
3066使用 Task 梳理应用的流程,并且尽可能使用数据来表达 Task 之间的依赖。当然如果两个 Task 之间如无数据依赖,仅存在控制依赖,您也可以创建一个虚拟的(或者逻辑上的)数据依赖。
3067
3068<img src="images/image-20220926152831526.png" style="zoom:70%" />
3069
3070<center>AIRAW 的数据流图</center>
3071
3072基于数据流图,可以很容易判定出哪些 Task 是可以并发的,比如,Slice0 的 NPU Task 和 Slice1 的 GPU Pre Task 是可以并发的,因为它们没有任何依赖。
3073
3074反过来,如果并发的效果不理想,也可以通过调整数据流图来优化并发。例如,假如上图中GPU Pre Task 执行时间有很大波动,但平均耗时略小于 NPU Task,会出现某些时刻 GPU Pre Task 拖慢整个执行时间。此时,如果将 GPU Pre Task 的输出 Buffer 改成3个(或者更多)的 Buffer ,可以增加 GPU Pre Task 和 NPU Task 的并发机会,将降低波动对总执行时间的影响。
3075
3076
3077
3078【建议2】:这里不用太担心 Task 大或小的问题,因为 FFRT 允许你在 Task 内部继续拆分 SubTask,可以逐步细化。
3079
3080下图中,第一次画数据流图时,可以不将 FaceDirection 和 UpdateExistFaceImageInfo 两个 Task 展开,可以逐步细化。
3081
3082<img src="images/image-20220926153003884.png" style="zoom:70%" />
3083
3084<center>某拍照业务的数据流图</center>
3085
3086【建议3】:上述流程图或者数据流图不要求是静态图(即 Task 数量和 Task 依赖关系是固定的)
3087
3088FFRT 允许动态提交 Task ,在编程界面上不体现图的概念,FFRT 内部会根据Task 之间的依赖关系动态调整数据流图的节点。
3089
3090
3091【建议4】:尽可能对应用做热点分析
3092
3093如果是对存量代码的 FFRT 化改造,那么,使用 System Trace 这类工具能帮助您聚焦在性能热点上,比如下图可以很容易知道当前的性能Bound,在分析数据流图时,可以重点关注这些热点任务。
3094
3095<img src="images/image-20220926153030993.png" style="zoom:70%" />
3096
3097<center>某业务的System Trace</center>
3098
3099## 步骤2: 并行化应用
3100
3101【建议1】:不要直接使用线程,使用 FFRT 提交Task。
3102
3103如果应用中有明显的数据依赖关系,那么 FFRT 将会非常适合;最差的情况是应用没有数据依赖或难以并行(如果真的存在),您仍然可以把 FFRT 当做一个高效的进程级线程池、或者协程库去使用它,但非常不建议你继续创建线程。
3104
3105
3106
3107【建议2】:Task 最好被建模为纯函数。
3108
3109纯函数是指其执行没有副作用,例如更新全局数据结构。每个任务都依赖于其输入/输出签名来连接到其他任务。
3110
3111请注意,即使 Task 不是"纯"的,FFRT 仍然适用。只要任务使用的数据依赖或者锁足以保证正确执行,FFRT 就能正常工作。
3112
3113
3114
3115【建议3】:尽可能尝试通过 inDeps/outDeps 表达依赖,而不是使用 ffrt::wait()。
3116
3117这是因为 FFRT 跟踪和处理 inDeps/outDeps 比调用显式 ffrt::wait() 函数更自然、更便宜。
3118
3119
3120
3121【建议4】:注意 Task 粒度
3122
3123以适当的粒度提交任务至关重要:目前每个任务的调度开销约为 10 us。如果 Task 的粒度非常小,那么开销的百分比将会很高。FFRT 会继续基于软硬件的方式优化调度开销。
3124
3125
3126
3127【建议5】:尽可能使用 FFRT 原语
3128
3129如果需要mutex、sleep、异步 I/O,请使用 FFRT 原语,而不是使用OS 提供的版本。因为这些 FFRT 提供的实现在与 FFRT 配合时开销将会更小。
3130
3131
3132
3133【建议6】:在需要时,使用 ffrt::wait() 确保栈变量的生命周期。
3134
3135如果子任务使用驻留在父任务栈上的数据,则父任务应避免在子任务执行完成前返回。在父任务的末尾添加 ffrt::wait() 可以解决这个问题。
3136
3137
3138
3139## 步骤3: 优化应用
3140
3141【建议1】:基于System Trace,分析并行是否符合预期
3142
3143FFRT 已经内置 SysTrace 支持,默认以txx.xx表示,非常有利于分析 Task 粒度和并发度。未来,在性能分析和维测方面将继续增强。
3144
3145<img src="images/image-20220926153209875.png" style="zoom:70%" />
3146
3147【建议2】:对于耗时的 Task,尝试提交 SubTask,提升应用的并行度
3148
3149
3150【建议3】:在合适的场景,使用 Deadline 调度,实现能效和性能的平衡
3151
3152方案正在验证中,待更新。
3153
3154
3155
3156## 样例: CameraHal QuickThumb
3157
3158### 步骤1: 分析应用
3159
3160<img src="images/image-20220926153255824.png" style="zoom:70%" />
3161
31621)   QuickThumb 是 CameraHal 中实现的对一张图片进行缩小的功能,整体运行时间约30 us;
3163
31642)   在实现上分为两层循环,外层的一次循环输出1行,内层的1次循环输出该行的m列;
3165
31663)   在划分 Task 时,一种简单的做法是1行的处理就是1个Task。
3167
3168### 步骤2: 并行化应用
3169<img src="images/image-20220926153509205.png" style="zoom:100%" />
3170
3171 1)   根据纯函数的定义,该 Task 的输入输出的数据是非常之多的,因此,这个场景下使用更宽泛的纯函数的定义,只需要考虑在 Task 内部会被写,但是却被定义在 Task 外部的变量即可;
3172
31732)   按照上面的原则,将 py/puv 的定义移到 Task 内部可避免多个 Task 同时写 py/puv 的问题;
3174
31753)   s32r 的处理可以有两种方式,都能得到正确的功能:a. 保持定义在Task 外部,但作为Task 的输出依赖;b. 将s32r定义在Task 内部,作为Task的私有变量。显然,b 方案能够获得更好的性能
3176
3177
3178
3179### 步骤3: 优化应用
3180
3181通过System Trace,会发现上述改造方案的Task 粒度较小,大约单个Task 耗时在5us左右,因此,扩大Task的粒度为32行处理,得到最终的并行结果,下图为使用4小核和3中核的结果。
3182
3183<img src="images/image-20220926153603572.png" style="zoom:100%" />
3184
3185
3186
3187## 样例: Camera AIRAW
3188
3189### 步骤1: 分析应用
3190<img src="images/image-20220926153611121.png" style="zoom:70%" />
3191
3192AIRAW 的处理包括了3个处理步骤,在数据面上,可以按slice 进行切分,在不考虑pre_outbuf和npu_outbuf 在slice间复用的情况下,数据流图如上图所示。
3193
3194<img src="images/image-20220926152831526.png" style="zoom:70%" />
3195
3196为了节省运行过程中的内存占用,但不影响整体性能,可以只保留2个pre_outbuf和2个npu_outbuf。
3197
3198`为此付出的代价是:Buffer 的复用产生了slice3 的GPU Pre Task 依赖slice1 的NPU Task 完成,俗称反压,又称生产者依赖关系。但是,如果您使用 FFRT 来实现,将会是非常自然而高效的`
3199
3200### 步骤2: 并行化应用
3201
3202```{.cpp}
3203constexpr uint32_t SLICE_NUM = 24;
3204constexpr uint32_t BUFFER_NUM = 2;
3205
3206int input[SLICE_NUM]; // input is split into SLICE_NUM slices
3207int pre_outbuf[BUFFER_NUM]; // gpu pre task output buffers
3208int npu_outbuf[BUFFER_NUM]; // npu output buffers
3209int output[SLICE_NUM]; // output is split into SLICE_NUM slices
3210
3211for (uint32_t i = 0; i < SLICE_NUM; i++) {
3212  uint32_t buf_id = i % BUFFER_NUM;
3213  ffrt::submit(gpuPreTask, {input + i}, {pre_outbuf + buf_id});
3214  ffrt::submit(npuTask, {pre_outbuf + buf_id}, {npu_outbuf + buf_id});
3215  ffrt::submit(gpuPostTask, {npu_outbuf + buf_id}, {output + i});
3216}
3217
3218ffrt::wait();
3219```
3220
3221### 步骤3: 优化应用
3222
3223<img src="images/image-20220926153825527.png" style="zoom:100%" />
3224
3225基于以上实现,从上面的Trace中我们看到,NPU 的硬件时间被完全用满,系统端到端性能达到最优,而付出的开发代价将会比GCD 或多线程小的多。
3226
3227
3228
3229## 样例: Camera FaceStory
3230
3231### 步骤1: 分析应用
3232
3233<img src="images/image-20220926153003884.png" style="zoom:70%" />
3234
3235
3236
3237### 步骤2: 并行化应用
3238
3239<img src="images/image-20220926153906692.png" style="zoom:100%" />
3240
3241代码改造样例
3242
32431)     该场景输出存量代码迁移,只需将原先串行的代码以Task的方式提交即可;
3244
32452)     过程中需要考虑Data Race和数据生命周期;
3246
32473)     先提交大的Task,根据需要逐步拆分SubTask。
3248
3249
3250
3251### 步骤3: 优化应用
3252
3253<img src="images/image-20220926153030993.png" style="zoom:100%" />
3254
3255<center>原始System Trace</center>
3256
3257<img src="images/image-20220926153925963.png" style="zoom:100%" />
3258
3259
3260
3261
3262<center>改造后System Trace</center>
3263
3264并行化的收益来自于:
3265
32661)    多分支或循环并发,实现CPU前后处理和NPU的并发
3267
32682)    子任务拆分,进一步提升并行度
3269
32703)    基于数据流图优化CPU L2 Cache Flush频次
3271
32724)    NPU Worker Thread实时优先级调整,后续FFRT中考虑独立出XPU调度Worker来保证实时性
3273
32745)    在未来,模型加载使用FFRT submit,模型加载内部也可以使用submit来继续拆分,能够优化整个业务的启动耗时。
3275
3276<br/>
3277<br/>
3278<hr/>
3279
3280
3281
3282# 使用建议
3283
3284## 建议1: 函数化
3285
3286**基本思想:计算过程函数化**
3287
3288* 程序过程各步骤以函数封装表达,函数满足类纯函数特性
3289* 无全局数据访问
3290* 无内部状态保留
3291* 通过ffrt::submit()接口以异步任务方式提交函数执行
3292* 将函数访问的数据对象以及访问方式在ffrt::submit()接口中的in_deps/out_deps参数表达
3293* 程序员通过inDeps/outDeps参数表达任务间依赖关系以保证程序执行的正确性
3294
3295> 做到纯函数的好处在于:1. 能够最大化挖掘并行度,2.避免DataRace和锁的问题
3296
3297
3298
3299**在实际中,可以根据场景放松纯函数的约束,但前提是:**
3300
3301* 确定添加的in_deps/out_deps可确保程序正确执行
3302* 通过FFRT提供的锁机制保护对全局变量的访问
3303
3304
3305
3306## 建议2: 注意任务粒度
3307
3308* FFRT管理和调度异步任务执行有调度开销,任务粒度(执行时间)需匹配调度开销
3309* 大量小粒度任务造成FFRT调度开销占比增加,性能下降,解决方法:
3310  * 将多个小粒度任务聚合为大粒度任务一次发送给FFRT异步执行
3311  * 同步方式执行小粒度任务,不发送给FFRT异步执行。需注意和异步任务之间的数据同步问题,在需要同步的地方插入ffrt::wait()
3312  * 下面的例子中,fib_ffrt2会比fib_ffrt1拥有更好的性能
3313
3314  ```{.cpp}
3315  #include "ffrt.h"
3316  void fib_ffrt1(int x, int& y)
3317  {
3318      if (x <= 1) {
3319          y = x;
3320      } else {
3321          int y1, y2;
3322          ffrt::submit([&] {fib_ffrt1(x - 1, y1);}, {&x}, {&y1} );
3323          ffrt::submit([&] {fib_ffrt1(x - 2, y2);}, {&x}, {&y2} );
3324          ffrt::submit([&] {y = y1 + y2;}, {&y1, &y2}, {} );
3325          ffrt::wait();
3326      }
3327  }
3328
3329  void fib_ffrt2(int x, int& y)
3330  {
3331      if (x <= 1) {
3332          y = x;
3333      } else {
3334          int y1, y2;
3335          ffrt::submit([&] {fib_ffrt2(x - 1, y1);}, {&x}, {&y1} );
3336          ffrt::submit([&] {fib_ffrt2(x - 2, y2);}, {&x}, {&y2} );
3337          ffrt::wait({&y1, &y2});
3338          y = y1 + y2;
3339      }
3340  }
3341  ```
3342
3343
3344
3345## 建议3: 数据生命周期
3346
3347* FFRT的任务提交和执行是异步的,因此需要确保任务执行时,对任务中涉及的数据的访问是有效的
3348* 常见问题:子任务使用父任务栈数据,当父任务先于子任务执行完成时释放栈数据,子任务产生数据访问错误
3349* 解决方法1:父任务中增加ffrt::wait()等待子任务完成
3350
3351```{.cpp}
3352#include "ffrt.h"
3353void fib_ffrt(int x, int& y)
3354{
3355    if (x <= 1) {
3356        y = x;
3357    } else {
3358        int y1, y2;
3359        ffrt::submit([&] {fib_ffrt(x - 1, y1);}, {&x}, {&y1} );
3360        ffrt::submit([&] {fib_ffrt(x - 2, y2);}, {&x}, {&y2} );
3361        ffrt::submit([&] {y = y1 + y2;}, {&y1, &y2}, {} );
3362        ffrt::wait(); // 用于保证y1 y2的生命周期
3363    }
3364}
3365```
3366
3367* 解决方法2:将数据由栈移到堆,手动管理生命周期
3368
3369```{.cpp}
3370#include "ffrt.h"
3371void fib_ffrt(int x, int* y)
3372{
3373    if (x <= 1) {
3374        *y = x;
3375    } else {
3376        int *y1 = (int*)malloc(sizeof(int));
3377        int *y2 = (int*)malloc(sizeof(int));
3378
3379        ffrt::submit([=] {fib_ffrt(x - 1, y1);}, {}, {y1} );
3380        ffrt::submit([=] {fib_ffrt(x - 2, y2);}, {}, {y2} );
3381        ffrt::submit([=] {*y = *y1 + *y2; }, {y1, y2}, {} );
3382		ffrt::wait();
3383    }
3384}
3385```
3386
3387
3388
3389## 建议4: 使用FFRT提供的替代API
3390
3391* 禁止在FFRT任务中使用系统线程库API创建线程,使用submit提交任务
3392* 使用FFRT提供的锁,条件变量,睡眠,IO等API代替系统线程库API
3393  * 使用系统线程库API可能造成工作线程阻塞,引起额外性能开销
3394
3395
3396
3397## 建议5: Deadline机制
3398
3399* **必须用于具备周期/重复执行特征的处理流程**
3400* 在有明确时间约束和性能关键的处理流程中使用,避免滥用
3401* 在相对大颗粒度的处理流程中使用,例如具有16.6ms时间约束的帧处理流程
3402
3403
3404
3405## 建议6: 从线程模型迁移
3406
3407* 创建线程替代为创建FFRT任务
3408  * 线程从逻辑上类似无in_deps的任务
3409* 识别线程间的依赖关系,并将其表达在任务的依赖关系in_deps/out_deps3410* 线程内计算过程分解为异步任务调用
3411* 通过任务依赖关系和锁机制避免并发任务数据竞争问题
3412
3413
3414
3415# 已知限制
3416
3417## 不支持thread_local变量
3418
3419* Task内部创建或Task间传递的thread_local变量的行为都是不确定的
3420
3421* 原因在于FFRT在编程模型中已经没有thread的概念,只有task的概念
3422* 在C++的语义下,thread_local可以被正常编译,但是使用该thread_local变量的task在哪一个线程上执行时不确定的
3423* 对于使用了FFRT进程中的non-worker,thread_local的行为不受FFRT影响
3424
3425> 类似的,与thread绑定的thread_idx/pthread_specific/递归锁/线程优先级/线程亲和性/递归锁具有相似的问题
3426
3427`建议`
3428
3429* 避免使用这些特性,如必须使用,使用FFRT的task local来替代
3430
3431## 不支持用户在fork出的子进程内使用ffrt
3432
3433## 以动态库方式部署FFRT
3434
3435* 只能以动态库方式部署FFRT,静态库部署可能有多实例问题,例如:当多个被同一进程加载的so都以静态库的方式使用FFRT时,FFRT会被实例化成多份,其行为是未知的,这也不是FFRT设计的初衷
3436
3437## C API中初始化ffrt对象后,对象的置空与销毁由用户负责
3438
3439* 为保证较高的性能,ffrt的C API中内部不包含对对象的销毁状态的标记,用户需要合理地进行资源的释放,重复调用各个对象的destroy操作,其结果是未定义的
3440* 错误示例1,重复调用destroy可能造成不可预知的数据损坏
3441
3442```{.cpp}
3443#include "ffrt.h"
3444void abnormal_case_1()
3445{
3446    ffrt_task_handle_t h = ffrt_submit_h([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
3447    ...
3448    ffrt_task_handle_destroy(h);
3449    ffrt_task_handle_destroy(h); // double free
3450}
3451```
3452
3453* 错误示例2,未调用destroy会造成内存泄漏
3454
3455```{.cpp}
3456#include "ffrt.h"
3457void abnormal_case_2()
3458{
3459    ffrt_task_handle_t h = ffrt_submit_h([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
3460    ...
3461    // memory leak
3462}
3463```
3464
3465* 建议示例,仅调用一次destroy,如有必要可进行置空
3466
3467```{.cpp}
3468#include "ffrt.h"
3469void normal_case()
3470{
3471    ffrt_task_handle_t h = ffrt_submit_h([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
3472    ...
3473    ffrt_task_handle_destroy(h);
3474    h = nullptr; // if necessary
3475}
3476```
3477
3478## 输入输出依赖数量的限制
3479
3480* 使用submit接口进行任务提交时,每个任务的输入依赖和输出依赖的数量之和不能超过8个。
3481* 使用submit_h接口进行任务提交时,每个任务的输入依赖和输出依赖的数量之和不能超过7个。
3482* 参数既作为输入依赖又作为输出依赖的时候,统计依赖数量时只统计一次,如输入依赖是{&x},输出依赖也是{&x},实际依赖的数量是1。