1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include <map>
6#include <memory>
7#include <set>
8#include <string>
9
10#include "platform/globals.h"
11
12#include "platform/assert.h"
13#include "vm/class_finalizer.h"
14#include "vm/dart_api_impl.h"
15#include "vm/globals.h"
16#include "vm/heap/become.h"
17#include "vm/heap/heap.h"
18#include "vm/message_handler.h"
19#include "vm/object_graph.h"
20#include "vm/port.h"
21#include "vm/symbols.h"
22#include "vm/unit_test.h"
23
24namespace dart {
25
26TEST_CASE(OldGC) {
27 const char* kScriptChars =
28 "main() {\n"
29 " return [1, 2, 3];\n"
30 "}\n";
31 NOT_IN_PRODUCT(FLAG_verbose_gc = true);
32 Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
33 Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
34
35 EXPECT_VALID(result);
36 EXPECT(!Dart_IsNull(result));
37 EXPECT(Dart_IsList(result));
38 TransitionNativeToVM transition(thread);
39 GCTestHelper::CollectOldSpace();
40}
41
42#if !defined(PRODUCT)
43TEST_CASE(OldGC_Unsync) {
44 // Finalize any GC in progress as it is unsafe to change FLAG_marker_tasks
45 // when incremental marking is in progress.
46 {
47 TransitionNativeToVM transition(thread);
48 GCTestHelper::CollectAllGarbage();
49 }
50 FLAG_marker_tasks = 0;
51
52 const char* kScriptChars =
53 "main() {\n"
54 " return [1, 2, 3];\n"
55 "}\n";
56 FLAG_verbose_gc = true;
57 Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
58 Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
59
60 EXPECT_VALID(result);
61 EXPECT(!Dart_IsNull(result));
62 EXPECT(Dart_IsList(result));
63 TransitionNativeToVM transition(thread);
64 GCTestHelper::CollectOldSpace();
65}
66#endif // !defined(PRODUCT)
67
68TEST_CASE(LargeSweep) {
69 const char* kScriptChars =
70 "main() {\n"
71 " return List.filled(8 * 1024 * 1024, null);\n"
72 "}\n";
73 NOT_IN_PRODUCT(FLAG_verbose_gc = true);
74 Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
75 Dart_EnterScope();
76 Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
77
78 EXPECT_VALID(result);
79 EXPECT(!Dart_IsNull(result));
80 EXPECT(Dart_IsList(result));
81 {
82 TransitionNativeToVM transition(thread);
83 GCTestHelper::CollectOldSpace();
84 }
85 Dart_ExitScope();
86 {
87 TransitionNativeToVM transition(thread);
88 GCTestHelper::CollectOldSpace();
89 }
90}
91
92#ifndef PRODUCT
93static ClassPtr GetClass(const Library& lib, const char* name) {
94 const Class& cls = Class::Handle(
95 lib.LookupClass(String::Handle(Symbols::New(Thread::Current(), name))));
96 EXPECT(!cls.IsNull()); // No ambiguity error expected.
97 return cls.raw();
98}
99
100TEST_CASE(ClassHeapStats) {
101 const char* kScriptChars =
102 "class A {\n"
103 " var a;\n"
104 " var b;\n"
105 "}\n"
106 ""
107 "main() {\n"
108 " var x = new A();\n"
109 " return new A();\n"
110 "}\n";
111 Dart_Handle h_lib = TestCase::LoadTestScript(kScriptChars, NULL);
112 Isolate* isolate = Isolate::Current();
113 ClassTable* class_table = isolate->class_table();
114 {
115 // GC before main so allocations during the tests don't cause unexpected GC.
116 TransitionNativeToVM transition(thread);
117 GCTestHelper::CollectAllGarbage();
118 }
119 Dart_EnterScope();
120 Dart_Handle result = Dart_Invoke(h_lib, NewString("main"), 0, NULL);
121 EXPECT_VALID(result);
122 EXPECT(!Dart_IsNull(result));
123 intptr_t cid;
124 {
125 TransitionNativeToVM transition(thread);
126 Library& lib = Library::Handle();
127 lib ^= Api::UnwrapHandle(h_lib);
128 EXPECT(!lib.IsNull());
129 const Class& cls = Class::Handle(GetClass(lib, "A"));
130 ASSERT(!cls.IsNull());
131 cid = cls.id();
132
133 {
134 // Verify preconditions: allocated twice in new space.
135 CountObjectsVisitor visitor(thread, class_table->NumCids());
136 HeapIterationScope iter(thread);
137 iter.IterateObjects(&visitor);
138 isolate->group()->VisitWeakPersistentHandles(&visitor);
139 EXPECT_EQ(2, visitor.new_count_[cid]);
140 EXPECT_EQ(0, visitor.old_count_[cid]);
141 }
142
143 // Perform GC.
144 GCTestHelper::CollectNewSpace();
145
146 {
147 // Verify postconditions: Only one survived.
148 CountObjectsVisitor visitor(thread, class_table->NumCids());
149 HeapIterationScope iter(thread);
150 iter.IterateObjects(&visitor);
151 isolate->group()->VisitWeakPersistentHandles(&visitor);
152 EXPECT_EQ(1, visitor.new_count_[cid]);
153 EXPECT_EQ(0, visitor.old_count_[cid]);
154 }
155
156 // Perform GC. The following is heavily dependent on the behaviour
157 // of the GC: Retained instance of A will be promoted.
158 GCTestHelper::CollectNewSpace();
159
160 {
161 // Verify postconditions: One promoted instance.
162 CountObjectsVisitor visitor(thread, class_table->NumCids());
163 HeapIterationScope iter(thread);
164 iter.IterateObjects(&visitor);
165 isolate->group()->VisitWeakPersistentHandles(&visitor);
166 EXPECT_EQ(0, visitor.new_count_[cid]);
167 EXPECT_EQ(1, visitor.old_count_[cid]);
168 }
169
170 // Perform a GC on new space.
171 GCTestHelper::CollectNewSpace();
172
173 {
174 // Verify postconditions:
175 CountObjectsVisitor visitor(thread, class_table->NumCids());
176 HeapIterationScope iter(thread);
177 iter.IterateObjects(&visitor);
178 isolate->group()->VisitWeakPersistentHandles(&visitor);
179 EXPECT_EQ(0, visitor.new_count_[cid]);
180 EXPECT_EQ(1, visitor.old_count_[cid]);
181 }
182
183 GCTestHelper::CollectOldSpace();
184
185 {
186 // Verify postconditions:
187 CountObjectsVisitor visitor(thread, class_table->NumCids());
188 HeapIterationScope iter(thread);
189 iter.IterateObjects(&visitor);
190 isolate->group()->VisitWeakPersistentHandles(&visitor);
191 EXPECT_EQ(0, visitor.new_count_[cid]);
192 EXPECT_EQ(1, visitor.old_count_[cid]);
193 }
194 }
195 // Exit scope, freeing instance.
196 Dart_ExitScope();
197 {
198 TransitionNativeToVM transition(thread);
199 // Perform GC.
200 GCTestHelper::CollectOldSpace();
201 {
202 // Verify postconditions:
203 CountObjectsVisitor visitor(thread, class_table->NumCids());
204 HeapIterationScope iter(thread);
205 iter.IterateObjects(&visitor);
206 isolate->group()->VisitWeakPersistentHandles(&visitor);
207 EXPECT_EQ(0, visitor.new_count_[cid]);
208 EXPECT_EQ(0, visitor.old_count_[cid]);
209 }
210 }
211}
212#endif // !PRODUCT
213
214class FindOnly : public FindObjectVisitor {
215 public:
216 explicit FindOnly(ObjectPtr target) : target_(target) {
217#if defined(DEBUG)
218 EXPECT_GT(Thread::Current()->no_safepoint_scope_depth(), 0);
219#endif
220 }
221 virtual ~FindOnly() {}
222
223 virtual bool FindObject(ObjectPtr obj) const { return obj == target_; }
224
225 private:
226 ObjectPtr target_;
227};
228
229class FindNothing : public FindObjectVisitor {
230 public:
231 FindNothing() {}
232 virtual ~FindNothing() {}
233 virtual bool FindObject(ObjectPtr obj) const { return false; }
234};
235
236ISOLATE_UNIT_TEST_CASE(FindObject) {
237 Isolate* isolate = Isolate::Current();
238 Heap* heap = isolate->heap();
239 Heap::Space spaces[2] = {Heap::kOld, Heap::kNew};
240 for (size_t space = 0; space < ARRAY_SIZE(spaces); ++space) {
241 const String& obj = String::Handle(String::New("x", spaces[space]));
242 {
243 HeapIterationScope iteration(thread);
244 NoSafepointScope no_safepoint;
245 FindOnly find_only(obj.raw());
246 EXPECT(obj.raw() == heap->FindObject(&find_only));
247 }
248 }
249 {
250 HeapIterationScope iteration(thread);
251 NoSafepointScope no_safepoint;
252 FindNothing find_nothing;
253 EXPECT(Object::null() == heap->FindObject(&find_nothing));
254 }
255}
256
257ISOLATE_UNIT_TEST_CASE(IterateReadOnly) {
258 const String& obj = String::Handle(String::New("x", Heap::kOld));
259
260 // It is not safe to make the heap read-only if marking or sweeping is in
261 // progress.
262 GCTestHelper::WaitForGCTasks();
263
264 Heap* heap = Thread::Current()->isolate()->heap();
265 EXPECT(heap->Contains(ObjectLayout::ToAddr(obj.raw())));
266 heap->WriteProtect(true);
267 EXPECT(heap->Contains(ObjectLayout::ToAddr(obj.raw())));
268 heap->WriteProtect(false);
269 EXPECT(heap->Contains(ObjectLayout::ToAddr(obj.raw())));
270}
271
272void TestBecomeForward(Heap::Space before_space, Heap::Space after_space) {
273 const String& before_obj = String::Handle(String::New("old", before_space));
274 const String& after_obj = String::Handle(String::New("new", after_space));
275
276 EXPECT(before_obj.raw() != after_obj.raw());
277
278 // Allocate the arrays in old space to test the remembered set.
279 const Array& before = Array::Handle(Array::New(1, Heap::kOld));
280 before.SetAt(0, before_obj);
281 const Array& after = Array::Handle(Array::New(1, Heap::kOld));
282 after.SetAt(0, after_obj);
283
284 Become::ElementsForwardIdentity(before, after);
285
286 EXPECT(before_obj.raw() == after_obj.raw());
287
288 GCTestHelper::CollectAllGarbage();
289
290 EXPECT(before_obj.raw() == after_obj.raw());
291}
292
293ISOLATE_UNIT_TEST_CASE(BecomeFowardOldToOld) {
294 TestBecomeForward(Heap::kOld, Heap::kOld);
295}
296
297ISOLATE_UNIT_TEST_CASE(BecomeFowardNewToNew) {
298 TestBecomeForward(Heap::kNew, Heap::kNew);
299}
300
301ISOLATE_UNIT_TEST_CASE(BecomeFowardOldToNew) {
302 TestBecomeForward(Heap::kOld, Heap::kNew);
303}
304
305ISOLATE_UNIT_TEST_CASE(BecomeFowardNewToOld) {
306 TestBecomeForward(Heap::kNew, Heap::kOld);
307}
308
309ISOLATE_UNIT_TEST_CASE(BecomeForwardPeer) {
310 Isolate* isolate = Isolate::Current();
311 Heap* heap = isolate->heap();
312
313 const Array& before_obj = Array::Handle(Array::New(0, Heap::kOld));
314 const Array& after_obj = Array::Handle(Array::New(0, Heap::kOld));
315 EXPECT(before_obj.raw() != after_obj.raw());
316
317 void* peer = reinterpret_cast<void*>(42);
318 void* no_peer = reinterpret_cast<void*>(0);
319 heap->SetPeer(before_obj.raw(), peer);
320 EXPECT_EQ(peer, heap->GetPeer(before_obj.raw()));
321 EXPECT_EQ(no_peer, heap->GetPeer(after_obj.raw()));
322
323 const Array& before = Array::Handle(Array::New(1, Heap::kOld));
324 before.SetAt(0, before_obj);
325 const Array& after = Array::Handle(Array::New(1, Heap::kOld));
326 after.SetAt(0, after_obj);
327 Become::ElementsForwardIdentity(before, after);
328
329 EXPECT(before_obj.raw() == after_obj.raw());
330 EXPECT_EQ(peer, heap->GetPeer(before_obj.raw()));
331 EXPECT_EQ(peer, heap->GetPeer(after_obj.raw()));
332}
333
334ISOLATE_UNIT_TEST_CASE(BecomeForwardRememberedObject) {
335 const String& new_element = String::Handle(String::New("new", Heap::kNew));
336 const String& old_element = String::Handle(String::New("old", Heap::kOld));
337 const Array& before_obj = Array::Handle(Array::New(1, Heap::kOld));
338 const Array& after_obj = Array::Handle(Array::New(1, Heap::kOld));
339 before_obj.SetAt(0, new_element);
340 after_obj.SetAt(0, old_element);
341 EXPECT(before_obj.raw()->ptr()->IsRemembered());
342 EXPECT(!after_obj.raw()->ptr()->IsRemembered());
343
344 EXPECT(before_obj.raw() != after_obj.raw());
345
346 const Array& before = Array::Handle(Array::New(1, Heap::kOld));
347 before.SetAt(0, before_obj);
348 const Array& after = Array::Handle(Array::New(1, Heap::kOld));
349 after.SetAt(0, after_obj);
350
351 Become::ElementsForwardIdentity(before, after);
352
353 EXPECT(before_obj.raw() == after_obj.raw());
354 EXPECT(!after_obj.raw()->ptr()->IsRemembered());
355
356 GCTestHelper::CollectAllGarbage();
357
358 EXPECT(before_obj.raw() == after_obj.raw());
359}
360
361ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_DeadOldToNew) {
362 Isolate* isolate = Isolate::Current();
363 Heap* heap = isolate->heap();
364
365 heap->CollectAllGarbage();
366 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
367 intptr_t size_before =
368 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
369
370 Array& old = Array::Handle(Array::New(1, Heap::kOld));
371 Array& neu = Array::Handle(Array::New(1, Heap::kNew));
372 old.SetAt(0, neu);
373 old = Array::null();
374 neu = Array::null();
375
376 heap->CollectAllGarbage();
377 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
378
379 intptr_t size_after =
380 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
381
382 EXPECT_EQ(size_before, size_after);
383}
384
385ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_DeadNewToOld) {
386 Isolate* isolate = Isolate::Current();
387 Heap* heap = isolate->heap();
388
389 heap->CollectAllGarbage();
390 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
391 intptr_t size_before =
392 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
393
394 Array& old = Array::Handle(Array::New(1, Heap::kOld));
395 Array& neu = Array::Handle(Array::New(1, Heap::kNew));
396 neu.SetAt(0, old);
397 old = Array::null();
398 neu = Array::null();
399
400 heap->CollectAllGarbage();
401 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
402
403 intptr_t size_after =
404 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
405
406 EXPECT_EQ(size_before, size_after);
407}
408
409ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_DeadGenCycle) {
410 Isolate* isolate = Isolate::Current();
411 Heap* heap = isolate->heap();
412
413 heap->CollectAllGarbage();
414 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
415 intptr_t size_before =
416 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
417
418 Array& old = Array::Handle(Array::New(1, Heap::kOld));
419 Array& neu = Array::Handle(Array::New(1, Heap::kNew));
420 neu.SetAt(0, old);
421 old.SetAt(0, neu);
422 old = Array::null();
423 neu = Array::null();
424
425 heap->CollectAllGarbage();
426 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
427
428 intptr_t size_after =
429 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
430
431 EXPECT_EQ(size_before, size_after);
432}
433
434ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveNewToOld) {
435 Isolate* isolate = Isolate::Current();
436 Heap* heap = isolate->heap();
437
438 heap->CollectAllGarbage();
439 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
440 intptr_t size_before =
441 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
442
443 Array& old = Array::Handle(Array::New(1, Heap::kOld));
444 Array& neu = Array::Handle(Array::New(1, Heap::kNew));
445 neu.SetAt(0, old);
446 old = Array::null();
447
448 heap->CollectAllGarbage();
449 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
450
451 intptr_t size_after =
452 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
453
454 EXPECT(size_before < size_after);
455}
456
457ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveOldToNew) {
458 Isolate* isolate = Isolate::Current();
459 Heap* heap = isolate->heap();
460
461 heap->CollectAllGarbage();
462 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
463 intptr_t size_before =
464 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
465
466 Array& old = Array::Handle(Array::New(1, Heap::kOld));
467 Array& neu = Array::Handle(Array::New(1, Heap::kNew));
468 old.SetAt(0, neu);
469 neu = Array::null();
470
471 heap->CollectAllGarbage();
472 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
473
474 intptr_t size_after =
475 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
476
477 EXPECT(size_before < size_after);
478}
479
480ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveOldDeadNew) {
481 Isolate* isolate = Isolate::Current();
482 Heap* heap = isolate->heap();
483
484 heap->CollectAllGarbage();
485 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
486 intptr_t size_before =
487 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
488
489 Array& old = Array::Handle(Array::New(1, Heap::kOld));
490 Array& neu = Array::Handle(Array::New(1, Heap::kNew));
491 neu = Array::null();
492 old.SetAt(0, old);
493
494 heap->CollectAllGarbage();
495 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
496
497 intptr_t size_after =
498 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
499
500 EXPECT(size_before < size_after);
501}
502
503ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveNewDeadOld) {
504 Isolate* isolate = Isolate::Current();
505 Heap* heap = isolate->heap();
506
507 heap->CollectAllGarbage();
508 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
509 intptr_t size_before =
510 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
511
512 Array& old = Array::Handle(Array::New(1, Heap::kOld));
513 Array& neu = Array::Handle(Array::New(1, Heap::kNew));
514 old = Array::null();
515 neu.SetAt(0, neu);
516
517 heap->CollectAllGarbage();
518 heap->WaitForMarkerTasks(thread); // Finalize marking to get live size.
519
520 intptr_t size_after =
521 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
522
523 EXPECT(size_before < size_after);
524}
525
526ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveNewToOldChain) {
527 Isolate* isolate = Isolate::Current();
528 Heap* heap = isolate->heap();
529
530 heap->CollectAllGarbage();
531 intptr_t size_before =
532 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
533
534 Array& old = Array::Handle(Array::New(1, Heap::kOld));
535 Array& old2 = Array::Handle(Array::New(1, Heap::kOld));
536 Array& neu = Array::Handle(Array::New(1, Heap::kNew));
537 old.SetAt(0, old2);
538 neu.SetAt(0, old);
539 old = Array::null();
540 old2 = Array::null();
541
542 heap->CollectAllGarbage();
543
544 intptr_t size_after =
545 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
546
547 EXPECT(size_before < size_after);
548}
549
550ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveOldToNewChain) {
551 Isolate* isolate = Isolate::Current();
552 Heap* heap = isolate->heap();
553
554 heap->CollectAllGarbage();
555 intptr_t size_before =
556 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
557
558 Array& old = Array::Handle(Array::New(1, Heap::kOld));
559 Array& neu = Array::Handle(Array::New(1, Heap::kNew));
560 Array& neu2 = Array::Handle(Array::New(1, Heap::kOld));
561 neu.SetAt(0, neu2);
562 old.SetAt(0, neu);
563 neu = Array::null();
564 neu2 = Array::null();
565
566 heap->CollectAllGarbage();
567
568 intptr_t size_after =
569 heap->new_space()->UsedInWords() + heap->old_space()->UsedInWords();
570
571 EXPECT(size_before < size_after);
572}
573
574static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
575
576ISOLATE_UNIT_TEST_CASE(ExternalPromotion) {
577 Isolate* isolate = Isolate::Current();
578 Heap* heap = isolate->heap();
579
580 heap->CollectAllGarbage();
581 intptr_t size_before = kWordSize * (heap->new_space()->ExternalInWords() +
582 heap->old_space()->ExternalInWords());
583
584 Array& old = Array::Handle(Array::New(100, Heap::kOld));
585 Array& neu = Array::Handle();
586 for (intptr_t i = 0; i < 100; i++) {
587 neu = Array::New(1, Heap::kNew);
588 FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB,
589 /*auto_delete=*/true);
590 old.SetAt(i, neu);
591 }
592
593 intptr_t size_middle = kWordSize * (heap->new_space()->ExternalInWords() +
594 heap->old_space()->ExternalInWords());
595 EXPECT_EQ(size_before + 100 * MB, size_middle);
596
597 old = Array::null();
598 neu = Array::null();
599
600 heap->CollectAllGarbage();
601
602 intptr_t size_after = kWordSize * (heap->new_space()->ExternalInWords() +
603 heap->old_space()->ExternalInWords());
604
605 EXPECT_EQ(size_before, size_after);
606}
607
608#if !defined(PRODUCT)
609class HeapTestHelper {
610 public:
611 static void Scavenge(Thread* thread) {
612 thread->heap()->CollectNewSpaceGarbage(thread, Heap::kDebugging);
613 }
614 static void MarkSweep(Thread* thread) {
615 thread->heap()->CollectOldSpaceGarbage(thread, Heap::kMarkSweep,
616 Heap::kDebugging);
617 thread->heap()->WaitForMarkerTasks(thread);
618 thread->heap()->WaitForSweeperTasks(thread);
619 }
620};
621
622class MergeIsolatesHeapsHandler : public MessageHandler {
623 public:
624 explicit MergeIsolatesHeapsHandler(Isolate* owner)
625 : msg_(Utils::CreateCStringUniquePtr(nullptr)), owner_(owner) {}
626
627 const char* name() const { return "merge-isolates-heaps-handler"; }
628
629 ~MergeIsolatesHeapsHandler() { PortMap::ClosePorts(this); }
630
631 MessageStatus HandleMessage(std::unique_ptr<Message> message) {
632 // Parse the message.
633 Object& response_obj = Object::Handle();
634 if (message->IsRaw()) {
635 response_obj = message->raw_obj();
636 } else if (message->IsBequest()) {
637 Bequest* bequest = message->bequest();
638 PersistentHandle* handle = bequest->handle();
639 // Object in the receiving isolate's heap.
640 EXPECT(isolate()->heap()->Contains(ObjectLayout::ToAddr(handle->raw())));
641 response_obj = handle->raw();
642 isolate()->group()->api_state()->FreePersistentHandle(handle);
643 } else {
644 Thread* thread = Thread::Current();
645 MessageSnapshotReader reader(message.get(), thread);
646 response_obj = reader.ReadObject();
647 }
648 if (response_obj.IsString()) {
649 String& response = String::Handle();
650 response ^= response_obj.raw();
651 msg_.reset(Utils::StrDup(response.ToCString()));
652 } else {
653 ASSERT(response_obj.IsArray());
654 Array& response_array = Array::Handle();
655 response_array ^= response_obj.raw();
656 ASSERT(response_array.Length() == 1);
657 ExternalTypedData& response = ExternalTypedData::Handle();
658 response ^= response_array.At(0);
659 msg_.reset(Utils::StrDup(reinterpret_cast<char*>(response.DataAddr(0))));
660 }
661
662 return kOK;
663 }
664
665 const char* msg() const { return msg_.get(); }
666
667 virtual Isolate* isolate() const { return owner_; }
668
669 private:
670 Utils::CStringUniquePtr msg_;
671 Isolate* owner_;
672};
673
674VM_UNIT_TEST_CASE(CleanupBequestNeverReceived) {
675 const char* TEST_MESSAGE = "hello, world";
676 Dart_Isolate parent = TestCase::CreateTestIsolate("parent");
677 EXPECT_EQ(parent, Dart_CurrentIsolate());
678 {
679 MergeIsolatesHeapsHandler handler(Isolate::Current());
680 Dart_Port port_id = PortMap::CreatePort(&handler);
681 EXPECT_EQ(PortMap::GetIsolate(port_id), Isolate::Current());
682 Dart_ExitIsolate();
683
684 Dart_Isolate worker = TestCase::CreateTestIsolateInGroup("worker", parent);
685 EXPECT_EQ(worker, Dart_CurrentIsolate());
686 {
687 Thread* thread = Thread::Current();
688 TransitionNativeToVM transition(thread);
689 StackZone zone(thread);
690 HANDLESCOPE(thread);
691
692 String& string = String::Handle(String::New(TEST_MESSAGE));
693 PersistentHandle* handle =
694 Isolate::Current()->group()->api_state()->AllocatePersistentHandle();
695 handle->set_raw(string.raw());
696
697 reinterpret_cast<Isolate*>(worker)->bequeath(
698 std::unique_ptr<Bequest>(new Bequest(handle, port_id)));
699 }
700 }
701 Dart_ShutdownIsolate();
702 Dart_EnterIsolate(parent);
703 Dart_ShutdownIsolate();
704}
705
706VM_UNIT_TEST_CASE(ReceivesSendAndExitMessage) {
707 const char* TEST_MESSAGE = "hello, world";
708 Dart_Isolate parent = TestCase::CreateTestIsolate("parent");
709 EXPECT_EQ(parent, Dart_CurrentIsolate());
710 MergeIsolatesHeapsHandler handler(Isolate::Current());
711 Dart_Port port_id = PortMap::CreatePort(&handler);
712 EXPECT_EQ(PortMap::GetIsolate(port_id), Isolate::Current());
713 Dart_ExitIsolate();
714
715 Dart_Isolate worker = TestCase::CreateTestIsolateInGroup("worker", parent);
716 EXPECT_EQ(worker, Dart_CurrentIsolate());
717 {
718 Thread* thread = Thread::Current();
719 TransitionNativeToVM transition(thread);
720 StackZone zone(thread);
721 HANDLESCOPE(thread);
722
723 String& string = String::Handle(String::New(TEST_MESSAGE));
724
725 PersistentHandle* handle =
726 Isolate::Current()->group()->api_state()->AllocatePersistentHandle();
727 handle->set_raw(string.raw());
728
729 reinterpret_cast<Isolate*>(worker)->bequeath(
730 std::unique_ptr<Bequest>(new Bequest(handle, port_id)));
731 }
732
733 Dart_ShutdownIsolate();
734 Dart_EnterIsolate(parent);
735 {
736 Thread* thread = Thread::Current();
737 TransitionNativeToVM transition(thread);
738 StackZone zone(thread);
739 HANDLESCOPE(thread);
740
741 EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage());
742 }
743 EXPECT_STREQ(handler.msg(), TEST_MESSAGE);
744 Dart_ShutdownIsolate();
745}
746
747ISOLATE_UNIT_TEST_CASE(ExternalAllocationStats) {
748 Isolate* isolate = thread->isolate();
749 Heap* heap = thread->heap();
750
751 Array& old = Array::Handle(Array::New(100, Heap::kOld));
752 Array& neu = Array::Handle();
753 for (intptr_t i = 0; i < 100; i++) {
754 neu = Array::New(1, Heap::kNew);
755 FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB,
756 /*auto_delete=*/true);
757 old.SetAt(i, neu);
758
759 if ((i % 4) == 0) {
760 HeapTestHelper::MarkSweep(thread);
761 } else {
762 HeapTestHelper::Scavenge(thread);
763 }
764
765 CountObjectsVisitor visitor(thread, isolate->class_table()->NumCids());
766 HeapIterationScope iter(thread);
767 iter.IterateObjects(&visitor);
768 isolate->group()->VisitWeakPersistentHandles(&visitor);
769 EXPECT_LE(visitor.old_external_size_[kArrayCid],
770 heap->old_space()->ExternalInWords() * kWordSize);
771 EXPECT_LE(visitor.new_external_size_[kArrayCid],
772 heap->new_space()->ExternalInWords() * kWordSize);
773 }
774}
775#endif // !defined(PRODUCT)
776
777ISOLATE_UNIT_TEST_CASE(ArrayTruncationRaces) {
778 // Alternate between allocating new lists and truncating.
779 // For each list, the life cycle is
780 // 1) the list is allocated and filled with some elements
781 // 2) kNumLists other lists are allocated
782 // 3) the list's backing store is truncated; the list becomes unreachable
783 // 4) kNumLists other lists are allocated
784 // 5) the backing store becomes unreachable
785 // The goal is to cause truncation *during* concurrent mark or sweep, by
786 // truncating an array that had been alive for a while and will be visited by
787 // a GC triggering by the allocations in step 2.
788
789 intptr_t kMaxListLength = 100;
790 intptr_t kNumLists = 1000;
791 Array& lists = Array::Handle(Array::New(kNumLists));
792 Array& arrays = Array::Handle(Array::New(kNumLists));
793
794 GrowableObjectArray& list = GrowableObjectArray::Handle();
795 Array& array = Array::Handle();
796 Object& element = Object::Handle();
797
798 for (intptr_t i = 0; i < kNumLists; i++) {
799 list = GrowableObjectArray::New(Heap::kNew);
800 intptr_t length = i % kMaxListLength;
801 for (intptr_t j = 0; j < length; j++) {
802 list.Add(element, Heap::kNew);
803 }
804 lists.SetAt(i, list);
805 }
806
807 intptr_t kTruncations = 100000;
808 for (intptr_t i = 0; i < kTruncations; i++) {
809 list ^= lists.At(i % kNumLists);
810 array = Array::MakeFixedLength(list);
811 arrays.SetAt(i % kNumLists, array);
812
813 list = GrowableObjectArray::New(Heap::kOld);
814 intptr_t length = i % kMaxListLength;
815 for (intptr_t j = 0; j < length; j++) {
816 list.Add(element, Heap::kOld);
817 }
818 lists.SetAt(i % kNumLists, list);
819 }
820}
821
822} // namespace dart
823