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