1// Copyright 2020 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/concurrent-allocator.h"
6
7#include "src/common/globals.h"
8#include "src/execution/isolate.h"
9#include "src/handles/persistent-handles.h"
10#include "src/heap/concurrent-allocator-inl.h"
11#include "src/heap/heap.h"
12#include "src/heap/local-heap-inl.h"
13#include "src/heap/local-heap.h"
14#include "src/heap/marking.h"
15#include "src/heap/memory-chunk.h"
16#include "src/heap/parked-scope.h"
17
18namespace v8 {
19namespace internal {
20
21void StressConcurrentAllocatorTask::RunInternal() {
22  Heap* heap = isolate_->heap();
23  LocalHeap local_heap(heap, ThreadKind::kBackground);
24  UnparkedScope unparked_scope(&local_heap);
25
26  const int kNumIterations = 2000;
27  const int kSmallObjectSize = 10 * kTaggedSize;
28  const int kMediumObjectSize = 8 * KB;
29  const int kLargeObjectSize =
30      static_cast<int>(MemoryChunk::kPageSize -
31                       MemoryChunkLayout::ObjectStartOffsetInDataPage());
32
33  for (int i = 0; i < kNumIterations; i++) {
34    // Isolate tear down started, stop allocation...
35    if (heap->gc_state() == Heap::TEAR_DOWN) return;
36
37    AllocationResult result = local_heap.AllocateRaw(
38        kSmallObjectSize, AllocationType::kOld, AllocationOrigin::kRuntime,
39        AllocationAlignment::kTaggedAligned);
40    if (!result.IsFailure()) {
41      heap->CreateFillerObjectAtBackground(
42          result.ToAddress(), kSmallObjectSize,
43          ClearFreedMemoryMode::kDontClearFreedMemory);
44    } else {
45      local_heap.TryPerformCollection();
46    }
47
48    result = local_heap.AllocateRaw(kMediumObjectSize, AllocationType::kOld,
49                                    AllocationOrigin::kRuntime,
50                                    AllocationAlignment::kTaggedAligned);
51    if (!result.IsFailure()) {
52      heap->CreateFillerObjectAtBackground(
53          result.ToAddress(), kMediumObjectSize,
54          ClearFreedMemoryMode::kDontClearFreedMemory);
55    } else {
56      local_heap.TryPerformCollection();
57    }
58
59    result = local_heap.AllocateRaw(kLargeObjectSize, AllocationType::kOld,
60                                    AllocationOrigin::kRuntime,
61                                    AllocationAlignment::kTaggedAligned);
62    if (!result.IsFailure()) {
63      heap->CreateFillerObjectAtBackground(
64          result.ToAddress(), kLargeObjectSize,
65          ClearFreedMemoryMode::kDontClearFreedMemory);
66    } else {
67      local_heap.TryPerformCollection();
68    }
69    local_heap.Safepoint();
70  }
71
72  Schedule(isolate_);
73}
74
75// static
76void StressConcurrentAllocatorTask::Schedule(Isolate* isolate) {
77  auto task = std::make_unique<StressConcurrentAllocatorTask>(isolate);
78  const double kDelayInSeconds = 0.1;
79  V8::GetCurrentPlatform()->CallDelayedOnWorkerThread(std::move(task),
80                                                      kDelayInSeconds);
81}
82
83void ConcurrentAllocator::FreeLinearAllocationArea() {
84  // The code page of the linear allocation area needs to be unprotected
85  // because we are going to write a filler into that memory area below.
86  base::Optional<CodePageMemoryModificationScope> optional_scope;
87  if (lab_.IsValid() && space_->identity() == CODE_SPACE) {
88    optional_scope.emplace(MemoryChunk::FromAddress(lab_.top()));
89  }
90  lab_.CloseAndMakeIterable();
91}
92
93void ConcurrentAllocator::MakeLinearAllocationAreaIterable() {
94  // The code page of the linear allocation area needs to be unprotected
95  // because we are going to write a filler into that memory area below.
96  base::Optional<CodePageMemoryModificationScope> optional_scope;
97  if (lab_.IsValid() && space_->identity() == CODE_SPACE) {
98    optional_scope.emplace(MemoryChunk::FromAddress(lab_.top()));
99  }
100  lab_.MakeIterable();
101}
102
103void ConcurrentAllocator::MarkLinearAllocationAreaBlack() {
104  Address top = lab_.top();
105  Address limit = lab_.limit();
106
107  if (top != kNullAddress && top != limit) {
108    Page::FromAllocationAreaAddress(top)->CreateBlackAreaBackground(top, limit);
109  }
110}
111
112void ConcurrentAllocator::UnmarkLinearAllocationArea() {
113  Address top = lab_.top();
114  Address limit = lab_.limit();
115
116  if (top != kNullAddress && top != limit) {
117    Page::FromAllocationAreaAddress(top)->DestroyBlackAreaBackground(top,
118                                                                     limit);
119  }
120}
121
122AllocationResult ConcurrentAllocator::AllocateInLabSlow(
123    int object_size, AllocationAlignment alignment, AllocationOrigin origin) {
124  if (!EnsureLab(origin)) {
125    return AllocationResult::Failure();
126  }
127
128  AllocationResult allocation = lab_.AllocateRawAligned(object_size, alignment);
129  DCHECK(!allocation.IsFailure());
130
131  return allocation;
132}
133
134bool ConcurrentAllocator::EnsureLab(AllocationOrigin origin) {
135  auto result = space_->RawRefillLabBackground(
136      local_heap_, kLabSize, kMaxLabSize, kTaggedAligned, origin);
137  if (!result) return false;
138
139  if (IsBlackAllocationEnabled()) {
140    Address top = result->first;
141    Address limit = top + result->second;
142    Page::FromAllocationAreaAddress(top)->CreateBlackAreaBackground(top, limit);
143  }
144
145  HeapObject object = HeapObject::FromAddress(result->first);
146  LocalAllocationBuffer saved_lab = std::move(lab_);
147  lab_ = LocalAllocationBuffer::FromResult(
148      space_->heap(), AllocationResult::FromObject(object), result->second);
149  DCHECK(lab_.IsValid());
150  if (!lab_.TryMerge(&saved_lab)) {
151    saved_lab.CloseAndMakeIterable();
152  }
153  return true;
154}
155
156AllocationResult ConcurrentAllocator::AllocateOutsideLab(
157    int object_size, AllocationAlignment alignment, AllocationOrigin origin) {
158  auto result = space_->RawRefillLabBackground(local_heap_, object_size,
159                                               object_size, alignment, origin);
160  if (!result) return AllocationResult::Failure();
161
162  HeapObject object = HeapObject::FromAddress(result->first);
163
164  if (IsBlackAllocationEnabled()) {
165    owning_heap()->incremental_marking()->MarkBlackBackground(object,
166                                                              object_size);
167  }
168
169  return AllocationResult::FromObject(object);
170}
171
172bool ConcurrentAllocator::IsBlackAllocationEnabled() const {
173  return owning_heap()->incremental_marking()->black_allocation();
174}
175
176Heap* ConcurrentAllocator::owning_heap() const { return space_->heap(); }
177
178}  // namespace internal
179}  // namespace v8
180