Line data Source code
1 : /**
2 : * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 : #include "aicpusd_worker.h"
11 :
12 : #include <csignal>
13 : #include <cstring>
14 : #include <fstream>
15 :
16 : #include "aicpusd_status.h"
17 : #include "aicpusd_drv_manager.h"
18 : #include "aicpusd_monitor.h"
19 : #include "aicpusd_event_manager.h"
20 : #include "aicpu_context.h"
21 : #include "aicpusd_common.h"
22 : #include "aicpu_cust_sd_proc_mgr_sys_operator_agent.h"
23 : #include "aicpusd_hal_interface_ref.h"
24 : #include "aicpusd_util.h"
25 : #include "feature_ctrl.h"
26 : #ifndef _AOSCORE_
27 : #include "seccomp.h"
28 : #endif
29 :
30 : namespace {
31 : constexpr uint32_t EVENT_MASK = (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_RANDOM_KERNEL)) |
32 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_TS_HWTS_KERNEL)) |
33 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_AICPU_MSG)) |
34 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_TS_CTRL_MSG)) |
35 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_SPLIT_KERNEL)) |
36 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_FFTS_PLUS_MSG));
37 : constexpr const char_t* SYSCALL_WHITE_LIST = "/var/aicpu_custom_syscall_whitelist";
38 : } // namespace
39 :
40 : namespace AicpuSchedule {
41 18 : ThreadPool& ThreadPool::Instance()
42 : {
43 18 : static ThreadPool threadPool;
44 18 : return threadPool;
45 : }
46 :
47 29 : ThreadPool::ThreadPool() : semInitedNum_(0U) {}
48 :
49 29 : ThreadPool::~ThreadPool()
50 : {
51 32 : for (size_t i = 0UL; i < semInitedNum_; ++i) {
52 3 : (void)sem_destroy(&(sems_[i]));
53 : }
54 29 : }
55 :
56 7 : int32_t ThreadPool::CreateWorker()
57 : {
58 7 : const uint32_t aicpuNum = AicpuDrvManager::GetInstance().GetAicpuNum();
59 7 : if (aicpuNum == 0U) {
60 2 : aicpusd_run_info("aicpu num[0], not need create aicpu worker");
61 : } else {
62 : try {
63 5 : sems_ = std::move(std::vector<sem_t>(static_cast<size_t>(aicpuNum)));
64 0 : } catch (std::exception& e) {
65 0 : aicpusd_err("create sems failed, %s", e.what());
66 0 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
67 0 : }
68 9 : for (uint32_t threadIndex = 0U; threadIndex < aicpuNum; ++threadIndex) {
69 5 : const int32_t semInitRet = sem_init(&(sems_[static_cast<size_t>(threadIndex)]), 0, 0U);
70 5 : if (semInitRet == -1) {
71 1 : aicpusd_err("sem[%u] init failed, %s", threadIndex, strerror(errno));
72 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
73 : }
74 4 : semInitedNum_ = threadIndex + 1U;
75 : }
76 : try {
77 : threadStatus_ =
78 4 : std::move(std::vector<ThreadStatus>(static_cast<size_t>(aicpuNum), ThreadStatus::THREAD_INIT));
79 0 : } catch (std::exception& e) {
80 0 : aicpusd_err("create ThreadStatus failed, %s", e.what());
81 0 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
82 0 : }
83 4 : int32_t ret = AICPU_SCHEDULE_OK;
84 4 : const sighandler_t oldHandler = signal(SIGCHLD, SIG_DFL);
85 4 : aicpusd_info("Set SIGCHLD to %d, old sighandler[%d]", SIG_DFL, oldHandler);
86 4 : GetExpandedSysCalls(SYSCALL_WHITE_LIST);
87 7 : for (uint32_t threadIndex = 0U; threadIndex < aicpuNum; ++threadIndex) {
88 4 : ret = CreateOneWorker(threadIndex);
89 4 : if (ret != AICPU_SCHEDULE_OK) {
90 1 : (void)signal(SIGCHLD, oldHandler);
91 1 : return ret;
92 : }
93 : }
94 3 : for (size_t threadIndex = 0UL; threadIndex < static_cast<size_t>(aicpuNum); threadIndex++) {
95 3 : const int32_t semWaitRet = sem_wait(&(sems_[threadIndex]));
96 3 : if (semWaitRet == -1) {
97 1 : (void)signal(SIGCHLD, oldHandler);
98 1 : aicpusd_err("sem[%zu] wait failed, %s", threadIndex, strerror(errno));
99 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
100 : }
101 2 : if (threadStatus_[threadIndex] != ThreadStatus::THREAD_RUNNING) {
102 2 : (void)signal(SIGCHLD, oldHandler);
103 2 : aicpusd_err("create thread[%zu] failed, status[%d]", threadIndex, threadStatus_[threadIndex]);
104 2 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
105 : }
106 : }
107 0 : aicpusd_info("set SIGCHLD to old sighandler[%d]", oldHandler);
108 0 : (void)signal(SIGCHLD, oldHandler);
109 : }
110 : // GetInstance is not null, checked in InitAICPUScheduler
111 2 : auto ret = AicpuSchedule::AicpuMonitor::GetInstance().Run();
112 2 : if (ret != AICPU_SCHEDULE_OK) {
113 0 : aicpusd_err("aicpu monitor run failed, ret[%d]", ret);
114 0 : return ret;
115 : }
116 :
117 2 : return AICPU_SCHEDULE_OK;
118 : }
119 :
120 1 : int32_t ThreadPool::CreateOneWorker(const uint32_t threadIndex)
121 : {
122 1 : aicpusd_info("CreateOneWorker index[%d]", threadIndex);
123 : try {
124 1 : std::thread th(&ThreadPool::Work, threadIndex);
125 1 : workers_.emplace_back(std::move(th));
126 1 : } catch (std::exception& e) {
127 0 : aicpusd_err("create aicpu worker[%u] failed, %s", threadIndex, e.what());
128 0 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
129 0 : }
130 : try {
131 1 : workers_[static_cast<size_t>(threadIndex)].detach();
132 0 : } catch (std::exception& e) {
133 0 : aicpusd_err("thread[%u] detach failed, %s", threadIndex, e.what());
134 0 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
135 0 : }
136 :
137 1 : return AICPU_SCHEDULE_OK;
138 : }
139 :
140 6 : void ThreadPool::GetExpandedSysCalls(const char_t* const whitelist)
141 : {
142 6 : std::ifstream inFile(whitelist);
143 6 : if ((access(whitelist, R_OK) != 0) || !inFile) {
144 5 : aicpusd_info("syscall file: %s is invalid", whitelist);
145 5 : return;
146 : }
147 2 : const ScopeGuard fileGuard([&inFile]() { inFile.close(); });
148 :
149 1 : std::string syscallStr;
150 4 : while (getline(inFile, syscallStr)) {
151 3 : aicpusd_info("read syscall: %s", syscallStr.c_str());
152 3 : const int32_t syscallNo = seccomp_syscall_resolve_name(syscallStr.c_str());
153 3 : if (syscallNo < 0) {
154 1 : aicpusd_run_warn("Unknown syscall: %s, ret is %d.", syscallStr.c_str(), syscallNo);
155 1 : continue;
156 : }
157 2 : aicpusd_info("syscall: %s, syscallNo: %d", syscallStr.c_str(), syscallNo);
158 2 : (void)expandedSystemCalls_.insert(syscallNo);
159 : }
160 6 : }
161 :
162 1 : void ThreadPool::ExpandSysCallList(std::unordered_set<int32_t>& filterSystemCalls)
163 : {
164 3 : for (const auto expandedSystemCall : expandedSystemCalls_) {
165 2 : const auto insertRet = filterSystemCalls.insert(expandedSystemCall);
166 2 : if (insertRet.second) {
167 1 : aicpusd_run_info("Expand syscallNo: %d.", expandedSystemCall);
168 : }
169 : }
170 1 : }
171 :
172 3 : int32_t ThreadPool::SecureCompute(const uint32_t threadIndex)
173 : {
174 3 : if ((FeatureCtrl::IsAosCore() || (!AicpuSchedule::AicpuDrvManager::GetInstance().GetSafeVerifyFlag()))) {
175 3 : threadStatus_[static_cast<size_t>(threadIndex)] = ThreadStatus::THREAD_RUNNING;
176 3 : aicpusd_info("Execute seccomp_load success.");
177 3 : return AICPU_SCHEDULE_OK;
178 : }
179 : std::unordered_set<int32_t> filterSystemCalls = {
180 : SCMP_SYS(open),
181 : SCMP_SYS(close),
182 : SCMP_SYS(faccessat),
183 : SCMP_SYS(fstat),
184 : SCMP_SYS(futex),
185 : SCMP_SYS(getpid),
186 : SCMP_SYS(gettid),
187 : SCMP_SYS(ioctl),
188 : SCMP_SYS(lseek),
189 : SCMP_SYS(nanosleep),
190 : SCMP_SYS(openat),
191 : SCMP_SYS(newfstatat),
192 : SCMP_SYS(pselect6),
193 : SCMP_SYS(read),
194 : SCMP_SYS(readlinkat),
195 : SCMP_SYS(rt_sigaction),
196 : SCMP_SYS(mmap),
197 : SCMP_SYS(mprotect),
198 : SCMP_SYS(exit),
199 : SCMP_SYS(exit_group),
200 : SCMP_SYS(madvise),
201 : SCMP_SYS(sched_getaffinity),
202 : SCMP_SYS(rt_sigprocmask),
203 : SCMP_SYS(set_robust_list),
204 : SCMP_SYS(munmap),
205 : SCMP_SYS(sysinfo),
206 : SCMP_SYS(clock_nanosleep),
207 : SCMP_SYS(uname),
208 : SCMP_SYS(getcpu),
209 : SCMP_SYS(write),
210 0 : SCMP_SYS(getrandom)};
211 0 : ExpandSysCallList(filterSystemCalls);
212 :
213 : // filter enable system calls
214 0 : const scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ERRNO(1U));
215 0 : int32_t ret = 0;
216 0 : for (auto filterSystemCall : filterSystemCalls) {
217 0 : ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, filterSystemCall, 0U);
218 0 : if (ret != 0) {
219 0 : threadStatus_[static_cast<size_t>(threadIndex)] = ThreadStatus::THREAD_EXIT;
220 0 : aicpusd_err(
221 : "Add the system call failed, thread threadIndex[%u], ret[%d],"
222 : " syscall number[%d].",
223 : threadIndex, ret, filterSystemCall);
224 0 : return AICPU_SCHEDULE_ERROR_COMMON_ERROR;
225 : }
226 : }
227 :
228 0 : ret = seccomp_load(ctx);
229 0 : if (ret != 0) {
230 0 : threadStatus_[static_cast<size_t>(threadIndex)] = ThreadStatus::THREAD_EXIT;
231 0 : aicpusd_err("Execute seccomp_load failed, thread threadIndex[%u], ret[%d].", threadIndex, ret);
232 0 : return AICPU_SCHEDULE_ERROR_COMMON_ERROR;
233 : }
234 0 : threadStatus_[static_cast<size_t>(threadIndex)] = ThreadStatus::THREAD_RUNNING;
235 0 : aicpusd_info("Execute seccomp_load success.");
236 0 : return AICPU_SCHEDULE_OK;
237 0 : }
238 :
239 5 : void ThreadPool::Work(const uint32_t threadIndex)
240 : {
241 5 : const uint32_t deviceId = AicpuSchedule::AicpuDrvManager::GetInstance().GetDeviceId();
242 : aicpu::aicpuContext_t context;
243 5 : context.tsId = 0U;
244 5 : context.deviceId = deviceId;
245 5 : context.hostPid = AicpuDrvManager::GetInstance().GetHostPid();
246 5 : context.vfId = AicpuDrvManager::GetInstance().GetVfId();
247 5 : aicpu::SetUniqueVfId(AicpuDrvManager::GetInstance().GetUniqueVfId());
248 5 : const auto aicpuRet = aicpu::aicpuSetContext(&context);
249 5 : if (aicpuRet != aicpu::AICPU_ERROR_NONE) {
250 1 : aicpusd_err("Set aicpu context failed, deviceId[%u], thread[%u].", deviceId, threadIndex);
251 1 : AicpuSchedule::ThreadPool::Instance().PostSem(threadIndex);
252 3 : return;
253 : }
254 4 : aicpusd_info("Aicpu device[%u]:thread[%u] started.", deviceId, threadIndex);
255 4 : const auto ret = halEschedSubscribeEvent(deviceId, DEFAULT_GROUP_ID, threadIndex, EVENT_MASK);
256 4 : if (ret != static_cast<int32_t>(DRV_ERROR_NONE)) {
257 1 : aicpusd_err(
258 : "halEschedSubscribeEvent failed, deviceId[%u], groupId[%u], threadIndex[%u] eventBitmap[%llu].", deviceId,
259 : DEFAULT_GROUP_ID, threadIndex, EVENT_MASK);
260 1 : AicpuSchedule::ThreadPool::Instance().PostSem(threadIndex);
261 1 : return;
262 : }
263 3 : aicpusd_info(
264 : "halEschedSubscribeEvent success, deviceId[%u], groupId[%u], threadIndex[%u] eventBitmap[%llu].", deviceId,
265 : DEFAULT_GROUP_ID, threadIndex, EVENT_MASK);
266 3 : if (AicpuSchedule::ThreadPool::Instance().SetAffinity(static_cast<size_t>(threadIndex), deviceId) !=
267 : AICPU_SCHEDULE_OK) {
268 0 : AicpuSchedule::ThreadPool::Instance().PostSem(threadIndex);
269 0 : return;
270 : }
271 3 : if (AicpuSchedule::ThreadPool::Instance().SecureCompute(threadIndex) != AICPU_SCHEDULE_OK) {
272 1 : AicpuSchedule::ThreadPool::Instance().PostSem(threadIndex);
273 1 : return;
274 : }
275 2 : AicpuSchedule::ThreadPool::Instance().PostSem(threadIndex);
276 2 : (void)aicpu::SetAicpuThreadIndex(threadIndex);
277 2 : AicpuSchedule::AicpuEventManager::GetInstance().LoopProcess(threadIndex);
278 2 : aicpusd_info("Aicpu device[%u]:thread[%u] stopped.", deviceId, threadIndex);
279 : }
280 :
281 4 : int32_t ThreadPool::WriteTidForAffinity(const size_t threadIndex)
282 : {
283 4 : if (threadIndex >= threadStatus_.size()) {
284 1 : aicpusd_err("threadIndex[%zu], out of rank[0, %zu]", threadIndex, threadStatus_.size());
285 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
286 : }
287 :
288 6 : std::string command = "sudo /var/add_aicpu_tid_to_tasks.sh";
289 3 : std::string pathStr = "/var/add_aicpu_tid_to_tasks.sh";
290 3 : if (access(pathStr.c_str(), F_OK) != 0) {
291 2 : aicpusd_info("Not find add_aicpu_tid_to_tasks.sh.");
292 2 : return AICPU_SCHEDULE_OK;
293 : }
294 1 : command = command + " " + std::to_string(GetTid());
295 :
296 : // system() may fail due to "No child processes".
297 : // if SIGCHLD is set to SIG_IGN, waitpid() may report ECHILD error because it cannot find the child process.
298 : // The reason is that the system() relies on a feature of the system, that is,
299 : // when the kernel initializes the process, the processing mode of SIGCHLD signal is SIG_IGN.
300 1 : const int32_t ret = AicpuSchedule::AicpuUtil::ExecuteCmd(command);
301 1 : if (ret != 0) {
302 1 : threadStatus_[threadIndex] = ThreadStatus::THREAD_EXIT;
303 1 : aicpusd_err(
304 : "write tid[%lu] to /sys/fs/cgroup/cpuset/AICPU/tasks failed, ret[%d], strerror[%s]", GetTid(), ret,
305 : strerror(errno));
306 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
307 : }
308 0 : return AICPU_SCHEDULE_OK;
309 3 : }
310 :
311 7 : int32_t ThreadPool::AddPidToTask(const size_t threadIndex)
312 : {
313 7 : if (FeatureCtrl::IsBindPidByHal()) {
314 2 : if (&halBindCgroup != nullptr) {
315 2 : aicpusd_info("Bind pid by hal index:%zu.", threadIndex);
316 2 : const drvError_t drvRet = halBindCgroup(BIND_AICPU_CGROUP);
317 2 : if (drvRet != DRV_ERROR_NONE) {
318 1 : aicpusd_err("halBindCgroup failed, ret[%d]", drvRet);
319 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
320 : }
321 1 : aicpusd_info("halBindCgroup success");
322 : } else {
323 0 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
324 : }
325 : } else {
326 5 : aicpusd_run_info("AddPidToTask by WriteTidForAffinity");
327 5 : auto ret = WriteTidForAffinity(threadIndex);
328 5 : if (ret != static_cast<int32_t>(AICPU_SCHEDULE_OK)) {
329 1 : aicpusd_err("WriteTidForAffinity failed, ret[%d]", ret);
330 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
331 : }
332 4 : aicpusd_info("WriteTidForAffinity success");
333 : }
334 5 : return AICPU_SCHEDULE_OK;
335 : }
336 :
337 4 : int32_t ThreadPool::SetAffinityByPm(const size_t threadIndex)
338 : {
339 4 : const uint32_t physIndex = AicpuDrvManager::GetInstance().GetAicpuPhysIndex(static_cast<uint32_t>(threadIndex));
340 4 : const pid_t tid = static_cast<pid_t>(GetTid());
341 :
342 4 : std::vector<uint32_t> coreAffinity;
343 4 : coreAffinity.push_back(physIndex);
344 4 : const auto ret = ProcMgrBindThread(tid, coreAffinity);
345 4 : if (ret != 0U) {
346 1 : threadStatus_[threadIndex] = ThreadStatus::THREAD_EXIT;
347 1 : aicpusd_err(
348 : "set affinity failed ret[%d], aicpu logical threadIndex[%zu], "
349 : "aicpu physical threadIndex[%u],tid[%u], device id[%u]",
350 : ret, threadIndex, physIndex, tid, AicpuDrvManager::GetInstance().GetDeviceId());
351 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
352 : }
353 3 : threadStatus_[threadIndex] = ThreadStatus::THREAD_RUNNING;
354 3 : aicpusd_info(
355 : "set affinity success, aicpu logical threadIndex[%zu], aicpu physical threadIndex[%u], device id[%u]",
356 : threadIndex, physIndex, AicpuDrvManager::GetInstance().GetDeviceId());
357 3 : return AICPU_SCHEDULE_OK;
358 4 : }
359 :
360 5 : int32_t ThreadPool::SetAffinityBySelf(const size_t threadIndex)
361 : {
362 5 : if (AddPidToTask(threadIndex) != AICPU_SCHEDULE_OK) {
363 1 : aicpusd_err("AddPidToTask failed");
364 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
365 : }
366 :
367 : cpu_set_t mask;
368 4 : CPU_ZERO(&mask);
369 :
370 4 : uint32_t physIndex = 0;
371 4 : uint32_t devNum = 0U;
372 4 : if (&halGetVdevNum != nullptr) {
373 4 : int32_t result = halGetVdevNum(&devNum);
374 4 : if (result != 0) {
375 1 : aicpusd_err("custom halGetVdevNum, failed result[%d]", result);
376 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
377 : }
378 : }
379 3 : if (devNum > 0U) {
380 2 : physIndex = AicpuDrvManager::GetInstance().GetAicpuPhysIndexInVfMode(
381 1 : static_cast<uint32_t>(threadIndex), AicpuDrvManager::GetInstance().GetDeviceId());
382 : } else {
383 2 : physIndex = AicpuDrvManager::GetInstance().GetAicpuPhysIndex(static_cast<uint32_t>(threadIndex));
384 : }
385 3 : aicpusd_info(
386 : "[custom]SetAffinityBySelf, threadIndex[%u], physIndex[%u], devNum[%u]", static_cast<uint32_t>(threadIndex),
387 : physIndex, devNum);
388 :
389 : // cannot overflow, aicpu num < 65535, max [64=4*16]
390 3 : CPU_SET(static_cast<int32_t>(physIndex), &mask);
391 3 : const int32_t ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &mask);
392 3 : if (ret != 0) {
393 1 : threadStatus_[threadIndex] = ThreadStatus::THREAD_EXIT;
394 1 : aicpusd_err(
395 : "set affinity failed ret[%d], aicpu logical threadIndex[%u], aicpu physical threadIndex[%u], "
396 : "device id[%u]",
397 : ret, threadIndex, physIndex, AicpuDrvManager::GetInstance().GetDeviceId());
398 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
399 : }
400 2 : threadStatus_[threadIndex] = ThreadStatus::THREAD_RUNNING;
401 2 : aicpusd_info(
402 : "set affinity success, aicpu logical threadIndex[%u], aicpu physical threadIndex[%u], device id[%u]",
403 : threadIndex, physIndex, AicpuDrvManager::GetInstance().GetDeviceId());
404 2 : return AICPU_SCHEDULE_OK;
405 : }
406 :
407 5 : int32_t ThreadPool::SetAffinity(const size_t threadIndex, const uint32_t deviceId)
408 : {
409 : (void)deviceId;
410 5 : std::string cpuSetFlag;
411 5 : const char_t* const envValue = std::getenv("PROCMGR_AICPU_CPUSET");
412 5 : if (envValue != nullptr) {
413 10 : cpuSetFlag = std::string(envValue);
414 : }
415 :
416 5 : int32_t res = AICPU_SCHEDULE_OK;
417 5 : if (cpuSetFlag == "1") {
418 3 : res = SetAffinityByPm(threadIndex);
419 3 : aicpusd_run_info(
420 : "aicpu bind tid by pm, cpuSetFlag:[%s], threadIndex[%zu], deviceId[%u], res[%d].", cpuSetFlag.c_str(),
421 : threadIndex, AicpuDrvManager::GetInstance().GetDeviceId(), res);
422 : } else {
423 2 : res = SetAffinityBySelf(threadIndex);
424 2 : aicpusd_run_info(
425 : "aicpu bind tid by self, cpuSetFlag:[%s], threadIndex[%zu], deviceId[%u], res[%d].", cpuSetFlag.c_str(),
426 : threadIndex, AicpuDrvManager::GetInstance().GetDeviceId(), res);
427 : }
428 5 : return res;
429 5 : }
430 :
431 1 : void ThreadPool::PostSem(const uint32_t threadIndex) { (void)sem_post(&(sems_[static_cast<size_t>(threadIndex)])); }
432 : } // namespace AicpuSchedule
|