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; |
| 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 { |
| 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 TEST(HeapTest, TypesWithCustomWeaknessHandling) |
| 4057 { |
| 4058 HeapHashSet<PairWithWeakHandling> set; |
| 4059 HeapHashSet<PairWithWeakHandling>* set2 = new HeapHashSet<PairWithWeakHandli
ng>(); |
| 4060 Persistent<HeapHashSet<PairWithWeakHandling> > set3(new HeapHashSet<PairWith
WeakHandling>()); |
| 4061 Persistent<IntWrapper> livingInt(IntWrapper::create(42)); |
| 4062 set.add(PairWithWeakHandling(IntWrapper::create(0), IntWrapper::create(1))); |
| 4063 set2->add(PairWithWeakHandling(IntWrapper::create(2), IntWrapper::create(3))
); |
| 4064 set3->add(PairWithWeakHandling(IntWrapper::create(4), IntWrapper::create(5))
); |
| 4065 Heap::collectGarbage(ThreadState::HeapPointersOnStack); |
| 4066 typedef HeapHashSet<PairWithWeakHandling>::iterator Iterator; |
| 4067 // The first set is on-stack, so its backing store must be referenced from |
| 4068 // the stack. That makes the weak references strong. |
| 4069 Iterator i = set.begin(); |
| 4070 EXPECT_EQ(0, i->first->value()); |
| 4071 EXPECT_EQ(1, i->second->value()); |
| 4072 // The second set is pointed to from the stack, so it's referenced, but the |
| 4073 // weak processing may have taken place. |
| 4074 if (set2->size()) { |
| 4075 Iterator i2 = set2->begin(); |
| 4076 EXPECT_EQ(2, i2->first->value()); |
| 4077 EXPECT_EQ(3, i2->second->value()); |
| 4078 } |
| 4079 // The second set is pointed to from a persistent, so it's referenced, but |
| 4080 // the weak processing may have taken place. |
| 4081 if (set3->size()) { |
| 4082 Iterator i3 = set3->begin(); |
| 4083 EXPECT_EQ(4, i3->first->value()); |
| 4084 EXPECT_EQ(5, i3->second->value()); |
| 4085 } |
| 4086 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 4087 // No looking at the set and set2 now, they are busted by our lying about th
e heap pointers on the stack! |
| 4088 if (set3->size()) { |
| 4089 Iterator i3 = set3->begin(); |
| 4090 EXPECT_EQ(4, i3->first->value()); |
| 4091 EXPECT_EQ(5, i3->second->value()); |
| 4092 } |
| 4093 set3->clear(); |
| 4094 set3->add(PairWithWeakHandling(IntWrapper::create(103), livingInt)); |
| 4095 set3->add(PairWithWeakHandling(livingInt, IntWrapper::create(103))); // This
one gets zapped at GC time because nothing holds the 103 alive. |
| 4096 set3->add(PairWithWeakHandling(IntWrapper::create(103), IntWrapper::create(1
03))); // This one gets zapped too. |
| 4097 set3->add(PairWithWeakHandling(livingInt, livingInt)); |
| 4098 set3->add(PairWithWeakHandling(livingInt, livingInt)); // This one is identi
cal to the previous and doesn't add anything. |
| 4099 EXPECT_EQ(4u, set3->size()); |
| 4100 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 4101 EXPECT_EQ(2u, set3->size()); |
| 4102 Iterator i3 = set3->begin(); |
| 4103 EXPECT_TRUE(i3->first->value() == 103 || i3->first == livingInt); |
| 4104 EXPECT_EQ(livingInt, i3->second); |
| 4105 ++i3; |
| 4106 EXPECT_TRUE(i3->first->value() == 103 || i3->first == livingInt); |
| 4107 EXPECT_EQ(livingInt, i3->second); |
| 4108 } |
| 4109 |
3981 } // WebCore namespace | 4110 } // WebCore namespace |
OLD | NEW |