• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-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 #ifndef PANDA_RUNTIME_ETS_FFI_CLASSES_ETS_PROMISE_H_
17 #define PANDA_RUNTIME_ETS_FFI_CLASSES_ETS_PROMISE_H_
18 
19 #include "libpandabase/macros.h"
20 #include "plugins/ets/runtime/ets_panda_file_items.h"
21 #include "plugins/ets/runtime/types/ets_object.h"
22 #include "plugins/ets/runtime/types/ets_array.h"
23 #include "plugins/ets/runtime/types/ets_sync_primitives.h"
24 #include "plugins/ets/runtime/types/ets_primitives.h"
25 #include "runtime/coroutines/coroutine_events.h"
26 
27 namespace ark::ets {
28 
29 class EtsCoroutine;
30 
31 namespace test {
32 class EtsPromiseTest;
33 }  // namespace test
34 
35 class EtsPromise : public ObjectHeader {
36 public:
37     // temp
38     static constexpr EtsInt STATE_PENDING = 0;
39     static constexpr EtsInt STATE_LINKED = 1;
40     static constexpr EtsInt STATE_RESOLVED = 2;
41     static constexpr EtsInt STATE_REJECTED = 3;
42 
43     EtsPromise() = delete;
44     ~EtsPromise() = delete;
45 
46     NO_COPY_SEMANTIC(EtsPromise);
47     NO_MOVE_SEMANTIC(EtsPromise);
48 
49     PANDA_PUBLIC_API static EtsPromise *Create(EtsCoroutine *etsCoroutine = EtsCoroutine::GetCurrent());
50 
FromCoreType(ObjectHeader * promise)51     static EtsPromise *FromCoreType(ObjectHeader *promise)
52     {
53         return reinterpret_cast<EtsPromise *>(promise);
54     }
55 
GetCoreType()56     ObjectHeader *GetCoreType() const
57     {
58         return static_cast<ObjectHeader *>(const_cast<EtsPromise *>(this));
59     }
60 
AsObject()61     EtsObject *AsObject()
62     {
63         return EtsObject::FromCoreType(this);
64     }
65 
AsObject()66     const EtsObject *AsObject() const
67     {
68         return EtsObject::FromCoreType(this);
69     }
70 
FromEtsObject(EtsObject * promise)71     static EtsPromise *FromEtsObject(EtsObject *promise)
72     {
73         return reinterpret_cast<EtsPromise *>(promise);
74     }
75 
GetCallbackQueue(EtsCoroutine * coro)76     EtsObjectArray *GetCallbackQueue(EtsCoroutine *coro) const
77     {
78         return EtsObjectArray::FromCoreType(
79             ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, callbackQueue_)));
80     }
81 
SetCallbackQueue(EtsCoroutine * coro,EtsObjectArray * callbackQueue)82     void SetCallbackQueue(EtsCoroutine *coro, EtsObjectArray *callbackQueue)
83     {
84         ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, callbackQueue_), callbackQueue->GetCoreType());
85     }
86 
GetLaunchModeQueue(EtsCoroutine * coro)87     EtsIntArray *GetLaunchModeQueue(EtsCoroutine *coro) const
88     {
89         return reinterpret_cast<EtsIntArray *>(
90             ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, launchModeQueue_)));
91     }
92 
SetLaunchModeQueue(EtsCoroutine * coro,EtsIntArray * launchModeQueue)93     void SetLaunchModeQueue(EtsCoroutine *coro, EtsIntArray *launchModeQueue)
94     {
95         ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, launchModeQueue_),
96                                   launchModeQueue->GetCoreType());
97     }
98 
GetQueueSize()99     EtsInt GetQueueSize() const
100     {
101         return queueSize_;
102     }
103 
IsResolved()104     bool IsResolved() const
105     {
106         return (state_ == STATE_RESOLVED);
107     }
108 
IsRejected()109     bool IsRejected() const
110     {
111         return (state_ == STATE_REJECTED);
112     }
113 
IsPending()114     bool IsPending() const
115     {
116         return (state_ == STATE_PENDING);
117     }
118 
IsLinked()119     bool IsLinked() const
120     {
121         return (state_ == STATE_LINKED);
122     }
123 
IsProxy()124     bool IsProxy() const
125     {
126         return GetLinkedPromise(EtsCoroutine::GetCurrent()) != nullptr;
127     }
128 
GetInteropObject(EtsCoroutine * coro)129     EtsObject *GetInteropObject(EtsCoroutine *coro) const
130     {
131         auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, interopObject_));
132         return EtsObject::FromCoreType(obj);
133     }
134 
SetInteropObject(EtsCoroutine * coro,EtsObject * o)135     void SetInteropObject(EtsCoroutine *coro, EtsObject *o)
136     {
137         ASSERT(o != nullptr);
138         ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, interopObject_), o->GetCoreType());
139     }
140 
GetLinkedPromise(EtsCoroutine * coro)141     EtsObject *GetLinkedPromise(EtsCoroutine *coro) const
142     {
143         auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, linkedPromise_));
144         return EtsObject::FromCoreType(obj);
145     }
146 
SetLinkedPromise(EtsCoroutine * coro,EtsObject * p)147     void SetLinkedPromise(EtsCoroutine *coro, EtsObject *p)
148     {
149         ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, linkedPromise_), p->GetCoreType());
150     }
151 
CreateLink(EtsObject * source,EtsPromise * target)152     static void CreateLink(EtsObject *source, EtsPromise *target)
153     {
154         ASSERT(target != nullptr);
155         EtsCoroutine *currentCoro = EtsCoroutine::GetCurrent();
156         auto *jobQueue = currentCoro->GetExternalIfaceTable()->GetJobQueue();
157         if (jobQueue != nullptr) {
158             ASSERT(target != nullptr);
159             jobQueue->CreateLink(source, target->AsObject());
160         }
161     }
162 
GetMutex(EtsCoroutine * coro)163     EtsMutex *GetMutex(EtsCoroutine *coro) const
164     {
165         auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, mutex_));
166         return EtsMutex::FromCoreType(obj);
167     }
168 
SetMutex(EtsCoroutine * coro,EtsMutex * mutex)169     void SetMutex(EtsCoroutine *coro, EtsMutex *mutex)
170     {
171         ASSERT(mutex != nullptr);
172         ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, mutex_), mutex->GetCoreType());
173     }
174 
GetEvent(EtsCoroutine * coro)175     EtsEvent *GetEvent(EtsCoroutine *coro) const
176     {
177         auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, event_));
178         return EtsEvent::FromCoreType(obj);
179     }
180 
SetEvent(EtsCoroutine * coro,EtsEvent * event)181     void SetEvent(EtsCoroutine *coro, EtsEvent *event)
182     {
183         ASSERT(event != nullptr);
184         ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, event_), event->GetCoreType());
185     }
186 
Resolve(EtsCoroutine * coro,EtsObject * value)187     void Resolve(EtsCoroutine *coro, EtsObject *value)
188     {
189         ASSERT(IsPending() || IsLinked());
190         auto coreValue = (value == nullptr) ? nullptr : value->GetCoreType();
191         ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, value_), coreValue);
192         state_ = STATE_RESOLVED;
193         OnPromiseCompletion(coro);
194     }
195 
Reject(EtsCoroutine * coro,EtsObject * error)196     void Reject(EtsCoroutine *coro, EtsObject *error)
197     {
198         ASSERT(IsPending() || IsLinked());
199         ASSERT(error != nullptr);
200         ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, value_), error->GetCoreType());
201         state_ = STATE_REJECTED;
202         OnPromiseCompletion(coro);
203     }
204 
SubmitCallback(EtsCoroutine * coro,EtsObject * callback,CoroutineLaunchMode launchMode)205     void SubmitCallback(EtsCoroutine *coro, EtsObject *callback, CoroutineLaunchMode launchMode)
206     {
207         ASSERT(IsLocked());
208         ASSERT(queueSize_ < static_cast<int>(GetCallbackQueue(coro)->GetLength()));
209         auto *cbQueue = GetCallbackQueue(coro);
210         auto *launchModeQueue = GetLaunchModeQueue(coro);
211         launchModeQueue->Set(queueSize_, static_cast<int>(launchMode));
212         cbQueue->Set(queueSize_, callback);
213         queueSize_++;
214     }
215 
GetValue(EtsCoroutine * coro)216     EtsObject *GetValue(EtsCoroutine *coro) const
217     {
218         return EtsObject::FromCoreType(ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, value_)));
219     }
220 
GetState()221     uint32_t GetState() const
222     {
223         return state_;
224     }
225 
ValueOffset()226     static size_t ValueOffset()
227     {
228         return MEMBER_OFFSET(EtsPromise, value_);
229     }
230 
231     /// Allows to get exclusive access to the promise state
Lock()232     void Lock()
233     {
234         auto *mutex = GetMutex(EtsCoroutine::GetCurrent());
235         ASSERT(mutex != nullptr);
236         mutex->Lock();
237     }
238 
Unlock()239     void Unlock()
240     {
241         auto *mutex = GetMutex(EtsCoroutine::GetCurrent());
242         ASSERT(mutex != nullptr);
243         ASSERT(mutex->IsHeld());
244         mutex->Unlock();
245     }
246 
IsLocked()247     bool IsLocked()
248     {
249         auto *mutex = GetMutex(EtsCoroutine::GetCurrent());
250         ASSERT(mutex != nullptr);
251         return mutex->IsHeld();
252     }
253 
254     /// Blocks current coroutine until promise is resolved/rejected
Wait()255     void Wait()
256     {
257         auto *event = GetEvent(EtsCoroutine::GetCurrent());
258         ASSERT(event != nullptr);
259         event->Wait();
260     }
261 
ChangeStateToPendingFromLinked()262     void ChangeStateToPendingFromLinked()
263     {
264         ASSERT(IsLinked());
265         state_ = STATE_PENDING;
266     }
267 
268     // launch promise then/catch callback: void()
269     static void LaunchCallback(EtsCoroutine *coro, EtsObject *callback, CoroutineLaunchMode launchMode);
270 
271 private:
272     void OnPromiseCompletion(EtsCoroutine *coro);
273 
ClearQueues(EtsCoroutine * coro)274     void ClearQueues(EtsCoroutine *coro)
275     {
276         ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, callbackQueue_), nullptr);
277         ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, launchModeQueue_), nullptr);
278         queueSize_ = 0;
279     }
280 
281     ObjectPointer<EtsObject> value_;  // the completion value of the Promise
282     ObjectPointer<EtsMutex> mutex_;
283     ObjectPointer<EtsEvent> event_;
284     ObjectPointer<EtsObjectArray>
285         callbackQueue_;  // the queue of 'then and catch' calbacks which will be called when the Promise gets fulfilled
286     ObjectPointer<EtsIntArray> launchModeQueue_;  // the queue of callbacks' launch mode
287     ObjectPointer<EtsObject> interopObject_;      // internal object used in js interop
288     ObjectPointer<EtsObject> linkedPromise_;      // linked JS promise as JSValue (if exists)
289     EtsInt queueSize_;
290     uint32_t state_;  // the Promise's state
291 
292     friend class test::EtsPromiseTest;
293 };
294 
295 }  // namespace ark::ets
296 
297 #endif  // PANDA_RUNTIME_ETS_FFI_CLASSES_ETS_PROMISE_H_
298