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