1 /** 2 * Copyright (c) 2023 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 "plugins/ets/runtime/types/ets_object.h" 20 #include "plugins/ets/runtime/types/ets_array.h" 21 #include "plugins/ets/runtime/types/ets_primitives.h" 22 #include "runtime/coroutines/coroutine_events.h" 23 24 namespace panda::ets { 25 26 class EtsCoroutine; 27 28 namespace test { 29 class EtsPromiseMembers; 30 } // namespace test 31 32 class EtsPromise : public ObjectHeader { 33 public: 34 // temp 35 static constexpr EtsInt STATE_PENDING = 0; 36 static constexpr EtsInt STATE_RESOLVED = 1; 37 static constexpr EtsInt STATE_REJECTED = 2; 38 39 EtsPromise() = delete; 40 ~EtsPromise() = delete; 41 42 NO_COPY_SEMANTIC(EtsPromise); 43 NO_MOVE_SEMANTIC(EtsPromise); 44 45 PANDA_PUBLIC_API static EtsPromise *Create(EtsCoroutine *etsCoroutine = EtsCoroutine::GetCurrent()); 46 FromCoreType(ObjectHeader * promise)47 static EtsPromise *FromCoreType(ObjectHeader *promise) 48 { 49 return reinterpret_cast<EtsPromise *>(promise); 50 } 51 GetCoreType()52 ObjectHeader *GetCoreType() 53 { 54 return reinterpret_cast<ObjectHeader *>(this); 55 } 56 AsObject()57 EtsObject *AsObject() 58 { 59 return EtsObject::FromCoreType(this); 60 } 61 AsObject()62 const EtsObject *AsObject() const 63 { 64 return EtsObject::FromCoreType(this); 65 } 66 FromEtsObject(EtsObject * promise)67 static EtsPromise *FromEtsObject(EtsObject *promise) 68 { 69 return reinterpret_cast<EtsPromise *>(promise); 70 } 71 GetThenQueue(EtsCoroutine * coro)72 EtsObjectArray *GetThenQueue(EtsCoroutine *coro) 73 { 74 return EtsObjectArray::FromCoreType( 75 ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, thenQueue_))); 76 } 77 GetThenQueueSize()78 EtsInt GetThenQueueSize() 79 { 80 return thenQueueSize_; 81 } 82 GetCatchQueue(EtsCoroutine * coro)83 EtsObjectArray *GetCatchQueue(EtsCoroutine *coro) 84 { 85 return EtsObjectArray::FromCoreType( 86 ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, catchQueue_))); 87 } 88 GetCatchQueueSize()89 EtsInt GetCatchQueueSize() 90 { 91 return catchQueueSize_; 92 } 93 ClearQueues(EtsCoroutine * coro)94 void ClearQueues(EtsCoroutine *coro) 95 { 96 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, thenQueue_), nullptr); 97 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, catchQueue_), nullptr); 98 thenQueueSize_ = 0; 99 catchQueueSize_ = 0; 100 } 101 GetEventPtr()102 CoroutineEvent *GetEventPtr() 103 { 104 return reinterpret_cast<CoroutineEvent *>(event_); 105 } 106 SetEventPtr(CoroutineEvent * e)107 void SetEventPtr(CoroutineEvent *e) 108 { 109 event_ = reinterpret_cast<EtsLong>(e); 110 } 111 IsResolved()112 bool IsResolved() const 113 { 114 return (state_ == STATE_RESOLVED); 115 } 116 IsRejected()117 bool IsRejected() const 118 { 119 return (state_ == STATE_REJECTED); 120 } 121 IsPending()122 bool IsPending() const 123 { 124 return (state_ == STATE_PENDING); 125 } 126 IsProxy()127 bool IsProxy() 128 { 129 return GetLinkedPromise(EtsCoroutine::GetCurrent()) != nullptr; 130 } 131 GetLinkedPromise(EtsCoroutine * coro)132 EtsObject *GetLinkedPromise(EtsCoroutine *coro) 133 { 134 auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, linkedPromise_)); 135 return EtsObject::FromCoreType(obj); 136 } 137 SetLinkedPromise(EtsCoroutine * coro,EtsObject * p)138 void SetLinkedPromise(EtsCoroutine *coro, EtsObject *p) 139 { 140 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, linkedPromise_), p->GetCoreType()); 141 } 142 Resolve(EtsCoroutine * coro,EtsObject * value)143 void Resolve(EtsCoroutine *coro, EtsObject *value) 144 { 145 os::memory::LockHolder lk(*this); 146 ASSERT(state_ == STATE_PENDING); 147 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, value_), value->GetCoreType()); 148 state_ = STATE_RESOLVED; 149 SetEventPtr(nullptr); 150 } 151 Reject(EtsCoroutine * coro,EtsObject * error)152 void Reject(EtsCoroutine *coro, EtsObject *error) 153 { 154 os::memory::LockHolder lk(*this); 155 ASSERT(state_ == STATE_PENDING); 156 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsPromise, value_), error->GetCoreType()); 157 state_ = STATE_REJECTED; 158 SetEventPtr(nullptr); 159 } 160 GetValue(EtsCoroutine * coro)161 EtsObject *GetValue(EtsCoroutine *coro) 162 { 163 return EtsObject::FromCoreType(ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsPromise, value_))); 164 } 165 GetState()166 uint32_t GetState() const 167 { 168 return state_; 169 } 170 ValueOffset()171 static size_t ValueOffset() 172 { 173 return MEMBER_OFFSET(EtsPromise, value_); 174 } 175 Lock()176 void Lock() 177 { 178 auto tid = EtsCoroutine::GetCurrent()->GetCoroutineId(); 179 ASSERT((tid & MarkWord::LIGHT_LOCK_THREADID_MASK) != 0); 180 auto mwExpected = AtomicGetMark(); 181 switch (mwExpected.GetState()) { 182 case MarkWord::STATE_GC: 183 LOG(FATAL, COROUTINES) << "Cannot lock a GC-collected Promise object!"; 184 break; 185 case MarkWord::STATE_LIGHT_LOCKED: 186 case MarkWord::STATE_HEAVY_LOCKED: 187 // should wait until unlock... 188 // NOTE(konstanting, #I67QXC): check tid and decide what to do in case when locked by current tid 189 mwExpected = mwExpected.DecodeFromUnlocked(); 190 break; 191 case MarkWord::STATE_HASHED: 192 // NOTE(konstanting, #I67QXC): should save the hash 193 LOG(FATAL, COROUTINES) 194 << "Cannot lock a hashed Promise object! This functionality is not implemented yet."; 195 break; 196 case MarkWord::STATE_UNLOCKED: 197 default: 198 break; 199 } 200 auto newMw = mwExpected.DecodeFromLightLock(tid, 0); 201 #ifndef NDEBUG 202 size_t cycles = 0; 203 #endif /* NDEBUG */ 204 while (!AtomicSetMark<false>(mwExpected, newMw)) { 205 mwExpected = mwExpected.DecodeFromUnlocked(); 206 newMw = mwExpected.DecodeFromLightLock(tid, 0); 207 #ifndef NDEBUG 208 ++cycles; 209 #endif /* NDEBUG */ 210 } 211 #ifndef NDEBUG 212 LOG(DEBUG, COROUTINES) << "Promise::Lock(): took " << cycles << " cycles"; 213 #endif /* NDEBUG */ 214 } 215 Unlock()216 void Unlock() 217 { 218 // precondition: is locked AND if the event pointer is set then the event is locked 219 // NOTE(konstanting, #I67QXC): find a way to add an ASSERT for (the event is locked) 220 // NOTE(konstanting, #I67QXC): should load the hash once we support the hashed state in Lock() 221 auto mw = AtomicGetMark(); 222 ASSERT(mw.GetState() == MarkWord::STATE_LIGHT_LOCKED); 223 auto newMw = mw.DecodeFromUnlocked(); 224 while (!AtomicSetMark<false>(mw, newMw)) { 225 ASSERT(mw.GetState() == MarkWord::STATE_LIGHT_LOCKED); 226 newMw = mw.DecodeFromUnlocked(); 227 } 228 } 229 230 private: 231 ObjectPointer<EtsObject> value_; // the completion value of the Promise 232 ObjectPointer<EtsObjectArray> 233 thenQueue_; // the queue of 'then' calbacks which will be called when the Promise gets resolved 234 ObjectPointer<EtsObjectArray> 235 catchQueue_; // the queue of 'catch' callback which will be called when the Promise gets rejected 236 ObjectPointer<EtsObject> linkedPromise_; // linked JS promise as JSValue (if exists) 237 EtsLong event_; 238 EtsInt thenQueueSize_; 239 EtsInt catchQueueSize_; 240 uint32_t state_; // the Promise's state 241 242 friend class test::EtsPromiseMembers; 243 }; 244 245 } // namespace panda::ets 246 247 #endif // PANDA_RUNTIME_ETS_FFI_CLASSES_ETS_PROMISE_H 248