Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 37 #include "platform/heap/ThreadState.h" | 37 #include "platform/heap/ThreadState.h" |
| 38 #include "platform/heap/Visitor.h" | 38 #include "platform/heap/Visitor.h" |
| 39 #include "public/platform/Platform.h" | 39 #include "public/platform/Platform.h" |
| 40 #include "wtf/HashTraits.h" | 40 #include "wtf/HashTraits.h" |
| 41 #include "wtf/LinkedHashSet.h" | 41 #include "wtf/LinkedHashSet.h" |
| 42 | 42 |
| 43 #include <gtest/gtest.h> | 43 #include <gtest/gtest.h> |
| 44 | 44 |
| 45 namespace WebCore { | 45 namespace WebCore { |
| 46 | 46 |
| 47 class IntWrapper; | |
|
Mads Ager (chromium)
2014/06/06 09:59:17
Can we just move IntWrapper to right after ThreadM
Erik Corry
2014/06/10 11:21:18
Done.
| |
| 48 | |
| 47 class ThreadMarker { | 49 class ThreadMarker { |
| 48 public: | 50 public: |
| 49 ThreadMarker() : m_creatingThread(reinterpret_cast<ThreadState*>(0)), m_num( 0) { } | 51 ThreadMarker() : m_creatingThread(reinterpret_cast<ThreadState*>(0)), m_num( 0) { } |
| 50 ThreadMarker(unsigned i) : m_creatingThread(ThreadState::current()), m_num(i ) { } | 52 ThreadMarker(unsigned i) : m_creatingThread(ThreadState::current()), m_num(i ) { } |
| 51 ThreadMarker(WTF::HashTableDeletedValueType deleted) : m_creatingThread(rein terpret_cast<ThreadState*>(-1)), m_num(0) { } | 53 ThreadMarker(WTF::HashTableDeletedValueType deleted) : m_creatingThread(rein terpret_cast<ThreadState*>(-1)), m_num(0) { } |
| 52 ~ThreadMarker() | 54 ~ThreadMarker() |
| 53 { | 55 { |
| 54 EXPECT_TRUE((m_creatingThread == ThreadState::current()) | 56 EXPECT_TRUE((m_creatingThread == ThreadState::current()) |
| 55 || (m_creatingThread == reinterpret_cast<ThreadState*>(0)) | 57 || (m_creatingThread == reinterpret_cast<ThreadState*>(0)) |
| 56 || (m_creatingThread == reinterpret_cast<ThreadState*>(-1))); | 58 || (m_creatingThread == reinterpret_cast<ThreadState*>(-1))); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 68 } | 70 } |
| 69 | 71 |
| 70 static bool equal(const ThreadMarker& a, const ThreadMarker& b) | 72 static bool equal(const ThreadMarker& a, const ThreadMarker& b) |
| 71 { | 73 { |
| 72 return a == b; | 74 return a == b; |
| 73 } | 75 } |
| 74 | 76 |
| 75 static const bool safeToCompareToEmptyOrDeleted = false; | 77 static const bool safeToCompareToEmptyOrDeleted = false; |
| 76 }; | 78 }; |
| 77 | 79 |
| 80 typedef std::pair<Member<IntWrapper>, WeakMember<IntWrapper> > StrongWeakPair; | |
| 81 | |
| 82 struct PairWithWeakHandling : public StrongWeakPair { | |
|
Mads Ager (chromium)
2014/06/06 09:59:17
This is clever since it shuts up the gc plugin. ;-
Erik Corry
2014/06/10 11:21:18
OK
| |
| 83 ALLOW_ONLY_INLINE_ALLOCATION(); | |
| 84 | |
| 85 public: | |
| 86 // Regular constructor. | |
| 87 PairWithWeakHandling(IntWrapper* one, IntWrapper* two) | |
| 88 : StrongWeakPair(one, two) | |
| 89 { | |
| 90 ASSERT(one); // We use null first field to indicate empty slots in the h ash table. | |
| 91 } | |
| 92 | |
| 93 // The HashTable (via the HashTrait) calls this constructor with a | |
| 94 // placement new to mark slots in the hash table as being deleted. We will | |
| 95 // never call trace or the destructor on these slots. We mark ourselves dele ted | |
| 96 // with a pointer to -1 in the first field. | |
| 97 PairWithWeakHandling(WTF::HashTableDeletedValueType) | |
| 98 : StrongWeakPair(reinterpret_cast<IntWrapper*>(-1), nullptr) | |
| 99 { | |
| 100 } | |
| 101 | |
| 102 // Used by the HashTable (via the HashTrait) to skip deleted slots in the | |
| 103 // table. Recognizes objects that were 'constructed' using the above | |
| 104 // constructor. | |
| 105 bool isHashTableDeletedValue() const { return first == reinterpret_cast<IntW rapper*>(-1); } | |
| 106 | |
| 107 // Since we don't allocate independent objects of this type, we don't need | |
| 108 // a regular trace method. Instead, we use a traceInCollection method. | |
| 109 void traceInCollection(Visitor* visitor, ShouldWeakPointersBeMarkedStrongly strongify) | |
| 110 { | |
| 111 visitor->trace(first); | |
| 112 visitor->traceInCollection(second, strongify); | |
| 113 } | |
| 114 // The traceInCollection may not trace the weak members, so it is vital | |
| 115 // that shouldRemoveFromCollection checks them so the entry can be removed | |
| 116 // from the collection (otherwise it would contain a dangling pointer). | |
| 117 bool shouldRemoveFromCollection(Visitor* visitor) | |
| 118 { | |
| 119 return !visitor->isAlive(second); | |
| 120 } | |
| 121 }; | |
| 122 | |
| 78 } | 123 } |
| 79 | 124 |
| 80 namespace WTF { | 125 namespace WTF { |
| 81 | 126 |
| 82 template<typename T> struct DefaultHash; | 127 template<typename T> struct DefaultHash; |
| 83 template<> struct DefaultHash<WebCore::ThreadMarker> { | 128 template<> struct DefaultHash<WebCore::ThreadMarker> { |
| 84 typedef WebCore::ThreadMarkerHash Hash; | 129 typedef WebCore::ThreadMarkerHash Hash; |
| 85 }; | 130 }; |
| 86 | 131 |
| 87 // ThreadMarkerHash is the default hash for ThreadMarker | 132 // ThreadMarkerHash is the default hash for ThreadMarker |
| 88 template<> struct HashTraits<WebCore::ThreadMarker> : GenericHashTraits<WebCore: :ThreadMarker> { | 133 template<> struct HashTraits<WebCore::ThreadMarker> : GenericHashTraits<WebCore: :ThreadMarker> { |
| 89 static const bool emptyValueIsZero = true; | 134 static const bool emptyValueIsZero = true; |
| 90 static void constructDeletedValue(WebCore::ThreadMarker& slot) { new (NotNul l, &slot) WebCore::ThreadMarker(HashTableDeletedValue); } | 135 static void constructDeletedValue(WebCore::ThreadMarker& slot) { new (NotNul l, &slot) WebCore::ThreadMarker(HashTableDeletedValue); } |
| 91 static bool isDeletedValue(const WebCore::ThreadMarker& slot) { return slot. isHashTableDeletedValue(); } | 136 static bool isDeletedValue(const WebCore::ThreadMarker& slot) { return slot. isHashTableDeletedValue(); } |
| 92 }; | 137 }; |
| 93 | 138 |
| 139 // The hash algorithm for our custom pair class is just the standard double | |
| 140 // hash for pairs. Note that this means you can't mutate either of the parts of | |
| 141 // the pair while they are in the hash table, as that would change their hash | |
| 142 // code and thus their preferred placement in the table. | |
| 143 template<> struct DefaultHash<WebCore::PairWithWeakHandling> { | |
| 144 typedef PairHash<WebCore::Member<WebCore::IntWrapper>, WebCore::WeakMember<W ebCore::IntWrapper> > Hash; | |
| 145 }; | |
| 146 | |
| 147 // Custom traits for the pair. These are weakness handling traits, which means | |
| 148 // PairWithWeakHandling must implement the traceInCollection and | |
| 149 // shouldRemoveFromCollection methods. In addition, these traits are concerned | |
| 150 // with the two magic values for the object, that represent empty and deleted | |
| 151 // slots in the hash table. The SimpleClassHashTraits allow empty slots in the | |
| 152 // table to be initialzed with memset to zero, and we use -1 in the first part | |
| 153 // of the pair to represent deleted slots. | |
| 154 template<> struct HashTraits<WebCore::PairWithWeakHandling> : WebCore::WeakHandl ingHashTraits<WebCore::PairWithWeakHandling> { | |
| 155 static const bool needsDestruction = false; | |
| 156 static const bool hasIsEmptyValueFunction = true; | |
| 157 static bool isEmptyValue(const WebCore::PairWithWeakHandling& value) { retur n !value.first; } | |
| 158 static void constructDeletedValue(WebCore::PairWithWeakHandling& slot) { new (NotNull, &slot) WebCore::PairWithWeakHandling(HashTableDeletedValue); } | |
| 159 static bool isDeletedValue(const WebCore::PairWithWeakHandling& value) { ret urn value.isHashTableDeletedValue(); } | |
| 160 }; | |
| 161 | |
| 94 } | 162 } |
| 95 | 163 |
| 96 namespace WebCore { | 164 namespace WebCore { |
| 97 | 165 |
| 98 class TestGCScope { | 166 class TestGCScope { |
| 99 public: | 167 public: |
| 100 explicit TestGCScope(ThreadState::StackState state) | 168 explicit TestGCScope(ThreadState::StackState state) |
| 101 : m_state(ThreadState::current()) | 169 : m_state(ThreadState::current()) |
| 102 , m_safePointScope(state) | 170 , m_safePointScope(state) |
| 103 , m_parkedAllThreads(false) | 171 , m_parkedAllThreads(false) |
| (...skipping 3205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3309 } | 3377 } |
| 3310 // This forces a GC without stack scanning which results in the objects | 3378 // This forces a GC without stack scanning which results in the objects |
| 3311 // being collected. This will also rebuild the above mentioned freelists, | 3379 // being collected. This will also rebuild the above mentioned freelists, |
| 3312 // however we don't rely on that below since we don't have any allocations. | 3380 // however we don't rely on that below since we don't have any allocations. |
| 3313 clearOutOldGarbage(&initialHeapStats); | 3381 clearOutOldGarbage(&initialHeapStats); |
| 3314 { | 3382 { |
| 3315 TestGCScope scope(ThreadState::HeapPointersOnStack); | 3383 TestGCScope scope(ThreadState::HeapPointersOnStack); |
| 3316 EXPECT_TRUE(scope.allThreadsParked()); | 3384 EXPECT_TRUE(scope.allThreadsParked()); |
| 3317 Heap::makeConsistentForGC(); | 3385 Heap::makeConsistentForGC(); |
| 3318 for (size_t i = 0; i < objectAddresses.size(); i++) { | 3386 for (size_t i = 0; i < objectAddresses.size(); i++) { |
| 3319 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, objectAddresses[i]) ); | 3387 // We would like to assert that checkAndMarkPointer returned false |
| 3320 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, endAddresses[i])); | 3388 // here because the pointers no longer point into a valid object |
| 3389 // (it's been freed by the GCs. But checkAndMarkPointer will return | |
| 3390 // true for any pointer that points into a heap page, regardless of | |
| 3391 // whether it points at a valid object (this ensures the | |
| 3392 // correctness of the page-based on-heap address caches), so we | |
| 3393 // can't make that assert. | |
| 3394 Heap::checkAndMarkPointer(&visitor, objectAddresses[i]); | |
| 3395 Heap::checkAndMarkPointer(&visitor, endAddresses[i]); | |
| 3321 } | 3396 } |
| 3322 EXPECT_EQ(0ul, visitor.count()); | 3397 EXPECT_EQ(0ul, visitor.count()); |
| 3323 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectAddress)); | 3398 Heap::checkAndMarkPointer(&visitor, largeObjectAddress); |
| 3324 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress)) ; | 3399 Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress); |
| 3325 EXPECT_EQ(0ul, visitor.count()); | 3400 EXPECT_EQ(0ul, visitor.count()); |
| 3326 } | 3401 } |
| 3327 // This round of GC is important to make sure that the object start | 3402 // This round of GC is important to make sure that the object start |
| 3328 // bitmap are cleared out and that the free lists are rebuild. | 3403 // bitmap are cleared out and that the free lists are rebuild. |
| 3329 clearOutOldGarbage(&initialHeapStats); | 3404 clearOutOldGarbage(&initialHeapStats); |
| 3330 } | 3405 } |
| 3331 | 3406 |
| 3332 TEST(HeapTest, VisitOffHeapCollections) | 3407 TEST(HeapTest, VisitOffHeapCollections) |
| 3333 { | 3408 { |
| 3334 HeapStats initialHeapStats; | 3409 HeapStats initialHeapStats; |
| (...skipping 636 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3971 | 4046 |
| 3972 // class SimpleObject : public GarbageCollected<SimpleObject> {}; | 4047 // class SimpleObject : public GarbageCollected<SimpleObject> {}; |
| 3973 EXPECT_FALSE(NeedsAdjustAndMark<SimpleObject>::value); | 4048 EXPECT_FALSE(NeedsAdjustAndMark<SimpleObject>::value); |
| 3974 EXPECT_FALSE(NeedsAdjustAndMark<const SimpleObject>::value); | 4049 EXPECT_FALSE(NeedsAdjustAndMark<const SimpleObject>::value); |
| 3975 | 4050 |
| 3976 // class UseMixin : public SimpleObject, public Mixin {}; | 4051 // class UseMixin : public SimpleObject, public Mixin {}; |
| 3977 EXPECT_FALSE(NeedsAdjustAndMark<UseMixin>::value); | 4052 EXPECT_FALSE(NeedsAdjustAndMark<UseMixin>::value); |
| 3978 EXPECT_FALSE(NeedsAdjustAndMark<const UseMixin>::value); | 4053 EXPECT_FALSE(NeedsAdjustAndMark<const UseMixin>::value); |
| 3979 } | 4054 } |
| 3980 | 4055 |
| 4056 template<typename Set> | |
| 4057 void setWithCustomWeaknessHandling() | |
| 4058 { | |
| 4059 Set set; | |
| 4060 Set* set2 = new Set(); | |
| 4061 Persistent<Set> set3(new Set()); | |
| 4062 Persistent<IntWrapper> livingInt(IntWrapper::create(42)); | |
| 4063 set.add(PairWithWeakHandling(IntWrapper::create(0), IntWrapper::create(1))); | |
| 4064 set2->add(PairWithWeakHandling(IntWrapper::create(2), IntWrapper::create(3)) ); | |
| 4065 set3->add(PairWithWeakHandling(IntWrapper::create(4), IntWrapper::create(5)) ); | |
| 4066 Heap::collectGarbage(ThreadState::HeapPointersOnStack); | |
| 4067 typedef typename Set::iterator Iterator; | |
| 4068 // The first set is on-stack, so its backing store must be referenced from | |
| 4069 // the stack. That makes the weak references strong. | |
| 4070 Iterator i = set.begin(); | |
| 4071 EXPECT_EQ(0, i->first->value()); | |
| 4072 EXPECT_EQ(1, i->second->value()); | |
| 4073 // The second set is pointed to from the stack, so it's referenced, but the | |
| 4074 // weak processing may have taken place. | |
| 4075 if (set2->size()) { | |
| 4076 Iterator i2 = set2->begin(); | |
| 4077 EXPECT_EQ(2, i2->first->value()); | |
| 4078 EXPECT_EQ(3, i2->second->value()); | |
| 4079 } | |
| 4080 // The third set is pointed to from a persistent, so it's referenced, but | |
| 4081 // the weak processing may have taken place. | |
| 4082 if (set3->size()) { | |
| 4083 Iterator i3 = set3->begin(); | |
| 4084 EXPECT_EQ(4, i3->first->value()); | |
| 4085 EXPECT_EQ(5, i3->second->value()); | |
| 4086 } | |
| 4087 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | |
| 4088 // No looking at the set and set2 now, they are busted by our lying about th e heap pointers on the stack! | |
|
Mads Ager (chromium)
2014/06/06 09:59:17
How about scoping them so we can't touch them?
Erik Corry
2014/06/10 11:21:18
Done.
| |
| 4089 if (set3->size()) { | |
|
Mads Ager (chromium)
2014/06/06 09:59:17
Since we ignore all heap pointers on the stack thi
Erik Corry
2014/06/10 11:21:18
Done.
| |
| 4090 Iterator i3 = set3->begin(); | |
| 4091 EXPECT_EQ(4, i3->first->value()); | |
| 4092 EXPECT_EQ(5, i3->second->value()); | |
| 4093 } | |
| 4094 set3->clear(); | |
|
Mads Ager (chromium)
2014/06/06 09:59:17
And then this is not needed.
Erik Corry
2014/06/10 11:21:18
Done.
| |
| 4095 set3->add(PairWithWeakHandling(IntWrapper::create(103), livingInt)); | |
| 4096 set3->add(PairWithWeakHandling(livingInt, IntWrapper::create(103))); // This one gets zapped at GC time because nothing holds the 103 alive. | |
| 4097 set3->add(PairWithWeakHandling(IntWrapper::create(103), IntWrapper::create(1 03))); // This one gets zapped too. | |
| 4098 set3->add(PairWithWeakHandling(livingInt, livingInt)); | |
| 4099 set3->add(PairWithWeakHandling(livingInt, livingInt)); // This one is identi cal to the previous and doesn't add anything. | |
| 4100 EXPECT_EQ(4u, set3->size()); | |
| 4101 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | |
| 4102 EXPECT_EQ(2u, set3->size()); | |
| 4103 Iterator i3 = set3->begin(); | |
| 4104 EXPECT_TRUE(i3->first->value() == 103 || i3->first == livingInt); | |
| 4105 EXPECT_EQ(livingInt, i3->second); | |
| 4106 ++i3; | |
| 4107 EXPECT_TRUE(i3->first->value() == 103 || i3->first == livingInt); | |
| 4108 EXPECT_EQ(livingInt, i3->second); | |
| 4109 } | |
| 4110 | |
| 4111 TEST(HeapTest, SetWithCustomWeaknessHandling) | |
| 4112 { | |
| 4113 setWithCustomWeaknessHandling<HeapHashSet<PairWithWeakHandling> >(); | |
| 4114 setWithCustomWeaknessHandling<HeapLinkedHashSet<PairWithWeakHandling> >(); | |
| 4115 } | |
| 4116 | |
| 4117 TEST(HeapTest, MapWithCustomWeaknessHandling) | |
| 4118 { | |
| 4119 typedef HeapHashMap<PairWithWeakHandling, RefPtr<OffHeapInt> > Map; | |
| 4120 HeapStats initialHeapSize; | |
| 4121 clearOutOldGarbage(&initialHeapSize); | |
| 4122 OffHeapInt::s_destructorCalls = 0; | |
| 4123 | |
| 4124 Map map; | |
| 4125 Map* map2 = new Map(); | |
| 4126 Persistent<Map> map3(new Map()); | |
| 4127 Persistent<IntWrapper> livingInt(IntWrapper::create(42)); | |
| 4128 map.add(PairWithWeakHandling(IntWrapper::create(0), IntWrapper::create(1)), OffHeapInt::create(1001)); | |
| 4129 map2->add(PairWithWeakHandling(IntWrapper::create(2), IntWrapper::create(3)) , OffHeapInt::create(1002)); | |
| 4130 map3->add(PairWithWeakHandling(IntWrapper::create(4), IntWrapper::create(5)) , OffHeapInt::create(1003)); | |
| 4131 EXPECT_EQ(0, OffHeapInt::s_destructorCalls); | |
| 4132 | |
| 4133 Heap::collectGarbage(ThreadState::HeapPointersOnStack); | |
| 4134 typedef typename Map::iterator Iterator; | |
| 4135 // The first map is on-stack, so its backing store must be referenced from | |
| 4136 // the stack. That makes the weak references strong. | |
| 4137 Iterator i = map.begin(); | |
| 4138 EXPECT_EQ(0, i->key.first->value()); | |
| 4139 EXPECT_EQ(1, i->key.second->value()); | |
| 4140 EXPECT_EQ(1001, i->value->value()); | |
| 4141 // The second map is pointed to from the stack, so it's referenced, but the | |
| 4142 // weak processing may have taken place. | |
| 4143 if (map2->size()) { | |
| 4144 Iterator i2 = map2->begin(); | |
| 4145 EXPECT_EQ(2, i2->key.first->value()); | |
| 4146 EXPECT_EQ(3, i2->key.second->value()); | |
| 4147 EXPECT_EQ(1002, i2->value->value()); | |
| 4148 } | |
| 4149 // The third map is pointed to from a persistent, so it's referenced, but | |
| 4150 // the weak processing may have taken place. | |
| 4151 if (map3->size()) { | |
| 4152 Iterator i3 = map3->begin(); | |
| 4153 EXPECT_EQ(4, i3->key.first->value()); | |
| 4154 EXPECT_EQ(5, i3->key.second->value()); | |
| 4155 EXPECT_EQ(1003, i3->value->value()); | |
| 4156 } | |
| 4157 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | |
| 4158 // No looking at the map and map2 now, they are busted by our lying about th e heap pointers on the stack! | |
| 4159 if (map3->size()) { | |
|
Mads Ager (chromium)
2014/06/06 09:59:16
Same here. This should be reduced to the EXPECT_EQ
Erik Corry
2014/06/10 11:21:18
Done.
| |
| 4160 Iterator i3 = map3->begin(); | |
| 4161 EXPECT_EQ(4, i3->key.first->value()); | |
| 4162 EXPECT_EQ(5, i3->key.second->value()); | |
| 4163 EXPECT_EQ(1003, i3->value->value()); | |
| 4164 EXPECT_EQ(2, OffHeapInt::s_destructorCalls); | |
| 4165 } else { | |
| 4166 EXPECT_EQ(3, OffHeapInt::s_destructorCalls); | |
| 4167 } | |
| 4168 map3->clear(); | |
| 4169 | |
| 4170 EXPECT_EQ(3, OffHeapInt::s_destructorCalls); | |
| 4171 OffHeapInt::s_destructorCalls = 0; | |
| 4172 | |
| 4173 map3->add(PairWithWeakHandling(IntWrapper::create(103), livingInt), OffHeapI nt::create(2000)); | |
| 4174 map3->add(PairWithWeakHandling(livingInt, IntWrapper::create(103)), OffHeapI nt::create(2001)); // This one gets zapped at GC time because nothing holds the 103 alive. | |
| 4175 map3->add(PairWithWeakHandling(IntWrapper::create(103), IntWrapper::create(1 03)), OffHeapInt::create(2002)); // This one gets zapped too. | |
| 4176 RefPtr<OffHeapInt> dupeInt(OffHeapInt::create(2003)); | |
| 4177 map3->add(PairWithWeakHandling(livingInt, livingInt), dupeInt); | |
| 4178 map3->add(PairWithWeakHandling(livingInt, livingInt), dupeInt); // This one is identical to the previous and doesn't add anything. | |
| 4179 dupeInt.clear(); | |
| 4180 | |
| 4181 EXPECT_EQ(0, OffHeapInt::s_destructorCalls); | |
| 4182 EXPECT_EQ(4u, map3->size()); | |
| 4183 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | |
| 4184 EXPECT_EQ(2, OffHeapInt::s_destructorCalls); | |
| 4185 EXPECT_EQ(2u, map3->size()); | |
| 4186 Iterator i3 = map3->begin(); | |
| 4187 EXPECT_TRUE(i3->key.first->value() == 103 || i3->key.first == livingInt); | |
| 4188 EXPECT_EQ(livingInt, i3->key.second); | |
| 4189 ++i3; | |
| 4190 EXPECT_TRUE(i3->key.first->value() == 103 || i3->key.first == livingInt); | |
| 4191 EXPECT_EQ(livingInt, i3->key.second); | |
| 4192 } | |
| 4193 | |
| 4194 TEST(HeapTest, MapWithCustomWeaknessHandling2) | |
| 4195 { | |
| 4196 typedef HeapHashMap<RefPtr<OffHeapInt>, PairWithWeakHandling> Map; | |
| 4197 HeapStats initialHeapSize; | |
| 4198 clearOutOldGarbage(&initialHeapSize); | |
| 4199 OffHeapInt::s_destructorCalls = 0; | |
| 4200 | |
| 4201 Map map; | |
| 4202 Map* map2 = new Map(); | |
| 4203 Persistent<Map> map3(new Map()); | |
| 4204 Persistent<IntWrapper> livingInt(IntWrapper::create(42)); | |
| 4205 map.add(OffHeapInt::create(1001), PairWithWeakHandling(IntWrapper::create(0) , IntWrapper::create(1))); | |
| 4206 map2->add(OffHeapInt::create(1002), PairWithWeakHandling(IntWrapper::create( 2), IntWrapper::create(3))); | |
| 4207 map3->add(OffHeapInt::create(1003), PairWithWeakHandling(IntWrapper::create( 4), IntWrapper::create(5))); | |
| 4208 EXPECT_EQ(0, OffHeapInt::s_destructorCalls); | |
| 4209 | |
| 4210 Heap::collectGarbage(ThreadState::HeapPointersOnStack); | |
| 4211 typedef typename Map::iterator Iterator; | |
| 4212 // The first map is on-stack, so its backing store must be referenced from | |
| 4213 // the stack. That makes the weak references strong. | |
| 4214 Iterator i = map.begin(); | |
| 4215 EXPECT_EQ(0, i->value.first->value()); | |
| 4216 EXPECT_EQ(1, i->value.second->value()); | |
| 4217 EXPECT_EQ(1001, i->key->value()); | |
| 4218 // The second map is pointed to from the stack, so it's referenced, but the | |
| 4219 // weak processing may have taken place. | |
| 4220 if (map2->size()) { | |
| 4221 Iterator i2 = map2->begin(); | |
| 4222 EXPECT_EQ(2, i2->value.first->value()); | |
| 4223 EXPECT_EQ(3, i2->value.second->value()); | |
| 4224 EXPECT_EQ(1002, i2->key->value()); | |
| 4225 } | |
| 4226 // The third map is pointed to from a persistent, so it's referenced, but | |
| 4227 // the weak processing may have taken place. | |
| 4228 if (map3->size()) { | |
| 4229 Iterator i3 = map3->begin(); | |
| 4230 EXPECT_EQ(4, i3->value.first->value()); | |
| 4231 EXPECT_EQ(5, i3->value.second->value()); | |
| 4232 EXPECT_EQ(1003, i3->key->value()); | |
| 4233 } | |
| 4234 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | |
| 4235 // No looking at the map and map2 now, they are busted by our lying about th e heap pointers on the stack! | |
| 4236 if (map3->size()) { | |
|
Mads Ager (chromium)
2014/06/06 09:59:17
This one too.
Erik Corry
2014/06/10 11:21:18
Done.
| |
| 4237 Iterator i3 = map3->begin(); | |
| 4238 EXPECT_EQ(4, i3->value.first->value()); | |
| 4239 EXPECT_EQ(5, i3->value.second->value()); | |
| 4240 EXPECT_EQ(1003, i3->key->value()); | |
| 4241 EXPECT_EQ(2, OffHeapInt::s_destructorCalls); | |
| 4242 } else { | |
| 4243 EXPECT_EQ(3, OffHeapInt::s_destructorCalls); | |
| 4244 } | |
| 4245 map3->clear(); | |
| 4246 | |
| 4247 EXPECT_EQ(3, OffHeapInt::s_destructorCalls); | |
| 4248 OffHeapInt::s_destructorCalls = 0; | |
| 4249 | |
| 4250 map3->add(OffHeapInt::create(2000), PairWithWeakHandling(IntWrapper::create( 103), livingInt)); | |
| 4251 map3->add(OffHeapInt::create(2001), PairWithWeakHandling(livingInt, IntWrapp er::create(103))); // This one gets zapped at GC time because nothing holds the 103 alive. | |
| 4252 map3->add(OffHeapInt::create(2002), PairWithWeakHandling(IntWrapper::create( 103), IntWrapper::create(103))); // This one gets zapped too. | |
| 4253 RefPtr<OffHeapInt> dupeInt(OffHeapInt::create(2003)); | |
| 4254 map3->add(dupeInt, PairWithWeakHandling(livingInt, livingInt)); | |
| 4255 map3->add(dupeInt, PairWithWeakHandling(livingInt, livingInt)); // This one is identical to the previous and doesn't add anything. | |
| 4256 dupeInt.clear(); | |
| 4257 | |
| 4258 EXPECT_EQ(0, OffHeapInt::s_destructorCalls); | |
| 4259 EXPECT_EQ(4u, map3->size()); | |
| 4260 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | |
| 4261 EXPECT_EQ(2, OffHeapInt::s_destructorCalls); | |
| 4262 EXPECT_EQ(2u, map3->size()); | |
| 4263 Iterator i3 = map3->begin(); | |
| 4264 EXPECT_TRUE(i3->value.first->value() == 103 || i3->value.first == livingInt) ; | |
| 4265 EXPECT_EQ(livingInt, i3->value.second); | |
| 4266 ++i3; | |
| 4267 EXPECT_TRUE(i3->value.first->value() == 103 || i3->value.first == livingInt) ; | |
| 4268 EXPECT_EQ(livingInt, i3->value.second); | |
| 4269 } | |
| 4270 | |
| 3981 } // WebCore namespace | 4271 } // WebCore namespace |
| OLD | NEW |