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