1 /** 2 * Copyright (c) 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 #ifndef PANDA_PLUGINS_ETS_RUNTIME_TYPES_ETS_MUTEX_H 16 #define PANDA_PLUGINS_ETS_RUNTIME_TYPES_ETS_MUTEX_H 17 18 #include "libpandabase/mem/object_pointer.h" 19 #include "plugins/ets/runtime/ets_coroutine.h" 20 #include "plugins/ets/runtime/types/ets_object.h" 21 #include "plugins/ets/runtime/types/ets_waiters_list.h" 22 23 #include <cstdint> 24 25 namespace ark::ets { 26 27 template <typename T> 28 class EtsHandle; 29 30 namespace test { 31 class EtsSyncPrimitivesTest; 32 } // namespace test 33 34 /// @brief The base class for sync primitives. Should not be used directly. 35 template <typename T> 36 class EtsSyncPrimitive : public EtsObject { 37 public: FromCoreType(ObjectHeader * syncPrimitive)38 static T *FromCoreType(ObjectHeader *syncPrimitive) 39 { 40 return reinterpret_cast<T *>(syncPrimitive); 41 } 42 FromEtsObject(EtsObject * syncPrimitive)43 static T *FromEtsObject(EtsObject *syncPrimitive) 44 { 45 return reinterpret_cast<T *>(syncPrimitive); 46 } 47 AsObject()48 EtsObject *AsObject() 49 { 50 return this; 51 } 52 AsObject()53 const EtsObject *AsObject() const 54 { 55 return this; 56 } 57 GetWaitersList(EtsCoroutine * coro)58 EtsWaitersList *GetWaitersList(EtsCoroutine *coro) 59 { 60 return EtsWaitersList::FromCoreType( 61 ObjectAccessor::GetObject(coro, this, MEMBER_OFFSET(EtsSyncPrimitive, waitersList_))); 62 } 63 SetWaitersList(EtsCoroutine * coro,EtsWaitersList * waitersList)64 void SetWaitersList(EtsCoroutine *coro, EtsWaitersList *waitersList) 65 { 66 ObjectAccessor::SetObject(coro, this, MEMBER_OFFSET(EtsSyncPrimitive, waitersList_), 67 waitersList->GetCoreType()); 68 } 69 70 /** 71 * Blocks current coroutine. It can be used concurrently with other Suspend and Resume 72 * @param awaitee - EtsWaitersList node that contains GenericEvent and probably other useful data 73 * NOTE: `this` and all other raw ObjectHeaders may become invalid after this call due to GC 74 */ SuspendCoroutine(EtsWaitersList::Node * awaitee)75 ALWAYS_INLINE void SuspendCoroutine(EtsWaitersList::Node *awaitee) 76 { 77 auto *coro = EtsCoroutine::GetCurrent(); 78 auto *coroManager = coro->GetCoroutineManager(); 79 auto &event = awaitee->GetEvent(); 80 // Need to lock event before PushBack 81 // to avoid use-after-free in CoroutineEvent::Happen method 82 event.Lock(); 83 GetWaitersList(coro)->PushBack(awaitee); 84 coroManager->Await(&event); 85 } 86 87 /** 88 * Unblocks suspended coroutine. It can be used concurrently with Suspend, 89 * BUT not with other Resume (PopFront is not thread-safety) 90 */ ResumeCoroutine()91 ALWAYS_INLINE void ResumeCoroutine() 92 { 93 auto *coro = EtsCoroutine::GetCurrent(); 94 auto *awaitee = GetWaitersList(coro)->PopFront(); 95 awaitee->GetEvent().Happen(); 96 } 97 98 private: 99 ObjectPointer<EtsWaitersList> waitersList_; 100 101 friend class test::EtsSyncPrimitivesTest; 102 }; 103 104 /// @brief Coroutine mutex. This allows to get exclusive access to the critical section. 105 class EtsMutex : public EtsSyncPrimitive<EtsMutex> { 106 public: 107 template <typename T> 108 class LockHolder { 109 public: LockHolder(EtsHandle<T> & hLock)110 explicit LockHolder(EtsHandle<T> &hLock) : hLock_(hLock) 111 { 112 hLock_->Lock(); 113 } 114 115 NO_COPY_SEMANTIC(LockHolder); 116 NO_MOVE_SEMANTIC(LockHolder); 117 ~LockHolder()118 ~LockHolder() 119 { 120 hLock_->Unlock(); 121 } 122 123 private: 124 EtsHandle<T> &hLock_; 125 }; 126 127 /** 128 * Method gives exclusive access to critical section is case of successful mutex lock. 129 * Otherwise, blocks current coroutine until other coroutine will unlock the mutex. 130 */ 131 void Lock(); 132 133 /** 134 * Calling the method means exiting the critical section. 135 * If there are blocked coroutines, unblocks one of them. 136 */ 137 void Unlock(); 138 139 /// This method should be used to make sure that mutex is locked by current coroutine. 140 bool IsHeld(); 141 142 static EtsMutex *Create(EtsCoroutine *coro); 143 144 private: 145 std::atomic<uint32_t> waiters_; 146 147 friend class test::EtsSyncPrimitivesTest; 148 }; 149 150 /// @brief Coroutine one-shot event. This allows to block current coroutine until event is fired 151 class EtsEvent : public EtsSyncPrimitive<EtsEvent> { 152 public: 153 /** 154 * Blocks current coroutine until another coroutine will fire the same event. 155 * It can be used concurrently with other wait/fire. 156 * It has no effect in case fire has already been caused, but guarantees happens-before. 157 */ 158 void Wait(); 159 160 /** 161 * Unblocks all coroutines that are waiting the same event. 162 * Can be used concurrently with other wait/fire but multiply calls have no effect. 163 */ 164 void Fire(); 165 166 static EtsEvent *Create(EtsCoroutine *coro); 167 168 private: 169 static constexpr uint32_t STATE_BIT = 1U; 170 static constexpr uint32_t FIRE_STATE = 1U; 171 static constexpr uint32_t ONE_WAITER = 2U; 172 IsFireState(uint32_t state)173 static bool IsFireState(uint32_t state) 174 { 175 return (state & STATE_BIT) == FIRE_STATE; 176 } 177 GetNumberOfWaiters(uint32_t state)178 static uint32_t GetNumberOfWaiters(uint32_t state) 179 { 180 return state >> STATE_BIT; 181 } 182 183 std::atomic<uint32_t> state_; 184 185 friend class test::EtsSyncPrimitivesTest; 186 }; 187 188 /// Coroutine conditional variable 189 class EtsCondVar : public EtsSyncPrimitive<EtsCondVar> { 190 public: 191 /** 192 * precondition: mutex is locked 193 * 1. Unlocks mutex 194 * 2. Blocks current coroutine 195 * 3. Locks mutex 196 * This method is thread-safe in relation to other methods of the CondVar 197 */ 198 void Wait(EtsHandle<EtsMutex> &mutex); 199 200 /** 201 * precondition: mutex is locked 202 * Unblocks ONE suspended coroutine associated with this CondVar, if it exists. 203 * This method is thread-safe in relation to other methods of the CondVar 204 */ 205 void NotifyOne([[maybe_unused]] EtsMutex *mutex); 206 207 /** 208 * precondition: mutex is locked 209 * Unblocks ALL suspended coroutine associated with this CondVar. 210 * This method is thread-safe in relation to other methods of the CondVar 211 */ 212 void NotifyAll([[maybe_unused]] EtsMutex *mutex); 213 214 static EtsCondVar *Create(EtsCoroutine *coro); 215 216 private: 217 EtsInt waiters_; 218 219 friend class test::EtsSyncPrimitivesTest; 220 }; 221 222 } // namespace ark::ets 223 224 #endif // PANDA_PLUGINS_ETS_RUNTIME_TYPES_ETS_MUTEX_H 225