Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/art/art/runtime/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 16 kB image not shown  

Quelle  monitor_test.cc

  Sprache: C
 

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "monitor.h"

#include <cstdint>
#include <memory>
#include <string>

#include "base/atomic.h"
#include "barrier.h"
#include "base/mutex.h"
#include "base/time_utils.h"
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "gtest/gtest.h"
#include "handle_scope-inl.h"
#include "jni/java_vm_ext.h"
#include "mirror/class-inl.h"
#include "mirror/string-inl.h"  // Strings are easiest to allocate
#include "monitor-inl.h"
#include "object_lock.h"
#include "scoped_thread_state_change-inl.h"
#include "thread_pool.h"

namespace art HIDDEN {

class MonitorTest : public CommonRuntimeTest {
 protected:
  MonitorTest() {
    use_boot_image_ = true;  // Make the Runtime creation cheaper.
  }

  void SetUpRuntimeOptions(RuntimeOptions *options) override {
    // Use a smaller heap
    SetUpRuntimeOptionsForFillHeap(options);

    options->push_back(std::make_pair("-Xint", nullptr));
  }

 public:
  std::unique_ptr<Monitor> monitor_;
  jobject object_;
  jobject watchdog_object_;
  // One exception test is for waiting on another Thread's lock. This is used to race-free &
  // loop-free pass
  Thread* thread_;
  std::unique_ptr<Barrier> barrier_;
  std::unique_ptr<Barrier> complete_barrier_;
  bool completed_;
};

// Check that an exception can be thrown correctly.
// This test is potentially racy, but the timeout is long enough that it should work.

class CreateTask : public Task {
 public:
  CreateTask(MonitorTest* monitor_test, uint64_t initial_sleep, int64_t millis, bool expected) :
      monitor_test_(monitor_test), initial_sleep_(initial_sleep), millis_(millis),
      expected_(expected) {}

  void Run(Thread* self) override {
    ScopedObjectAccess soa(self);
    StackHandleScope<1u> hs(self);
    Handle<mirror::Object> obj = hs.NewHandle(soa.Decode<mirror::Object>(monitor_test_->object_));

    monitor_test_->thread_ = self;        // Pass the Thread.
    obj->MonitorEnter(self);  // Lock the object. This should transition
    LockWord lock_after = obj->GetLockWord(false);  // it to thinLocked.
    LockWord::LockState new_state = lock_after.GetState();

    // Cannot use ASSERT only, as analysis thinks we'll keep holding the mutex.
    if (LockWord::LockState::kThinLocked != new_state) {
      obj->MonitorExit(self);         // To appease analysis.
      ASSERT_EQ(LockWord::LockState::kThinLocked, new_state);  // To fail the test.
      return;
    }

    // Force a fat lock by running identity hashcode to fill up lock word.
    obj->IdentityHashCode();
    LockWord lock_after2 = obj->GetLockWord(false);
    LockWord::LockState new_state2 = lock_after2.GetState();

    // Cannot use ASSERT only, as analysis thinks we'll keep holding the mutex.
    if (LockWord::LockState::kFatLocked != new_state2) {
      obj->MonitorExit(self);         // To appease analysis.
      ASSERT_EQ(LockWord::LockState::kFatLocked, new_state2);  // To fail the test.
      return;
    }

    {
      // Need to drop the mutator lock to use the barrier.
      ScopedThreadSuspension sts(self, ThreadState::kSuspended);
      monitor_test_->barrier_->Wait(self);           // Let the other thread know we're done.
    }

    // Give the other task a chance to do its thing.
    NanoSleep(initial_sleep_ * 1000 * 1000);

    // Now try to Wait on the Monitor.
    Monitor::Wait(self, obj.Get(), millis_, 0true, ThreadState::kTimedWaiting);

    // Check the exception status against what we expect.
    EXPECT_EQ(expected_, self->IsExceptionPending());
    if (expected_) {
      self->ClearException();
    }

    {
      // Need to drop the mutator lock to use the barrier.
      ScopedThreadSuspension sts(self, ThreadState::kSuspended);
      monitor_test_->complete_barrier_->Wait(self);  // Wait for test completion.
    }

    obj->MonitorExit(self);  // Release the object. Appeases analysis.
  }

  void Finalize() override {
    delete this;
  }

 private:
  MonitorTest* monitor_test_;
  uint64_t initial_sleep_;
  int64_t millis_;
  bool expected_;
};


class UseTask : public Task {
 public:
  UseTask(MonitorTest* monitor_test, uint64_t initial_sleep, int64_t millis, bool expected) :
      monitor_test_(monitor_test), initial_sleep_(initial_sleep), millis_(millis),
      expected_(expected) {}

