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 =
32 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_RANDOM_KERNEL)) |
33 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_TS_HWTS_KERNEL)) |
34 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_AICPU_MSG)) |
35 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_TS_CTRL_MSG)) |
36 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_SPLIT_KERNEL)) |
37 : (static_cast<uint32_t>(1U) << static_cast<uint32_t>(EVENT_FFTS_PLUS_MSG));
38 : constexpr const char_t *SYSCALL_WHITE_LIST = "/var/aicpu_custom_syscall_whitelist";
39 : }
40 :
41 : namespace AicpuSchedule {
42 18 : ThreadPool &ThreadPool::Instance()
43 : {
44 18 : static ThreadPool threadPool;
45 18 : return threadPool;
46 : }
47 :
48 29 : ThreadPool::ThreadPool()
49 29 : : semInitedNum_(0U) {}
50 :
51 29 : ThreadPool::~ThreadPool()
52 : {
53 32 : for (size_t i = 0UL; i < semInitedNum_; ++i) {
54 3 : (void)sem_destroy(&(sems_[i]));
55 : }
56 29 : }
57 :
58 7 : int32_t ThreadPool::CreateWorker()
59 : {
60 7 : const uint32_t aicpuNum = AicpuDrvManager::GetInstance().GetAicpuNum();
61 7 : if (aicpuNum == 0U) {
62 2 : aicpusd_run_info("aicpu num[0], not need create aicpu worker");
63 : } else {
64 : try {
65 5 : sems_ = std::move(std::vector<sem_t>(static_cast<size_t>(aicpuNum)));
66 0 : } catch (std::exception &e) {
67 0 : aicpusd_err("create sems failed, %s", e.what());
68 0 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
69 0 : }
70 9 : for (uint32_t threadIndex = 0U; threadIndex < aicpuNum; ++threadIndex) {
71 5 : const int32_t semInitRet = sem_init(&(sems_[static_cast<size_t>(threadIndex)]), 0, 0U);
72 5 : if (semInitRet == -1) {
73 1 : aicpusd_err("sem[%u] init failed, %s", threadIndex, strerror(errno));
74 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
75 : }
76 4 : semInitedNum_ = threadIndex + 1U;
77 : }
78 : try {
79 8 : threadStatus_ = std::move(std::vector<ThreadStatus>(static_cast<size_t>(aicpuNum),
80 12 : ThreadStatus::THREAD_INIT));
81 0 : } catch (std::exception &e) {
82 0 : aicpusd_err("create ThreadStatus failed, %s", e.what());
83 0 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
84 0 : }
85 4 : int32_t ret = AICPU_SCHEDULE_OK;
86 4 : const sighandler_t oldHandler = signal(SIGCHLD, SIG_DFL);
87 4 : aicpusd_info("Set SIGCHLD to %d, old sighandler[%d]", SIG_DFL, oldHandler);
88 4 : GetExpandedSysCalls(SYSCALL_WHITE_LIST);
89 7 : for (uint32_t threadIndex = 0U; threadIndex < aicpuNum; ++threadIndex) {
90 4 : ret = CreateOneWorker(threadIndex);
91 4 : if (ret != AICPU_SCHEDULE_OK) {
92 1 : (void)signal(SIGCHLD, oldHandler);
93 1 : return ret;
94 : }
95 : }
96 3 : for (size_t threadIndex = 0UL; threadIndex < static_cast<size_t>(aicpuNum); threadIndex++) {
97 3 : const int32_t semWaitRet = sem_wait(&(sems_[threadIndex]));
98 3 : if (semWaitRet == -1) {
99 1 : (void)signal(SIGCHLD, oldHandler);
100 1 : aicpusd_err("sem[%zu] wait failed, %s", threadIndex, strerror(errno));
101 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
102 : }
103 2 : if (threadStatus_[threadIndex] != ThreadStatus::THREAD_RUNNING) {
104 2 : (void)signal(SIGCHLD, oldHandler);
105 2 : aicpusd_err("create thread[%zu] failed, status[%d]", threadIndex, threadStatus_[threadIndex]);
106 2 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
107 : }
108 : }
109 0 : aicpusd_info("set SIGCHLD to old sighandler[%d]", oldHandler);
110 0 : (void)signal(SIGCHLD, oldHandler);
111 : }
112 : // GetInstance is not null, checked in InitAICPUScheduler
113 2 : auto ret = AicpuSchedule::AicpuMonitor::GetInstance().Run();
114 2 : if (ret != AICPU_SCHEDULE_OK) {
115 0 : aicpusd_err("aicpu monitor run failed, ret[%d]", ret);
116 0 : return ret;
117 : }
118 :
119 2 : return AICPU_SCHEDULE_OK;
120 : }
121 :
122 1 : int32_t ThreadPool::CreateOneWorker(const uint32_t threadIndex)
123 : {
124 1 : aicpusd_info("CreateOneWorker index[%d]", threadIndex);
125 : try {
126 1 : std::thread th(&ThreadPool::Work, threadIndex);
127 1 : workers_.emplace_back(std::move(th));
128 1 : } catch (std::exception &e) {
129 0 : aicpusd_err("create aicpu worker[%u] failed, %s", threadIndex, e.what());
130 0 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
131 0 : }
132 : try {
133 1 : workers_[static_cast<size_t>(threadIndex)].detach();
134 0 : } catch (std::exception &e) {
135 0 : aicpusd_err("thread[%u] detach failed, %s", threadIndex, e.what());
136 0 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
137 0 : }
138 :
139 1 : return AICPU_SCHEDULE_OK;
140 : }
141 :
142 6 : void ThreadPool::GetExpandedSysCalls(const char_t * const whitelist)
143 : {
144 6 : std::ifstream inFile(whitelist);
145 6 : if ((access(whitelist, R_OK) != 0) || !inFile) {
146 5 : aicpusd_info("syscall file: %s is invalid", whitelist);
147 5 : return;
148 : }
149 2 : const ScopeGuard fileGuard([&inFile] () { inFile.close(); });
150 :
151 1 : std::string syscallStr;
152 4 : while (getline(inFile, syscallStr)) {
153 3 : aicpusd_info("read syscall: %s", syscallStr.c_str());
154 3 : const int32_t syscallNo = seccomp_syscall_resolve_name(syscallStr.c_str());
155 3 : if (syscallNo < 0) {
156 1 : aicpusd_run_warn("Unknown syscall: %s, ret is %d.", syscallStr.c_str(), syscallNo);
157 1 : continue;
158 : }
159 2 : aicpusd_info("syscall: %s, syscallNo: %d", syscallStr.c_str(), syscallNo);
160 2 : (void) expandedSystemCalls_.insert(syscallNo);
161 : }
162 6 : }
163 :
164 1 : void ThreadPool::ExpandSysCallList(std::unordered_set<int32_t> &filterSystemCalls) {
165 3 : for (const auto expandedSystemCall : expandedSystemCalls_) {
166 2 : const auto insertRet = filterSystemCalls.insert(expandedSystemCall);
167 2 : if (insertRet.second) {
168 1 : aicpusd_run_info("Expand syscallNo: %d.", expandedSystemCall);
169 : }
170 : }
171 1 : }
172 :
173 3 : int32_t ThreadPool::SecureCompute(const uint32_t threadIndex)
174 : {
175 3 : if ((FeatureCtrl::IsAosCore() || (!AicpuSchedule::AicpuDrvManager::GetInstance().GetSafeVerifyFlag()))) {
176 3 : threadStatus_[static_cast<size_t>(threadIndex)] = ThreadStatus::THREAD_RUNNING;
177 3 : aicpusd_info("Execute seccomp_load success.");
178 3 : return AICPU_SCHEDULE_OK;
179 : }
180 : std::unordered_set<int32_t> filterSystemCalls = {
181 : SCMP_SYS(open),
182 : SCMP_SYS(close),
183 : SCMP_SYS(faccessat),
184 : SCMP_SYS(fstat),
185 : SCMP_SYS(futex),
186 : SCMP_SYS(getpid),
187 : SCMP_SYS(gettid),
188 : SCMP_SYS(ioctl),
189 : SCMP_SYS(lseek),
190 : SCMP_SYS(nanosleep),
191 : SCMP_SYS(openat),
192 : SCMP_SYS(newfstatat),
193 : SCMP_SYS(pselect6),
194 : SCMP_SYS(read),
195 : SCMP_SYS(readlinkat),
196 : SCMP_SYS(rt_sigaction),
197 : SCMP_SYS(mmap),
198 : SCMP_SYS(mprotect),
199 : SCMP_SYS(exit),
200 : SCMP_SYS(exit_group),
201 : SCMP_SYS(madvise),
202 : SCMP_SYS(sched_getaffinity),
203 : SCMP_SYS(rt_sigprocmask),
204 : SCMP_SYS(set_robust_list),
205 : SCMP_SYS(munmap),
206 : SCMP_SYS(sysinfo),
207 : SCMP_SYS(clock_nanosleep),
208 : SCMP_SYS(uname),
209 : SCMP_SYS(getcpu),
210 : SCMP_SYS(write),
211 : SCMP_SYS(getrandom)
212 0 : };
213 0 : ExpandSysCallList(filterSystemCalls);
214 :
215 : // filter enable system calls
216 0 : const scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ERRNO(1U));
217 0 : int32_t ret = 0;
218 0 : for (auto filterSystemCall : filterSystemCalls) {
219 0 : ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, filterSystemCall, 0U);
220 0 : if (ret != 0) {
221 0 : threadStatus_[static_cast<size_t>(threadIndex)] = ThreadStatus::THREAD_EXIT;
222 0 : aicpusd_err("Add the system call failed, thread threadIndex[%u], ret[%d],"
223 : " syscall number[%d].",
224 : threadIndex, ret, filterSystemCall);
225 0 : return AICPU_SCHEDULE_ERROR_COMMON_ERROR;
226 : }
227 : }
228 :
229 0 : ret = seccomp_load(ctx);
230 0 : if (ret != 0) {
231 0 : threadStatus_[static_cast<size_t>(threadIndex)] = ThreadStatus::THREAD_EXIT;
232 0 : aicpusd_err("Execute seccomp_load failed, thread threadIndex[%u], ret[%d].", threadIndex, ret);
233 0 : return AICPU_SCHEDULE_ERROR_COMMON_ERROR;
234 : }
235 0 : threadStatus_[static_cast<size_t>(threadIndex)] = ThreadStatus::THREAD_RUNNING;
236 0 : aicpusd_info("Execute seccomp_load success.");
237 0 : return AICPU_SCHEDULE_OK;
238 0 : }
239 :
240 5 : void ThreadPool::Work(const uint32_t threadIndex)
241 : {
242 5 : const uint32_t deviceId = AicpuSchedule::AicpuDrvManager::GetInstance().GetDeviceId();
243 : aicpu::aicpuContext_t context;
244 5 : context.tsId = 0U;
245 5 : context.deviceId = deviceId;
246 5 : context.hostPid = AicpuDrvManager::GetInstance().GetHostPid();
247 5 : context.vfId = AicpuDrvManager::GetInstance().GetVfId();
248 5 : aicpu::SetUniqueVfId(AicpuDrvManager::GetInstance().GetUniqueVfId());
249 5 : const auto aicpuRet = aicpu::aicpuSetContext(&context);
250 5 : if (aicpuRet != aicpu::AICPU_ERROR_NONE) {
251 1 : aicpusd_err("Set aicpu context failed, deviceId[%u], thread[%u].", deviceId, threadIndex);
252 1 : AicpuSchedule::ThreadPool::Instance().PostSem(threadIndex);
253 3 : return;
254 : }
255 4 : aicpusd_info("Aicpu device[%u]:thread[%u] started.", deviceId, threadIndex);
256 4 : const auto ret = halEschedSubscribeEvent(deviceId, DEFAULT_GROUP_ID, threadIndex, EVENT_MASK);
257 4 : if (ret != static_cast<int32_t>(DRV_ERROR_NONE)) {
258 1 : aicpusd_err("halEschedSubscribeEvent failed, deviceId[%u], groupId[%u], threadIndex[%u] eventBitmap[%llu].",
259 : deviceId, DEFAULT_GROUP_ID, threadIndex, EVENT_MASK);
260 1 : AicpuSchedule::ThreadPool::Instance().PostSem(threadIndex);
261 1 : return;
262 : }
263 3 : aicpusd_info("halEschedSubscribeEvent success, deviceId[%u], groupId[%u], threadIndex[%u] eventBitmap[%llu].",
264 : deviceId, DEFAULT_GROUP_ID, threadIndex, EVENT_MASK);
265 3 : if (AicpuSchedule::ThreadPool::Instance().SetAffinity(static_cast<size_t>(threadIndex), deviceId) !=
266 : AICPU_SCHEDULE_OK) {
267 0 : AicpuSchedule::ThreadPool::Instance().PostSem(threadIndex);
268 0 : return;
269 : }
270 3 : if (AicpuSchedule::ThreadPool::Instance().SecureCompute(threadIndex) !=
271 : 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]",
285 : threadIndex, threadStatus_.size());
286 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
287 : }
288 :
289 6 : std::string command = "sudo /var/add_aicpu_tid_to_tasks.sh";
290 3 : std::string pathStr = "/var/add_aicpu_tid_to_tasks.sh";
291 3 : if (access(pathStr.c_str(), F_OK) != 0) {
292 2 : aicpusd_info("Not find add_aicpu_tid_to_tasks.sh.");
293 2 : return AICPU_SCHEDULE_OK;
294 : }
295 1 : command = command + " " + std::to_string(GetTid());
296 :
297 : // system() may fail due to "No child processes".
298 : // if SIGCHLD is set to SIG_IGN, waitpid() may report ECHILD error because it cannot find the child process.
299 : // The reason is that the system() relies on a feature of the system, that is,
300 : // when the kernel initializes the process, the processing mode of SIGCHLD signal is SIG_IGN.
301 1 : const int32_t ret = AicpuSchedule::AicpuUtil::ExecuteCmd(command);
302 1 : if (ret != 0) {
303 1 : threadStatus_[threadIndex] = ThreadStatus::THREAD_EXIT;
304 1 : aicpusd_err("write tid[%lu] to /sys/fs/cgroup/cpuset/AICPU/tasks failed, ret[%d], strerror[%s]",
305 : GetTid(), ret, 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 :
338 4 : int32_t ThreadPool::SetAffinityByPm(const size_t threadIndex)
339 : {
340 4 : const uint32_t physIndex = AicpuDrvManager::GetInstance().GetAicpuPhysIndex(static_cast<uint32_t>(threadIndex));
341 4 : const pid_t tid = static_cast<pid_t>(GetTid());
342 :
343 4 : std::vector<uint32_t> coreAffinity;
344 4 : coreAffinity.push_back(physIndex);
345 4 : const auto ret = ProcMgrBindThread(tid, coreAffinity);
346 4 : if (ret != 0U) {
347 1 : threadStatus_[threadIndex] = ThreadStatus::THREAD_EXIT;
348 1 : aicpusd_err("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(static_cast<uint32_t>(threadIndex),
381 1 : AicpuDrvManager::GetInstance().GetDeviceId());
382 : } else {
383 2 : physIndex = AicpuDrvManager::GetInstance().GetAicpuPhysIndex(static_cast<uint32_t>(threadIndex));
384 : }
385 3 : aicpusd_info("[custom]SetAffinityBySelf, threadIndex[%u], physIndex[%u], devNum[%u]",
386 : static_cast<uint32_t>(threadIndex), physIndex, devNum);
387 :
388 : // cannot overflow, aicpu num < 65535, max [64=4*16]
389 3 : CPU_SET(static_cast<int32_t>(physIndex), &mask);
390 3 : const int32_t ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &mask);
391 3 : if (ret != 0) {
392 1 : threadStatus_[threadIndex] = ThreadStatus::THREAD_EXIT;
393 1 : aicpusd_err("set affinity failed ret[%d], aicpu logical threadIndex[%u], aicpu physical threadIndex[%u], "
394 : "device id[%u]",
395 : ret, threadIndex, physIndex, AicpuDrvManager::GetInstance().GetDeviceId());
396 1 : return AICPU_SCHEDULE_ERROR_INIT_FAILED;
397 : }
398 2 : threadStatus_[threadIndex] = ThreadStatus::THREAD_RUNNING;
399 2 : aicpusd_info(
400 : "set affinity success, aicpu logical threadIndex[%u], aicpu physical threadIndex[%u], device id[%u]",
401 : threadIndex, physIndex, AicpuDrvManager::GetInstance().GetDeviceId());
402 2 : return AICPU_SCHEDULE_OK;
403 : }
404 :
405 5 : int32_t ThreadPool::SetAffinity(const size_t threadIndex, const uint32_t deviceId)
406 : {
407 : (void)deviceId;
408 5 : std::string cpuSetFlag;
409 5 : const char_t *const envValue = std::getenv("PROCMGR_AICPU_CPUSET");
410 5 : if (envValue != nullptr) {
411 10 : cpuSetFlag = std::string(envValue);
412 : }
413 :
414 5 : int32_t res = AICPU_SCHEDULE_OK;
415 5 : if (cpuSetFlag == "1") {
416 3 : res = SetAffinityByPm(threadIndex);
417 3 : aicpusd_run_info("aicpu bind tid by pm, cpuSetFlag:[%s], threadIndex[%zu], deviceId[%u], res[%d].",
418 : cpuSetFlag.c_str(), threadIndex, AicpuDrvManager::GetInstance().GetDeviceId(), res);
419 : } else {
420 2 : res = SetAffinityBySelf(threadIndex);
421 2 : aicpusd_run_info("aicpu bind tid by self, cpuSetFlag:[%s], threadIndex[%zu], deviceId[%u], res[%d].",
422 : cpuSetFlag.c_str(), threadIndex, AicpuDrvManager::GetInstance().GetDeviceId(), res);
423 : }
424 5 : return res;
425 5 : }
426 :
427 1 : void ThreadPool::PostSem(const uint32_t threadIndex)
428 : {
429 1 : (void)sem_post(&(sems_[static_cast<size_t>(threadIndex)]));
430 1 : }
431 : } // namespace AicpuSchedule
|