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