  void Run(Thread* self) override {
    monitor_test_->barrier_->Wait(self);  // Wait for the other thread to set up the monitor.

    {
      ScopedObjectAccess soa(self);

      // Give the other task a chance to do its thing.
      NanoSleep(initial_sleep_ * 1000 * 1000);

      ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(monitor_test_->object_);
      Monitor::Wait(self, obj, millis_, 0true, ThreadState::kTimedWaiting);

      // Check the exception status against what we expect.
      EXPECT_EQ(expected_, self->IsExceptionPending());
      if (expected_) {
        self->ClearException();
      }
    }

    monitor_test_->complete_barrier_->Wait(self);  // Wait for test completion.
  }

  void Finalize() override {
    delete this;
  }

 private:
  MonitorTest* monitor_test_;
  uint64_t initial_sleep_;
  int64_t millis_;
  bool expected_;
};

class InterruptTask : public Task {
 public:
  InterruptTask(MonitorTest* monitor_test, uint64_t initial_sleep, uint64_t millis) :
      monitor_test_(monitor_test), initial_sleep_(initial_sleep), millis_(millis) {}

  void Run(Thread* self) override {
    monitor_test_->barrier_->Wait(self);  // Wait for the other thread to set up the monitor.

    {
      ScopedObjectAccess soa(self);

      // Give the other task a chance to do its thing.
      NanoSleep(initial_sleep_ * 1000 * 1000);

      // Interrupt the other thread.
      monitor_test_->thread_->Interrupt(self);

      // Give it some more time to get to the exception code.
      NanoSleep(millis_ * 1000 * 1000);

      // Now try to Wait.
      ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(monitor_test_->object_);
      Monitor::Wait(self, obj, 100true, ThreadState::kTimedWaiting);

      // No check here, as depending on scheduling we may or may not fail.
      if (self->IsExceptionPending()) {
        self->ClearException();
      }
    }

    monitor_test_->complete_barrier_->Wait(self);  // Wait for test completion.
  }

  void Finalize() override {
    delete this;
  }

 private:
  MonitorTest* monitor_test_;
  uint64_t initial_sleep_;
  uint64_t millis_;
};

class WatchdogTask : public Task {
 public:
  explicit WatchdogTask(MonitorTest* monitor_test) : monitor_test_(monitor_test) {}

  void Run(Thread* self) override {
    ScopedObjectAccess soa(self);
    StackHandleScope<1u> hs(self);
    Handle<mirror::Object> watchdog_obj =
        hs.NewHandle(soa.Decode<mirror::Object>(monitor_test_->watchdog_object_));

    watchdog_obj->MonitorEnter(self);        // Lock the object.

    watchdog_obj->Wait(self, 30 * 10000);  // Wait for 30s, or being woken up.

    watchdog_obj->MonitorExit(self);         // Release the lock.

    if (!monitor_test_->completed_) {
      LOG(FATAL) << "Watchdog timeout!";
    }
  }

  void Finalize() override {
    delete this;
  }

 private:
  MonitorTest* monitor_test_;
};

static void CommonWaitSetup(MonitorTest* test, ClassLinker* class_linker, uint64_t create_sleep,
                            int64_t c_millis, bool c_expected, bool interrupt, uint64_t use_sleep,
                            int64_t u_millis, bool u_expected, const char* pool_name) {
  Thread* const self = Thread::Current();
  ScopedObjectAccess soa(self);
  // First create the object we lock. String is easiest.
  StackHandleScope<2u> hs(soa.Self());
  Handle<mirror::Object> obj =
      hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "hello, world!"));
  test->object_ = soa.Vm()->AddGlobalRef(self, obj.Get());
  ASSERT_TRUE(test->object_ != nullptr);
  Handle<mirror::Object> watchdog_obj =
      hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "hello, world!"));
  test->watchdog_object_ = soa.Vm()->AddGlobalRef(self, watchdog_obj.Get());
  ASSERT_TRUE(test->watchdog_object_ != nullptr);

  // Create the barrier used to synchronize.
  test->barrier_ = std::make_unique<Barrier>(2);
  test->complete_barrier_ = std::make_unique<Barrier>(3);
  test->completed_ = false;

  // Our job: Fill the heap, then try Wait.
  {
    VariableSizedHandleScope vhs(soa.Self());
    test->FillHeap(soa.Self(), class_linker, &vhs);

    // Now release everything.
  }

  // Need to drop the mutator lock to allow barriers.
  ScopedThreadSuspension sts(soa.Self(), ThreadState::kNative);
  std::unique_ptr<ThreadPool> thread_pool(ThreadPool::Create(pool_name, 3));
  thread_pool->AddTask(self, new CreateTask(test, create_sleep, c_millis, c_expected));
  if (interrupt) {
    thread_pool->AddTask(self, new InterruptTask(test, use_sleep, static_cast<uint64_t>(u_millis)));
  } else {
    thread_pool->AddTask(self, new UseTask(test, use_sleep, u_millis, u_expected));
  }
  thread_pool->AddTask(self, new WatchdogTask(test));
  thread_pool->StartWorkers(self);

  // Wait on completion barrier.
  test->complete_barrier_->Wait(self);
  test->completed_ = true;

  // Wake the watchdog.
  {
    ScopedObjectAccess soa2(self);
    watchdog_obj->MonitorEnter(self);     // Lock the object.
    watchdog_obj->NotifyAll(self);        // Wake up waiting parties.
    watchdog_obj->MonitorExit(self);      // Release the lock.
  }

  thread_pool->StopWorkers(self);
}


