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 |