Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Unified Diff: Source/platform/heap/HeapTest.cpp

Issue 319593004: Oilpan:Allow custom handling of classes that contain weak pointers that are embedded in collections. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Feedback from Haraken Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/platform/heap/HeapTest.cpp
diff --git a/Source/platform/heap/HeapTest.cpp b/Source/platform/heap/HeapTest.cpp
index 5c93ea68e5a3d0207641dd5d8846a9288692f6cd..dd70f34a6569098d31437fc736bd9f4e70c6bda1 100644
--- a/Source/platform/heap/HeapTest.cpp
+++ b/Source/platform/heap/HeapTest.cpp
@@ -44,6 +44,8 @@
namespace WebCore {
+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.
+
class ThreadMarker {
public:
ThreadMarker() : m_creatingThread(reinterpret_cast<ThreadState*>(0)), m_num(0) { }
@@ -75,6 +77,49 @@ struct ThreadMarkerHash {
static const bool safeToCompareToEmptyOrDeleted = false;
};
+typedef std::pair<Member<IntWrapper>, WeakMember<IntWrapper> > StrongWeakPair;
+
+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
+ ALLOW_ONLY_INLINE_ALLOCATION();
+
+public:
+ // Regular constructor.
+ PairWithWeakHandling(IntWrapper* one, IntWrapper* two)
+ : StrongWeakPair(one, two)
+ {
+ ASSERT(one); // We use null first field to indicate empty slots in the hash table.
+ }
+
+ // The HashTable (via the HashTrait) calls this constructor with a
+ // placement new to mark slots in the hash table as being deleted. We will
+ // never call trace or the destructor on these slots. We mark ourselves deleted
+ // with a pointer to -1 in the first field.
+ PairWithWeakHandling(WTF::HashTableDeletedValueType)
+ : StrongWeakPair(reinterpret_cast<IntWrapper*>(-1), nullptr)
+ {
+ }
+
+ // Used by the HashTable (via the HashTrait) to skip deleted slots in the
+ // table. Recognizes objects that were 'constructed' using the above
+ // constructor.
+ bool isHashTableDeletedValue() const { return first == reinterpret_cast<IntWrapper*>(-1); }
+
+ // Since we don't allocate independent objects of this type, we don't need
+ // a regular trace method. Instead, we use a traceInCollection method.
+ void traceInCollection(Visitor* visitor, ShouldWeakPointersBeMarkedStrongly strongify)
+ {
+ visitor->trace(first);
+ visitor->traceInCollection(second, strongify);
+ }
+ // The traceInCollection may not trace the weak members, so it is vital
+ // that shouldRemoveFromCollection checks them so the entry can be removed
+ // from the collection (otherwise it would contain a dangling pointer).
+ bool shouldRemoveFromCollection(Visitor* visitor)
+ {
+ return !visitor->isAlive(second);
+ }
+};
+
}
namespace WTF {
@@ -91,6 +136,29 @@ template<> struct HashTraits<WebCore::ThreadMarker> : GenericHashTraits<WebCore:
static bool isDeletedValue(const WebCore::ThreadMarker& slot) { return slot.isHashTableDeletedValue(); }
};
+// The hash algorithm for our custom pair class is just the standard double
+// hash for pairs. Note that this means you can't mutate either of the parts of
+// the pair while they are in the hash table, as that would change their hash
+// code and thus their preferred placement in the table.
+template<> struct DefaultHash<WebCore::PairWithWeakHandling> {
+ typedef PairHash<WebCore::Member<WebCore::IntWrapper>, WebCore::WeakMember<WebCore::IntWrapper> > Hash;
+};
+
+// Custom traits for the pair. These are weakness handling traits, which means
+// PairWithWeakHandling must implement the traceInCollection and
+// shouldRemoveFromCollection methods. In addition, these traits are concerned
+// with the two magic values for the object, that represent empty and deleted
+// slots in the hash table. The SimpleClassHashTraits allow empty slots in the
+// table to be initialzed with memset to zero, and we use -1 in the first part
+// of the pair to represent deleted slots.
+template<> struct HashTraits<WebCore::PairWithWeakHandling> : WebCore::WeakHandlingHashTraits<WebCore::PairWithWeakHandling> {
+ static const bool needsDestruction = false;
+ static const bool hasIsEmptyValueFunction = true;
+ static bool isEmptyValue(const WebCore::PairWithWeakHandling& value) { return !value.first; }
+ static void constructDeletedValue(WebCore::PairWithWeakHandling& slot) { new (NotNull, &slot) WebCore::PairWithWeakHandling(HashTableDeletedValue); }
+ static bool isDeletedValue(const WebCore::PairWithWeakHandling& value) { return value.isHashTableDeletedValue(); }
+};
+
}
namespace WebCore {
@@ -3316,12 +3384,19 @@ TEST(HeapTest, CheckAndMarkPointer)
EXPECT_TRUE(scope.allThreadsParked());
Heap::makeConsistentForGC();
for (size_t i = 0; i < objectAddresses.size(); i++) {
- EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, objectAddresses[i]));
- EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, endAddresses[i]));
+ // We would like to assert that checkAndMarkPointer returned false
+ // here because the pointers no longer point into a valid object
+ // (it's been freed by the GCs. But checkAndMarkPointer will return
+ // true for any pointer that points into a heap page, regardless of
+ // whether it points at a valid object (this ensures the
+ // correctness of the page-based on-heap address caches), so we
+ // can't make that assert.
+ Heap::checkAndMarkPointer(&visitor, objectAddresses[i]);
+ Heap::checkAndMarkPointer(&visitor, endAddresses[i]);
}
EXPECT_EQ(0ul, visitor.count());
- EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectAddress));
- EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress));
+ Heap::checkAndMarkPointer(&visitor, largeObjectAddress);
+ Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress);
EXPECT_EQ(0ul, visitor.count());
}
// This round of GC is important to make sure that the object start
@@ -3978,4 +4053,219 @@ TEST(HeapTest, NeedsAdjustAndMark)
EXPECT_FALSE(NeedsAdjustAndMark<const UseMixin>::value);
}
+template<typename Set>
+void setWithCustomWeaknessHandling()
+{
+ Set set;
+ Set* set2 = new Set();
+ Persistent<Set> set3(new Set());
+ Persistent<IntWrapper> livingInt(IntWrapper::create(42));
+ set.add(PairWithWeakHandling(IntWrapper::create(0), IntWrapper::create(1)));
+ set2->add(PairWithWeakHandling(IntWrapper::create(2), IntWrapper::create(3)));
+ set3->add(PairWithWeakHandling(IntWrapper::create(4), IntWrapper::create(5)));
+ Heap::collectGarbage(ThreadState::HeapPointersOnStack);
+ typedef typename Set::iterator Iterator;
+ // The first set is on-stack, so its backing store must be referenced from
+ // the stack. That makes the weak references strong.
+ Iterator i = set.begin();
+ EXPECT_EQ(0, i->first->value());
+ EXPECT_EQ(1, i->second->value());
+ // The second set is pointed to from the stack, so it's referenced, but the
+ // weak processing may have taken place.
+ if (set2->size()) {
+ Iterator i2 = set2->begin();
+ EXPECT_EQ(2, i2->first->value());
+ EXPECT_EQ(3, i2->second->value());
+ }
+ // The third set is pointed to from a persistent, so it's referenced, but
+ // the weak processing may have taken place.
+ if (set3->size()) {
+ Iterator i3 = set3->begin();
+ EXPECT_EQ(4, i3->first->value());
+ EXPECT_EQ(5, i3->second->value());
+ }
+ Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
+ // No looking at the set and set2 now, they are busted by our lying about the 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.
+ 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.
+ Iterator i3 = set3->begin();
+ EXPECT_EQ(4, i3->first->value());
+ EXPECT_EQ(5, i3->second->value());
+ }
+ 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.
+ set3->add(PairWithWeakHandling(IntWrapper::create(103), livingInt));
+ set3->add(PairWithWeakHandling(livingInt, IntWrapper::create(103))); // This one gets zapped at GC time because nothing holds the 103 alive.
+ set3->add(PairWithWeakHandling(IntWrapper::create(103), IntWrapper::create(103))); // This one gets zapped too.
+ set3->add(PairWithWeakHandling(livingInt, livingInt));
+ set3->add(PairWithWeakHandling(livingInt, livingInt)); // This one is identical to the previous and doesn't add anything.
+ EXPECT_EQ(4u, set3->size());
+ Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
+ EXPECT_EQ(2u, set3->size());
+ Iterator i3 = set3->begin();
+ EXPECT_TRUE(i3->first->value() == 103 || i3->first == livingInt);
+ EXPECT_EQ(livingInt, i3->second);
+ ++i3;
+ EXPECT_TRUE(i3->first->value() == 103 || i3->first == livingInt);
+ EXPECT_EQ(livingInt, i3->second);
+}
+
+TEST(HeapTest, SetWithCustomWeaknessHandling)
+{
+ setWithCustomWeaknessHandling<HeapHashSet<PairWithWeakHandling> >();
+ setWithCustomWeaknessHandling<HeapLinkedHashSet<PairWithWeakHandling> >();
+}
+
+TEST(HeapTest, MapWithCustomWeaknessHandling)
+{
+ typedef HeapHashMap<PairWithWeakHandling, RefPtr<OffHeapInt> > Map;
+ HeapStats initialHeapSize;
+ clearOutOldGarbage(&initialHeapSize);
+ OffHeapInt::s_destructorCalls = 0;
+
+ Map map;
+ Map* map2 = new Map();
+ Persistent<Map> map3(new Map());
+ Persistent<IntWrapper> livingInt(IntWrapper::create(42));
+ map.add(PairWithWeakHandling(IntWrapper::create(0), IntWrapper::create(1)), OffHeapInt::create(1001));
+ map2->add(PairWithWeakHandling(IntWrapper::create(2), IntWrapper::create(3)), OffHeapInt::create(1002));
+ map3->add(PairWithWeakHandling(IntWrapper::create(4), IntWrapper::create(5)), OffHeapInt::create(1003));
+ EXPECT_EQ(0, OffHeapInt::s_destructorCalls);
+
+ Heap::collectGarbage(ThreadState::HeapPointersOnStack);
+ typedef typename Map::iterator Iterator;
+ // The first map is on-stack, so its backing store must be referenced from
+ // the stack. That makes the weak references strong.
+ Iterator i = map.begin();
+ EXPECT_EQ(0, i->key.first->value());
+ EXPECT_EQ(1, i->key.second->value());
+ EXPECT_EQ(1001, i->value->value());
+ // The second map is pointed to from the stack, so it's referenced, but the
+ // weak processing may have taken place.
+ if (map2->size()) {
+ Iterator i2 = map2->begin();
+ EXPECT_EQ(2, i2->key.first->value());
+ EXPECT_EQ(3, i2->key.second->value());
+ EXPECT_EQ(1002, i2->value->value());
+ }
+ // The third map is pointed to from a persistent, so it's referenced, but
+ // the weak processing may have taken place.
+ if (map3->size()) {
+ Iterator i3 = map3->begin();
+ EXPECT_EQ(4, i3->key.first->value());
+ EXPECT_EQ(5, i3->key.second->value());
+ EXPECT_EQ(1003, i3->value->value());
+ }
+ Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
+ // No looking at the map and map2 now, they are busted by our lying about the heap pointers on the stack!
+ 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.
+ Iterator i3 = map3->begin();
+ EXPECT_EQ(4, i3->key.first->value());
+ EXPECT_EQ(5, i3->key.second->value());
+ EXPECT_EQ(1003, i3->value->value());
+ EXPECT_EQ(2, OffHeapInt::s_destructorCalls);
+ } else {
+ EXPECT_EQ(3, OffHeapInt::s_destructorCalls);
+ }
+ map3->clear();
+
+ EXPECT_EQ(3, OffHeapInt::s_destructorCalls);
+ OffHeapInt::s_destructorCalls = 0;
+
+ map3->add(PairWithWeakHandling(IntWrapper::create(103), livingInt), OffHeapInt::create(2000));
+ map3->add(PairWithWeakHandling(livingInt, IntWrapper::create(103)), OffHeapInt::create(2001)); // This one gets zapped at GC time because nothing holds the 103 alive.
+ map3->add(PairWithWeakHandling(IntWrapper::create(103), IntWrapper::create(103)), OffHeapInt::create(2002)); // This one gets zapped too.
+ RefPtr<OffHeapInt> dupeInt(OffHeapInt::create(2003));
+ map3->add(PairWithWeakHandling(livingInt, livingInt), dupeInt);
+ map3->add(PairWithWeakHandling(livingInt, livingInt), dupeInt); // This one is identical to the previous and doesn't add anything.
+ dupeInt.clear();
+
+ EXPECT_EQ(0, OffHeapInt::s_destructorCalls);
+ EXPECT_EQ(4u, map3->size());
+ Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
+ EXPECT_EQ(2, OffHeapInt::s_destructorCalls);
+ EXPECT_EQ(2u, map3->size());
+ Iterator i3 = map3->begin();
+ EXPECT_TRUE(i3->key.first->value() == 103 || i3->key.first == livingInt);
+ EXPECT_EQ(livingInt, i3->key.second);
+ ++i3;
+ EXPECT_TRUE(i3->key.first->value() == 103 || i3->key.first == livingInt);
+ EXPECT_EQ(livingInt, i3->key.second);
+}
+
+TEST(HeapTest, MapWithCustomWeaknessHandling2)
+{
+ typedef HeapHashMap<RefPtr<OffHeapInt>, PairWithWeakHandling> Map;
+ HeapStats initialHeapSize;
+ clearOutOldGarbage(&initialHeapSize);
+ OffHeapInt::s_destructorCalls = 0;
+
+ Map map;
+ Map* map2 = new Map();
+ Persistent<Map> map3(new Map());
+ Persistent<IntWrapper> livingInt(IntWrapper::create(42));
+ map.add(OffHeapInt::create(1001), PairWithWeakHandling(IntWrapper::create(0), IntWrapper::create(1)));
+ map2->add(OffHeapInt::create(1002), PairWithWeakHandling(IntWrapper::create(2), IntWrapper::create(3)));
+ map3->add(OffHeapInt::create(1003), PairWithWeakHandling(IntWrapper::create(4), IntWrapper::create(5)));
+ EXPECT_EQ(0, OffHeapInt::s_destructorCalls);
+
+ Heap::collectGarbage(ThreadState::HeapPointersOnStack);
+ typedef typename Map::iterator Iterator;
+ // The first map is on-stack, so its backing store must be referenced from
+ // the stack. That makes the weak references strong.
+ Iterator i = map.begin();
+ EXPECT_EQ(0, i->value.first->value());
+ EXPECT_EQ(1, i->value.second->value());
+ EXPECT_EQ(1001, i->key->value());
+ // The second map is pointed to from the stack, so it's referenced, but the
+ // weak processing may have taken place.
+ if (map2->size()) {
+ Iterator i2 = map2->begin();
+ EXPECT_EQ(2, i2->value.first->value());
+ EXPECT_EQ(3, i2->value.second->value());
+ EXPECT_EQ(1002, i2->key->value());
+ }
+ // The third map is pointed to from a persistent, so it's referenced, but
+ // the weak processing may have taken place.
+ if (map3->size()) {
+ Iterator i3 = map3->begin();
+ EXPECT_EQ(4, i3->value.first->value());
+ EXPECT_EQ(5, i3->value.second->value());
+ EXPECT_EQ(1003, i3->key->value());
+ }
+ Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
+ // No looking at the map and map2 now, they are busted by our lying about the heap pointers on the stack!
+ if (map3->size()) {
Mads Ager (chromium) 2014/06/06 09:59:17 This one too.
Erik Corry 2014/06/10 11:21:18 Done.
+ Iterator i3 = map3->begin();
+ EXPECT_EQ(4, i3->value.first->value());
+ EXPECT_EQ(5, i3->value.second->value());
+ EXPECT_EQ(1003, i3->key->value());
+ EXPECT_EQ(2, OffHeapInt::s_destructorCalls);
+ } else {
+ EXPECT_EQ(3, OffHeapInt::s_destructorCalls);
+ }
+ map3->clear();
+
+ EXPECT_EQ(3, OffHeapInt::s_destructorCalls);
+ OffHeapInt::s_destructorCalls = 0;
+
+ map3->add(OffHeapInt::create(2000), PairWithWeakHandling(IntWrapper::create(103), livingInt));
+ map3->add(OffHeapInt::create(2001), PairWithWeakHandling(livingInt, IntWrapper::create(103))); // This one gets zapped at GC time because nothing holds the 103 alive.
+ map3->add(OffHeapInt::create(2002), PairWithWeakHandling(IntWrapper::create(103), IntWrapper::create(103))); // This one gets zapped too.
+ RefPtr<OffHeapInt> dupeInt(OffHeapInt::create(2003));
+ map3->add(dupeInt, PairWithWeakHandling(livingInt, livingInt));
+ map3->add(dupeInt, PairWithWeakHandling(livingInt, livingInt)); // This one is identical to the previous and doesn't add anything.
+ dupeInt.clear();
+
+ EXPECT_EQ(0, OffHeapInt::s_destructorCalls);
+ EXPECT_EQ(4u, map3->size());
+ Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
+ EXPECT_EQ(2, OffHeapInt::s_destructorCalls);
+ EXPECT_EQ(2u, map3->size());
+ Iterator i3 = map3->begin();
+ EXPECT_TRUE(i3->value.first->value() == 103 || i3->value.first == livingInt);
+ EXPECT_EQ(livingInt, i3->value.second);
+ ++i3;
+ EXPECT_TRUE(i3->value.first->value() == 103 || i3->value.first == livingInt);
+ EXPECT_EQ(livingInt, i3->value.second);
+}
+
} // WebCore namespace

Powered by Google App Engine
This is Rietveld 408576698