// First test: throwing an exception when trying to wait in Monitor with another thread.
TEST_F(MonitorTest, CheckExceptionsWait1) {
  // Make the CreateTask wait 10ms, the UseTask wait 10ms.
  // => The use task will get the lock first and get to self == owner check.
  // This will lead to OOM and monitor error messages in the log.
  ScopedLogSeverity sls(LogSeverity::FATAL);
  CommonWaitSetup(this, class_linker_, 1050falsefalse250true,
                  "Monitor test thread pool 1");
}

// Second test: throwing an exception for invalid wait time.
TEST_F(MonitorTest, CheckExceptionsWait2) {
  // Make the CreateTask wait 0ms, the UseTask wait 10ms.
  // => The create task will get the lock first and get to ms >= 0
  // This will lead to OOM and monitor error messages in the log.
  ScopedLogSeverity sls(LogSeverity::FATAL);
  CommonWaitSetup(this, class_linker_, 0, -1truefalse1050true,
                  "Monitor test thread pool 2");
}

// Third test: throwing an interrupted-exception.
TEST_F(MonitorTest, CheckExceptionsWait3) {
  // Make the CreateTask wait 0ms, then Wait for a long time. Make the InterruptTask wait 10ms,
  // after which it will interrupt the create task and then wait another 10ms.
  // => The create task will get to the interrupted-exception throw.
  // This will lead to OOM and monitor error messages in the log.
  ScopedLogSeverity sls(LogSeverity::FATAL);
  CommonWaitSetup(this, class_linker_, 0500truetrue1050true,
                  "Monitor test thread pool 3");
}

class TryLockTask : public Task {
 public:
  explicit TryLockTask(jobject obj) : obj_(obj) {}

  void Run(Thread* self) override {
    ScopedObjectAccess soa(self);
    StackHandleScope<1u> hs(self);
    Handle<mirror::Object> obj = hs.NewHandle(soa.Decode<mirror::Object>(obj_));
    // Lock is held by other thread, try lock should fail.
    ObjectTryLock<mirror::Object> lock(self, obj);
    EXPECT_FALSE(lock.Acquired());
  }

  void Finalize() override {
    delete this;
  }

 private:
  jobject obj_;
};

// Test trylock in deadlock scenarios.
TEST_F(MonitorTest, TestTryLock) {
  ScopedLogSeverity sls(LogSeverity::FATAL);

  Thread* const self = Thread::Current();
  std::unique_ptr<ThreadPool> thread_pool(ThreadPool::Create("the pool"2));
  ScopedObjectAccess soa(self);
  StackHandleScope<1> hs(self);
  Handle<mirror::Object> obj1(
      hs.NewHandle<mirror::Object>(mirror::String::AllocFromModifiedUtf8(self, "hello, world!")));
  jobject g_obj1 = soa.Vm()->AddGlobalRef(self, obj1.Get());
  ASSERT_TRUE(g_obj1 != nullptr);
  {
    ObjectLock<mirror::Object> lock1(self, obj1);
    {
      ObjectTryLock<mirror::Object> trylock(self, obj1);
      EXPECT_TRUE(trylock.Acquired());
    }
    // Test failure case.
    thread_pool->AddTask(self, new TryLockTask(g_obj1));
    thread_pool->StartWorkers(self);
    ScopedThreadSuspension sts(self, ThreadState::kSuspended);
    thread_pool->Wait(Thread::Current(), /*do_work=*/false, /*may_hold_locks=*/false);
  }
  // Test that the trylock actually locks the object.
  {
    ObjectTryLock<mirror::Object> trylock(self, obj1);
    EXPECT_TRUE(trylock.Acquired());
    obj1->Notify(self);
    // Since we hold the lock there should be no monitor state exeception.
    self->AssertNoPendingException();
  }
  thread_pool->StopWorkers(self);
}

