• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024-2025 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 "intrinsics.h"
17 #include "libpandabase/os/mutex.h"
18 #include "runtime/include/exceptions.h"
19 #include "runtime/include/thread_scopes.h"
20 #include "runtime/mem/refstorage/reference.h"
21 #include "plugins/ets/runtime/ets_coroutine.h"
22 #include "plugins/ets/runtime/ets_utils.h"
23 
24 #include <cstdint>
25 #include <thread>
26 
27 namespace ark::ets::intrinsics {
28 
RunExclusiveTask(mem::Reference * taskRef,mem::GlobalObjectStorage * refStorage)29 static void RunExclusiveTask(mem::Reference *taskRef, mem::GlobalObjectStorage *refStorage)
30 {
31     ScopedManagedCodeThread managedCode(EtsCoroutine::GetCurrent());
32     auto *taskObj = EtsObject::FromCoreType(refStorage->Get(taskRef));
33     refStorage->Remove(taskRef);
34     LambdaUtils::InvokeVoid(EtsCoroutine::GetCurrent(), taskObj);
35 }
36 
TryCreateEACoroutine(PandaEtsVM * etsVM,bool needInterop,bool & limitIsReached,bool & jsEnvEmpty,os::memory::Event & event)37 Coroutine *TryCreateEACoroutine(PandaEtsVM *etsVM, bool needInterop, bool &limitIsReached, bool &jsEnvEmpty,
38                                 os::memory::Event &event)
39 {
40     auto *runtime = Runtime::GetCurrent();
41     auto *coroMan = etsVM->GetCoroutineManager();
42     auto *ifaceTable = EtsCoroutine::CastFromThread(coroMan->GetMainThread())->GetExternalIfaceTable();
43 
44     auto *exclusiveCoro = coroMan->CreateExclusiveWorkerForThread(runtime, etsVM);
45     // exclusiveCoro == nullptr means that we reached the limit of eaworkers count or memory resources
46     if (exclusiveCoro == nullptr) {
47         limitIsReached = true;
48         event.Fire();
49         return nullptr;
50     }
51 
52     // early return to avoid waste time on creating jsEnv
53     if (!needInterop) {
54         event.Fire();
55         return exclusiveCoro;
56     }
57 
58     auto *jsEnv = ifaceTable->CreateJSRuntime();
59     // current we cannot create JSVM instance without jsEnv
60     // so we cannot create eaworker support interop withoutJSEnv
61     if (jsEnv == nullptr) {
62         jsEnvEmpty = true;
63         event.Fire();
64         return nullptr;
65     }
66 
67     ifaceTable->CreateInteropCtx(exclusiveCoro, jsEnv);
68     event.Fire();
69     return exclusiveCoro;
70 }
71 
RunTaskOnEACoroutine(PandaEtsVM * etsVM,bool needInterop,mem::Reference * taskRef)72 void RunTaskOnEACoroutine(PandaEtsVM *etsVM, bool needInterop, mem::Reference *taskRef)
73 {
74     auto *refStorage = etsVM->GetGlobalObjectStorage();
75     auto *coroMan = etsVM->GetCoroutineManager();
76 
77     if (needInterop) {
78         auto poster = etsVM->CreateCallbackPoster();
79         ASSERT(poster != nullptr);
80         poster->Post(RunExclusiveTask, taskRef, refStorage);
81         // 2 NativeEngine async_t and 1 async_t for each of the two instances of CallbackPoster
82         // CC-OFFNXT(G.NAM.03-CPP) project code style
83         static constexpr uint32_t MANUALLY_HANDLED_ASYNC_COUNT = 4U;
84         WalkEventLoopCallback cntHandles = []([[maybe_unused]] void *handle, void *arg) {
85             auto *cnt = reinterpret_cast<uint32_t *>(arg);
86             (*cnt)++;
87         };
88         // CC-OFFNXT(G.CTL.03) implementation feature
89         while (true) {
90             // NOTE(ksarychev, #25367): change to handle corner cases
91             etsVM->RunEventLoop(EventLoopRunMode::RUN_ONCE);
92             uint32_t handleCount = 0;
93             etsVM->WalkEventLoop(cntHandles, &handleCount);
94             if (handleCount <= MANUALLY_HANDLED_ASYNC_COUNT) {
95                 break;
96             }
97         }
98     } else {
99         RunExclusiveTask(taskRef, refStorage);
100     }
101     coroMan->DestroyExclusiveWorker();
102 }
103 
ExclusiveLaunch(EtsObject * task,uint8_t needInterop)104 void ExclusiveLaunch(EtsObject *task, uint8_t needInterop)
105 {
106     auto *coro = EtsCoroutine::GetCurrent();
107     ASSERT(coro != nullptr);
108     auto *etsVM = coro->GetPandaVM();
109     if (etsVM->GetCoroutineManager()->IsExclusiveWorkersLimitReached()) {
110         ThrowCoroutinesLimitExceedError("The limit of Exclusive Workers has been reached");
111         return;
112     }
113     auto *refStorage = etsVM->GetGlobalObjectStorage();
114     auto *taskRef = refStorage->Add(task->GetCoreType(), mem::Reference::ObjectType::GLOBAL);
115     ASSERT(taskRef != nullptr);
116     auto limitIsReached = false;
117     auto jsEnvEmpty = false;
118     bool supportInterop = static_cast<bool>(needInterop);
119     auto *worker = coro->GetWorker();
120     auto *interopCtx = worker->GetLocalStorage().Get<CoroutineWorker::DataIdx::INTEROP_CTX_PTR, void *>();
121 
122     if (supportInterop && interopCtx == nullptr) {
123         ThrowRuntimeException("Cannot create EAWorker support interop without JsEnv");
124         return;
125     }
126     {
127         ScopedNativeCodeThread nativeScope(coro);
128         auto event = os::memory::Event();
129         auto t = std::thread([&jsEnvEmpty, &limitIsReached, &event, etsVM, taskRef, supportInterop]() {
130             auto *eaCoro = TryCreateEACoroutine(etsVM, supportInterop, limitIsReached, jsEnvEmpty, event);
131             if (eaCoro == nullptr) {
132                 return;
133             }
134             RunTaskOnEACoroutine(etsVM, supportInterop, taskRef);
135         });
136         event.Wait();
137         t.detach();
138     }
139     if (limitIsReached) {
140         ThrowCoroutinesLimitExceedError("The limit of Exclusive Workers has been reached");
141         return;
142     }
143     if (jsEnvEmpty) {
144         ThrowRuntimeException("Cannot create EAWorker support interop without JsEnv");
145         return;
146     }
147 }
148 
TaskPosterCreate()149 int64_t TaskPosterCreate()
150 {
151     auto *coro = EtsCoroutine::GetCurrent();
152     ASSERT(coro != nullptr);
153     auto poster = coro->GetPandaVM()->CreateCallbackPoster();
154     ASSERT(poster != nullptr);
155     return reinterpret_cast<int64_t>(poster.release());
156 }
157 
TaskPosterDestroy(int64_t poster)158 void TaskPosterDestroy(int64_t poster)
159 {
160     auto *taskPoster = reinterpret_cast<CallbackPoster *>(poster);
161     ASSERT(taskPoster != nullptr);
162     Runtime::GetCurrent()->GetInternalAllocator()->Delete(taskPoster);
163 }
164 
TaskPosterPost(int64_t poster,EtsObject * task)165 void TaskPosterPost(int64_t poster, EtsObject *task)
166 {
167     auto *taskPoster = reinterpret_cast<CallbackPoster *>(poster);
168     ASSERT(taskPoster != nullptr);
169 
170     auto *coro = EtsCoroutine::GetCurrent();
171     ASSERT(coro != nullptr);
172     auto *refStorage = coro->GetPandaVM()->GetGlobalObjectStorage();
173     auto *taskRef = refStorage->Add(task->GetCoreType(), mem::Reference::ObjectType::GLOBAL);
174     taskPoster->Post(RunExclusiveTask, taskRef, refStorage);
175 }
176 
177 }  // namespace ark::ets::intrinsics
178