• 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 
16 #include "runtime/monitor.h"
17 
18 #include "libpandabase/os/thread.h"
19 #include "runtime/include/object_header.h"
20 #include "runtime/include/runtime.h"
21 #include "runtime/include/runtime_notification.h"
22 #include "runtime/include/thread_scopes.h"
23 #include "runtime/include/thread-inl.h"
24 #include "runtime/interpreter/runtime_interface.h"
25 #include "runtime/mark_word.h"
26 #include "runtime/monitor_pool.h"
27 #include "runtime/handle_base-inl.h"
28 #include "runtime/mem/vm_handle.h"
29 
30 #include <cinttypes>
31 #include <string>
32 #include <sched.h>
33 
34 namespace ark {
35 template <typename T>
36 template <typename Predicate>
RemoveIf(Predicate pred)37 bool ThreadList<T>::RemoveIf(Predicate pred)
38 {
39     bool found = false;
40     auto prev = head_;
41     for (auto current = head_; current != nullptr; current = current->GetNextWait()) {
42         if (pred(*current)) {
43             found = true;
44             EraseAfter(prev, current);
45             current = prev;
46         } else {
47             prev = current;
48         }
49     }
50     return found;
51 }
52 
53 template <typename T>
Splice(ThreadList & other)54 void ThreadList<T>::Splice(ThreadList &other)
55 {
56     if (Empty()) {
57         head_ = other.head_;
58     } else {
59         T *last = head_;
60         for (; last->GetNextWait() != nullptr; last = last->GetNextWait()) {
61         }
62         last->SetWaitNext(other.head_);
63     }
64     other.Clear();
65 }
66 
67 template <typename T>
EraseAfter(T * prev,T * current)68 void ThreadList<T>::EraseAfter(T *prev, T *current)
69 {
70     if (current == head_) {
71         head_ = current->GetNextWait();
72     } else {
73         prev->SetWaitNext(current->GetNextWait());
74     }
75 }
76 
77 template <typename T>
PopFront()78 void ThreadList<T>::PopFront()
79 {
80     head_ = head_->GetNextWait();
81 }
82 
83 template <typename T>
PushFront(T & thread)84 void ThreadList<T>::PushFront(T &thread)
85 {
86     thread.SetWaitNext(head_);
87     head_ = &thread;
88 }
89 
InflateThinLock(MTManagedThread * thread,const VMHandle<ObjectHeader> & objHandle)90 void Monitor::InflateThinLock(MTManagedThread *thread, [[maybe_unused]] const VMHandle<ObjectHeader> &objHandle)
91 {
92 #if defined(PANDA_USE_FUTEX)
93     // Futex inflation policy: suspend target thread, wait until it actually gets suspended
94     // and try inflating light monitor (`Inflate` expects lock to still be acquired by target;
95     // otherwise markword CAS fails). If it fails (i.e. thread got suspended when this monitor is
96     // no longer taken), we restart lightlock acquisition policy again.
97     // Compared to forced inflation (actively retry inflation once MAX_TRYLOCK_RETRY is reached
98     // or inflate monitor once this thread acquires light lock), this policy yields much better
99     // performance for short running synchronized blocks or functions, and is still expected to
100     // succeeed on longer blocks which should have safepoints and suspend successfully with
101     // monitor still acquired.
102     // We are trying to inflate light lock acquired by other thread, suspend it first
103     MTManagedThread *owner = nullptr;
104     ASSERT(objHandle.GetPtr() != nullptr);
105     MarkWord mark = objHandle.GetPtr()->AtomicGetMark();
106     auto threadManager = reinterpret_cast<MTThreadManager *>(thread->GetVM()->GetThreadManager());
107     os::thread::ThreadId ownerThreadId = mark.GetThreadId();
108     {
109         ScopedChangeThreadStatus sts(thread, ThreadStatus::IS_WAITING_INFLATION);
110         owner = threadManager->SuspendAndWaitThreadByInternalThreadId(ownerThreadId);
111     }
112     thread->SetEnterMonitorObject(nullptr);
113     thread->SetWaitingMonitorOldStatus(ThreadStatus::FINISHED);
114     // Thread could have finished by the time we tried stopping it
115     if (owner != nullptr) {
116         // NB! Inflate can do nothing if monitor is already unlocked or acquired by other thread.
117         Inflate<true>(objHandle.GetPtr(), owner);
118         {
119             // ResumeImpl can be called during thread termination, which leads to destruction of locked suspend_lock_
120             // inside ResumeImpl.
121             os::memory::LockHolder threadLock(*threadManager->GetThreadsLock());
122             owner->ResumeImpl(true);
123         }
124     }
125 #else
126     // Non-futex inflation policy: Wait until light lock is released, acquire it and inflate
127     // to heavy monitor
128     {
129         static constexpr uint64_t SLEEP_MS = 10;
130         thread->TimedWait(ThreadStatus::IS_WAITING_INFLATION, SLEEP_MS, 0);
131     }
132     thread->SetEnterMonitorObject(nullptr);
133     thread->SetWaitingMonitorOldStatus(ThreadStatus::FINISHED);
134 #endif
135 }
136 
HandleLightLockedState(MarkWord & mark,MTManagedThread * thread,VMHandle<ObjectHeader> & objHandle,uint32_t & lightlockRetryCount,bool & shouldInflate,bool trylock)137 std::optional<Monitor::State> Monitor::HandleLightLockedState(MarkWord &mark, MTManagedThread *thread,
138                                                               VMHandle<ObjectHeader> &objHandle,
139                                                               uint32_t &lightlockRetryCount,
140                                                               [[maybe_unused]] bool &shouldInflate, bool trylock)
141 {
142     os::thread::ThreadId ownerThreadId = mark.GetThreadId();
143     if (ownerThreadId == thread->GetInternalId()) {
144         uint32_t newCount = mark.GetLockCount() + 1;
145         if (newCount < MarkWord::LIGHT_LOCK_LOCK_MAX_COUNT) {
146             auto newMark = mark.DecodeFromLightLock(thread->GetInternalId(), newCount);
147             // Strong CAS as the loop iteration is large
148             bool ret = objHandle.GetPtr()->AtomicSetMark(mark, newMark);
149             if (ret) {
150                 LOG(DEBUG, RUNTIME) << "The lightweight monitor was successfully recursively acquired";
151                 Monitor::TraceMonitorLock(objHandle.GetPtr(), false);
152                 thread->PushLocalObjectLocked(objHandle.GetPtr());
153                 return Monitor::State::OK;
154             }
155         } else {
156             Monitor::Inflate(objHandle.GetPtr(), thread);
157             // Inflate set up recursive counter to just current amount, loop again.
158         }
159     } else {
160         // Lock acquired by other thread.
161         if (trylock) {
162             return Monitor::State::ILLEGAL;
163         }
164 
165         // Retry acquiring light lock in loop first to avoid excessive inflation
166         static constexpr uint32_t MAX_TRYLOCK_RETRY = 100;
167         static constexpr uint32_t YIELD_AFTER = 50;
168 
169         lightlockRetryCount++;
170         if (lightlockRetryCount < MAX_TRYLOCK_RETRY) {
171             if (lightlockRetryCount > YIELD_AFTER) {
172                 MTManagedThread::Yield();
173             }
174         } else {
175             // Retried acquiring light lock for too long, do inflation
176 
177             thread->SetEnterMonitorObject(objHandle.GetPtr());
178             thread->SetWaitingMonitorOldStatus(ThreadStatus::IS_WAITING_INFLATION);
179             Monitor::InflateThinLock(thread, objHandle);
180 #if defined(PANDA_USE_FUTEX)
181             lightlockRetryCount = 0;
182 #else
183             shouldInflate = true;
184 #endif
185         }
186     }
187     // Couldn't update mark.
188     if (trylock) {
189         return Monitor::State::ILLEGAL;
190     }
191 
192     return std::nullopt;
193 }
194 
HandleUnlockedState(MarkWord & mark,MTManagedThread * thread,VMHandle<ObjectHeader> & objHandle,bool & shouldInflate,bool trylock)195 std::optional<Monitor::State> Monitor::HandleUnlockedState(MarkWord &mark, MTManagedThread *thread,
196                                                            VMHandle<ObjectHeader> &objHandle, bool &shouldInflate,
197                                                            bool trylock)
198 {
199     if (shouldInflate) {
200         if (Monitor::Inflate(objHandle.GetPtr(), thread)) {
201             thread->PushLocalObjectLocked(objHandle.GetPtr());
202             return Monitor::State::OK;
203         }
204         // Couldn't inflate.
205         if (trylock) {
206             return Monitor::State::ILLEGAL;
207         }
208 
209         return std::nullopt;
210     }
211 
212     ASSERT(thread->GetInternalId() <= MarkWord::LIGHT_LOCK_THREADID_MAX_COUNT);
213     auto newMark = mark.DecodeFromLightLock(thread->GetInternalId(), 1);
214     // Strong CAS as the loop iteration is large
215     auto ret = objHandle.GetPtr()->AtomicSetMark(mark, newMark);
216     if (ret) {
217         LOG(DEBUG, RUNTIME) << "The lightweight monitor was successfully acquired for the first time";
218         Monitor::TraceMonitorLock(objHandle.GetPtr(), false);
219         thread->PushLocalObjectLocked(objHandle.GetPtr());
220         return Monitor::State::OK;
221     }
222     // Couldn't update mark.
223     if (trylock) {
224         return Monitor::State::ILLEGAL;
225     }
226 
227     return std::nullopt;
228 }
229 
HandleHeavyLockedState(Monitor * monitor,MTManagedThread * thread,VMHandle<ObjectHeader> & objHandle,bool trylock)230 Monitor::State Monitor::HandleHeavyLockedState(Monitor *monitor, MTManagedThread *thread,
231                                                VMHandle<ObjectHeader> &objHandle, bool trylock)
232 {
233     if (monitor == nullptr) {
234         // Not sure if it is possible
235         return Monitor::State::ILLEGAL;
236     }
237     bool ret = monitor->Acquire(thread, objHandle, trylock);
238     if (ret) {
239         thread->PushLocalObjectLocked(objHandle.GetPtr());
240     }
241     return ret ? Monitor::State::OK : Monitor::State::ILLEGAL;
242 }
243 
244 /**
245  * Static call, which implements the basic functionality of monitors:
246  * heavyweight, lightweight and so on.
247  *
248  * @param  obj  an object header of corresponding object
249  * @param  trylock is true if the function should fail in case of lock was already acquired by other thread
250  * @return      state of function execution (ok, illegal)
251  */
MonitorEnter(ObjectHeader * obj,bool trylock)252 Monitor::State Monitor::MonitorEnter(ObjectHeader *obj, bool trylock)
253 {
254     auto *thread = MTManagedThread::GetCurrent();
255     ASSERT(thread != nullptr);
256     // This function can unlock MutatorLock, so GC can run during lock acquire waiting
257     // so we need to use handle to get updated header pointer
258     [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
259     VMHandle<ObjectHeader> objHandle(thread, obj);
260     bool shouldInflate = false;
261     uint32_t lightlockRetryCount = 0;
262 
263     while (true) {
264         MarkWord mark = objHandle.GetPtr()->AtomicGetMark();
265         MarkWord::ObjectState state = mark.GetState();
266 
267         LOG(DEBUG, RUNTIME) << "Try to enter monitor " << std::hex << obj << "  with state " << std::dec << state;
268 
269         switch (state) {
270             case MarkWord::STATE_HEAVY_LOCKED: {
271                 auto monitor = thread->GetMonitorPool()->LookupMonitor(mark.GetMonitorId());
272                 return HandleHeavyLockedState(monitor, thread, objHandle, trylock);
273             }
274             case MarkWord::STATE_LIGHT_LOCKED: {
275                 auto retState =
276                     HandleLightLockedState(mark, thread, objHandle, lightlockRetryCount, shouldInflate, trylock);
277                 if (retState.has_value()) {
278                     return retState.value();
279                 }
280                 // Go to the next iteration
281                 continue;
282             }
283             case MarkWord::STATE_HASHED:
284                 if (Inflate(objHandle.GetPtr(), thread)) {
285                     thread->PushLocalObjectLocked(objHandle.GetPtr());
286                     return State::OK;
287                 }
288                 // Couldn't inflate.
289                 if (trylock) {
290                     return State::ILLEGAL;
291                 }
292                 // Go to the next iteration
293                 continue;
294             case MarkWord::STATE_UNLOCKED: {
295                 auto retState = HandleUnlockedState(mark, thread, objHandle, shouldInflate, trylock);
296                 if (retState.has_value()) {
297                     return retState.value();
298                 }
299                 // Go to the next iteration
300                 continue;
301             }
302             case MarkWord::STATE_GC:
303                 LOG(FATAL, RUNTIME) << "Not yet implemented";
304                 return State::ILLEGAL;
305             default:
306                 LOG(FATAL, RUNTIME) << "Undefined object state";
307                 return State::ILLEGAL;
308         }
309     }
310 }
311 
MonitorExit(ObjectHeader * obj)312 Monitor::State Monitor::MonitorExit(ObjectHeader *obj)
313 {
314     auto thread = MTManagedThread::GetCurrent();
315     bool ret = false;
316 
317     while (true) {
318         MarkWord mark = obj->AtomicGetMark();
319         MarkWord newMark = mark;
320         MarkWord::ObjectState state = mark.GetState();
321         LOG(DEBUG, RUNTIME) << "Try to exit monitor " << std::hex << obj << "  with state " << std::dec << state;
322         switch (state) {
323             case MarkWord::STATE_HEAVY_LOCKED: {
324                 auto monitor = thread->GetMonitorPool()->LookupMonitor(mark.GetMonitorId());
325                 ret = monitor->Release(thread);
326                 if (ret) {
327                     thread->PopLocalObjectLocked(obj);
328                 }
329                 return ret ? State::OK : State::ILLEGAL;
330             }
331             case MarkWord::STATE_LIGHT_LOCKED: {
332                 if (mark.GetThreadId() != thread->GetInternalId()) {
333                     LOG(DEBUG, RUNTIME) << "Caling MonitorEnter on object which isn't owned by this thread";
334                     return State::ILLEGAL;
335                 }
336                 uint32_t newCount = mark.GetLockCount() - 1;
337                 if (newCount != 0) {
338                     newMark = mark.DecodeFromLightLock(thread->GetInternalId(), newCount);
339                 } else {
340                     newMark = mark.DecodeFromUnlocked();
341                 }
342                 // Strong CAS as the loop iteration is large
343                 ret = obj->AtomicSetMark(mark, newMark);
344                 if (ret) {
345                     LOG(DEBUG, RUNTIME) << "Exited lightweight lock";
346                     TraceMonitorUnLock();
347                     thread->PopLocalObjectLocked(obj);
348                     return State::OK;
349                 }
350                 // CAS failed, must have been heavy locked by other thread. Retry unlock.
351                 continue;
352             }
353             case MarkWord::STATE_HASHED:
354             case MarkWord::STATE_UNLOCKED:
355                 LOG(ERROR, RUNTIME) << "Try to perform monitor exit from unlocked state";
356                 return State::ILLEGAL;
357             case MarkWord::STATE_GC:
358                 LOG(FATAL, RUNTIME) << "Not yet implemented";
359                 return State::ILLEGAL;
360             default:
361                 LOG(FATAL, RUNTIME) << "Undefined object state";
362                 return State::ILLEGAL;
363         }
364     }
365 }
366 
DoWaitInternal(MTManagedThread * thread,ThreadStatus status,uint64_t timeout,uint64_t nanos)367 static inline bool DoWaitInternal(MTManagedThread *thread, ThreadStatus status, uint64_t timeout, uint64_t nanos)
368     REQUIRES(*(thread->GetWaitingMutex()))
369 {
370     bool isTimeout = false;
371     if (timeout == 0 && nanos == 0) {
372         // Normal wait
373         thread->WaitWithLockHeld(status);
374     } else {
375         isTimeout = thread->TimedWaitWithLockHeld(status, timeout, nanos, false);
376     }
377     return isTimeout;
378 }
379 
WaitWithHeavyLockedState(MTManagedThread * thread,VMHandle<ObjectHeader> & objHandle,MarkWord & mark,ThreadStatus status,uint64_t timeout,uint64_t nanos,bool ignoreInterruption)380 Monitor::State Monitor::WaitWithHeavyLockedState(MTManagedThread *thread, VMHandle<ObjectHeader> &objHandle,
381                                                  MarkWord &mark, ThreadStatus status, uint64_t timeout, uint64_t nanos,
382                                                  bool ignoreInterruption)
383 {
384     State resultState = State::OK;
385     auto monitor = thread->GetMonitorPool()->LookupMonitor(mark.GetMonitorId());
386     if (monitor->GetOwner() != thread) {
387         // The monitor is acquired by other thread
388         // throw an internal exception?
389         LOG(ERROR, RUNTIME) << "Illegal monitor state: try to wait with monitor acquired by other thread";
390         return State::ILLEGAL;
391     }
392 
393     thread->GetWaitingMutex()->Lock();
394 
395     // Use LockHolder inside scope
396     uint64_t counter = monitor->recursiveCounter_;
397     // Wait should be called under the monitor. We checked it in the previous if.
398     // Thus, the operation with queues are thread-safe
399     monitor->waiters_.PushFront(*thread);
400     thread->SetWaitingMonitor(monitor);
401     thread->SetWaitingMonitorOldStatus(status);
402 
403     monitor->recursiveCounter_ = 1;
404     // Atomic with relaxed order reason: memory access in monitor
405     monitor->waitersCounter_.fetch_add(1, std::memory_order_relaxed);
406     monitor->Release(thread);
407 
408     bool isTimeout = false;
409     if (thread->IsInterruptedWithLockHeld() && !ignoreInterruption) {
410         resultState = State::INTERRUPTED;
411         thread->GetWaitingMutex()->Unlock();
412         // Calling Safepoing to let GC start if needed
413         // in the else branch GC can start during Wait
414         ark::interpreter::RuntimeInterface::Safepoint();
415     } else {
416         TraceMonitorLock(objHandle.GetPtr(), true);
417         isTimeout = DoWaitInternal(thread, status, timeout, nanos);
418         TraceMonitorUnLock();  // End Wait().
419         thread->GetWaitingMutex()->Unlock();
420     }
421     // Unlock WaitingMutex before to avoid deadlock
422     // Nothing happen, if the thread is rescheduled between,
423     // As the monitor was already released for external users
424     [[maybe_unused]] bool ret = monitor->Acquire(thread, objHandle, false);
425     ASSERT(ret);
426     // Atomic with relaxed order reason: memory access in monitor
427     monitor->waitersCounter_.fetch_sub(1, std::memory_order_relaxed);
428     monitor->recursiveCounter_ = counter;
429 
430     if (thread->IsInterrupted()) {
431         // NOTE(dtrubenkov): call ark::ThrowException when it will be imlemented
432         resultState = State::INTERRUPTED;
433     }
434 
435     // problems with equality of MTManagedThread's
436     bool found = monitor->waiters_.RemoveIf(
437         [thread](MTManagedThread &t) { return thread->GetInternalId() == t.GetInternalId(); });
438     // If no matching thread found in waiters_, it should have been moved to to_wakeup_
439     // but this thread timed out or got interrupted
440     if (!found) {
441         monitor->toWakeup_.RemoveIf(
442             [thread](MTManagedThread &t) { return thread->GetInternalId() == t.GetInternalId(); });
443     }
444 
445     thread->SetWaitingMonitor(nullptr);
446     thread->SetWaitingMonitorOldStatus(ThreadStatus::FINISHED);
447     Runtime::GetCurrent()->GetNotificationManager()->MonitorWaitedEvent(objHandle.GetPtr(), isTimeout);
448 
449     return resultState;
450 }
451 
452 /** Zero timeout is used as infinite wait (see docs)
453  */
Wait(ObjectHeader * obj,ThreadStatus status,uint64_t timeout,uint64_t nanos,bool ignoreInterruption)454 Monitor::State Monitor::Wait(ObjectHeader *obj, ThreadStatus status, uint64_t timeout, uint64_t nanos,
455                              bool ignoreInterruption)
456 {
457     ASSERT(obj != nullptr);
458     auto *thread = MTManagedThread::GetCurrent();
459     ASSERT(thread != nullptr);
460     State resultState = State::OK;
461 
462     // This function can unlock MutatorLock, so GC can run during wait
463     // so we need to use handle to get updated header pointer
464     [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
465     VMHandle<ObjectHeader> objHandle(thread, obj);
466 
467     Runtime::GetCurrent()->GetNotificationManager()->MonitorWaitEvent(obj, timeout);
468 
469     while (true) {
470         MarkWord mark = objHandle->AtomicGetMark();
471         MarkWord::ObjectState state = mark.GetState();
472         LOG(DEBUG, RUNTIME) << "Try to wait with state " << state;
473         switch (state) {
474             case MarkWord::STATE_HEAVY_LOCKED: {
475                 return WaitWithHeavyLockedState(thread, objHandle, mark, status, timeout, nanos, ignoreInterruption);
476             }
477             case MarkWord::STATE_LIGHT_LOCKED:
478                 if (mark.GetThreadId() != thread->GetInternalId()) {
479                     LOG(FATAL, RUNTIME) << "Illegal monitor state: try to wait with monitor acquired by other thread";
480                     return resultState;
481                 }
482                 Inflate(objHandle.GetPtr(), thread);
483                 // Go to the next iteration.
484                 continue;
485             case MarkWord::STATE_UNLOCKED:
486             case MarkWord::STATE_HASHED:
487             case MarkWord::STATE_GC:
488                 LOG(ERROR, RUNTIME) << "Try to perform Wait from unsupported state";
489                 return State::ILLEGAL;
490             default:
491                 LOG(FATAL, RUNTIME) << "Undefined object state";
492                 UNREACHABLE();
493         }
494     }
495 }
496 
Notify(ObjectHeader * obj)497 Monitor::State Monitor::Notify(ObjectHeader *obj)
498 {
499     ASSERT(obj != nullptr);
500     MarkWord mark = obj->AtomicGetMark();
501     MarkWord::ObjectState state = mark.GetState();
502     auto thread = MTManagedThread::GetCurrent();
503     LOG(DEBUG, RUNTIME) << "Try to notify with state " << state;
504 
505     switch (state) {
506         case MarkWord::STATE_HEAVY_LOCKED: {
507             auto monitor = thread->GetMonitorPool()->LookupMonitor(mark.GetMonitorId());
508             if (monitor->GetOwner() != thread) {
509                 // The monitor is acquired by other thread
510                 // throw an internal exception?
511                 LOG(ERROR, RUNTIME) << "Illegal monitor state: try to notify with monitor acquired by other thread";
512                 return State::ILLEGAL;
513             }
514 
515             // Notify should be called under the monitor. We checked it in the previous if.
516             // Thus, the operation with queues are thread-safe
517 
518             // Move one thread from waiters to wake_up
519             if (!monitor->waiters_.Empty()) {
520                 // With current ark::List implementation this reference is valid.
521                 // This can be broken with future changes.
522                 auto &waiter = monitor->waiters_.Front();
523                 monitor->waiters_.PopFront();
524                 monitor->toWakeup_.PushFront(waiter);
525             }
526             return State::OK;  // Success
527         }
528         case MarkWord::STATE_LIGHT_LOCKED:
529             if (mark.GetThreadId() != thread->GetInternalId()) {
530                 LOG(ERROR, RUNTIME) << "Illegal monitor state: try to notify with monitor acquired by other thread";
531                 return State::ILLEGAL;
532             }
533             return State::OK;  // Success
534         case MarkWord::STATE_UNLOCKED:
535         case MarkWord::STATE_HASHED:
536         case MarkWord::STATE_GC:
537             LOG(ERROR, RUNTIME) << "Try to perform Notify from unsupported state";
538             return State::ILLEGAL;
539         default:
540             LOG(FATAL, RUNTIME) << "Undefined object state";
541             UNREACHABLE();
542     }
543 }
544 
NotifyAll(ObjectHeader * obj)545 Monitor::State Monitor::NotifyAll(ObjectHeader *obj)
546 {
547     ASSERT(obj != nullptr);
548     MarkWord mark = obj->AtomicGetMark();
549     MarkWord::ObjectState state = mark.GetState();
550     auto thread = MTManagedThread::GetCurrent();
551     LOG(DEBUG, RUNTIME) << "Try to notify all with state " << state;
552 
553     switch (state) {
554         case MarkWord::STATE_HEAVY_LOCKED: {
555             auto monitor = thread->GetMonitorPool()->LookupMonitor(mark.GetMonitorId());
556             if (monitor->GetOwner() != thread) {
557                 // The monitor is acquired by other thread
558                 // throw an internal exception?
559                 LOG(ERROR, RUNTIME) << "Illegal monitor state: try to notify with monitor acquired by other thread";
560                 return State::ILLEGAL;
561             }
562 
563             // NotifyAll should be called under the monitor. We checked it in the previous if.
564             // Thus, the operation with queues are thread-safe
565             if (monitor->toWakeup_.Empty()) {
566                 monitor->toWakeup_.Swap(monitor->waiters_);
567                 return State::OK;
568             }
569 
570             // Concatenate two queues
571             if (!monitor->waiters_.Empty()) {
572                 monitor->toWakeup_.Splice(monitor->waiters_);
573                 monitor->waiters_.Clear();
574             }
575             return State::OK;  // Success
576         }
577         case MarkWord::STATE_LIGHT_LOCKED:
578             if (mark.GetThreadId() != thread->GetInternalId()) {
579                 LOG(ERROR, RUNTIME) << "Illegal monitor state: try to notify with monitor acquired by other thread";
580                 return State::ILLEGAL;
581             }
582             return State::OK;  // Success
583         case MarkWord::STATE_UNLOCKED:
584         case MarkWord::STATE_HASHED:
585         case MarkWord::STATE_GC:
586             LOG(ERROR, RUNTIME) << "Try to perform NotifyAll from unsupported state";
587             return State::ILLEGAL;
588         default:
589             LOG(FATAL, RUNTIME) << "Undefined object state";
590             UNREACHABLE();
591     }
592 }
593 
Acquire(MTManagedThread * thread,const VMHandle<ObjectHeader> & objHandle)594 void Monitor::Acquire(MTManagedThread *thread, const VMHandle<ObjectHeader> &objHandle)
595 {
596     Runtime::GetCurrent()->GetNotificationManager()->MonitorContendedEnterEvent(objHandle.GetPtr());
597     // If not trylock...
598     // Do atomic add out of scope to prevent GC getting old waiters_counter_
599     // Atomic with relaxed order reason: memory access in monitor
600     waitersCounter_.fetch_add(1, std::memory_order_relaxed);
601     thread->SetEnterMonitorObject(objHandle.GetPtr());
602     thread->SetWaitingMonitorOldStatus(ThreadStatus::IS_BLOCKED);
603     {
604         ScopedChangeThreadStatus sts(thread, ThreadStatus::IS_BLOCKED);
605         // Save current monitor, on which the given thread is blocked.
606         // It can be used to detect potential deadlock with daemon threds.
607         thread->SetEnteringMonitor(this);
608         lock_.Lock();
609         // Deadlock is no longer possible with the given thread.
610         thread->SetEnteringMonitor(nullptr);
611         // Do this inside scope for thread to release this monitor during runtime destroy
612         if (!this->SetOwner(nullptr, thread)) {
613             LOG(FATAL, RUNTIME) << "Set monitor owner failed in Acquire";
614         }
615         thread->AddMonitor(this);
616         this->recursiveCounter_++;
617     }
618     thread->SetEnterMonitorObject(nullptr);
619     thread->SetWaitingMonitorOldStatus(ThreadStatus::FINISHED);
620     // Atomic with relaxed order reason: memory access in monitor
621     waitersCounter_.fetch_sub(1, std::memory_order_relaxed);
622     // Even thout these 2 warnings are valid, We suppress them. Reason is to have consistent logging
623     // Otherwise we would see that lock was done on one monitor address,
624     // and unlock (after GC) - ona different one
625     // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader)
626     Runtime::GetCurrent()->GetNotificationManager()->MonitorContendedEnteredEvent(objHandle.GetPtr());
627     LOG(DEBUG, RUNTIME) << "The fat monitor was successfully acquired for the first time";
628     // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader)
629     TraceMonitorLock(objHandle.GetPtr(), false);
630 }
631 
Acquire(MTManagedThread * thread,const VMHandle<ObjectHeader> & objHandle,bool trylock)632 bool Monitor::Acquire(MTManagedThread *thread, const VMHandle<ObjectHeader> &objHandle, bool trylock)
633 {
634     ASSERT_MANAGED_CODE();
635 
636     MTManagedThread *owner = this->GetOwner();
637     if (owner == thread) {
638         // Do we need to hold a lock here?
639         this->recursiveCounter_++;
640         LOG(DEBUG, RUNTIME) << "The fat monitor was successfully recursively acquired";
641         TraceMonitorLock(objHandle.GetPtr(), false);
642         return true;
643     }
644 
645     // Use trylock first
646     if (trylock) {
647         if (!lock_.TryLock()) {
648             return false;
649         }
650     } else {
651 #ifdef PANDA_USE_FUTEX
652         if (!lock_.TryLockWithSpinning()) {
653 #else
654         if (!lock_.TryLock()) {
655 #endif  // PANDA_USE_FUTEX
656             Acquire(thread, objHandle);
657             return true;
658         }
659     }
660 
661     if (!this->SetOwner(nullptr, thread)) {
662         LOG(FATAL, RUNTIME) << "Set monitor owner failed in Acquire";
663     }
664     thread->AddMonitor(this);
665     this->recursiveCounter_++;
666     LOG(DEBUG, RUNTIME) << "The fat monitor was successfully acquired for the first time";
667     TraceMonitorLock(objHandle.GetPtr(), false);
668     return true;
669 }
670 
671 void Monitor::InitWithOwner(MTManagedThread *thread, ObjectHeader *obj)
672 {
673     ASSERT(this->GetOwner() == nullptr);
674 
675 #ifdef PANDA_USE_FUTEX
676     ASSERT(thread == MTManagedThread::GetCurrent() || thread->GetStatus() != ThreadStatus::RUNNING);
677     lock_.LockForOther(thread->GetId());
678 #else
679     ASSERT(thread == MTManagedThread::GetCurrent());
680     [[maybe_unused]] bool res = lock_.TryLock();
681     ASSERT(res);
682 #endif  // PANDA_USE_FUTEX
683 
684     if (!this->SetOwner(nullptr, thread)) {
685         LOG(FATAL, RUNTIME) << "Set monitor owner failed in InitWithOwner";
686     }
687     this->recursiveCounter_++;
688     LOG(DEBUG, RUNTIME) << "The fat monitor was successfully initialized for the first time";
689     TraceMonitorLock(obj, false);
690 }
691 
692 void Monitor::ReleaseOnFailedInflate(MTManagedThread *thread)
693 {
694     if (thread != this->GetOwner()) {
695         LOG(FATAL, RUNTIME) << "Releasing lock which isn't owned by this thread";
696     }
697     TraceMonitorUnLock();
698     this->recursiveCounter_--;
699     ASSERT(this->recursiveCounter_ == 0);
700     // This should never fail
701     [[maybe_unused]] bool success = this->SetOwner(thread, nullptr);
702     ASSERT(success);
703 #ifdef PANDA_USE_FUTEX
704     ASSERT(thread == MTManagedThread::GetCurrent() || thread->GetStatus() != ThreadStatus::RUNNING);
705     lock_.UnlockForOther(thread->GetId());
706 #else
707     ASSERT(thread == MTManagedThread::GetCurrent());
708     lock_.Unlock();
709 #endif  // PANDA_USE_FUTEX
710     LOG(DEBUG, RUNTIME) << "The fat monitor was successfully released after failed inflation";
711 }
712 
713 bool Monitor::Release(MTManagedThread *thread)
714 {
715     if (thread != this->GetOwner()) {
716         LOG(FATAL, RUNTIME) << "Releasing lock which isn't owned by this thread";
717         return false;
718     }
719     TraceMonitorUnLock();
720     this->recursiveCounter_--;
721     if (this->recursiveCounter_ == 0) {
722         if (!this->SetOwner(thread, nullptr)) {
723             LOG(FATAL, RUNTIME) << "Set monitor owner failed in Release";
724         }
725         // Signal the only waiter (the other one will be signaled after the next release)
726         MTManagedThread *waiter = nullptr;
727         Monitor *waitingMon = nullptr;
728         if (!this->toWakeup_.Empty()) {
729             // NB! Current list implementation leaves this pointer valid after PopFront, change this
730             // if List implementation is changed.
731             waiter = &(this->toWakeup_.Front());
732             waitingMon = waiter->GetWaitingMonitor();
733             this->toWakeup_.PopFront();
734         }
735         thread->RemoveMonitor(this);
736         // Signal waiter after mutex unlock so that signalled thread doesn't get stuck on lock_
737         if (waiter != nullptr && waitingMon == this) {
738             waiter->Signal();
739             LOG(DEBUG, RUNTIME) << "Send the notifing signal to " << waiter->GetId();
740         }
741         lock_.Unlock();
742     }
743     LOG(DEBUG, RUNTIME) << "The fat monitor was successfully released";
744     return true;
745 }
746 
747 bool Monitor::TryAddMonitor(Monitor *monitor, MarkWord &oldMark, MTManagedThread *thread, ObjectHeader *obj,
748                             MonitorPool *monitorPool)
749 {
750     MarkWord newMark = oldMark.DecodeFromMonitor(monitor->GetId());
751     bool ret = obj->AtomicSetMark(oldMark, newMark);
752     if (!ret) {
753         // Means, someone changed the mark
754         monitor->recursiveCounter_ = 1;
755         monitor->ReleaseOnFailedInflate(thread);
756         monitorPool->FreeMonitor(monitor->GetId());
757     } else {
758         // Unlike normal Acquire, AddMonitor should be done not in InitWithOwner but after successful inflation to avoid
759         // data race
760         thread->AddMonitor(monitor);
761     }
762 
763     return ret;
764 }
765 
766 template <bool FOR_OTHER_THREAD>
767 bool Monitor::Inflate(ObjectHeader *obj, MTManagedThread *thread)
768 {
769     ASSERT(obj != nullptr);
770     MarkWord oldMark = obj->AtomicGetMark();
771     MarkWord::ObjectState state = oldMark.GetState();
772 
773     // Dont inflate if someone already inflated the lock.
774     if (state == MarkWord::STATE_HEAVY_LOCKED) {
775         return false;
776     }
777     // NOLINTNEXTLINE(readability-braces-around-statements, hicpp-braces-around-statements)
778     if constexpr (FOR_OTHER_THREAD) {  // NOLINT(bugprone-suspicious-semicolon)
779         // Dont inflate if monitor got unlocked or acquired by other thread.
780         if (state != MarkWord::STATE_LIGHT_LOCKED || oldMark.GetThreadId() != thread->GetInternalId()) {
781             return false;
782         }
783     }
784 
785     auto *monitorPool = thread->GetMonitorPool();
786     auto *monitor = monitorPool->CreateMonitor(obj);
787     if (monitor == nullptr) {
788         LOG(FATAL, RUNTIME) << "Couldn't create new monitor. Out of memory?";
789         return false;
790     }
791     monitor->InitWithOwner(thread, obj);
792 
793     switch (state) {
794         case MarkWord::STATE_LIGHT_LOCKED:
795             if (oldMark.GetThreadId() != thread->GetInternalId()) {
796                 monitor->ReleaseOnFailedInflate(thread);
797                 monitorPool->FreeMonitor(monitor->GetId());
798                 return false;
799             }
800             monitor->recursiveCounter_ = oldMark.GetLockCount();
801             break;
802         case MarkWord::STATE_HASHED:
803             monitor->SetHashCode(oldMark.GetHash());
804             /* fallthrough */
805             [[fallthrough]];
806         case MarkWord::STATE_UNLOCKED:
807             // NOLINTNEXTLINE(readability-braces-around-statements, hicpp-braces-around-statements)
808             if constexpr (FOR_OTHER_THREAD) {  // NOLINT(bugprone-suspicious-semicolon)
809                 // We did check above, has to be unreachable
810                 UNREACHABLE();
811             } else {  // NOLINT(readability-misleading-indentation)
812                 break;
813             }
814         case MarkWord::STATE_HEAVY_LOCKED:
815             // Has to be unreachable
816             UNREACHABLE();
817         case MarkWord::STATE_GC:
818             LOG(FATAL, RUNTIME) << "Trying to inflate object in GC state";
819             return false;
820         default:
821             LOG(FATAL, RUNTIME) << "Undefined object state";
822             return false;
823     }
824 
825     return TryAddMonitor(monitor, oldMark, thread, obj, monitorPool);
826 }
827 
828 bool Monitor::Deflate(ObjectHeader *obj)
829 {
830     Monitor *monitor = nullptr;
831     MarkWord oldMark = obj->AtomicGetMark();
832     MarkWord::ObjectState state = oldMark.GetState();
833     bool ret = false;
834 
835     if (state != MarkWord::STATE_HEAVY_LOCKED) {
836         LOG(DEBUG, RUNTIME) << "Trying to deflate non-heavy locked object";
837         return false;
838     }
839 
840     auto *monitorPool = MTManagedThread::GetCurrent()->GetMonitorPool();
841     monitor = monitorPool->LookupMonitor(oldMark.GetMonitorId());
842     if (monitor == nullptr) {
843         LOG(DEBUG, RUNTIME) << "Monitor was already destroyed by someone else.";
844         return false;
845     }
846 
847     ret = monitor->DeflateInternal();
848     if (ret) {
849         monitorPool->FreeMonitor(monitor->GetId());
850     }
851     return ret;
852 }
853 
854 bool Monitor::DeflateInternal()
855 {
856     if (GetOwner() != nullptr) {
857         LOG(DEBUG, RUNTIME) << "Trying to deflate monitor which already has owner";
858         return false;
859     }
860     // Atomic with relaxed order reason: memory access in monitor
861     if (waitersCounter_.load(std::memory_order_relaxed) > 0) {
862         LOG(DEBUG, RUNTIME) << "Trying to deflate monitor which is trying to be acquired by other threads";
863         return false;
864     }
865     if (!lock_.TryLock()) {
866         LOG(DEBUG, RUNTIME) << "Couldn't TryLock monitor for deflation";
867         return false;
868     }
869     ASSERT(obj_ != nullptr);
870     ASSERT(recursiveCounter_ == 0);
871     ASSERT(waiters_.Empty());
872     ASSERT(toWakeup_.Empty());
873     ASSERT(GetOwner() == static_cast<MTManagedThread *>(nullptr));
874     MarkWord oldMark = obj_->AtomicGetMark();
875     MarkWord newMark = oldMark;
876     if (HasHashCode()) {
877         newMark = oldMark.DecodeFromHash(GetHashCode());
878         LOG(DEBUG, RUNTIME) << "Deflating monitor to hash";
879     } else {
880         newMark = oldMark.DecodeFromUnlocked();
881         LOG(DEBUG, RUNTIME) << "Deflating monitor to unlocked";
882     }
883 
884     // Warning: AtomicSetMark is weak, retry
885     while (!obj_->AtomicSetMark<false>(oldMark, newMark)) {
886         newMark = HasHashCode() ? oldMark.DecodeFromHash(GetHashCode()) : oldMark.DecodeFromUnlocked();
887     }
888     lock_.Unlock();
889     return true;
890 }
891 
892 uint8_t Monitor::HoldsLock(ObjectHeader *obj)
893 {
894     MarkWord mark = obj->AtomicGetMark();
895     MarkWord::ObjectState state = mark.GetState();
896     MTManagedThread *thread = MTManagedThread::GetCurrent();
897 
898     switch (state) {
899         case MarkWord::STATE_HEAVY_LOCKED: {
900             Monitor *monitor = thread->GetMonitorPool()->LookupMonitor(mark.GetMonitorId());
901             // asm has no boolean type
902             return (monitor->GetOwner() == thread) ? 1 : 0;
903         }
904         case MarkWord::STATE_LIGHT_LOCKED:
905             return (mark.GetThreadId() == thread->GetInternalId()) ? 1 : 0;
906         case MarkWord::STATE_UNLOCKED:
907         case MarkWord::STATE_HASHED:
908         case MarkWord::STATE_GC:
909             return 0;
910         default:
911             LOG(FATAL, RUNTIME) << "Undefined object state";
912             return 0;
913     }
914 }
915 
916 uint32_t Monitor::GetLockOwnerOsThreadID(ObjectHeader *obj)
917 {
918     if (obj == nullptr) {
919         return MTManagedThread::NON_INITIALIZED_THREAD_ID;
920     }
921     MarkWord mark = obj->AtomicGetMark();
922     MarkWord::ObjectState state = mark.GetState();
923 
924     switch (state) {
925         case MarkWord::STATE_HEAVY_LOCKED: {
926             Monitor *monitor = MTManagedThread::GetCurrent()->GetMonitorPool()->LookupMonitor(mark.GetMonitorId());
927             MTManagedThread *owner = monitor->GetOwner();
928             if (owner == nullptr) {
929                 return MTManagedThread::NON_INITIALIZED_THREAD_ID;
930             }
931             return owner->GetId();
932         }
933         case MarkWord::STATE_LIGHT_LOCKED: {
934             return mark.GetThreadId();
935         }
936         case MarkWord::STATE_UNLOCKED:
937         case MarkWord::STATE_HASHED:
938         case MarkWord::STATE_GC:
939             return 0;
940         default:
941             LOG(FATAL, RUNTIME) << "Undefined object state";
942             return 0;
943     }
944 }
945 
946 Monitor *Monitor::GetMonitorFromObject(ObjectHeader *obj)
947 {
948     if (obj != nullptr) {
949         MarkWord mark = obj->AtomicGetMark();
950         MarkWord::ObjectState state = mark.GetState();
951         switch (state) {
952             case MarkWord::STATE_HEAVY_LOCKED:
953                 return MTManagedThread::GetCurrent()->GetMonitorPool()->LookupMonitor(mark.GetMonitorId());
954             case MarkWord::STATE_LIGHT_LOCKED:
955                 return nullptr;
956             default:
957                 // Shouldn't happen, return nullptr
958                 LOG(WARNING, RUNTIME) << "obj:" << obj << " not locked by heavy or light locked";
959         }
960     }
961     return nullptr;
962 }
963 
964 inline void Monitor::TraceMonitorLock(ObjectHeader *obj, bool isWait)
965 {
966     if (UNLIKELY(ark::trace::IsEnabled())) {
967         // Use stack memory to avoid "Too many allocations" error.
968         constexpr int BUF_SIZE = 32;
969         std::array<char, BUF_SIZE> buf = {};
970         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
971         int ret = snprintf_s(buf.data(), BUF_SIZE, BUF_SIZE - 1,
972                              (isWait ? "Waiting on 0x%" PRIxPTR : "Locking 0x%" PRIxPTR), ToUintPtr(obj));
973         if (ret < 0) {
974             UNREACHABLE();
975         }
976         trace::BeginTracePoint(buf.data());
977     }
978 }
979 
980 inline void Monitor::TraceMonitorUnLock()
981 {
982     if (UNLIKELY(ark::trace::IsEnabled())) {
983         trace::EndTracePoint();
984     }
985 }
986 
987 uint32_t Monitor::GetHashCode()
988 {
989     while (!HasHashCode()) {
990         uint32_t expected = 0;
991         uint32_t newHash = ObjectHeader::GenerateHashCode();
992         if (hashCode_.compare_exchange_weak(expected, newHash)) {
993             return newHash;
994         }
995     }
996     ASSERT(HasHashCode());
997     // Atomic with relaxed order reason: memory access in monitor
998     return hashCode_.load(std::memory_order_relaxed);
999 }
1000 
1001 bool Monitor::HasHashCode() const
1002 {
1003     // Atomic with relaxed order reason: memory access in monitor
1004     return hashCode_.load(std::memory_order_relaxed) != 0;
1005 }
1006 
1007 void Monitor::SetHashCode(uint32_t hash)
1008 {
1009     ASSERT(GetOwner() == MTManagedThread::GetCurrent());
1010     if (!HasHashCode()) {
1011         // Atomic with relaxed order reason: memory access in monitor
1012         hashCode_.store(hash, std::memory_order_relaxed);
1013     } else {
1014         LOG(FATAL, RUNTIME) << "Attempt to rewrite hash in monitor";
1015     }
1016 }
1017 }  // namespace ark
1018