|  | /* | 
|  | * 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 |