• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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