| 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 18 matching lines...) Expand all Loading... |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include "config.h" | 31 #include "config.h" |
| 32 | 32 |
| 33 #include "platform/heap/Handle.h" | 33 #include "platform/heap/Handle.h" |
| 34 #include "platform/heap/Heap.h" | 34 #include "platform/heap/Heap.h" |
| 35 #include "platform/heap/HeapLinkedStack.h" | 35 #include "platform/heap/HeapLinkedStack.h" |
| 36 #include "platform/heap/HeapTerminatedArrayBuilder.h" | 36 #include "platform/heap/HeapTerminatedArrayBuilder.h" |
| 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 "wtf/HashTraits.h" | 40 #include "wtf/HashTraits.h" |
| 40 #include "wtf/LinkedHashSet.h" | 41 #include "wtf/LinkedHashSet.h" |
| 41 | 42 |
| 42 #include <gtest/gtest.h> | 43 #include <gtest/gtest.h> |
| 43 | 44 |
| 44 namespace WebCore { | 45 namespace WebCore { |
| 45 | 46 |
| 46 class ThreadMarker { | 47 class ThreadMarker { |
| 47 public: | 48 public: |
| 48 ThreadMarker() : m_creatingThread(reinterpret_cast<ThreadState*>(0)), m_num(
0) { } | 49 ThreadMarker() : m_creatingThread(reinterpret_cast<ThreadState*>(0)), m_num(
0) { } |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 92 | 93 |
| 93 } | 94 } |
| 94 | 95 |
| 95 namespace WebCore { | 96 namespace WebCore { |
| 96 | 97 |
| 97 class TestGCScope { | 98 class TestGCScope { |
| 98 public: | 99 public: |
| 99 explicit TestGCScope(ThreadState::StackState state) | 100 explicit TestGCScope(ThreadState::StackState state) |
| 100 : m_state(ThreadState::current()) | 101 : m_state(ThreadState::current()) |
| 101 , m_safePointScope(state) | 102 , m_safePointScope(state) |
| 103 , m_parkedAllThreads(false) |
| 102 { | 104 { |
| 103 m_state->checkThread(); | 105 m_state->checkThread(); |
| 104 EXPECT_FALSE(m_state->isInGC()); | 106 EXPECT_FALSE(m_state->isInGC()); |
| 105 ThreadState::stopThreads(); | 107 if (LIKELY(ThreadState::stopThreads())) { |
| 106 m_state->enterGC(); | 108 m_state->enterGC(); |
| 109 m_parkedAllThreads = true; |
| 110 } |
| 107 } | 111 } |
| 108 | 112 |
| 113 bool allThreadsParked() { return m_parkedAllThreads; } |
| 114 |
| 109 ~TestGCScope() | 115 ~TestGCScope() |
| 110 { | 116 { |
| 111 m_state->leaveGC(); | 117 // Only cleanup if we parked all threads in which case the GC happened |
| 112 EXPECT_FALSE(m_state->isInGC()); | 118 // and we need to resume the other threads. |
| 113 ThreadState::resumeThreads(); | 119 if (LIKELY(m_parkedAllThreads)) { |
| 120 m_state->leaveGC(); |
| 121 EXPECT_FALSE(m_state->isInGC()); |
| 122 ThreadState::resumeThreads(); |
| 123 } |
| 114 } | 124 } |
| 115 | 125 |
| 116 private: | 126 private: |
| 117 ThreadState* m_state; | 127 ThreadState* m_state; |
| 118 ThreadState::SafePointScope m_safePointScope; | 128 ThreadState::SafePointScope m_safePointScope; |
| 129 bool m_parkedAllThreads; // False if we fail to park all threads |
| 119 }; | 130 }; |
| 120 | 131 |
| 121 static void getHeapStats(HeapStats* stats) | 132 static void getHeapStats(HeapStats* stats) |
| 122 { | 133 { |
| 123 TestGCScope scope(ThreadState::NoHeapPointersOnStack); | 134 TestGCScope scope(ThreadState::NoHeapPointersOnStack); |
| 135 EXPECT_TRUE(scope.allThreadsParked()); |
| 124 Heap::getStats(stats); | 136 Heap::getStats(stats); |
| 125 } | 137 } |
| 126 | 138 |
| 127 #define DEFINE_VISITOR_METHODS(Type) \ | 139 #define DEFINE_VISITOR_METHODS(Type) \ |
| 128 virtual void mark(const Type* object, TraceCallback callback) OVERRIDE \ | 140 virtual void mark(const Type* object, TraceCallback callback) OVERRIDE \ |
| 129 { \ | 141 { \ |
| 130 if (object) \ | 142 if (object) \ |
| 131 m_count++; \ | 143 m_count++; \ |
| 132 } \ | 144 } \ |
| 133 virtual bool isMarked(const Type*) OVERRIDE { return false; } | 145 virtual bool isMarked(const Type*) OVERRIDE { return false; } |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 343 int IntWrapper::s_destructorCalls = 0; | 355 int IntWrapper::s_destructorCalls = 0; |
| 344 int OffHeapInt::s_destructorCalls = 0; | 356 int OffHeapInt::s_destructorCalls = 0; |
| 345 | 357 |
| 346 class ThreadedTesterBase { | 358 class ThreadedTesterBase { |
| 347 protected: | 359 protected: |
| 348 static void test(ThreadedTesterBase* tester) | 360 static void test(ThreadedTesterBase* tester) |
| 349 { | 361 { |
| 350 for (int i = 0; i < numberOfThreads; i++) | 362 for (int i = 0; i < numberOfThreads; i++) |
| 351 createThread(&threadFunc, tester, "testing thread"); | 363 createThread(&threadFunc, tester, "testing thread"); |
| 352 while (tester->m_threadsToFinish) { | 364 while (tester->m_threadsToFinish) { |
| 353 ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack
); | 365 ThreadState::SafePointScope scope(ThreadState::NoHeapPointersOnStack
); |
| 354 yield(); | 366 yield(); |
| 355 } | 367 } |
| 356 delete tester; | 368 delete tester; |
| 357 } | 369 } |
| 358 | 370 |
| 359 virtual void runThread() = 0; | 371 virtual void runThread() = 0; |
| 360 | 372 |
| 361 protected: | 373 protected: |
| 362 static const int numberOfThreads = 10; | 374 static const int numberOfThreads = 10; |
| 363 static const int gcPerThread = 5; | 375 static const int gcPerThread = 5; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 401 { | 413 { |
| 402 Persistent<IntWrapper> wrapper; | 414 Persistent<IntWrapper> wrapper; |
| 403 | 415 |
| 404 typedef Persistent<IntWrapper, GlobalPersistents> GlobalIntWrapp
erPersistent; | 416 typedef Persistent<IntWrapper, GlobalPersistents> GlobalIntWrapp
erPersistent; |
| 405 OwnPtr<GlobalIntWrapperPersistent> globalPersistent = adoptPtr(n
ew GlobalIntWrapperPersistent(IntWrapper::create(0x0ed0cabb))); | 417 OwnPtr<GlobalIntWrapperPersistent> globalPersistent = adoptPtr(n
ew GlobalIntWrapperPersistent(IntWrapper::create(0x0ed0cabb))); |
| 406 | 418 |
| 407 for (int i = 0; i < numberOfAllocations; i++) { | 419 for (int i = 0; i < numberOfAllocations; i++) { |
| 408 wrapper = IntWrapper::create(0x0bbac0de); | 420 wrapper = IntWrapper::create(0x0bbac0de); |
| 409 if (!(i % 10)) { | 421 if (!(i % 10)) { |
| 410 globalPersistent = adoptPtr(new GlobalIntWrapperPersiste
nt(IntWrapper::create(0x0ed0cabb))); | 422 globalPersistent = adoptPtr(new GlobalIntWrapperPersiste
nt(IntWrapper::create(0x0ed0cabb))); |
| 411 ThreadState::current()->safePoint(ThreadState::NoHeapPoi
ntersOnStack); | |
| 412 } | 423 } |
| 424 ThreadState::SafePointScope scope(ThreadState::NoHeapPointer
sOnStack); |
| 413 yield(); | 425 yield(); |
| 414 } | 426 } |
| 415 | 427 |
| 416 if (gcCount < gcPerThread) { | 428 if (gcCount < gcPerThread) { |
| 417 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | 429 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 418 gcCount++; | 430 gcCount++; |
| 419 atomicIncrement(&m_gcCount); | 431 atomicIncrement(&m_gcCount); |
| 420 } | 432 } |
| 421 | 433 |
| 422 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | 434 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 423 EXPECT_EQ(wrapper->value(), 0x0bbac0de); | 435 EXPECT_EQ(wrapper->value(), 0x0bbac0de); |
| 424 EXPECT_EQ((*globalPersistent)->value(), 0x0ed0cabb); | 436 EXPECT_EQ((*globalPersistent)->value(), 0x0ed0cabb); |
| 425 } | 437 } |
| 438 ThreadState::SafePointScope scope(ThreadState::NoHeapPointersOnStack
); |
| 426 yield(); | 439 yield(); |
| 427 } | 440 } |
| 428 ThreadState::detach(); | 441 ThreadState::detach(); |
| 429 atomicDecrement(&m_threadsToFinish); | 442 atomicDecrement(&m_threadsToFinish); |
| 430 } | 443 } |
| 431 }; | 444 }; |
| 432 | 445 |
| 433 class ThreadedWeaknessTester : public ThreadedTesterBase { | 446 class ThreadedWeaknessTester : public ThreadedTesterBase { |
| 434 public: | 447 public: |
| 435 static void test() | 448 static void test() |
| 436 { | 449 { |
| 437 ThreadedTesterBase::test(new ThreadedWeaknessTester); | 450 ThreadedTesterBase::test(new ThreadedWeaknessTester); |
| 438 } | 451 } |
| 439 | 452 |
| 440 private: | 453 private: |
| 441 virtual void runThread() OVERRIDE | 454 virtual void runThread() OVERRIDE |
| 442 { | 455 { |
| 443 ThreadState::attach(); | 456 ThreadState::attach(); |
| 444 | 457 |
| 445 int gcCount = 0; | 458 int gcCount = 0; |
| 446 while (!done()) { | 459 while (!done()) { |
| 447 ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack
); | 460 ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack
); |
| 448 { | 461 { |
| 449 Persistent<HeapHashMap<ThreadMarker, WeakMember<IntWrapper> > >
weakMap = new HeapHashMap<ThreadMarker, WeakMember<IntWrapper> >; | 462 Persistent<HeapHashMap<ThreadMarker, WeakMember<IntWrapper> > >
weakMap = new HeapHashMap<ThreadMarker, WeakMember<IntWrapper> >; |
| 450 PersistentHeapHashMap<ThreadMarker, WeakMember<IntWrapper> > wea
kMap2; | 463 PersistentHeapHashMap<ThreadMarker, WeakMember<IntWrapper> > wea
kMap2; |
| 451 | 464 |
| 452 for (int i = 0; i < numberOfAllocations; i++) { | 465 for (int i = 0; i < numberOfAllocations; i++) { |
| 453 weakMap->add(static_cast<unsigned>(i), IntWrapper::create(0)
); | 466 weakMap->add(static_cast<unsigned>(i), IntWrapper::create(0)
); |
| 454 weakMap2.add(static_cast<unsigned>(i), IntWrapper::create(0)
); | 467 weakMap2.add(static_cast<unsigned>(i), IntWrapper::create(0)
); |
| 455 if (!(i % 10)) { | 468 ThreadState::SafePointScope scope(ThreadState::NoHeapPointer
sOnStack); |
| 456 ThreadState::current()->safePoint(ThreadState::NoHeapPoi
ntersOnStack); | |
| 457 } | |
| 458 yield(); | 469 yield(); |
| 459 } | 470 } |
| 460 | 471 |
| 461 if (gcCount < gcPerThread) { | 472 if (gcCount < gcPerThread) { |
| 462 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | 473 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 463 gcCount++; | 474 gcCount++; |
| 464 atomicIncrement(&m_gcCount); | 475 atomicIncrement(&m_gcCount); |
| 465 } | 476 } |
| 466 | 477 |
| 467 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | 478 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 468 EXPECT_TRUE(weakMap->isEmpty()); | 479 EXPECT_TRUE(weakMap->isEmpty()); |
| 469 EXPECT_TRUE(weakMap2.isEmpty()); | 480 EXPECT_TRUE(weakMap2.isEmpty()); |
| 470 } | 481 } |
| 482 ThreadState::SafePointScope scope(ThreadState::NoHeapPointersOnStack
); |
| 471 yield(); | 483 yield(); |
| 472 } | 484 } |
| 473 ThreadState::detach(); | 485 ThreadState::detach(); |
| 474 atomicDecrement(&m_threadsToFinish); | 486 atomicDecrement(&m_threadsToFinish); |
| 475 } | 487 } |
| 476 }; | 488 }; |
| 477 | 489 |
| 478 // The accounting for memory includes the memory used by rounding up object | 490 // The accounting for memory includes the memory used by rounding up object |
| 479 // sizes. This is done in a different way on 32 bit and 64 bit, so we have to | 491 // sizes. This is done in a different way on 32 bit and 64 bit, so we have to |
| 480 // have some slack in the tests. | 492 // have some slack in the tests. |
| (...skipping 2785 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3266 | 3278 |
| 3267 // This is a low-level test where we call checkAndMarkPointer. This method | 3279 // This is a low-level test where we call checkAndMarkPointer. This method |
| 3268 // causes the object start bitmap to be computed which requires the heap | 3280 // causes the object start bitmap to be computed which requires the heap |
| 3269 // to be in a consistent state (e.g. the free allocation area must be put | 3281 // to be in a consistent state (e.g. the free allocation area must be put |
| 3270 // into a free list header). However when we call makeConsistentForGC it | 3282 // into a free list header). However when we call makeConsistentForGC it |
| 3271 // also clears out the freelists so we have to rebuild those before trying | 3283 // also clears out the freelists so we have to rebuild those before trying |
| 3272 // to allocate anything again. We do this by forcing a GC after doing the | 3284 // to allocate anything again. We do this by forcing a GC after doing the |
| 3273 // checkAndMarkPointer tests. | 3285 // checkAndMarkPointer tests. |
| 3274 { | 3286 { |
| 3275 TestGCScope scope(ThreadState::HeapPointersOnStack); | 3287 TestGCScope scope(ThreadState::HeapPointersOnStack); |
| 3288 EXPECT_TRUE(scope.allThreadsParked()); // Fail the test if we could not
park all threads. |
| 3276 Heap::makeConsistentForGC(); | 3289 Heap::makeConsistentForGC(); |
| 3277 for (size_t i = 0; i < objectAddresses.size(); i++) { | 3290 for (size_t i = 0; i < objectAddresses.size(); i++) { |
| 3278 EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, objectAddresses[i]))
; | 3291 EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, objectAddresses[i]))
; |
| 3279 EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, endAddresses[i])); | 3292 EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, endAddresses[i])); |
| 3280 } | 3293 } |
| 3281 EXPECT_EQ(objectAddresses.size() * 2, visitor.count()); | 3294 EXPECT_EQ(objectAddresses.size() * 2, visitor.count()); |
| 3282 visitor.reset(); | 3295 visitor.reset(); |
| 3283 EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, largeObjectAddress)); | 3296 EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, largeObjectAddress)); |
| 3284 EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress)); | 3297 EXPECT_TRUE(Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress)); |
| 3285 EXPECT_EQ(2ul, visitor.count()); | 3298 EXPECT_EQ(2ul, visitor.count()); |
| 3286 visitor.reset(); | 3299 visitor.reset(); |
| 3287 } | 3300 } |
| 3288 // This forces a GC without stack scanning which results in the objects | 3301 // This forces a GC without stack scanning which results in the objects |
| 3289 // being collected. This will also rebuild the above mentioned freelists, | 3302 // being collected. This will also rebuild the above mentioned freelists, |
| 3290 // however we don't rely on that below since we don't have any allocations. | 3303 // however we don't rely on that below since we don't have any allocations. |
| 3291 clearOutOldGarbage(&initialHeapStats); | 3304 clearOutOldGarbage(&initialHeapStats); |
| 3292 { | 3305 { |
| 3293 TestGCScope scope(ThreadState::HeapPointersOnStack); | 3306 TestGCScope scope(ThreadState::HeapPointersOnStack); |
| 3307 EXPECT_TRUE(scope.allThreadsParked()); |
| 3294 Heap::makeConsistentForGC(); | 3308 Heap::makeConsistentForGC(); |
| 3295 for (size_t i = 0; i < objectAddresses.size(); i++) { | 3309 for (size_t i = 0; i < objectAddresses.size(); i++) { |
| 3296 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, objectAddresses[i])
); | 3310 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, objectAddresses[i])
); |
| 3297 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, endAddresses[i])); | 3311 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, endAddresses[i])); |
| 3298 } | 3312 } |
| 3299 EXPECT_EQ(0ul, visitor.count()); | 3313 EXPECT_EQ(0ul, visitor.count()); |
| 3300 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectAddress)); | 3314 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectAddress)); |
| 3301 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress))
; | 3315 EXPECT_FALSE(Heap::checkAndMarkPointer(&visitor, largeObjectEndAddress))
; |
| 3302 EXPECT_EQ(0ul, visitor.count()); | 3316 EXPECT_EQ(0ul, visitor.count()); |
| 3303 } | 3317 } |
| (...skipping 569 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3873 } | 3887 } |
| 3874 { | 3888 { |
| 3875 Persistent<MixinB> b = obj; | 3889 Persistent<MixinB> b = obj; |
| 3876 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | 3890 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 3877 EXPECT_EQ(0, IntWrapper::s_destructorCalls); | 3891 EXPECT_EQ(0, IntWrapper::s_destructorCalls); |
| 3878 } | 3892 } |
| 3879 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); | 3893 Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); |
| 3880 EXPECT_EQ(3, IntWrapper::s_destructorCalls); | 3894 EXPECT_EQ(3, IntWrapper::s_destructorCalls); |
| 3881 } | 3895 } |
| 3882 | 3896 |
| 3897 class GCParkingThreadTester { |
| 3898 public: |
| 3899 static void test() |
| 3900 { |
| 3901 createThread(&sleeperMainFunc, 0, "SleepingThread"); |
| 3902 |
| 3903 // Wait for the sleeper to run. |
| 3904 while (!s_sleeperRunning) { |
| 3905 yield(); |
| 3906 } |
| 3907 |
| 3908 { |
| 3909 // Expect the first attempt to park the sleeping thread to fail |
| 3910 TestGCScope scope(ThreadState::NoHeapPointersOnStack); |
| 3911 EXPECT_FALSE(scope.allThreadsParked()); |
| 3912 } |
| 3913 |
| 3914 s_sleeperDone = true; |
| 3915 |
| 3916 // Wait for the sleeper to finish. |
| 3917 while (s_sleeperRunning) { |
| 3918 // We enter the safepoint here since the sleeper thread will detach |
| 3919 // causing it to GC. |
| 3920 ThreadState::current()->safePoint(ThreadState::NoHeapPointersOnStack
); |
| 3921 yield(); |
| 3922 } |
| 3923 { |
| 3924 // Since the sleeper thread has detached this is the only thread. |
| 3925 TestGCScope scope(ThreadState::NoHeapPointersOnStack); |
| 3926 EXPECT_TRUE(scope.allThreadsParked()); |
| 3927 } |
| 3928 } |
| 3929 |
| 3930 private: |
| 3931 static void sleeperMainFunc(void* data) |
| 3932 { |
| 3933 ThreadState::attach(); |
| 3934 s_sleeperRunning = true; |
| 3935 |
| 3936 // Simulate a long running op that is not entering a safepoint. |
| 3937 while (!s_sleeperDone) { |
| 3938 yield(); |
| 3939 } |
| 3940 |
| 3941 ThreadState::detach(); |
| 3942 s_sleeperRunning = false; |
| 3943 } |
| 3944 |
| 3945 static volatile bool s_sleeperRunning; |
| 3946 static volatile bool s_sleeperDone; |
| 3947 }; |
| 3948 |
| 3949 volatile bool GCParkingThreadTester::s_sleeperRunning = false; |
| 3950 volatile bool GCParkingThreadTester::s_sleeperDone = false; |
| 3951 |
| 3952 TEST(HeapTest, GCParkingTimeout) |
| 3953 { |
| 3954 GCParkingThreadTester::test(); |
| 3955 } |
| 3956 |
| 3883 } // WebCore namespace | 3957 } // WebCore namespace |
| OLD | NEW |