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