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