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 "vm/heap/pointer_block.h"
6
7#include "platform/assert.h"
8#include "vm/lockers.h"
9#include "vm/runtime_entry.h"
10
11namespace dart {
12
13DEFINE_LEAF_RUNTIME_ENTRY(void, StoreBufferBlockProcess, 1, Thread* thread) {
14 thread->StoreBufferBlockProcess(StoreBuffer::kCheckThreshold);
15}
16END_LEAF_RUNTIME_ENTRY
17
18DEFINE_LEAF_RUNTIME_ENTRY(void, MarkingStackBlockProcess, 1, Thread* thread) {
19 thread->MarkingStackBlockProcess();
20}
21END_LEAF_RUNTIME_ENTRY
22
23template <int BlockSize>
24typename BlockStack<BlockSize>::List* BlockStack<BlockSize>::global_empty_ =
25 NULL;
26template <int BlockSize>
27Mutex* BlockStack<BlockSize>::global_mutex_ = NULL;
28
29template <int BlockSize>
30void BlockStack<BlockSize>::Init() {
31 global_empty_ = new List();
32 if (global_mutex_ == NULL) {
33 global_mutex_ = new Mutex();
34 }
35}
36
37template <int BlockSize>
38void BlockStack<BlockSize>::Cleanup() {
39 delete global_empty_;
40 global_empty_ = NULL;
41}
42
43template <int BlockSize>
44BlockStack<BlockSize>::BlockStack() : mutex_() {}
45
46template <int BlockSize>
47BlockStack<BlockSize>::~BlockStack() {
48 Reset();
49}
50
51template <int BlockSize>
52void BlockStack<BlockSize>::Reset() {
53 MutexLocker local_mutex_locker(&mutex_);
54 {
55 // Empty all blocks and move them to the global cache.
56 MutexLocker global_mutex_locker(global_mutex_);
57 while (!full_.IsEmpty()) {
58 Block* block = full_.Pop();
59 block->Reset();
60 global_empty_->Push(block);
61 }
62 while (!partial_.IsEmpty()) {
63 Block* block = partial_.Pop();
64 block->Reset();
65 global_empty_->Push(block);
66 }
67 TrimGlobalEmpty();
68 }
69}
70
71template <int BlockSize>
72typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::TakeBlocks() {
73 MutexLocker ml(&mutex_);
74 while (!partial_.IsEmpty()) {
75 full_.Push(partial_.Pop());
76 }
77 return full_.PopAll();
78}
79
80template <int BlockSize>
81void BlockStack<BlockSize>::PushBlockImpl(Block* block) {
82 ASSERT(block->next() == NULL); // Should be just a single block.
83 if (block->IsFull()) {
84 MutexLocker ml(&mutex_);
85 full_.Push(block);
86 } else if (block->IsEmpty()) {
87 MutexLocker ml(global_mutex_);
88 global_empty_->Push(block);
89 TrimGlobalEmpty();
90 } else {
91 MutexLocker ml(&mutex_);
92 partial_.Push(block);
93 }
94}
95
96template <int Size>
97void PointerBlock<Size>::VisitObjectPointers(ObjectPointerVisitor* visitor) {
98 // Generated code appends to store buffers; tell MemorySanitizer.
99 MSAN_UNPOISON(this, sizeof(*this));
100 visitor->VisitPointers(&pointers_[0], top_);
101}
102
103void StoreBuffer::PushBlock(Block* block, ThresholdPolicy policy) {
104 BlockStack<Block::kSize>::PushBlockImpl(block);
105 if ((policy == kCheckThreshold) && Overflowed()) {
106 MutexLocker ml(&mutex_);
107 Thread* thread = Thread::Current();
108 // Sanity check: it makes no sense to schedule the GC in another isolate
109 // group.
110 // (If Isolate ever gets multiple store buffers, we should avoid this
111 // coupling by passing in an explicit callback+parameter at construction.)
112 ASSERT(thread->isolate_group()->store_buffer() == this);
113 thread->ScheduleInterrupts(Thread::kVMInterrupt);
114 }
115}
116
117template <int BlockSize>
118typename BlockStack<BlockSize>::Block*
119BlockStack<BlockSize>::PopNonFullBlock() {
120 {
121 MutexLocker ml(&mutex_);
122 if (!partial_.IsEmpty()) {
123 return partial_.Pop();
124 }
125 }
126 return PopEmptyBlock();
127}
128
129template <int BlockSize>
130typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::PopEmptyBlock() {
131 {
132 MutexLocker ml(global_mutex_);
133 if (!global_empty_->IsEmpty()) {
134 return global_empty_->Pop();
135 }
136 }
137 return new Block();
138}
139
140template <int BlockSize>
141typename BlockStack<BlockSize>::Block*
142BlockStack<BlockSize>::PopNonEmptyBlock() {
143 MutexLocker ml(&mutex_);
144 if (!full_.IsEmpty()) {
145 return full_.Pop();
146 } else if (!partial_.IsEmpty()) {
147 return partial_.Pop();
148 } else {
149 return NULL;
150 }
151}
152
153template <int BlockSize>
154bool BlockStack<BlockSize>::IsEmpty() {
155 MutexLocker ml(&mutex_);
156 return full_.IsEmpty() && partial_.IsEmpty();
157}
158
159template <int BlockSize>
160BlockStack<BlockSize>::List::~List() {
161 while (!IsEmpty()) {
162 delete Pop();
163 }
164}
165
166template <int BlockSize>
167typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::List::Pop() {
168 Block* result = head_;
169 head_ = head_->next_;
170 --length_;
171 result->next_ = NULL;
172 return result;
173}
174
175template <int BlockSize>
176typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::List::PopAll() {
177 Block* result = head_;
178 head_ = NULL;
179 length_ = 0;
180 return result;
181}
182
183template <int BlockSize>
184void BlockStack<BlockSize>::List::Push(Block* block) {
185 ASSERT(block->next_ == NULL);
186 block->next_ = head_;
187 head_ = block;
188 ++length_;
189}
190
191bool StoreBuffer::Overflowed() {
192 MutexLocker ml(&mutex_);
193 return (full_.length() + partial_.length()) > kMaxNonEmpty;
194}
195
196void StoreBuffer::VisitObjectPointers(ObjectPointerVisitor* visitor) {
197 for (Block* block = full_.Peek(); block != NULL; block = block->next()) {
198 block->VisitObjectPointers(visitor);
199 }
200 for (Block* block = partial_.Peek(); block != NULL; block = block->next()) {
201 block->VisitObjectPointers(visitor);
202 }
203}
204
205template <int BlockSize>
206void BlockStack<BlockSize>::TrimGlobalEmpty() {
207 DEBUG_ASSERT(global_mutex_->IsOwnedByCurrentThread());
208 while (global_empty_->length() > kMaxGlobalEmpty) {
209 delete global_empty_->Pop();
210 }
211}
212
213template class BlockStack<kStoreBufferBlockSize>;
214template class BlockStack<kMarkingStackBlockSize>;
215
216} // namespace dart
217