class ScopedVirtualThreadId {
 public:
  ScopedVirtualThreadId(): id_(AllocThreadId()) {}
  ~ScopedVirtualThreadId() {
    ThreadList* thread_list = Runtime::Current()->GetThreadList();
    thread_list->ReleaseVirtualThreadSuspendCount(id_);
    thread_list->ReleaseThreadId(Thread::Current(), id_);
  }
  uint32_t GetId() const {
    return id_;
  }
 private:
  static uint32_t AllocThreadId() {
    ThreadList* thread_list = Runtime::Current()->GetThreadList();
    Thread* self = Thread::Current();
    uint32_t id = thread_list->AllocThreadId(self);
    thread_list->AllocVirtualThreadSuspendCount(id);
    return id;
  }
 private:
  const uint32_t id_;
};

class VirtualThreadMounter {
 public:
  explicit VirtualThreadMounter(MountedVirtualThreadData* mounted_data)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    Thread* self = Thread::Current();
    self->TrySetMountedVirtualThreadData(mounted_data);
  }
  ~VirtualThreadMounter() {
    Thread* self = Thread::Current();
    self->TryClearMountedVirtualThreadData();
  }
};

TEST_F(MonitorTest, TestMonitorOwner) {
  ASSERT_TRUE(Runtime::Current() != nullptr);
  Thread* self = Thread::Current();
  pid_t tid = self->GetTid();
  uint32_t carrier_id = self->GetThreadId();

  {
    // thread_list_lock_ is needed for the thread safety analysis of GetThreadId().
    MutexLock mu(self, *Locks::thread_list_lock_);
    // Case 1: platform thread
    MonitorOwner owner = MonitorOwner::FromThread(self);
    EXPECT_EQ(carrier_id, owner.GetThreadId());
    EXPECT_FALSE(owner.IsVirtualThread());
    EXPECT_EQ(tid, owner.GetMutexOwnerId());
    EXPECT_TRUE(owner.IsOwner(self));
    EXPECT_TRUE(owner == self);
    EXPECT_FALSE(owner.IsNull());
    EXPECT_EQ(reinterpret_cast<uintptr_t>(self), owner.getStorageValue());

    MonitorOwner owner2 = MonitorOwner::FromPlatformThread(self);
    EXPECT_EQ(carrier_id, owner2.GetThreadId());
    EXPECT_EQ(owner.getStorageValue(), owner.getStorageValue());
    EXPECT_TRUE(owner == owner2);

    // Case 2: null value.
    owner = MonitorOwner();
    EXPECT_EQ(ThreadList::kInvalidThreadId, owner.GetThreadId());
    EXPECT_FALSE(owner.IsVirtualThread());
    EXPECT_EQ(0, owner.GetMutexOwnerId());
    EXPECT_FALSE(owner.IsOwner(self));
    EXPECT_FALSE(owner == self);
    EXPECT_TRUE(owner.IsNull());
    EXPECT_EQ(0, owner.getStorageValue());
  }


  if (!kIsVirtualThreadEnabled) {
    return;
  }

  ScopedVirtualThreadId virtual_thread_id_holder;
  uint32_t virtual_thread_id = virtual_thread_id_holder.GetId();
  EXPECT_NE(virtual_thread_id, carrier_id);

  MountedVirtualThreadData mounted_data(virtual_thread_id, carrier_id, 0);
  ScopedObjectAccess soa(self);
  VirtualThreadMounter virtual_thread_mounter(&mounted_data);

  {
    // thread_list_lock_ is needed for the thread safety analysis of GetThreadId().
    MutexLock mu(self, *Locks::thread_list_lock_);
    // Case 3: virtual thread
    MonitorOwner owner = MonitorOwner::FromVirtualThreadId(virtual_thread_id);
    EXPECT_TRUE(owner.IsVirtualThread());
    EXPECT_EQ(virtual_thread_id, owner.GetVirtualThreadId());
    EXPECT_EQ(virtual_thread_id, owner.GetThreadId());
    EXPECT_EQ(virtual_thread_id | MonitorMutex::kVTFlag, owner.GetMutexOwnerId());
    EXPECT_TRUE(owner.IsOwner(self));
    EXPECT_TRUE(owner == self);
    EXPECT_FALSE(owner.IsNull());
    EXPECT_EQ(virtual_thread_id << 1 | 1, owner.getStorageValue());

    MonitorOwner owner2 = MonitorOwner::FromThread(self);
    EXPECT_EQ(virtual_thread_id, owner2.GetThreadId());
    EXPECT_EQ(owner.getStorageValue(), owner.getStorageValue());
    EXPECT_TRUE(owner == owner2);
  }
}

}  // namespace art

Messung V0.5 in Prozent
C=88 H=95 G=91

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.