blob: b331f6bf3a5a8a3eee261e14449ccf96a57a8ecc [file] [log] [blame]
Ben Murdoch61f157c2016-09-16 13:49:30 +01001// Copyright 2016 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/heap/array-buffer-tracker.h"
6#include "test/cctest/cctest.h"
7#include "test/cctest/heap/heap-utils.h"
8
9namespace {
10
11typedef i::LocalArrayBufferTracker LocalTracker;
12
13bool IsTracked(i::JSArrayBuffer* buf) {
14 return i::ArrayBufferTracker::IsTracked(buf);
15}
16
17} // namespace
18
19namespace v8 {
20namespace internal {
21
22// The following tests make sure that JSArrayBuffer tracking works expected when
23// moving the objects through various spaces during GC phases.
24
25TEST(ArrayBuffer_OnlyMC) {
26 CcTest::InitializeVM();
27 LocalContext env;
28 v8::Isolate* isolate = env->GetIsolate();
29 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
30
31 JSArrayBuffer* raw_ab = nullptr;
32 {
33 v8::HandleScope handle_scope(isolate);
34 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100);
35 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab);
36 CHECK(IsTracked(*buf));
37 heap::GcAndSweep(heap, OLD_SPACE);
38 CHECK(IsTracked(*buf));
39 heap::GcAndSweep(heap, OLD_SPACE);
40 CHECK(IsTracked(*buf));
41 raw_ab = *buf;
42 // Prohibit page from being released.
43 Page::FromAddress(buf->address())->MarkNeverEvacuate();
44 }
45 // 2 GCs are needed because we promote to old space as live, meaning that
46 // we will survive one GC.
47 heap::GcAndSweep(heap, OLD_SPACE);
48 heap::GcAndSweep(heap, OLD_SPACE);
49 CHECK(!IsTracked(raw_ab));
50}
51
52TEST(ArrayBuffer_OnlyScavenge) {
53 CcTest::InitializeVM();
54 LocalContext env;
55 v8::Isolate* isolate = env->GetIsolate();
56 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
57
58 JSArrayBuffer* raw_ab = nullptr;
59 {
60 v8::HandleScope handle_scope(isolate);
61 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100);
62 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab);
63 CHECK(IsTracked(*buf));
64 heap::GcAndSweep(heap, NEW_SPACE);
65 CHECK(IsTracked(*buf));
66 heap::GcAndSweep(heap, NEW_SPACE);
67 CHECK(IsTracked(*buf));
68 heap::GcAndSweep(heap, NEW_SPACE);
69 CHECK(IsTracked(*buf));
70 raw_ab = *buf;
71 // Prohibit page from being released.
72 Page::FromAddress(buf->address())->MarkNeverEvacuate();
73 }
74 // 2 GCs are needed because we promote to old space as live, meaning that
75 // we will survive one GC.
76 heap::GcAndSweep(heap, OLD_SPACE);
77 heap::GcAndSweep(heap, OLD_SPACE);
78 CHECK(!IsTracked(raw_ab));
79}
80
81TEST(ArrayBuffer_ScavengeAndMC) {
82 CcTest::InitializeVM();
83 LocalContext env;
84 v8::Isolate* isolate = env->GetIsolate();
85 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
86
87 JSArrayBuffer* raw_ab = nullptr;
88 {
89 v8::HandleScope handle_scope(isolate);
90 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100);
91 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab);
92 CHECK(IsTracked(*buf));
93 heap::GcAndSweep(heap, NEW_SPACE);
94 CHECK(IsTracked(*buf));
95 heap::GcAndSweep(heap, NEW_SPACE);
96 CHECK(IsTracked(*buf));
97 heap::GcAndSweep(heap, OLD_SPACE);
98 CHECK(IsTracked(*buf));
99 heap::GcAndSweep(heap, NEW_SPACE);
100 CHECK(IsTracked(*buf));
101 raw_ab = *buf;
102 // Prohibit page from being released.
103 Page::FromAddress(buf->address())->MarkNeverEvacuate();
104 }
105 // 2 GCs are needed because we promote to old space as live, meaning that
106 // we will survive one GC.
107 heap::GcAndSweep(heap, OLD_SPACE);
108 heap::GcAndSweep(heap, OLD_SPACE);
109 CHECK(!IsTracked(raw_ab));
110}
111
112TEST(ArrayBuffer_Compaction) {
113 FLAG_manual_evacuation_candidates_selection = true;
114 CcTest::InitializeVM();
115 LocalContext env;
116 v8::Isolate* isolate = env->GetIsolate();
117 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
118 heap::AbandonCurrentlyFreeMemory(heap->old_space());
119
120 v8::HandleScope handle_scope(isolate);
121 Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::New(isolate, 100);
122 Handle<JSArrayBuffer> buf1 = v8::Utils::OpenHandle(*ab1);
123 CHECK(IsTracked(*buf1));
124 heap::GcAndSweep(heap, NEW_SPACE);
125 heap::GcAndSweep(heap, NEW_SPACE);
126
127 Page* page_before_gc = Page::FromAddress(buf1->address());
128 page_before_gc->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
129 CHECK(IsTracked(*buf1));
130
131 heap->CollectAllGarbage();
132
133 Page* page_after_gc = Page::FromAddress(buf1->address());
134 CHECK(IsTracked(*buf1));
135
136 CHECK_NE(page_before_gc, page_after_gc);
137}
138
139TEST(ArrayBuffer_UnregisterDuringSweep) {
140// Regular pages in old space (without compaction) are processed concurrently
141// in the sweeper. If we happen to unregister a buffer (either explicitly, or
142// implicitly through e.g. |Externalize|) we need to sync with the sweeper
143// task.
144//
145// Note: This test will will only fail on TSAN configurations.
146
147// Disable verify-heap since it forces sweeping to be completed in the
148// epilogue of the GC.
149#ifdef VERIFY_HEAP
150 i::FLAG_verify_heap = false;
151#endif // VERIFY_HEAP
152
153 CcTest::InitializeVM();
154 LocalContext env;
155 v8::Isolate* isolate = env->GetIsolate();
156 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
157 {
158 v8::HandleScope handle_scope(isolate);
159 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100);
160 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab);
161
162 {
163 v8::HandleScope handle_scope(isolate);
164 // Allocate another buffer on the same page to force processing a
165 // non-empty set of buffers in the last GC.
166 Local<v8::ArrayBuffer> ab2 = v8::ArrayBuffer::New(isolate, 100);
167 Handle<JSArrayBuffer> buf2 = v8::Utils::OpenHandle(*ab2);
168 CHECK(IsTracked(*buf));
169 CHECK(IsTracked(*buf));
170 heap::GcAndSweep(heap, NEW_SPACE);
171 CHECK(IsTracked(*buf));
172 CHECK(IsTracked(*buf));
173 heap::GcAndSweep(heap, NEW_SPACE);
174 CHECK(IsTracked(*buf));
175 CHECK(IsTracked(*buf2));
176 }
177
178 heap->CollectGarbage(OLD_SPACE);
179 // |Externalize| will cause the buffer to be |Unregister|ed. Without
180 // barriers and proper synchronization this will trigger a data race on
181 // TSAN.
182 v8::ArrayBuffer::Contents contents = ab->Externalize();
183 heap->isolate()->array_buffer_allocator()->Free(contents.Data(),
184 contents.ByteLength());
185 }
186}
187
188TEST(ArrayBuffer_NonLivePromotion) {
189 // The test verifies that the marking state is preserved when promoting
190 // a buffer to old space.
191 CcTest::InitializeVM();
192 LocalContext env;
193 v8::Isolate* isolate = env->GetIsolate();
194 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
195
196 JSArrayBuffer* raw_ab = nullptr;
197 {
198 v8::HandleScope handle_scope(isolate);
199 Handle<FixedArray> root =
200 heap->isolate()->factory()->NewFixedArray(1, TENURED);
201 {
202 v8::HandleScope handle_scope(isolate);
203 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100);
204 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab);
205 root->set(0, *buf); // Buffer that should not be promoted as live.
206 }
207 heap::SimulateIncrementalMarking(heap, false);
208 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0))));
209 heap::GcAndSweep(heap, NEW_SPACE);
210 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0))));
211 heap::GcAndSweep(heap, NEW_SPACE);
212 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0))));
213 raw_ab = JSArrayBuffer::cast(root->get(0));
214 root->set(0, heap->undefined_value());
215 heap::SimulateIncrementalMarking(heap, true);
216 // Prohibit page from being released.
217 Page::FromAddress(raw_ab->address())->MarkNeverEvacuate();
218 heap::GcAndSweep(heap, OLD_SPACE);
219 CHECK(!IsTracked(raw_ab));
220 }
221}
222
223TEST(ArrayBuffer_LivePromotion) {
224 // The test verifies that the marking state is preserved when promoting
225 // a buffer to old space.
226 CcTest::InitializeVM();
227 LocalContext env;
228 v8::Isolate* isolate = env->GetIsolate();
229 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
230
231 JSArrayBuffer* raw_ab = nullptr;
232 {
233 v8::HandleScope handle_scope(isolate);
234 Handle<FixedArray> root =
235 heap->isolate()->factory()->NewFixedArray(1, TENURED);
236 {
237 v8::HandleScope handle_scope(isolate);
238 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100);
239 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab);
240 root->set(0, *buf); // Buffer that should be promoted as live.
241 }
242 heap::SimulateIncrementalMarking(heap, true);
243 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0))));
244 heap::GcAndSweep(heap, NEW_SPACE);
245 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0))));
246 heap::GcAndSweep(heap, NEW_SPACE);
247 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0))));
248 raw_ab = JSArrayBuffer::cast(root->get(0));
249 root->set(0, heap->undefined_value());
250 // Prohibit page from being released.
251 Page::FromAddress(raw_ab->address())->MarkNeverEvacuate();
252 heap::GcAndSweep(heap, OLD_SPACE);
253 CHECK(IsTracked(raw_ab));
254 }
255}
256
257TEST(ArrayBuffer_SemiSpaceCopyThenPagePromotion) {
258 // The test verifies that the marking state is preserved across semispace
259 // copy.
260 CcTest::InitializeVM();
261 LocalContext env;
262 v8::Isolate* isolate = env->GetIsolate();
263 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
264
265 heap::SealCurrentObjects(heap);
266 {
267 v8::HandleScope handle_scope(isolate);
268 Handle<FixedArray> root =
269 heap->isolate()->factory()->NewFixedArray(1, TENURED);
270 {
271 v8::HandleScope handle_scope(isolate);
272 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100);
273 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab);
274 root->set(0, *buf); // Buffer that should be promoted as live.
275 Page::FromAddress(buf->address())->MarkNeverEvacuate();
276 }
277 std::vector<Handle<FixedArray>> handles;
278 // Make the whole page transition from new->old, getting the buffers
279 // processed in the sweeper (relying on marking information) instead of
280 // processing during newspace evacuation.
281 heap::FillCurrentPage(heap->new_space(), &handles);
282 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0))));
283 heap::GcAndSweep(heap, NEW_SPACE);
284 heap::SimulateIncrementalMarking(heap, true);
285 heap::GcAndSweep(heap, OLD_SPACE);
286 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0))));
287 }
288}
289
290UNINITIALIZED_TEST(ArrayBuffer_SemiSpaceCopyMultipleTasks) {
291 if (FLAG_optimize_for_size) return;
292 // Test allocates JSArrayBuffer on different pages before triggering a
293 // full GC that performs the semispace copy. If parallelized, this test
294 // ensures proper synchronization in TSAN configurations.
295 FLAG_min_semi_space_size = 2 * Page::kPageSize / MB;
296 v8::Isolate::CreateParams create_params;
297 create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
298 v8::Isolate* isolate = v8::Isolate::New(create_params);
299 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
300 {
301 v8::Isolate::Scope isolate_scope(isolate);
302 v8::HandleScope handle_scope(isolate);
303 v8::Context::New(isolate)->Enter();
304 Heap* heap = i_isolate->heap();
305
306 Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::New(isolate, 100);
307 Handle<JSArrayBuffer> buf1 = v8::Utils::OpenHandle(*ab1);
308 heap::FillCurrentPage(heap->new_space());
309 Local<v8::ArrayBuffer> ab2 = v8::ArrayBuffer::New(isolate, 100);
310 Handle<JSArrayBuffer> buf2 = v8::Utils::OpenHandle(*ab2);
311 CHECK_NE(Page::FromAddress(buf1->address()),
312 Page::FromAddress(buf2->address()));
313 heap::GcAndSweep(heap, OLD_SPACE);
314 }
315}
316
317} // namespace internal
318} // namespace v8