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