| Index: Source/platform/heap/HeapTest.cpp
|
| diff --git a/Source/platform/heap/HeapTest.cpp b/Source/platform/heap/HeapTest.cpp
|
| index 44a4285eab9c520004971e10849a8e8a74fe7537..b89faae96d862c2ffd7cac108013eb4110d6a094 100644
|
| --- a/Source/platform/heap/HeapTest.cpp
|
| +++ b/Source/platform/heap/HeapTest.cpp
|
| @@ -1697,8 +1697,8 @@ TEST(HeapTest, TypedHeapSanity)
|
| // We use TraceCounter for allocating an object on the general heap.
|
| Persistent<TraceCounter> generalHeapObject = TraceCounter::create();
|
| Persistent<TestTypedHeapClass> typedHeapObject = TestTypedHeapClass::create();
|
| - EXPECT_NE(pageHeaderAddress(reinterpret_cast<Address>(generalHeapObject.get())),
|
| - pageHeaderAddress(reinterpret_cast<Address>(typedHeapObject.get())));
|
| + EXPECT_NE(pageHeaderFromObject(generalHeapObject.get()),
|
| + pageHeaderFromObject(typedHeapObject.get()));
|
| }
|
|
|
| TEST(HeapTest, NoAllocation)
|
| @@ -3429,6 +3429,7 @@ TEST(HeapTest, PersistentHeapCollectionTypes)
|
| typedef PersistentHeapListHashSet<Member<IntWrapper> > PListSet;
|
| typedef PersistentHeapLinkedHashSet<Member<IntWrapper> > PLinkedSet;
|
| typedef PersistentHeapHashMap<Member<IntWrapper>, Member<IntWrapper> > PMap;
|
| + typedef PersistentHeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper> > WeakPMap;
|
| typedef PersistentHeapDeque<Member<IntWrapper> > PDeque;
|
|
|
| clearOutOldGarbage(&initialHeapSize);
|
| @@ -3439,6 +3440,7 @@ TEST(HeapTest, PersistentHeapCollectionTypes)
|
| PListSet pListSet;
|
| PLinkedSet pLinkedSet;
|
| PMap pMap;
|
| + WeakPMap wpMap;
|
|
|
| IntWrapper* one(IntWrapper::create(1));
|
| IntWrapper* two(IntWrapper::create(2));
|
| @@ -3449,6 +3451,8 @@ TEST(HeapTest, PersistentHeapCollectionTypes)
|
| IntWrapper* seven(IntWrapper::create(7));
|
| IntWrapper* eight(IntWrapper::create(8));
|
| IntWrapper* nine(IntWrapper::create(9));
|
| + Persistent<IntWrapper> ten(IntWrapper::create(10));
|
| + IntWrapper* eleven(IntWrapper::create(11));
|
|
|
| pVec.append(one);
|
| pVec.append(two);
|
| @@ -3466,6 +3470,7 @@ TEST(HeapTest, PersistentHeapCollectionTypes)
|
| pListSet.add(eight);
|
| pLinkedSet.add(nine);
|
| pMap.add(five, six);
|
| + wpMap.add(ten, eleven);
|
|
|
| // Collect |vec| and |one|.
|
| vec = 0;
|
| @@ -3494,11 +3499,17 @@ TEST(HeapTest, PersistentHeapCollectionTypes)
|
|
|
| EXPECT_EQ(1u, pMap.size());
|
| EXPECT_EQ(six, pMap.get(five));
|
| +
|
| + EXPECT_EQ(1u, wpMap.size());
|
| + EXPECT_EQ(eleven, wpMap.get(ten));
|
| + ten.clear();
|
| + Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
|
| + EXPECT_EQ(0u, wpMap.size());
|
| }
|
|
|
| // Collect previous roots.
|
| Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
|
| - EXPECT_EQ(9, IntWrapper::s_destructorCalls);
|
| + EXPECT_EQ(11, IntWrapper::s_destructorCalls);
|
| }
|
|
|
| TEST(HeapTest, CollectionNesting)
|
| @@ -4583,5 +4594,275 @@ TEST(HeapTest, IndirectStrongToWeak)
|
| EXPECT_EQ(0u, map->size());
|
| }
|
|
|
| +static Mutex& mainThreadMutex()
|
| +{
|
| + AtomicallyInitializedStatic(Mutex&, mainMutex = *new Mutex);
|
| + return mainMutex;
|
| +}
|
| +
|
| +static ThreadCondition& mainThreadCondition()
|
| +{
|
| + AtomicallyInitializedStatic(ThreadCondition&, mainCondition = *new ThreadCondition);
|
| + return mainCondition;
|
| +}
|
| +
|
| +static void parkMainThread()
|
| +{
|
| + mainThreadCondition().wait(mainThreadMutex());
|
| +}
|
| +
|
| +static void wakeMainThread()
|
| +{
|
| + MutexLocker locker(mainThreadMutex());
|
| + mainThreadCondition().signal();
|
| +}
|
| +
|
| +static Mutex& workerThreadMutex()
|
| +{
|
| + AtomicallyInitializedStatic(Mutex&, workerMutex = *new Mutex);
|
| + return workerMutex;
|
| +}
|
| +
|
| +static ThreadCondition& workerThreadCondition()
|
| +{
|
| + AtomicallyInitializedStatic(ThreadCondition&, workerCondition = *new ThreadCondition);
|
| + return workerCondition;
|
| +}
|
| +
|
| +static void parkWorkerThread()
|
| +{
|
| + workerThreadCondition().wait(workerThreadMutex());
|
| +}
|
| +
|
| +static void wakeWorkerThread()
|
| +{
|
| + MutexLocker locker(workerThreadMutex());
|
| + workerThreadCondition().signal();
|
| +}
|
| +
|
| +class CrossThreadObject : public GarbageCollectedFinalized<CrossThreadObject> {
|
| +public:
|
| + static CrossThreadObject* create(IntWrapper* workerObjectPointer)
|
| + {
|
| + return new CrossThreadObject(workerObjectPointer);
|
| + }
|
| +
|
| + virtual ~CrossThreadObject()
|
| + {
|
| + ++s_destructorCalls;
|
| + }
|
| +
|
| + static int s_destructorCalls;
|
| + void trace(Visitor* visitor) { visitor->trace(m_workerObject); }
|
| +
|
| +private:
|
| + CrossThreadObject(IntWrapper* workerObjectPointer) : m_workerObject(workerObjectPointer) { }
|
| +
|
| +private:
|
| + Member<IntWrapper> m_workerObject;
|
| +};
|
| +
|
| +int CrossThreadObject::s_destructorCalls = 0;
|
| +
|
| +class CrossThreadPointerTester {
|
| +public:
|
| + static void test()
|
| + {
|
| + CrossThreadObject::s_destructorCalls = 0;
|
| + IntWrapper::s_destructorCalls = 0;
|
| +
|
| + MutexLocker locker(mainThreadMutex());
|
| + createThread(&workerThreadMain, 0, "Worker Thread");
|
| +
|
| + parkMainThread();
|
| +
|
| + uintptr_t stackPtrValue = 0;
|
| + {
|
| + // Create an object with a pointer to the other heap's IntWrapper.
|
| + Persistent<CrossThreadObject> cto = CrossThreadObject::create(const_cast<IntWrapper*>(s_workerObjectPointer));
|
| + s_workerObjectPointer = 0;
|
| +
|
| + Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
|
| +
|
| + // Nothing should have been collected/destructed.
|
| + EXPECT_EQ(0, CrossThreadObject::s_destructorCalls);
|
| + EXPECT_EQ(0, IntWrapper::s_destructorCalls);
|
| +
|
| + // Put cto into a stack value. This is used to check that a conservative
|
| + // GC succeeds even though we are tracing the other thread heap after
|
| + // shutting it down.
|
| + stackPtrValue = reinterpret_cast<uintptr_t>(cto.get());
|
| + }
|
| + RELEASE_ASSERT(stackPtrValue);
|
| +
|
| + // At this point it is "programatically" okay to shut down the worker thread
|
| + // since the cto object should be dead. However out stackPtrValue will cause a
|
| + // trace of the object when doing a conservative GC.
|
| + // The worker thread's thread local GC's should just add the worker thread's
|
| + // pages to the heap after finalizing IntWrapper.
|
| + wakeWorkerThread();
|
| +
|
| + // Wait for the worker to shutdown.
|
| + parkMainThread();
|
| +
|
| + // After the worker thread has detached it should have finalized the
|
| + // IntWrapper object on its heaps. Since there has been no global GC
|
| + // the cto object should not have been finalized.
|
| + EXPECT_EQ(0, CrossThreadObject::s_destructorCalls);
|
| + EXPECT_EQ(1, IntWrapper::s_destructorCalls);
|
| +
|
| + // Now do a conservative GC. The stackPtrValue should keep cto alive
|
| + // and will also cause the orphaned page of the other thread to be
|
| + // traced. At this point cto should still not be finalized.
|
| + Heap::collectGarbage(ThreadState::HeapPointersOnStack);
|
| + EXPECT_EQ(0, CrossThreadObject::s_destructorCalls);
|
| + EXPECT_EQ(1, IntWrapper::s_destructorCalls);
|
| +
|
| + // Do a GC with no pointers on the stack to see the cto being collected.
|
| + Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
|
| + EXPECT_EQ(1, CrossThreadObject::s_destructorCalls);
|
| + EXPECT_EQ(1, IntWrapper::s_destructorCalls);
|
| + }
|
| +
|
| +private:
|
| + static void workerThreadMain(void* data)
|
| + {
|
| + MutexLocker locker(workerThreadMutex());
|
| + ThreadState::attach();
|
| +
|
| + {
|
| + // Create a worker object that is only kept alive by a cross thread
|
| + // pointer (from CrossThreadObject).
|
| + IntWrapper* workerObject = IntWrapper::create(42);
|
| + s_workerObjectPointer = workerObject;
|
| + }
|
| +
|
| + // Wake up the main thread which is waiting for the worker to do its
|
| + // allocation and passing the pointer.
|
| + wakeMainThread();
|
| +
|
| + // Wait for main thread to signal the worker to shutdown.
|
| + {
|
| + ThreadState::SafePointScope scope(ThreadState::NoHeapPointersOnStack);
|
| + parkWorkerThread();
|
| + }
|
| +
|
| + ThreadState::detach();
|
| +
|
| + // Tell the main thread the worker has done its shutdown.
|
| + wakeMainThread();
|
| + }
|
| +
|
| + static volatile IntWrapper* s_workerObjectPointer;
|
| +};
|
| +
|
| +volatile IntWrapper* CrossThreadPointerTester::s_workerObjectPointer = 0;
|
| +
|
| +TEST(HeapTest, CrossThreadPointerToOrphanedPage)
|
| +{
|
| + CrossThreadPointerTester::test();
|
| +}
|
| +
|
| +class DeadBitTester {
|
| +public:
|
| + static void test()
|
| + {
|
| + IntWrapper::s_destructorCalls = 0;
|
| +
|
| + MutexLocker locker(mainThreadMutex());
|
| + createThread(&workerThreadMain, 0, "Worker Thread");
|
| +
|
| + // Wait for the worker thread to have done its initialization,
|
| + // IE. the worker allocates an object and then throw aways any
|
| + // pointers to it.
|
| + parkMainThread();
|
| +
|
| + // Now do a GC. This will not find the worker threads object since it
|
| + // is not referred from any of the threads. Even a conservative
|
| + // GC will not find it.
|
| + // Also at this point the worker is waiting for the main thread
|
| + // to be parked and will not do any sweep of its heap.
|
| + Heap::collectGarbage(ThreadState::NoHeapPointersOnStack);
|
| +
|
| + // Since the worker thread is not sweeping the worker object should
|
| + // not have been finalized.
|
| + EXPECT_EQ(0, IntWrapper::s_destructorCalls);
|
| +
|
| + // Put the worker thread's object address on the stack and do a
|
| + // conservative GC. This should find the worker object, but since
|
| + // it was dead in the previous GC it should not be traced in this
|
| + // GC.
|
| + uintptr_t stackPtrValue = s_workerObjectPointer;
|
| + s_workerObjectPointer = 0;
|
| + ASSERT_UNUSED(stackPtrValue, stackPtrValue);
|
| + Heap::collectGarbage(ThreadState::HeapPointersOnStack);
|
| +
|
| + // Since the worker thread is not sweeping the worker object should
|
| + // not have been finalized.
|
| + EXPECT_EQ(0, IntWrapper::s_destructorCalls);
|
| +
|
| + // Wake up the worker thread so it can continue with its sweeping.
|
| + // This should finalized the worker object which we test below.
|
| + // The worker thread will go back to sleep once sweeping to ensure
|
| + // we don't have thread local GCs until after validating the destructor
|
| + // was called.
|
| + wakeWorkerThread();
|
| +
|
| + // Wait for the worker thread to sweep its heaps before checking.
|
| + parkMainThread();
|
| + EXPECT_EQ(1, IntWrapper::s_destructorCalls);
|
| +
|
| + // Wake up the worker to allow it thread to continue with thread
|
| + // shutdown.
|
| + wakeWorkerThread();
|
| + }
|
| +
|
| +private:
|
| +
|
| + static void workerThreadMain(void* data)
|
| + {
|
| + MutexLocker locker(workerThreadMutex());
|
| +
|
| + ThreadState::attach();
|
| +
|
| + {
|
| + // Create a worker object that is not kept alive except the
|
| + // main thread will keep it as an integer value on its stack.
|
| + IntWrapper* workerObject = IntWrapper::create(42);
|
| + s_workerObjectPointer = reinterpret_cast<uintptr_t>(workerObject);
|
| + }
|
| +
|
| + // Signal the main thread that the worker is done with its allocation.
|
| + wakeMainThread();
|
| +
|
| + {
|
| + // Wait for the main thread to do two GCs without sweeping this thread
|
| + // heap. The worker waits within a safepoint, but there is no sweeping
|
| + // until leaving the safepoint scope.
|
| + ThreadState::SafePointScope scope(ThreadState::NoHeapPointersOnStack);
|
| + parkWorkerThread();
|
| + }
|
| +
|
| + // Wake up the main thread when done sweeping.
|
| + wakeMainThread();
|
| +
|
| + // Wait with detach until the main thread says so. This is not strictly
|
| + // necessary, but it means the worker thread will not do its thread local
|
| + // GCs just yet, making it easier to reason about that no new GC has occurred
|
| + // and the above sweep was the one finalizing the worker object.
|
| + parkWorkerThread();
|
| +
|
| + ThreadState::detach();
|
| + }
|
| +
|
| + static volatile uintptr_t s_workerObjectPointer;
|
| +};
|
| +
|
| +volatile uintptr_t DeadBitTester::s_workerObjectPointer = 0;
|
| +
|
| +TEST(HeapTest, ObjectDeadBit)
|
| +{
|
| + DeadBitTester::test();
|
| +}
|
|
|
| } // WebCore namespace
|
|
|