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 |