217 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * 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 {
 | |
| };
 | |
| 
 | |
| 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::kCollectorTypeCC:
 | |
|     case CollectorType::kCollectorTypeSS:
 | |
|       return true;
 | |
| 
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static bool CollectorDoesDisallow() {
 | |
|   CollectorType type = Runtime::Current()->GetHeap()->CurrentCollectorType();
 | |
|   switch (type) {
 | |
|     case CollectorType::kCollectorTypeCMS:
 | |
|       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_);
 | |
|   EXPECT_EQ(1U, 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_);
 | |
|   EXPECT_EQ(1U, 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_);
 | |
|   ASSERT_EQ(1U, 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(1U, cswh.sweep_count_);
 | |
| }
 | |
| 
 | |
| }  // namespace gc
 | |
| }  // namespace art
 |