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 | |
11 | namespace dart { |
12 | |
13 | DEFINE_LEAF_RUNTIME_ENTRY(void, StoreBufferBlockProcess, 1, Thread* thread) { |
14 | thread->StoreBufferBlockProcess(StoreBuffer::kCheckThreshold); |
15 | } |
16 | END_LEAF_RUNTIME_ENTRY |
17 | |
18 | DEFINE_LEAF_RUNTIME_ENTRY(void, MarkingStackBlockProcess, 1, Thread* thread) { |
19 | thread->MarkingStackBlockProcess(); |
20 | } |
21 | END_LEAF_RUNTIME_ENTRY |
22 | |
23 | template <int BlockSize> |
24 | typename BlockStack<BlockSize>::List* BlockStack<BlockSize>::global_empty_ = |
25 | NULL; |
26 | template <int BlockSize> |
27 | Mutex* BlockStack<BlockSize>::global_mutex_ = NULL; |
28 | |
29 | template <int BlockSize> |
30 | void BlockStack<BlockSize>::Init() { |
31 | global_empty_ = new List(); |
32 | if (global_mutex_ == NULL) { |
33 | global_mutex_ = new Mutex(); |
34 | } |
35 | } |
36 | |
37 | template <int BlockSize> |
38 | void BlockStack<BlockSize>::Cleanup() { |
39 | delete global_empty_; |
40 | global_empty_ = NULL; |
41 | } |
42 | |
43 | template <int BlockSize> |
44 | BlockStack<BlockSize>::BlockStack() : mutex_() {} |
45 | |
46 | template <int BlockSize> |
47 | BlockStack<BlockSize>::~BlockStack() { |
48 | Reset(); |
49 | } |
50 | |
51 | template <int BlockSize> |
52 | void 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 | |
71 | template <int BlockSize> |
72 | typename 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 | |
80 | template <int BlockSize> |
81 | void 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 | |
96 | template <int Size> |
97 | void 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 | |
103 | void 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 | |
117 | template <int BlockSize> |
118 | typename BlockStack<BlockSize>::Block* |
119 | BlockStack<BlockSize>::PopNonFullBlock() { |
120 | { |
121 | MutexLocker ml(&mutex_); |
122 | if (!partial_.IsEmpty()) { |
123 | return partial_.Pop(); |
124 | } |
125 | } |
126 | return PopEmptyBlock(); |
127 | } |
128 | |
129 | template <int BlockSize> |
130 | typename 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 | |
140 | template <int BlockSize> |
141 | typename BlockStack<BlockSize>::Block* |
142 | BlockStack<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 | |
153 | template <int BlockSize> |
154 | bool BlockStack<BlockSize>::IsEmpty() { |
155 | MutexLocker ml(&mutex_); |
156 | return full_.IsEmpty() && partial_.IsEmpty(); |
157 | } |
158 | |
159 | template <int BlockSize> |
160 | BlockStack<BlockSize>::List::~List() { |
161 | while (!IsEmpty()) { |
162 | delete Pop(); |
163 | } |
164 | } |
165 | |
166 | template <int BlockSize> |
167 | typename 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 | |
175 | template <int BlockSize> |
176 | typename BlockStack<BlockSize>::Block* BlockStack<BlockSize>::List::PopAll() { |
177 | Block* result = head_; |
178 | head_ = NULL; |
179 | length_ = 0; |
180 | return result; |
181 | } |
182 | |
183 | template <int BlockSize> |
184 | void BlockStack<BlockSize>::List::Push(Block* block) { |
185 | ASSERT(block->next_ == NULL); |
186 | block->next_ = head_; |
187 | head_ = block; |
188 | ++length_; |
189 | } |
190 | |
191 | bool StoreBuffer::Overflowed() { |
192 | MutexLocker ml(&mutex_); |
193 | return (full_.length() + partial_.length()) > kMaxNonEmpty; |
194 | } |
195 | |
196 | void 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 | |
205 | template <int BlockSize> |
206 | void BlockStack<BlockSize>::TrimGlobalEmpty() { |
207 | DEBUG_ASSERT(global_mutex_->IsOwnedByCurrentThread()); |
208 | while (global_empty_->length() > kMaxGlobalEmpty) { |
209 | delete global_empty_->Pop(); |
210 | } |
211 | } |
212 | |
213 | template class BlockStack<kStoreBufferBlockSize>; |
214 | template class BlockStack<kMarkingStackBlockSize>; |
215 | |
216 | } // namespace dart |
217 | |