• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "co_routine.h"
17 #include <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <string>
21 #include "dfx/trace/ffrt_trace.h"
22 #include "core/dependence_manager.h"
23 #include "core/entity.h"
24 #include "sched/scheduler.h"
25 #include "sync/sync.h"
26 #include "util/slab.h"
27 #ifndef _MSC_VER
28 #include <sys/mman.h>
29 #else
30 #define PROT_READ 0x1
31 #define PROT_WRITE 0x2
32 #endif
33 #include "sched/sched_deadline.h"
34 #include "sync/perf_counter.h"
35 #include "sync/io_poller.h"
36 #include "dfx/bbox/bbox.h"
37 
38 
39 static thread_local CoRoutineEnv* g_CoThreadEnv = nullptr;
40 
CoSwitch(CoCtx * from,CoCtx * to)41 static inline void CoSwitch(CoCtx* from, CoCtx* to)
42 {
43     co2_switch_context(from, to);
44 }
45 
CoExit(CoRoutine * co)46 static inline void CoExit(CoRoutine* co)
47 {
48     CoSwitch(&co->ctx, &co->thEnv->schCtx);
49 }
50 
CoStartEntry(void * arg)51 static inline void CoStartEntry(void* arg)
52 {
53     CoRoutine* co = reinterpret_cast<CoRoutine*>(arg);
54     {
55         FFRT_LOGD("Execute func() task[%lu], name[%s]", co->task->gid, co->task->label.c_str());
56         auto f = reinterpret_cast<ffrt_function_header_t*>(co->task->func_storage);
57         auto exp = ffrt::SkipStatus::SUBMITTED;
58         if (likely(__atomic_compare_exchange_n(&co->task->skipped, &exp, ffrt::SkipStatus::EXECUTED, 0,
59             __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))) {
60             f->exec(f);
61         }
62         f->destroy(f);
63     }
64     FFRT_TASKDONE_MARKER(co->task->gid);
65     co->task->UpdateState(ffrt::TaskState::EXITED);
66     co->status.store(static_cast<int>(CoStatus::CO_UNINITIALIZED));
67     CoExit(co);
68 }
69 
CoInitThreadEnv(void)70 static inline void CoInitThreadEnv(void)
71 {
72     g_CoThreadEnv = reinterpret_cast<CoRoutineEnv*>(calloc(1, sizeof(CoRoutineEnv)));
73     CoRoutineEnv* t = g_CoThreadEnv;
74     if (!t) {
75         abort();
76     }
77 }
78 
CoSetStackProt(CoRoutine * co,int prot)79 static void CoSetStackProt(CoRoutine* co, int prot)
80 {
81     /* set the attribute of the page table closest to the stack top in the user stack to read-only,
82      * and 1~2 page table space will be wasted
83      */
84 #ifndef _MSC_VER
85     size_t p_size = getpagesize();
86 #else
87     size_t p_size = 4 * 1024;
88 #endif
89     if (CoStackAttr::Instance()->size < p_size * 3) {
90         abort();
91     }
92 
93 #ifndef _MSC_VER
94     uint64_t mp = reinterpret_cast<uint64_t>(co->stkMem.stk);
95     mp = (mp + p_size - 1) / p_size * p_size;
96     int ret = mprotect(reinterpret_cast<void *>(static_cast<uintptr_t>(mp)), p_size, PROT_READ);
97     if (ret < 0) {
98         printf("coroutine size:%lu, mp:0x%lx, page_size:%zu,result:%d,prot:%d, err:%d,%s.\n",
99             static_cast<unsigned long>(sizeof(struct CoRoutine)), static_cast<unsigned long>(mp),
100             p_size, ret, prot, errno, strerror(errno));
101         abort();
102     }
103 #endif
104 }
105 
AllocNewCoRoutine(void)106 static inline CoRoutine* AllocNewCoRoutine(void)
107 {
108     std::size_t stack_size = CoStackAttr::Instance()->size + sizeof(CoRoutine) - 8;
109     CoRoutine* co = ffrt::QSimpleAllocator<CoRoutine>::allocMem(stack_size);
110     if (co == nullptr) {
111         abort();
112     }
113 
114     co->stkMem.size = CoStackAttr::Instance()->size;
115     co->stkMem.magic = STACK_MAGIC;
116     if (CoStackAttr::Instance()->type == CoStackProtectType::CO_STACK_STRONG_PROTECT) {
117         CoSetStackProt(co, PROT_READ);
118     }
119     co->status.store(static_cast<int>(CoStatus::CO_UNINITIALIZED));
120     return co;
121 }
122 
CoMemFree(CoRoutine * co)123 static inline void CoMemFree(CoRoutine* co)
124 {
125     if (CoStackAttr::Instance()->type == CoStackProtectType::CO_STACK_STRONG_PROTECT) {
126         CoSetStackProt(co, PROT_WRITE | PROT_READ);
127     }
128     ffrt::QSimpleAllocator<CoRoutine>::freeMem(co);
129 }
130 
CoWorkerExit()131 void CoWorkerExit()
132 {
133     if (g_CoThreadEnv) {
134         ::free(g_CoThreadEnv);
135         g_CoThreadEnv = nullptr;
136     }
137 }
138 
BindNewCoRoutione(ffrt::TaskCtx * task)139 static inline void BindNewCoRoutione(ffrt::TaskCtx* task)
140 {
141     task->coRoutine = g_CoThreadEnv->runningCo;
142     task->coRoutine->task = task;
143     task->coRoutine->thEnv = g_CoThreadEnv;
144 }
145 
UnbindCoRoutione(ffrt::TaskCtx * task)146 static inline void UnbindCoRoutione(ffrt::TaskCtx* task)
147 {
148     task->coRoutine->task = nullptr;
149     task->coRoutine = nullptr;
150 }
151 
CoAlloc(ffrt::TaskCtx * task)152 static inline int CoAlloc(ffrt::TaskCtx* task)
153 {
154     if (!g_CoThreadEnv) {
155         CoInitThreadEnv();
156     }
157     if (task->coRoutine) {
158         if (g_CoThreadEnv->runningCo) {
159             CoMemFree(g_CoThreadEnv->runningCo);
160         }
161         g_CoThreadEnv->runningCo = task->coRoutine;
162     } else {
163         if (!g_CoThreadEnv->runningCo) {
164             g_CoThreadEnv->runningCo = AllocNewCoRoutine();
165         }
166     }
167     BindNewCoRoutione(task);
168     return 0;
169 }
170 
171 // call CoCreat when task creat
CoCreat(ffrt::TaskCtx * task)172 static inline int CoCreat(ffrt::TaskCtx* task)
173 {
174     CoAlloc(task);
175     auto co = task->coRoutine;
176     if (co->status.load() == static_cast<int>(CoStatus::CO_UNINITIALIZED)) {
177         co2_init_context(&co->ctx, CoStartEntry, static_cast<void*>(co), co->stkMem.stk, co->stkMem.size);
178     }
179     return 0;
180 }
181 
CoStackCheck(CoRoutine * co)182 static inline void CoStackCheck(CoRoutine* co)
183 {
184     if (co->stkMem.magic != STACK_MAGIC) {
185         FFRT_LOGE("sp offset:%lu.\n", (uint64_t)co->stkMem.stk +
186             CoStackAttr::Instance()->size - co->ctx.regs[REG_SP]);
187         FFRT_LOGE("stack over flow, check local variable in you tasks or use api 'ffrt_set_co_stack_attribute'.\n");
188         abort();
189     }
190 }
191 
CoSwitchInTrace(ffrt::TaskCtx * task)192 static inline void CoSwitchInTrace(ffrt::TaskCtx* task)
193 {
194     if (task->coRoutine->status == static_cast<int>(CoStatus::CO_NOT_FINISH)) {
195         for (auto name : task->traceTag) {
196             FFRT_TRACE_BEGIN(name.c_str());
197         }
198     }
199     FFRT_FAKE_TRACE_MARKER(task->gid);
200 }
201 
CoSwitchOutTrace(ffrt::TaskCtx * task)202 static inline void CoSwitchOutTrace(ffrt::TaskCtx* task)
203 {
204     FFRT_FAKE_TRACE_MARKER(task->gid);
205     int traceTagNum = static_cast<int>(task->traceTag.size());
206     for (int i = 0; i < traceTagNum; ++i) {
207         FFRT_TRACE_END();
208     }
209 }
210 
211 // called by thread work
CoStart(ffrt::TaskCtx * task)212 void CoStart(ffrt::TaskCtx* task)
213 {
214     if (task->coRoutine) {
215         int ret = task->coRoutine->status.exchange(static_cast<int>(CoStatus::CO_RUNNING));
216         if (ret == static_cast<int>(CoStatus::CO_RUNNING) && GetBboxEnableState() != 0) {
217             FFRT_BBOX_LOG("executed by worker suddenly, ignore backtrace");
218             return;
219         }
220     }
221     CoCreat(task);
222     auto co = task->coRoutine;
223 
224 #ifdef FFRT_BBOX_ENABLE
225     TaskRunCounterInc();
226 #endif
227 
228     for (;;) {
229         FFRT_LOGD("Costart task[%lu], name[%s]", task->gid, task->label.c_str());
230         ffrt::TaskLoadTracking::Begin(task);
231         FFRT_TASK_BEGIN(task->label, task->gid);
232         CoSwitchInTrace(task);
233 
234         CoSwitch(&co->thEnv->schCtx, &co->ctx);
235         FFRT_TASK_END();
236         ffrt::TaskLoadTracking::End(task); // Todo: deal with CoWait()
237         CoStackCheck(co);
238         auto pending = g_CoThreadEnv->pending;
239         if (pending == nullptr) {
240 #ifdef FFRT_BBOX_ENABLE
241             TaskFinishCounterInc();
242 #endif
243             break;
244         }
245         g_CoThreadEnv->pending = nullptr;
246         // Fast path: skip state transition
247         if ((*pending)(task)) {
248             FFRT_LOGD("Cowait task[%lu], name[%s]", task->gid, task->label.c_str());
249 #ifdef FFRT_BBOX_ENABLE
250             TaskSwitchCounterInc();
251 #endif
252             return;
253         }
254         FFRT_WAKE_TRACER(task->gid); // fast path wk
255         g_CoThreadEnv->runningCo = co;
256     }
257 }
258 
259 // called by thread work
CoYield(void)260 void CoYield(void)
261 {
262     CoRoutine* co = static_cast<CoRoutine*>(g_CoThreadEnv->runningCo);
263     co->status.store(static_cast<int>(CoStatus::CO_NOT_FINISH));
264     g_CoThreadEnv->runningCo = nullptr;
265     CoSwitchOutTrace(co->task);
266     FFRT_BLOCK_MARKER(co->task->gid);
267     CoSwitch(&co->ctx, &g_CoThreadEnv->schCtx);
268     while (GetBboxEnableState() != 0) {
269         if (GetBboxEnableState() != gettid()) {
270             BboxFreeze(); // freeze non-crash thread
271             return;
272         }
273         const int IGNORE_DEPTH = 3;
274         backtrace(IGNORE_DEPTH);
275         co->status.store(static_cast<int>(CoStatus::CO_NOT_FINISH)); // recovery to old state
276         CoExit(co);
277     }
278 }
279 
CoWait(const std::function<bool (ffrt::TaskCtx *)> & pred)280 void CoWait(const std::function<bool(ffrt::TaskCtx*)>& pred)
281 {
282     g_CoThreadEnv->pending = &pred;
283     CoYield();
284 }
285 
CoWake(ffrt::TaskCtx * task,bool timeOut)286 void CoWake(ffrt::TaskCtx* task, bool timeOut)
287 {
288     if (task == nullptr) {
289         FFRT_LOGE("task is nullptr");
290         return;
291     }
292     // Fast path: state transition without lock
293     FFRT_LOGD("Cowake task[%lu], name[%s], timeOut[%d]", task->gid, task->label.c_str(), timeOut);
294     task->wakeupTimeOut = timeOut;
295     FFRT_WAKE_TRACER(task->gid);
296     task->UpdateState(ffrt::TaskState::READY);
297 }
298