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 #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 #include "plugins/ets/runtime/ets_remote_promise_resolver.h" 27 28 namespace ark::ets { 29 30 class EtsCoroutine; 31 32 namespace test { 33 class EtsPromiseTest; 34 } // namespace test 35 36 class EtsPromise : public ObjectHeader { 37 public: 38 // temp 39 static constexpr EtsInt STATE_PENDING = 0; 40 static constexpr EtsInt STATE_RESOLVED = 1; 41 static constexpr EtsInt STATE_REJECTED = 2; 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) 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) 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() 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 IsProxy()119 bool IsProxy() 120 { 121 return GetLinkedPromise(EtsCoroutine::GetCurrent()) != nullptr; 122 } 123 GetInteropObject(EtsCoroutine * coro)124 EtsObject *GetInteropObject(EtsCoroutine *coro) 125 { 126 auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, interopObject_)); 127 return EtsObject::FromCoreType(obj); 128 } 129 SetInteropObject(EtsCoroutine * coro,EtsObject * o)130 void SetInteropObject(EtsCoroutine *coro, EtsObject *o) 131 { 132 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, interopObject_), o->GetCoreType()); 133 } 134 GetLinkedPromise(EtsCoroutine * coro)135 EtsObject *GetLinkedPromise(EtsCoroutine *coro) 136 { 137 auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, linkedPromise_)); 138 return EtsObject::FromCoreType(obj); 139 } 140 SetLinkedPromise(EtsCoroutine * coro,EtsObject * p)141 void SetLinkedPromise(EtsCoroutine *coro, EtsObject *p) 142 { 143 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, linkedPromise_), p->GetCoreType()); 144 } 145 GetMutex(EtsCoroutine * coro)146 EtsMutex *GetMutex(EtsCoroutine *coro) 147 { 148 auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, mutex_)); 149 return EtsMutex::FromCoreType(obj); 150 } 151 SetMutex(EtsCoroutine * coro,EtsMutex * mutex)152 void SetMutex(EtsCoroutine *coro, EtsMutex *mutex) 153 { 154 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, mutex_), mutex->GetCoreType()); 155 } 156 GetEvent(EtsCoroutine * coro)157 EtsEvent *GetEvent(EtsCoroutine *coro) 158 { 159 auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, event_)); 160 return EtsEvent::FromCoreType(obj); 161 } 162 SetEvent(EtsCoroutine * coro,EtsEvent * event)163 void SetEvent(EtsCoroutine *coro, EtsEvent *event) 164 { 165 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, event_), event->GetCoreType()); 166 } 167 Resolve(EtsCoroutine * coro,EtsObject * value)168 void Resolve(EtsCoroutine *coro, EtsObject *value) 169 { 170 ASSERT(state_ == STATE_PENDING); 171 auto coreValue = (value == nullptr) ? nullptr : value->GetCoreType(); 172 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, value_), coreValue); 173 state_ = STATE_RESOLVED; 174 OnPromiseCompletion(coro); 175 } 176 Reject(EtsCoroutine * coro,EtsObject * error)177 void Reject(EtsCoroutine *coro, EtsObject *error) 178 { 179 ASSERT(state_ == STATE_PENDING); 180 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, value_), error->GetCoreType()); 181 state_ = STATE_REJECTED; 182 OnPromiseCompletion(coro); 183 } 184 SubmitCallback(EtsCoroutine * coro,EtsObject * callback,CoroutineLaunchMode launchMode)185 void SubmitCallback(EtsCoroutine *coro, EtsObject *callback, CoroutineLaunchMode launchMode) 186 { 187 ASSERT(IsLocked()); 188 ASSERT(queueSize_ < static_cast<int>(GetCallbackQueue(coro)->GetLength())); 189 auto *cbQueue = GetCallbackQueue(coro); 190 auto *launchModeQueue = GetLaunchModeQueue(coro); 191 launchModeQueue->Set(queueSize_, static_cast<int>(launchMode)); 192 cbQueue->Set(queueSize_, callback); 193 queueSize_++; 194 } 195 GetValue(EtsCoroutine * coro)196 EtsObject *GetValue(EtsCoroutine *coro) 197 { 198 return EtsObject::FromCoreType(ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, value_))); 199 } 200 GetState()201 uint32_t GetState() const 202 { 203 return state_; 204 } 205 ValueOffset()206 static size_t ValueOffset() 207 { 208 return MEMBER_OFFSET(EtsPromise, value_); 209 } 210 211 /// Allows to get exclusive access to the promise state Lock()212 void Lock() 213 { 214 auto *mutex = GetMutex(EtsCoroutine::GetCurrent()); 215 ASSERT(mutex != nullptr); 216 mutex->Lock(); 217 } 218 Unlock()219 void Unlock() 220 { 221 auto *mutex = GetMutex(EtsCoroutine::GetCurrent()); 222 ASSERT(mutex != nullptr); 223 ASSERT(mutex->IsHeld()); 224 mutex->Unlock(); 225 } 226 IsLocked()227 bool IsLocked() 228 { 229 auto *mutex = GetMutex(EtsCoroutine::GetCurrent()); 230 ASSERT(mutex != nullptr); 231 return mutex->IsHeld(); 232 } 233 234 /// Blocks current coroutine until promise is resolved/rejected Wait()235 void Wait() 236 { 237 auto *event = GetEvent(EtsCoroutine::GetCurrent()); 238 ASSERT(event != nullptr); 239 event->Wait(); 240 } 241 242 // launch promise then/catch callback: void() 243 static void LaunchCallback(EtsCoroutine *coro, EtsObject *callback, CoroutineLaunchMode launchMode); 244 SetEtsPromiseResolver(RemotePromiseResolver * resolver)245 void SetEtsPromiseResolver(RemotePromiseResolver *resolver) 246 { 247 remotePromiseResolver_ = reinterpret_cast<EtsLong>(resolver); 248 } 249 250 private: 251 void OnPromiseCompletion(EtsCoroutine *coro); 252 ClearQueues(EtsCoroutine * coro)253 void ClearQueues(EtsCoroutine *coro) 254 { 255 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, callbackQueue_), nullptr); 256 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, launchModeQueue_), nullptr); 257 queueSize_ = 0; 258 } 259 GetPromiseResolver()260 RemotePromiseResolver *GetPromiseResolver() const 261 { 262 return reinterpret_cast<RemotePromiseResolver *>(remotePromiseResolver_); 263 } 264 InvalidatePromiseResolver()265 void InvalidatePromiseResolver() 266 { 267 SetEtsPromiseResolver(nullptr); 268 } 269 270 ObjectPointer<EtsObject> value_; // the completion value of the Promise 271 ObjectPointer<EtsMutex> mutex_; 272 ObjectPointer<EtsEvent> event_; 273 ObjectPointer<EtsObjectArray> 274 callbackQueue_; // the queue of 'then and catch' calbacks which will be called when the Promise gets fulfilled 275 ObjectPointer<EtsIntArray> launchModeQueue_; // the queue of callbacks' launch mode 276 ObjectPointer<EtsObject> interopObject_; // internal object used in js interop 277 ObjectPointer<EtsObject> linkedPromise_; // linked JS promise as JSValue (if exists) 278 EtsInt queueSize_; 279 EtsLong remotePromiseResolver_; // resolver for mirror promise 280 uint32_t state_; // the Promise's state 281 282 friend class test::EtsPromiseTest; 283 }; 284 285 } // namespace ark::ets 286 287 #endif // PANDA_RUNTIME_ETS_FFI_CLASSES_ETS_PROMISE_H_ 288