| /* |
| * Copyright (C) 2016 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 "system_weak.h" |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <memory> |
| |
| #include "base/mutex.h" |
| #include "collector_type.h" |
| #include "common_runtime_test.h" |
| #include "gc_root-inl.h" |
| #include "handle_scope-inl.h" |
| #include "heap.h" |
| #include "mirror/object-inl.h" |
| #include "mirror/string.h" |
| #include "scoped_thread_state_change-inl.h" |
| #include "thread_list.h" |
| |
| namespace art { |
| namespace gc { |
| |
| class SystemWeakTest : public CommonRuntimeTest { |
| protected: |
| SystemWeakTest() { |
| use_boot_image_ = true; // Make the Runtime creation cheaper. |
| } |
| }; |
| |
| struct CountingSystemWeakHolder : public SystemWeakHolder { |
| CountingSystemWeakHolder() |
| : SystemWeakHolder(kAllocTrackerLock), |
| allow_count_(0), |
| disallow_count_(0), |
| sweep_count_(0) {} |
| |
| void Allow() override |
| REQUIRES_SHARED(Locks::mutator_lock_) |
| REQUIRES(!allow_disallow_lock_) { |
| SystemWeakHolder::Allow(); |
| |
| allow_count_++; |
| } |
| |
| void Disallow() override |
| REQUIRES_SHARED(Locks::mutator_lock_) |
| REQUIRES(!allow_disallow_lock_) { |
| SystemWeakHolder::Disallow(); |
| |
| disallow_count_++; |
| } |
| |
| void Broadcast(bool broadcast_for_checkpoint) override |
| REQUIRES(!allow_disallow_lock_) { |
| SystemWeakHolder::Broadcast(broadcast_for_checkpoint); |
| |
| if (!broadcast_for_checkpoint) { |
| // Don't count the broadcasts for running checkpoints. |
| allow_count_++; |
| } |
| } |
| |
| void Sweep(IsMarkedVisitor* visitor) override |
| REQUIRES_SHARED(Locks::mutator_lock_) |
| REQUIRES(!allow_disallow_lock_) { |
| MutexLock mu(Thread::Current(), allow_disallow_lock_); |
| mirror::Object* old_object = weak_.Read<kWithoutReadBarrier>(); |
| mirror::Object* new_object = old_object == nullptr ? nullptr : visitor->IsMarked(old_object); |
| weak_ = GcRoot<mirror::Object>(new_object); |
| |
| sweep_count_++; |
| } |
| |
| GcRoot<mirror::Object> Get() |
| REQUIRES_SHARED(Locks::mutator_lock_) |
| REQUIRES(!allow_disallow_lock_) { |
| Thread* self = Thread::Current(); |
| MutexLock mu(self, allow_disallow_lock_); |
| Wait(self); |
| |
| return weak_; |
| } |
| |
| void Set(GcRoot<mirror::Object> obj) |
| REQUIRES_SHARED(Locks::mutator_lock_) |
| REQUIRES(!allow_disallow_lock_) { |
| Thread* self = Thread::Current(); |
| MutexLock mu(self, allow_disallow_lock_); |
| Wait(self); |
| |
| weak_ = obj; |
| } |
| |
| size_t allow_count_; |
| size_t disallow_count_; |
| size_t sweep_count_; |
| GcRoot<mirror::Object> weak_ GUARDED_BY(allow_disallow_lock_); |
| }; |
| |
| static bool CollectorDoesAllowOrBroadcast() { |
| CollectorType type = Runtime::Current()->GetHeap()->CurrentCollectorType(); |
| switch (type) { |
| case CollectorType::kCollectorTypeCMS: |
| case CollectorType::kCollectorTypeCMC: |
| case CollectorType::kCollectorTypeCC: |
| case CollectorType::kCollectorTypeSS: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| static bool CollectorDoesDisallow() { |
| CollectorType type = Runtime::Current()->GetHeap()->CurrentCollectorType(); |
| switch (type) { |
| case CollectorType::kCollectorTypeCMS: |
| case CollectorType::kCollectorTypeCMC: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| TEST_F(SystemWeakTest, Keep) { |
| CountingSystemWeakHolder cswh; |
| Runtime::Current()->AddSystemWeakHolder(&cswh); |
| |
| ScopedObjectAccess soa(Thread::Current()); |
| |
| StackHandleScope<1> hs(soa.Self()); |
| |
| // We use Strings because they are very easy to allocate. |
| Handle<mirror::String> s(hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "ABC"))); |
| cswh.Set(GcRoot<mirror::Object>(s.Get())); |
| |
| // Trigger a GC. |
| Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references= */ false); |
| |
| // Expect the holder to have been called. |
| EXPECT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_); |
| EXPECT_EQ(CollectorDoesDisallow() ? 1U : 0U, cswh.disallow_count_); |
| // Userfaultfd GC uses SweepSystemWeaks also for concurrent updation. |
| // TODO: Explore this can be reverted back to unconditionally compare with 1 |
| // once concurrent updation of native roots is full implemented in userfaultfd |
| // GC. |
| size_t expected_sweep_count = gUseUserfaultfd ? 2U : 1U; |
| EXPECT_EQ(expected_sweep_count, cswh.sweep_count_); |
| |
| // Expect the weak to not be cleared. |
| EXPECT_FALSE(cswh.Get().IsNull()); |
| EXPECT_EQ(cswh.Get().Read(), s.Get()); |
| } |
| |
| TEST_F(SystemWeakTest, Discard) { |
| CountingSystemWeakHolder cswh; |
| Runtime::Current()->AddSystemWeakHolder(&cswh); |
| |
| ScopedObjectAccess soa(Thread::Current()); |
| |
| cswh.Set(GcRoot<mirror::Object>(mirror::String::AllocFromModifiedUtf8(soa.Self(), "ABC"))); |
| |
| // Trigger a GC. |
| Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references= */ false); |
| |
| // Expect the holder to have been called. |
| EXPECT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_); |
| EXPECT_EQ(CollectorDoesDisallow() ? 1U : 0U, cswh.disallow_count_); |
| // Userfaultfd GC uses SweepSystemWeaks also for concurrent updation. |
| // TODO: Explore this can be reverted back to unconditionally compare with 1 |
| // once concurrent updation of native roots is full implemented in userfaultfd |
| // GC. |
| size_t expected_sweep_count = gUseUserfaultfd ? 2U : 1U; |
| EXPECT_EQ(expected_sweep_count, cswh.sweep_count_); |
| |
| // Expect the weak to be cleared. |
| EXPECT_TRUE(cswh.Get().IsNull()); |
| } |
| |
| TEST_F(SystemWeakTest, Remove) { |
| CountingSystemWeakHolder cswh; |
| Runtime::Current()->AddSystemWeakHolder(&cswh); |
| |
| ScopedObjectAccess soa(Thread::Current()); |
| |
| StackHandleScope<1> hs(soa.Self()); |
| |
| // We use Strings because they are very easy to allocate. |
| Handle<mirror::String> s(hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "ABC"))); |
| cswh.Set(GcRoot<mirror::Object>(s.Get())); |
| |
| // Trigger a GC. |
| Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references= */ false); |
| |
| // Expect the holder to have been called. |
| ASSERT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_); |
| ASSERT_EQ(CollectorDoesDisallow() ? 1U : 0U, cswh.disallow_count_); |
| // Userfaultfd GC uses SweepSystemWeaks also for concurrent updation. |
| // TODO: Explore this can be reverted back to unconditionally compare with 1 |
| // once concurrent updation of native roots is full implemented in userfaultfd |
| // GC. |
| size_t expected_sweep_count = gUseUserfaultfd ? 2U : 1U; |
| EXPECT_EQ(expected_sweep_count, cswh.sweep_count_); |
| |
| // Expect the weak to not be cleared. |
| ASSERT_FALSE(cswh.Get().IsNull()); |
| ASSERT_EQ(cswh.Get().Read(), s.Get()); |
| |
| // Remove the holder. |
| Runtime::Current()->RemoveSystemWeakHolder(&cswh); |
| |
| // Trigger another GC. |
| Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references= */ false); |
| |
| // Expectation: no change in the numbers. |
| EXPECT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_); |
| EXPECT_EQ(CollectorDoesDisallow() ? 1U : 0U, cswh.disallow_count_); |
| EXPECT_EQ(expected_sweep_count, cswh.sweep_count_); |
| } |
| |
| } // namespace gc |
| } // namespace art |