1 /** 2 * Copyright (c) 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_JOB_H_ 17 #define PANDA_RUNTIME_ETS_FFI_CLASSES_ETS_JOB_H_ 18 19 #include "runtime/include/object_header.h" 20 #include "libpandabase/macros.h" 21 #include "plugins/ets/runtime/ets_vm.h" 22 #include "plugins/ets/runtime/types/ets_object.h" 23 #include "plugins/ets/runtime/types/ets_sync_primitives.h" 24 #include "plugins/ets/runtime/types/ets_primitives.h" 25 26 namespace ark::ets { 27 28 class EtsCoroutine; 29 30 namespace test { 31 class EtsJobTest; 32 } // namespace test 33 34 class EtsJob : public ObjectHeader { 35 public: 36 static constexpr EtsInt STATE_RUNNING = 0; 37 static constexpr EtsInt STATE_FINISHED = 1; 38 static constexpr EtsInt STATE_FAILED = 2; 39 40 EtsJob() = delete; 41 EtsJob(EtsJob &) = delete; 42 ~EtsJob() = default; 43 44 NO_COPY_SEMANTIC(EtsJob); 45 NO_MOVE_SEMANTIC(EtsJob); 46 47 PANDA_PUBLIC_API static EtsJob *Create(EtsCoroutine *etsCoroutine = EtsCoroutine::GetCurrent()); 48 49 static void EtsJobFinish(EtsJob *job, EtsObject *value); 50 51 static void EtsJobFail(EtsJob *job, EtsObject *error); 52 FromCoreType(ObjectHeader * job)53 static EtsJob *FromCoreType(ObjectHeader *job) 54 { 55 return reinterpret_cast<EtsJob *>(job); 56 } 57 GetCoreType()58 ObjectHeader *GetCoreType() const 59 { 60 return static_cast<ObjectHeader *>(const_cast<EtsJob *>(this)); 61 } 62 AsObject()63 EtsObject *AsObject() 64 { 65 return EtsObject::FromCoreType(this); 66 } 67 AsObject()68 const EtsObject *AsObject() const 69 { 70 return EtsObject::FromCoreType(this); 71 } 72 FromEtsObject(EtsObject * job)73 static EtsJob *FromEtsObject(EtsObject *job) 74 { 75 return reinterpret_cast<EtsJob *>(job); 76 } 77 GetMutex(EtsCoroutine * coro)78 EtsMutex *GetMutex(EtsCoroutine *coro) 79 { 80 auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsJob, mutex_)); 81 return EtsMutex::FromCoreType(obj); 82 } 83 SetMutex(EtsCoroutine * coro,EtsMutex * mutex)84 void SetMutex(EtsCoroutine *coro, EtsMutex *mutex) 85 { 86 ASSERT(mutex != nullptr); 87 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsJob, mutex_), mutex->GetCoreType()); 88 } 89 GetEvent(EtsCoroutine * coro)90 EtsEvent *GetEvent(EtsCoroutine *coro) 91 { 92 auto *obj = ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsJob, event_)); 93 return EtsEvent::FromCoreType(obj); 94 } 95 SetEvent(EtsCoroutine * coro,EtsEvent * event)96 void SetEvent(EtsCoroutine *coro, EtsEvent *event) 97 { 98 ASSERT(event != nullptr); 99 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsJob, event_), event->GetCoreType()); 100 } 101 GetValue(EtsCoroutine * coro)102 EtsObject *GetValue(EtsCoroutine *coro) 103 { 104 return EtsObject::FromCoreType(ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsJob, value_))); 105 } 106 107 /// Allows to get exclusive access to the job state Lock()108 void Lock() 109 { 110 auto *mutex = GetMutex(EtsCoroutine::GetCurrent()); 111 ASSERT(mutex != nullptr); 112 mutex->Lock(); 113 } 114 Unlock()115 void Unlock() 116 { 117 auto *mutex = GetMutex(EtsCoroutine::GetCurrent()); 118 ASSERT(mutex != nullptr); 119 ASSERT(mutex->IsHeld()); 120 mutex->Unlock(); 121 } 122 IsLocked()123 bool IsLocked() 124 { 125 auto *mutex = GetMutex(EtsCoroutine::GetCurrent()); 126 ASSERT(mutex != nullptr); 127 return mutex->IsHeld(); 128 } 129 130 /// Blocks current coroutine until job is resolved/rejected Wait()131 void Wait() 132 { 133 auto *event = GetEvent(EtsCoroutine::GetCurrent()); 134 ASSERT(event != nullptr); 135 event->Wait(); 136 } 137 IsRunning()138 bool IsRunning() const 139 { 140 return state_ == STATE_RUNNING; 141 } 142 IsFinished()143 bool IsFinished() const 144 { 145 return state_ == STATE_FINISHED; 146 } 147 IsFailed()148 bool IsFailed() const 149 { 150 return state_ == STATE_FAILED; 151 } 152 Finish(EtsCoroutine * coro,EtsObject * value)153 void Finish(EtsCoroutine *coro, EtsObject *value) 154 { 155 ASSERT(state_ == STATE_RUNNING); 156 auto coreValue = (value == nullptr) ? nullptr : value->GetCoreType(); 157 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsJob, value_), coreValue); 158 state_ = STATE_FINISHED; 159 // Unblock awaitee coros 160 GetEvent(coro)->Fire(); 161 } 162 Fail(EtsCoroutine * coro,EtsObject * error)163 void Fail(EtsCoroutine *coro, EtsObject *error) 164 { 165 ASSERT(error != nullptr); 166 ASSERT(state_ == STATE_RUNNING); 167 ASSERT(error != nullptr); 168 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsJob, value_), error->GetCoreType()); 169 state_ = STATE_FAILED; 170 171 if (Runtime::GetOptions().IsListUnhandledOnExitJobs(plugins::LangToRuntimeType(panda_file::SourceLang::ETS))) { 172 coro->GetPandaVM()->AddUnhandledFailedJob(this); 173 } 174 175 // Unblock awaitee coros 176 GetEvent(coro)->Fire(); 177 } 178 179 private: 180 ObjectPointer<EtsObject> value_; // the completion value of the Job 181 ObjectPointer<EtsMutex> mutex_; 182 ObjectPointer<EtsEvent> event_; 183 uint32_t state_; // the Job state 184 185 friend class test::EtsJobTest; 186 }; 187 188 } // namespace ark::ets 189 190 #endif // PANDA_RUNTIME_ETS_FFI_CLASSES_ETS_JOB_H_ 191