| 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 : public GarbageCollectedFinalized<IntWrapper> { |
| 48 public: |
| 49 static IntWrapper* create(int x) |
| 50 { |
| 51 return new IntWrapper(x); |
| 52 } |
| 53 |
| 54 virtual ~IntWrapper() |
| 55 { |
| 56 ++s_destructorCalls; |
| 57 } |
| 58 |
| 59 static int s_destructorCalls; |
| 60 static void trace(Visitor*) { } |
| 61 |
| 62 int value() const { return m_x; } |
| 63 |
| 64 bool operator==(const IntWrapper& other) const { return other.value() == val
ue(); } |
| 65 |
| 66 unsigned hash() { return IntHash<int>::hash(m_x); } |
| 67 |
| 68 protected: |
| 69 IntWrapper(int x) : m_x(x) { } |
| 70 |
| 71 private: |
| 72 IntWrapper(); |
| 73 int m_x; |
| 74 }; |
| 75 |
| 76 USED_FROM_MULTIPLE_THREADS(IntWrapper); |
| 77 |
| 47 class ThreadMarker { | 78 class ThreadMarker { |
| 48 public: | 79 public: |
| 49 ThreadMarker() : m_creatingThread(reinterpret_cast<ThreadState*>(0)), m_num(
0) { } | 80 ThreadMarker() : m_creatingThread(reinterpret_cast<ThreadState*>(0)), m_num(
0) { } |
| 50 ThreadMarker(unsigned i) : m_creatingThread(ThreadState::current()), m_num(i
) { } | 81 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) { } | 82 ThreadMarker(WTF::HashTableDeletedValueType deleted) : m_creatingThread(rein
terpret_cast<ThreadState*>(-1)), m_num(0) { } |
| 52 ~ThreadMarker() | 83 ~ThreadMarker() |
| 53 { | 84 { |
| 54 EXPECT_TRUE((m_creatingThread == ThreadState::current()) | 85 EXPECT_TRUE((m_creatingThread == ThreadState::current()) |
| 55 || (m_creatingThread == reinterpret_cast<ThreadState*>(0)) | 86 || (m_creatingThread == reinterpret_cast<ThreadState*>(0)) |
| 56 || (m_creatingThread == reinterpret_cast<ThreadState*>(-1))); | 87 || (m_creatingThread == reinterpret_cast<ThreadState*>(-1))); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 68 } | 99 } |
| 69 | 100 |
| 70 static bool equal(const ThreadMarker& a, const ThreadMarker& b) | 101 static bool equal(const ThreadMarker& a, const ThreadMarker& b) |
| 71 { | 102 { |
| 72 return a == b; | 103 return a == b; |
| 73 } | 104 } |
| 74 | 105 |
| 75 static const bool safeToCompareToEmptyOrDeleted = false; | 106 static const bool safeToCompareToEmptyOrDeleted = false; |
| 76 }; | 107 }; |
| 77 | 108 |
| 109 typedef std::pair<Member<IntWrapper>, WeakMember<IntWrapper> > StrongWeakPair; |
| 110 |
| 111 struct PairWithWeakHandling : public StrongWeakPair { |
| 112 ALLOW_ONLY_INLINE_ALLOCATION(); |
| 113 |
| 114 public: |
| 115 // Regular constructor. |
| 116 PairWithWeakHandling(IntWrapper* one, IntWrapper* two) |
| 117 : StrongWeakPair(one, two) |
| 118 { |
| 119 ASSERT(one); // We use null first field to indicate empty slots in the h
ash table. |
| 120 } |
| 121 |
| 122 // The HashTable (via the HashTrait) calls this constructor with a |
| 123 // placement new to mark slots in the hash table as being deleted. We will |
| 124 // never call trace or the destructor on these slots. We mark ourselves dele
ted |
| 125 // with a pointer to -1 in the first field. |
| 126 PairWithWeakHandling(WTF::HashTableDeletedValueType) |
| 127 : StrongWeakPair(reinterpret_cast<IntWrapper*>(-1), nullptr) |
| 128 { |
| 129 } |
| 130 |
| 131 // Used by the HashTable (via the HashTrait) to skip deleted slots in the |
| 132 // table. Recognizes objects that were 'constructed' using the above |
| 133 // constructor. |
| 134 bool isHashTableDeletedValue() const { return first == reinterpret_cast<IntW
rapper*>(-1); } |
| 135 |
| 136 // Since we don't allocate independent objects of this type, we don't need |
| 137 // a regular trace method. Instead, we use a traceInCollection method. |
| 138 void traceInCollection(Visitor* visitor, ShouldWeakPointersBeMarkedStrongly
strongify) |
| 139 { |
| 140 visitor->trace(first); |
| 141 visitor->traceInCollection(second, strongify); |
| 142 } |
| 143 // The traceInCollection may not trace the weak members, so it is vital |
| 144 // that shouldRemoveFromCollection checks them so the entry can be removed |
| 145 // from the collection (otherwise it would contain a dangling pointer). |
| 146 bool shouldRemoveFromCollection(Visitor* visitor) |
| 147 { |
| 148 return !visitor->isAlive(second); |
| 149 } |
| 150 }; |
| 151 |
| 78 } | 152 } |
| 79 | 153 |
| 80 namespace WTF { | 154 namespace WTF { |
| 81 | 155 |
| 82 template<typename T> struct DefaultHash; | 156 template<typename T> struct DefaultHash; |
| 83 template<> struct DefaultHash<WebCore::ThreadMarker> { | 157 template<> struct DefaultHash<WebCore::ThreadMarker> { |
| 84 typedef WebCore::ThreadMarkerHash Hash; | 158 typedef WebCore::ThreadMarkerHash Hash; |
| 85 }; | 159 }; |
| 86 | 160 |
| 87 // ThreadMarkerHash is the default hash for ThreadMarker | 161 // ThreadMarkerHash is the default hash for ThreadMarker |
| 88 template<> struct HashTraits<WebCore::ThreadMarker> : GenericHashTraits<WebCore:
:ThreadMarker> { | 162 template<> struct HashTraits<WebCore::ThreadMarker> : GenericHashTraits<WebCore:
:ThreadMarker> { |
| 89 static const bool emptyValueIsZero = true; | 163 static const bool emptyValueIsZero = true; |
| 90 static void constructDeletedValue(WebCore::ThreadMarker& slot) { new (NotNul
l, &slot) WebCore::ThreadMarker(HashTableDeletedValue); } | 164 static void constructDeletedValue(WebCore::ThreadMarker& slot) { new (NotNul
l, &slot) WebCore::ThreadMarker(HashTableDeletedValue); } |
| 91 static bool isDeletedValue(const WebCore::ThreadMarker& slot) { return slot.
isHashTableDeletedValue(); } | 165 static bool isDeletedValue(const WebCore::ThreadMarker& slot) { return slot.
isHashTableDeletedValue(); } |
| 92 }; | 166 }; |
| 93 | 167 |
| 168 // The hash algorithm for our custom pair class is just the standard double |
| 169 // hash for pairs. Note that this means you can't mutate either of the parts of |
| 170 // the pair while they are in the hash table, as that would change their hash |
| 171 // code and thus their preferred placement in the table. |
| 172 template<> struct DefaultHash<WebCore::PairWithWeakHandling> { |
| 173 typedef PairHash<WebCore::Member<WebCore::IntWrapper>, WebCore::WeakMember<W
ebCore::IntWrapper> > Hash; |
| 174 }; |
| 175 |
| 176 // Custom traits for the pair. These are weakness handling traits, which means |
| 177 // PairWithWeakHandling must implement the traceInCollection and |
| 178 // shouldRemoveFromCollection methods. In addition, these traits are concerned |
| 179 // with the two magic values for the object, that represent empty and deleted |
| 180 // slots in the hash table. The SimpleClassHashTraits allow empty slots in the |
| 181 // table to be initialzed with memset to zero, and we use -1 in the first part |
| 182 // of the pair to represent deleted slots. |
| 183 template<> struct HashTraits<WebCore::PairWithWeakHandling> : WebCore::WeakHandl
ingHashTraits<WebCore::PairWithWeakHandling> { |
| 184 static const bool needsDestruction = false; |
| 185 static const bool hasIsEmptyValueFunction = true; |
| 186 static bool isEmptyValue(const WebCore::PairWithWeakHandling& value) { retur
n !value.first; } |
| 187 static void constructDeletedValue(WebCore::PairWithWeakHandling& slot) { new
(NotNull, &slot) WebCore::PairWithWeakHandling(HashTableDeletedValue); } |
| 188 static bool isDeletedValue(const WebCore::PairWithWeakHandling& value) { ret
urn value.isHashTableDeletedValue(); } |
| 189 }; |
| 190 |
| 94 } | 191 } |
| 95 | 192 |
| 96 namespace WebCore { | 193 namespace WebCore { |
| 97 | 194 |
| 98 class TestGCScope { | 195 class TestGCScope { |
| 99 public: | 196 public: |
| 100 explicit TestGCScope(ThreadState::StackState state) | 197 explicit TestGCScope(ThreadState::StackState state) |
| 101 : m_state(ThreadState::current()) | 198 : m_state(ThreadState::current()) |
| 102 , m_safePointScope(state) | 199 , m_safePointScope(state) |
| 103 , m_parkedAllThreads(false) | 200 , m_parkedAllThreads(false) |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 286 while (true) { | 383 while (true) { |
| 287 getHeapStats(heapStats); | 384 getHeapStats(heapStats); |
| 288 size_t used = heapStats->totalObjectSpace(); | 385 size_t used = heapStats->totalObjectSpace(); |
| 289 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | 386 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 290 getHeapStats(heapStats); | 387 getHeapStats(heapStats); |
| 291 if (heapStats->totalObjectSpace() >= used) | 388 if (heapStats->totalObjectSpace() >= used) |
| 292 break; | 389 break; |
| 293 } | 390 } |
| 294 } | 391 } |
| 295 | 392 |
| 296 class IntWrapper : public GarbageCollectedFinalized<IntWrapper> { | |
| 297 public: | |
| 298 static IntWrapper* create(int x) | |
| 299 { | |
| 300 return new IntWrapper(x); | |
| 301 } | |
| 302 | |
| 303 virtual ~IntWrapper() | |
| 304 { | |
| 305 ++s_destructorCalls; | |
| 306 } | |
| 307 | |
| 308 static int s_destructorCalls; | |
| 309 static void trace(Visitor*) { } | |
| 310 | |
| 311 int value() const { return m_x; } | |
| 312 | |
| 313 bool operator==(const IntWrapper& other) const { return other.value() == val
ue(); } | |
| 314 | |
| 315 unsigned hash() { return IntHash<int>::hash(m_x); } | |
| 316 | |
| 317 protected: | |
| 318 IntWrapper(int x) : m_x(x) { } | |
| 319 | |
| 320 private: | |
| 321 IntWrapper(); | |
| 322 int m_x; | |
| 323 }; | |
| 324 | |
| 325 class OffHeapInt : public RefCounted<OffHeapInt> { | 393 class OffHeapInt : public RefCounted<OffHeapInt> { |
| 326 public: | 394 public: |
| 327 static RefPtr<OffHeapInt> create(int x) | 395 static RefPtr<OffHeapInt> create(int x) |
| 328 { | 396 { |
| 329 return adoptRef(new OffHeapInt(x)); | 397 return adoptRef(new OffHeapInt(x)); |
| 330 } | 398 } |
| 331 | 399 |
| 332 virtual ~OffHeapInt() | 400 virtual ~OffHeapInt() |
| 333 { | 401 { |
| 334 ++s_destructorCalls; | 402 ++s_destructorCalls; |
| 335 } | 403 } |
| 336 | 404 |
| 337 static int s_destructorCalls; | 405 static int s_destructorCalls; |
| 338 | 406 |
| 339 int value() const { return m_x; } | 407 int value() const { return m_x; } |
| 340 | 408 |
| 341 bool operator==(const OffHeapInt& other) const { return other.value() == val
ue(); } | 409 bool operator==(const OffHeapInt& other) const { return other.value() == val
ue(); } |
| 342 | 410 |
| 343 unsigned hash() { return IntHash<int>::hash(m_x); } | 411 unsigned hash() { return IntHash<int>::hash(m_x); } |
| 344 | 412 |
| 345 protected: | 413 protected: |
| 346 OffHeapInt(int x) : m_x(x) { } | 414 OffHeapInt(int x) : m_x(x) { } |
| 347 | 415 |
| 348 private: | 416 private: |
| 349 OffHeapInt(); | 417 OffHeapInt(); |
| 350 int m_x; | 418 int m_x; |
| 351 }; | 419 }; |
| 352 | 420 |
| 353 USED_FROM_MULTIPLE_THREADS(IntWrapper); | |
| 354 | |
| 355 int IntWrapper::s_destructorCalls = 0; | 421 int IntWrapper::s_destructorCalls = 0; |
| 356 int OffHeapInt::s_destructorCalls = 0; | 422 int OffHeapInt::s_destructorCalls = 0; |
| 357 | 423 |
| 358 class ThreadedTesterBase { | 424 class ThreadedTesterBase { |
| 359 protected: | 425 protected: |
| 360 static void test(ThreadedTesterBase* tester) | 426 static void test(ThreadedTesterBase* tester) |
| 361 { | 427 { |
| 362 for (int i = 0; i < numberOfThreads; i++) | 428 for (int i = 0; i < numberOfThreads; i++) |
| 363 createThread(&threadFunc, tester, "testing thread"); | 429 createThread(&threadFunc, tester, "testing thread"); |
| 364 while (tester->m_threadsToFinish) { | 430 while (tester->m_threadsToFinish) { |
| (...skipping 2944 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3309 } | 3375 } |
| 3310 // This forces a GC without stack scanning which results in the objects | 3376 // This forces a GC without stack scanning which results in the objects |
| 3311 // being collected. This will also rebuild the above mentioned freelists, | 3377 // 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. | 3378 // however we don't rely on that below since we don't have any allocations. |
| 3313 clearOutOldGarbage(&initialHeapStats); | 3379 clearOutOldGarbage(&initialHeapStats); |
| 3314 { | 3380 { |
| 3315 TestGCScope scope(ThreadState::HeapPointersOnStack); | 3381 TestGCScope scope(ThreadState::HeapPointersOnStack); |
| 3316 EXPECT_TRUE(scope.allThreadsParked()); | 3382 EXPECT_TRUE(scope.allThreadsParked()); |
| 3317 Heap::makeConsistentForGC(); | 3383 Heap::makeConsistentForGC(); |
| 3318 for (size_t i = 0; i < objectAddresses.size(); i++) { | 3384 for (size_t i = 0; i < objectAddresses.size(); i++) { |
| 3319 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, objectAddresses[i])
); | 3385 // We would like to assert that checkAndMarkPointer returned false |
| 3320 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, endAddresses[i])); | 3386 // here because the pointers no longer point into a valid object |
| 3387 // (it's been freed by the GCs. But checkAndMarkPointer will return |
| 3388 // true for any pointer that points into a heap page, regardless of |
| 3389 // whether it points at a valid object (this ensures the |
| 3390 // correctness of the page-based on-heap address caches), so we |
| 3391 // can't make that assert. |
| 3392 Heap::checkAndMarkPointer(&visitor, objectAddresses[i]); |
| 3393 Heap::checkAndMarkPointer(&visitor, endAddresses[i]); |
| 3321 } | 3394 } |
| 3322 EXPECT_EQ(0ul, visitor.count()); | 3395 EXPECT_EQ(0ul, visitor.count()); |
| 3323 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectAddress)); | 3396 Heap::checkAndMarkPointer(&visitor, largeObjectAddress); |
| 3324 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress))
; | 3397 Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress); |
| 3325 EXPECT_EQ(0ul, visitor.count()); | 3398 EXPECT_EQ(0ul, visitor.count()); |
| 3326 } | 3399 } |
| 3327 // This round of GC is important to make sure that the object start | 3400 // 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. | 3401 // bitmap are cleared out and that the free lists are rebuild. |
| 3329 clearOutOldGarbage(&initialHeapStats); | 3402 clearOutOldGarbage(&initialHeapStats); |
| 3330 } | 3403 } |
| 3331 | 3404 |
| 3332 TEST(HeapTest, VisitOffHeapCollections) | 3405 TEST(HeapTest, VisitOffHeapCollections) |
| 3333 { | 3406 { |
| 3334 HeapStats initialHeapStats; | 3407 HeapStats initialHeapStats; |
| (...skipping 636 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3971 | 4044 |
| 3972 // class SimpleObject : public GarbageCollected<SimpleObject> {}; | 4045 // class SimpleObject : public GarbageCollected<SimpleObject> {}; |
| 3973 EXPECT_FALSE(NeedsAdjustAndMark<SimpleObject>::value); | 4046 EXPECT_FALSE(NeedsAdjustAndMark<SimpleObject>::value); |
| 3974 EXPECT_FALSE(NeedsAdjustAndMark<const SimpleObject>::value); | 4047 EXPECT_FALSE(NeedsAdjustAndMark<const SimpleObject>::value); |
| 3975 | 4048 |
| 3976 // class UseMixin : public SimpleObject, public Mixin {}; | 4049 // class UseMixin : public SimpleObject, public Mixin {}; |
| 3977 EXPECT_FALSE(NeedsAdjustAndMark<UseMixin>::value); | 4050 EXPECT_FALSE(NeedsAdjustAndMark<UseMixin>::value); |
| 3978 EXPECT_FALSE(NeedsAdjustAndMark<const UseMixin>::value); | 4051 EXPECT_FALSE(NeedsAdjustAndMark<const UseMixin>::value); |
| 3979 } | 4052 } |
| 3980 | 4053 |
| 4054 template<typename Set> |
| 4055 void setWithCustomWeaknessHandling() |
| 4056 { |
| 4057 typedef typename Set::iterator Iterator; |
| 4058 Persistent<IntWrapper> livingInt(IntWrapper::create(42)); |
| 4059 Persistent<Set> set1(new Set()); |
| 4060 { |
| 4061 Set set2; |
| 4062 Set* set3 = new Set(); |
| 4063 set2.add(PairWithWeakHandling(IntWrapper::create(0), IntWrapper::create(
1))); |
| 4064 set3->add(PairWithWeakHandling(IntWrapper::create(2), IntWrapper::create
(3))); |
| 4065 set1->add(PairWithWeakHandling(IntWrapper::create(4), IntWrapper::create
(5))); |
| 4066 Heap::collectGarbage(ThreadState::HeapPointersOnStack); |
| 4067 // The first set is pointed to from a persistent, so it's referenced, bu
t |
| 4068 // the weak processing may have taken place. |
| 4069 if (set1->size()) { |
| 4070 Iterator i1 = set1->begin(); |
| 4071 EXPECT_EQ(4, i1->first->value()); |
| 4072 EXPECT_EQ(5, i1->second->value()); |
| 4073 } |
| 4074 // The second set is on-stack, so its backing store must be referenced f
rom |
| 4075 // the stack. That makes the weak references strong. |
| 4076 Iterator i2 = set2.begin(); |
| 4077 EXPECT_EQ(0, i2->first->value()); |
| 4078 EXPECT_EQ(1, i2->second->value()); |
| 4079 // The third set is pointed to from the stack, so it's referenced, but t
he |
| 4080 // weak processing may have taken place. |
| 4081 if (set3->size()) { |
| 4082 Iterator i3 = set3->begin(); |
| 4083 EXPECT_EQ(2, i3->first->value()); |
| 4084 EXPECT_EQ(3, i3->second->value()); |
| 4085 } |
| 4086 } |
| 4087 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 4088 EXPECT_EQ(0u, set1->size()); |
| 4089 set1->add(PairWithWeakHandling(IntWrapper::create(103), livingInt)); |
| 4090 set1->add(PairWithWeakHandling(livingInt, IntWrapper::create(103))); // This
one gets zapped at GC time because nothing holds the 103 alive. |
| 4091 set1->add(PairWithWeakHandling(IntWrapper::create(103), IntWrapper::create(1
03))); // This one gets zapped too. |
| 4092 set1->add(PairWithWeakHandling(livingInt, livingInt)); |
| 4093 set1->add(PairWithWeakHandling(livingInt, livingInt)); // This one is identi
cal to the previous and doesn't add anything. |
| 4094 EXPECT_EQ(4u, set1->size()); |
| 4095 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 4096 EXPECT_EQ(2u, set1->size()); |
| 4097 Iterator i1 = set1->begin(); |
| 4098 EXPECT_TRUE(i1->first->value() == 103 || i1->first == livingInt); |
| 4099 EXPECT_EQ(livingInt, i1->second); |
| 4100 ++i1; |
| 4101 EXPECT_TRUE(i1->first->value() == 103 || i1->first == livingInt); |
| 4102 EXPECT_EQ(livingInt, i1->second); |
| 4103 } |
| 4104 |
| 4105 TEST(HeapTest, SetWithCustomWeaknessHandling) |
| 4106 { |
| 4107 setWithCustomWeaknessHandling<HeapHashSet<PairWithWeakHandling> >(); |
| 4108 setWithCustomWeaknessHandling<HeapLinkedHashSet<PairWithWeakHandling> >(); |
| 4109 } |
| 4110 |
| 4111 TEST(HeapTest, MapWithCustomWeaknessHandling) |
| 4112 { |
| 4113 typedef HeapHashMap<PairWithWeakHandling, RefPtr<OffHeapInt> > Map; |
| 4114 typedef Map::iterator Iterator; |
| 4115 HeapStats initialHeapSize; |
| 4116 clearOutOldGarbage(&initialHeapSize); |
| 4117 OffHeapInt::s_destructorCalls = 0; |
| 4118 |
| 4119 Persistent<Map> map1(new Map()); |
| 4120 Persistent<IntWrapper> livingInt(IntWrapper::create(42)); |
| 4121 { |
| 4122 Map map2; |
| 4123 Map* map3 = new Map(); |
| 4124 map2.add(PairWithWeakHandling(IntWrapper::create(0), IntWrapper::create(
1)), OffHeapInt::create(1001)); |
| 4125 map3->add(PairWithWeakHandling(IntWrapper::create(2), IntWrapper::create
(3)), OffHeapInt::create(1002)); |
| 4126 map1->add(PairWithWeakHandling(IntWrapper::create(4), IntWrapper::create
(5)), OffHeapInt::create(1003)); |
| 4127 EXPECT_EQ(0, OffHeapInt::s_destructorCalls); |
| 4128 |
| 4129 Heap::collectGarbage(ThreadState::HeapPointersOnStack); |
| 4130 // The first map2 is pointed to from a persistent, so it's referenced, b
ut |
| 4131 // the weak processing may have taken place. |
| 4132 if (map1->size()) { |
| 4133 Iterator i1 = map1->begin(); |
| 4134 EXPECT_EQ(4, i1->key.first->value()); |
| 4135 EXPECT_EQ(5, i1->key.second->value()); |
| 4136 EXPECT_EQ(1003, i1->value->value()); |
| 4137 } |
| 4138 // The second map2 is on-stack, so its backing store must be referenced
from |
| 4139 // the stack. That makes the weak references strong. |
| 4140 Iterator i2 = map2.begin(); |
| 4141 EXPECT_EQ(0, i2->key.first->value()); |
| 4142 EXPECT_EQ(1, i2->key.second->value()); |
| 4143 EXPECT_EQ(1001, i2->value->value()); |
| 4144 // The third map2 is pointed to from the stack, so it's referenced, but
the |
| 4145 // weak processing may have taken place. |
| 4146 if (map3->size()) { |
| 4147 Iterator i3 = map3->begin(); |
| 4148 EXPECT_EQ(2, i3->key.first->value()); |
| 4149 EXPECT_EQ(3, i3->key.second->value()); |
| 4150 EXPECT_EQ(1002, i3->value->value()); |
| 4151 } |
| 4152 } |
| 4153 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 4154 |
| 4155 EXPECT_EQ(0u, map1->size()); |
| 4156 EXPECT_EQ(3, OffHeapInt::s_destructorCalls); |
| 4157 |
| 4158 OffHeapInt::s_destructorCalls = 0; |
| 4159 |
| 4160 map1->add(PairWithWeakHandling(IntWrapper::create(103), livingInt), OffHeapI
nt::create(2000)); |
| 4161 map1->add(PairWithWeakHandling(livingInt, IntWrapper::create(103)), OffHeapI
nt::create(2001)); // This one gets zapped at GC time because nothing holds the
103 alive. |
| 4162 map1->add(PairWithWeakHandling(IntWrapper::create(103), IntWrapper::create(1
03)), OffHeapInt::create(2002)); // This one gets zapped too. |
| 4163 RefPtr<OffHeapInt> dupeInt(OffHeapInt::create(2003)); |
| 4164 map1->add(PairWithWeakHandling(livingInt, livingInt), dupeInt); |
| 4165 map1->add(PairWithWeakHandling(livingInt, livingInt), dupeInt); // This one
is identical to the previous and doesn't add anything. |
| 4166 dupeInt.clear(); |
| 4167 |
| 4168 EXPECT_EQ(0, OffHeapInt::s_destructorCalls); |
| 4169 EXPECT_EQ(4u, map1->size()); |
| 4170 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 4171 EXPECT_EQ(2, OffHeapInt::s_destructorCalls); |
| 4172 EXPECT_EQ(2u, map1->size()); |
| 4173 Iterator i1 = map1->begin(); |
| 4174 EXPECT_TRUE(i1->key.first->value() == 103 || i1->key.first == livingInt); |
| 4175 EXPECT_EQ(livingInt, i1->key.second); |
| 4176 ++i1; |
| 4177 EXPECT_TRUE(i1->key.first->value() == 103 || i1->key.first == livingInt); |
| 4178 EXPECT_EQ(livingInt, i1->key.second); |
| 4179 } |
| 4180 |
| 4181 TEST(HeapTest, MapWithCustomWeaknessHandling2) |
| 4182 { |
| 4183 typedef HeapHashMap<RefPtr<OffHeapInt>, PairWithWeakHandling> Map; |
| 4184 typedef Map::iterator Iterator; |
| 4185 HeapStats initialHeapSize; |
| 4186 clearOutOldGarbage(&initialHeapSize); |
| 4187 OffHeapInt::s_destructorCalls = 0; |
| 4188 |
| 4189 Persistent<Map> map1(new Map()); |
| 4190 Persistent<IntWrapper> livingInt(IntWrapper::create(42)); |
| 4191 |
| 4192 { |
| 4193 Map map2; |
| 4194 Map* map3 = new Map(); |
| 4195 map2.add(OffHeapInt::create(1001), PairWithWeakHandling(IntWrapper::crea
te(0), IntWrapper::create(1))); |
| 4196 map3->add(OffHeapInt::create(1002), PairWithWeakHandling(IntWrapper::cre
ate(2), IntWrapper::create(3))); |
| 4197 map1->add(OffHeapInt::create(1003), PairWithWeakHandling(IntWrapper::cre
ate(4), IntWrapper::create(5))); |
| 4198 EXPECT_EQ(0, OffHeapInt::s_destructorCalls); |
| 4199 |
| 4200 Heap::collectGarbage(ThreadState::HeapPointersOnStack); |
| 4201 // The first map2 is pointed to from a persistent, so it's referenced, b
ut |
| 4202 // the weak processing may have taken place. |
| 4203 if (map1->size()) { |
| 4204 Iterator i1 = map1->begin(); |
| 4205 EXPECT_EQ(4, i1->value.first->value()); |
| 4206 EXPECT_EQ(5, i1->value.second->value()); |
| 4207 EXPECT_EQ(1003, i1->key->value()); |
| 4208 } |
| 4209 // The second map2 is on-stack, so its backing store must be referenced
from |
| 4210 // the stack. That makes the weak references strong. |
| 4211 Iterator i2 = map2.begin(); |
| 4212 EXPECT_EQ(0, i2->value.first->value()); |
| 4213 EXPECT_EQ(1, i2->value.second->value()); |
| 4214 EXPECT_EQ(1001, i2->key->value()); |
| 4215 // The third map2 is pointed to from the stack, so it's referenced, but
the |
| 4216 // weak processing may have taken place. |
| 4217 if (map3->size()) { |
| 4218 Iterator i3 = map3->begin(); |
| 4219 EXPECT_EQ(2, i3->value.first->value()); |
| 4220 EXPECT_EQ(3, i3->value.second->value()); |
| 4221 EXPECT_EQ(1002, i3->key->value()); |
| 4222 } |
| 4223 } |
| 4224 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 4225 |
| 4226 EXPECT_EQ(0u, map1->size()); |
| 4227 EXPECT_EQ(3, OffHeapInt::s_destructorCalls); |
| 4228 |
| 4229 OffHeapInt::s_destructorCalls = 0; |
| 4230 |
| 4231 map1->add(OffHeapInt::create(2000), PairWithWeakHandling(IntWrapper::create(
103), livingInt)); |
| 4232 map1->add(OffHeapInt::create(2001), PairWithWeakHandling(livingInt, IntWrapp
er::create(103))); // This one gets zapped at GC time because nothing holds the
103 alive. |
| 4233 map1->add(OffHeapInt::create(2002), PairWithWeakHandling(IntWrapper::create(
103), IntWrapper::create(103))); // This one gets zapped too. |
| 4234 RefPtr<OffHeapInt> dupeInt(OffHeapInt::create(2003)); |
| 4235 map1->add(dupeInt, PairWithWeakHandling(livingInt, livingInt)); |
| 4236 map1->add(dupeInt, PairWithWeakHandling(livingInt, livingInt)); // This one
is identical to the previous and doesn't add anything. |
| 4237 dupeInt.clear(); |
| 4238 |
| 4239 EXPECT_EQ(0, OffHeapInt::s_destructorCalls); |
| 4240 EXPECT_EQ(4u, map1->size()); |
| 4241 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 4242 EXPECT_EQ(2, OffHeapInt::s_destructorCalls); |
| 4243 EXPECT_EQ(2u, map1->size()); |
| 4244 Iterator i1 = map1->begin(); |
| 4245 EXPECT_TRUE(i1->value.first->value() == 103 || i1->value.first == livingInt)
; |
| 4246 EXPECT_EQ(livingInt, i1->value.second); |
| 4247 ++i1; |
| 4248 EXPECT_TRUE(i1->value.first->value() == 103 || i1->value.first == livingInt)
; |
| 4249 EXPECT_EQ(livingInt, i1->value.second); |
| 4250 } |
| 4251 |
| 3981 TEST(HeapTest, Bind) | 4252 TEST(HeapTest, Bind) |
| 3982 { | 4253 { |
| 3983 Closure closure = bind(&Bar::trace, Bar::create(), static_cast<Visitor*>(0))
; | 4254 Closure closure = bind(&Bar::trace, Bar::create(), static_cast<Visitor*>(0))
; |
| 3984 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | 4255 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 3985 // The closure should have a persistent handle to the Bar. | 4256 // The closure should have a persistent handle to the Bar. |
| 3986 EXPECT_EQ(1u, Bar::s_live); | 4257 EXPECT_EQ(1u, Bar::s_live); |
| 3987 | 4258 |
| 3988 Closure closure2 = bind(&Bar::trace, RawPtr<Bar>(Bar::create()), static_cast
<Visitor*>(0)); | 4259 Closure closure2 = bind(&Bar::trace, RawPtr<Bar>(Bar::create()), static_cast
<Visitor*>(0)); |
| 3989 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | 4260 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 3990 // The closure should have a persistent handle to the Bar. | 4261 // The closure should have a persistent handle to the Bar. |
| 3991 EXPECT_EQ(2u, Bar::s_live); | 4262 EXPECT_EQ(2u, Bar::s_live); |
| 3992 | 4263 |
| 3993 UseMixin::s_traceCount = 0; | 4264 UseMixin::s_traceCount = 0; |
| 3994 Mixin* mixin = UseMixin::create(); | 4265 Mixin* mixin = UseMixin::create(); |
| 3995 Closure mixinClosure = bind(&Mixin::trace, mixin, static_cast<Visitor*>(0)); | 4266 Closure mixinClosure = bind(&Mixin::trace, mixin, static_cast<Visitor*>(0)); |
| 3996 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | 4267 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 3997 // The closure should have a persistent handle to the mixin. | 4268 // The closure should have a persistent handle to the mixin. |
| 3998 EXPECT_EQ(1, UseMixin::s_traceCount); | 4269 EXPECT_EQ(1, UseMixin::s_traceCount); |
| 3999 } | 4270 } |
| 4000 | 4271 |
| 4001 } // WebCore namespace | 4272 } // WebCore namespace |
| OLD | NEW |