1 /**
2 * Copyright (c) 2023-2024 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 "runtime/coroutines/coroutine_events.h"
17 #include "plugins/ets/runtime/types/ets_promise.h"
18 #include "plugins/ets/runtime/ets_coroutine.h"
19 #include "plugins/ets/runtime/ets_vm.h"
20 #include "plugins/ets/runtime/ets_callback.h"
21
22 namespace ark::ets {
23
24 /*static*/
Create(EtsCoroutine * etsCoroutine)25 EtsPromise *EtsPromise::Create(EtsCoroutine *etsCoroutine)
26 {
27 EtsClass *klass = etsCoroutine->GetPandaVM()->GetClassLinker()->GetPromiseClass();
28 EtsObject *etsObject = EtsObject::Create(etsCoroutine, klass);
29 return reinterpret_cast<EtsPromise *>(etsObject);
30 }
31
OnPromiseCompletion(EtsCoroutine * coro)32 void EtsPromise::OnPromiseCompletion(EtsCoroutine *coro)
33 {
34 auto *event = GetEventPtr();
35 // event can be equal to nullptr in case of GENERIC CoroutineEvent
36 if (event != nullptr) {
37 event->Happen();
38 SetEventPtr(nullptr);
39 }
40
41 PandaUnorderedMap<EtsCoroutine *, PandaList<PandaUniquePtr<Callback>>> readyCallbacks;
42 auto *cbQueue = GetCallbackQueue(coro);
43 auto *coroPtrQueue = GetCoroPtrQueue(coro);
44 auto queueSize = GetQueueSize();
45
46 for (int idx = 0; idx < queueSize; ++idx) {
47 auto *callbackCoro = reinterpret_cast<EtsCoroutine *>(coroPtrQueue->Get(idx));
48 auto *thenCallback = cbQueue->Get(idx);
49 auto callback = EtsCallback::Create(coro, thenCallback);
50 readyCallbacks[callbackCoro].push_back(std::move(callback));
51 }
52
53 ClearQueues(coro);
54 for (auto &[callbackCoro, callbacks] : readyCallbacks) {
55 callbackCoro->AcceptAnnouncedCallbacks(std::move(callbacks));
56 }
57 }
58
EnsureCapacity(EtsCoroutine * coro)59 void EtsPromise::EnsureCapacity(EtsCoroutine *coro)
60 {
61 int queueLength = GetCallbackQueue(coro) == nullptr ? 0 : GetCallbackQueue(coro)->GetLength();
62 if (queueSize_ > queueLength) {
63 return;
64 }
65 auto newQueueLength = queueLength * 2U + 1U;
66 auto *objectClass = coro->GetPandaVM()->GetClassLinker()->GetObjectClass();
67 auto *tmpCbQueue = EtsObjectArray::Create(objectClass, newQueueLength);
68 if (queueSize_ != 0) {
69 GetCallbackQueue(coro)->CopyDataTo(tmpCbQueue);
70 }
71 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, callbackQueue_), tmpCbQueue->GetCoreType());
72 auto *newCoroPtrQueue = EtsLongArray::Create(newQueueLength);
73 if (queueSize_ != 0) {
74 auto *coroPtrQueueData = GetCoroPtrQueue(coro)->GetData<EtsCoroutine *>();
75 [[maybe_unused]] auto err =
76 memcpy_s(newCoroPtrQueue->GetData<EtsCoroutine *>(), newQueueLength * sizeof(EtsLong), coroPtrQueueData,
77 queueLength * sizeof(EtsCoroutine *));
78 ASSERT(err == EOK);
79 }
80 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, coroPtrQueue_), newCoroPtrQueue->GetCoreType());
81 }
82
PromotePromiseRef(EtsCoroutine * coro)83 void EtsPromise::PromotePromiseRef(EtsCoroutine *coro)
84 {
85 auto *event = GetEventPtr();
86 // event can be equal to nullptr in case of GENERIC CoroutineEvent
87 if (event == nullptr || event->GetType() != CoroutineEvent::Type::COMPLETION) {
88 return;
89 }
90 auto *completionEvent = static_cast<CompletionEvent *>(event);
91 os::memory::LockHolder lh(*completionEvent);
92 auto *promiseRef = completionEvent->ReleasePromise();
93 // promiseRef can be equal to nullptr in case of concurrent Promise.resolve
94 if (promiseRef == nullptr || promiseRef->IsGlobal()) {
95 completionEvent->SetPromise(promiseRef);
96 return;
97 }
98 ASSERT(promiseRef->IsWeak());
99 auto *storage = coro->GetPandaVM()->GetGlobalObjectStorage();
100 auto *globalPromiseRef = storage->Add(this, mem::Reference::ObjectType::GLOBAL);
101 completionEvent->SetPromise(globalPromiseRef);
102 storage->Remove(promiseRef);
103 }
104
GetOrCreateEventPtr()105 CoroutineEvent *EtsPromise::GetOrCreateEventPtr()
106 {
107 ASSERT(IsLocked());
108 auto *event = GetEventPtr();
109 if (event != nullptr) {
110 if (event->GetType() == CoroutineEvent::Type::GENERIC) {
111 eventRefCounter_++;
112 }
113 return event;
114 }
115 ASSERT(eventRefCounter_ == 0);
116 auto *coroManager = EtsCoroutine::GetCurrent()->GetCoroutineManager();
117 event = Runtime::GetCurrent()->GetInternalAllocator()->New<GenericEvent>(coroManager);
118 eventRefCounter_++;
119 SetEventPtr(event);
120 return event;
121 }
122
RetireEventPtr(CoroutineEvent * event)123 void EtsPromise::RetireEventPtr(CoroutineEvent *event)
124 {
125 ASSERT(event != nullptr);
126 os::memory::LockHolder lk(*this);
127 ASSERT(event->GetType() == CoroutineEvent::Type::GENERIC);
128 ASSERT(GetEventPtr() == event || GetEventPtr() == nullptr);
129 if (eventRefCounter_-- == 1) {
130 Runtime::GetCurrent()->GetInternalAllocator()->Delete(event);
131 SetEventPtr(nullptr);
132 }
133 }
134
135 } // namespace ark::ets
136