1 /** 2 * Copyright (c) 2021-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_RUNTIME_MONITOR_H_ 16 #define PANDA_RUNTIME_MONITOR_H_ 17 18 #include <atomic> 19 #include <optional> 20 21 #include "libpandabase/os/mutex.h" 22 #include "libpandabase/utils/list.h" 23 #include "libpandabase/utils/logger.h" 24 #include "runtime/include/thread_status.h" 25 26 namespace ark { 27 28 class MTManagedThread; 29 class ObjectHeader; 30 template <class T> 31 class VMHandle; 32 class MarkWord; 33 34 /// To avoid inheritance in the `Thread` class we don't use `List` (it forces list element to inherit `ListNode`). 35 template <typename T> 36 class ThreadList { 37 public: Empty()38 bool Empty() const 39 { 40 return head_ == nullptr; 41 } 42 Front()43 T &Front() 44 { 45 return *head_; 46 } 47 48 void PopFront(); 49 50 void PushFront(T &thread); 51 52 void EraseAfter(T *prev, T *current); 53 Swap(ThreadList & other)54 void Swap(ThreadList &other) 55 { 56 std::swap(head_, other.head_); 57 } 58 59 void Splice(ThreadList &other); 60 Clear()61 void Clear() 62 { 63 head_ = nullptr; 64 } 65 66 template <typename Predicate> 67 bool RemoveIf(Predicate pred); 68 69 private: 70 T *head_ {nullptr}; 71 }; 72 73 // NOTE(aWX851037): open questions for implementation: 74 // 1. Should we reset the state to unlocked from heavyweight lock? 75 // Potential benefit: less memory consumption and usage of lightweight locks 76 // Potential drawback: infrustructure to detect, when the monitor is not acquired by any thread and time for repeated 77 // inflation 78 // 2. If the state should be reseted, when it should be done? 79 // Potential targets: after monitor release check the owners of monitors, 80 // special request, for instance, from GC. 81 // 3. Do we really need try locks? 82 // 4. Is it useful to return ObjectHeader from monitorenter/exit? Right now it is enough to return bool value. 83 84 class Monitor { 85 public: 86 using MonitorId = uintptr_t; 87 88 enum State { 89 OK, 90 INTERRUPTED, 91 ILLEGAL, 92 }; 93 GetId()94 MonitorId GetId() const 95 { 96 return id_; 97 } 98 99 PANDA_PUBLIC_API static Monitor::State MonitorEnter(ObjectHeader *obj, bool trylock = false); 100 101 static void InflateThinLock(MTManagedThread *thread, const VMHandle<ObjectHeader> &obj); 102 103 PANDA_PUBLIC_API static Monitor::State MonitorExit(ObjectHeader *obj); 104 105 /** 106 * Static call which attempts to wait until timeout, interrupt, or notification. 107 * 108 * @param obj an object header of corresponding object 109 * @param status status to be set up during wait 110 * @param timeout waiting time in milliseconds 111 * @param nanos additional time in nanoseconds 112 * @param ignore_interruption ignore interruption event or not 113 * @return true if it was interrupted; false otherwise 114 */ 115 PANDA_PUBLIC_API static State Wait(ObjectHeader *obj, ThreadStatus status, uint64_t timeout, uint64_t nanos, 116 bool ignoreInterruption = false); 117 118 static State Notify(ObjectHeader *obj); 119 120 static State NotifyAll(ObjectHeader *obj); 121 122 /** 123 * Static call which attempts to inflate object lock (lightweight/unlocked) and acquires its lock if it's 124 * successful. Provides no guarantees on object having heavy lock unless it returns true. 125 * 126 * @param obj an object header of corresponding object 127 * @param thread pointer to thread which will acquire the monitor. 128 * @tparam for_other_thread include logic for inflation of monitor owned by other thread. Should be used 129 * only in futex build. 130 * @return true if new monitor was successfuly created and object's markword updated with monitor's ID; 131 * false otherwise 132 */ 133 template <bool FOR_OTHER_THREAD = false> 134 static bool Inflate(ObjectHeader *obj, MTManagedThread *thread); 135 136 /** 137 * Static call which attempts to deflate object's heavy lock if it's present and unlocked. 138 * Ignores object if it doesn't have heavy lock. 139 * 140 * @param obj an object header of corresponding object 141 * @return true if object's monitor was found, acquired and freed; false otherwise 142 */ 143 static bool Deflate(ObjectHeader *obj); 144 145 PANDA_PUBLIC_API static uint8_t HoldsLock(ObjectHeader *obj); 146 147 static uint32_t GetLockOwnerOsThreadID(ObjectHeader *obj); 148 149 static Monitor *GetMonitorFromObject(ObjectHeader *obj); 150 151 static void TraceMonitorLock(ObjectHeader *obj, bool isWait); 152 153 static void TraceMonitorUnLock(); 154 155 // NO_THREAD_SAFETY_ANALYSIS for monitor->lock_ 156 // Some more information in the issue #1662 157 bool Release(MTManagedThread *thread) NO_THREAD_SAFETY_ANALYSIS; 158 159 uint32_t GetHashCode(); 160 161 bool HasHashCode() const; 162 163 void SetHashCode(uint32_t hash); 164 SetObject(ObjectHeader * object)165 void SetObject(ObjectHeader *object) 166 { 167 obj_ = object; 168 } 169 GetObject()170 ObjectHeader *GetObject() 171 { 172 return obj_; 173 } 174 175 // Public constructor is needed for allocator Monitor(MonitorId id)176 explicit Monitor(MonitorId id) : id_(id), owner_(), hashCode_(0), waitersCounter_(0) 177 { 178 // Atomic with relaxed order reason: memory access in monitor 179 owner_.store(nullptr, std::memory_order_relaxed); 180 } 181 182 private: 183 MonitorId id_; 184 ObjectHeader *obj_ {nullptr}; // Used for GC deflation 185 std::atomic<MTManagedThread *> owner_; 186 // These are two lists, which are linked with nextThread 187 // Be careful when changing these two lists to other types, or changing List implementation, 188 // current Monitor::Notify implementation relies on the fact that reference to MTManagedThread is still valid 189 // when PopFront is called. 190 ThreadList<MTManagedThread> waiters_; 191 ThreadList<MTManagedThread> toWakeup_; 192 uint64_t recursiveCounter_ {0}; 193 os::memory::Mutex lock_; 194 std::atomic<uint32_t> hashCode_; 195 std::atomic<uint32_t> waitersCounter_; 196 197 // NO_THREAD_SAFETY_ANALYSIS for monitor->lock_ 198 // Some more information in the issue #1662 199 bool Acquire(MTManagedThread *thread, const VMHandle<ObjectHeader> &objHandle, 200 bool trylock) NO_THREAD_SAFETY_ANALYSIS; 201 202 void InitWithOwner(MTManagedThread *thread, ObjectHeader *obj) NO_THREAD_SAFETY_ANALYSIS; 203 204 void ReleaseOnFailedInflate(MTManagedThread *thread) NO_THREAD_SAFETY_ANALYSIS; 205 SetOwner(MTManagedThread * expected,MTManagedThread * thread)206 bool SetOwner(MTManagedThread *expected, MTManagedThread *thread) 207 { 208 return owner_.compare_exchange_strong(expected, thread); 209 } 210 211 static std::optional<ark::Monitor::State> HandleLightLockedState(MarkWord &mark, MTManagedThread *thread, 212 VMHandle<ObjectHeader> &objHandle, 213 uint32_t &lightlockRetryCount, 214 [[maybe_unused]] bool &shouldInflate, 215 bool trylock); 216 217 static std::optional<ark::Monitor::State> HandleUnlockedState(MarkWord &mark, MTManagedThread *thread, 218 VMHandle<ObjectHeader> &objHandle, 219 bool &shouldInflate, bool trylock); 220 GetOwner()221 MTManagedThread *GetOwner() 222 { 223 // Atomic with relaxed order reason: memory access in monitor 224 return owner_.load(std::memory_order_relaxed); 225 } 226 227 bool DeflateInternal(); 228 229 friend class MonitorPool; 230 }; 231 232 } // namespace ark 233 234 #endif // PANDA_RUNTIME_MONITOR_H_ 235