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