OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/store_buffer.h" | 5 #include "vm/store_buffer.h" |
6 | 6 |
7 #include "platform/assert.h" | 7 #include "platform/assert.h" |
8 #include "vm/lockers.h" | 8 #include "vm/lockers.h" |
9 #include "vm/runtime_entry.h" | 9 #include "vm/runtime_entry.h" |
10 | 10 |
11 namespace dart { | 11 namespace dart { |
12 | 12 |
13 DEFINE_LEAF_RUNTIME_ENTRY(void, StoreBufferBlockProcess, 1, Thread* thread) { | 13 DEFINE_LEAF_RUNTIME_ENTRY(void, StoreBufferBlockProcess, 1, Thread* thread) { |
14 thread->StoreBufferBlockProcess(true); | 14 thread->StoreBufferBlockProcess(true); |
15 } | 15 } |
16 END_LEAF_RUNTIME_ENTRY | 16 END_LEAF_RUNTIME_ENTRY |
17 | 17 |
18 | 18 |
| 19 StoreBuffer::List* StoreBuffer::global_empty_ = NULL; |
| 20 Mutex* StoreBuffer::global_mutex_ = NULL; |
| 21 |
| 22 |
| 23 void StoreBuffer::InitOnce() { |
| 24 global_empty_ = new List(); |
| 25 global_mutex_ = new Mutex(); |
| 26 } |
| 27 |
| 28 |
19 StoreBuffer::StoreBuffer() : mutex_(new Mutex()) { | 29 StoreBuffer::StoreBuffer() : mutex_(new Mutex()) { |
20 } | 30 } |
21 | 31 |
22 | 32 |
23 StoreBuffer::~StoreBuffer() { | 33 StoreBuffer::~StoreBuffer() { |
24 Reset(); | 34 Reset(); |
25 delete mutex_; | 35 delete mutex_; |
26 } | 36 } |
27 | 37 |
28 | 38 |
29 void StoreBuffer::Reset() { | 39 void StoreBuffer::Reset() { |
30 MutexLocker ml(mutex_); | 40 MutexLocker local_mutex_locker(mutex_); |
31 // TODO(koda): Reuse and share empty blocks between isolates. | 41 { |
32 while (!full_.IsEmpty()) { | 42 // Empty all blocks and move them to the global cache. |
33 delete full_.Pop(); | 43 MutexLocker global_mutex_locker(global_mutex_); |
34 } | 44 while (!full_.IsEmpty()) { |
35 while (!partial_.IsEmpty()) { | 45 StoreBufferBlock* block = full_.Pop(); |
36 delete partial_.Pop(); | 46 block->Reset(); |
| 47 global_empty_->Push(block); |
| 48 } |
| 49 while (!partial_.IsEmpty()) { |
| 50 StoreBufferBlock* block = partial_.Pop(); |
| 51 block->Reset(); |
| 52 global_empty_->Push(block); |
| 53 } |
| 54 TrimGlobalEmpty(); |
37 } | 55 } |
38 } | 56 } |
39 | 57 |
40 | 58 |
41 StoreBufferBlock* StoreBuffer::Blocks() { | 59 StoreBufferBlock* StoreBuffer::Blocks() { |
42 MutexLocker ml(mutex_); | 60 MutexLocker ml(mutex_); |
43 while (!partial_.IsEmpty()) { | 61 while (!partial_.IsEmpty()) { |
44 full_.Push(partial_.Pop()); | 62 full_.Push(partial_.Pop()); |
45 } | 63 } |
46 return full_.PopAll(); | 64 return full_.PopAll(); |
47 } | 65 } |
48 | 66 |
49 | 67 |
50 void StoreBuffer::PushBlock(StoreBufferBlock* block, bool check_threshold) { | 68 void StoreBuffer::PushBlock(StoreBufferBlock* block, bool check_threshold) { |
51 MutexLocker ml(mutex_); | 69 ASSERT(block->next() == NULL); // Should be just a single block. |
52 List* list = block->IsFull() ? &full_ : &partial_; | 70 if (block->IsFull()) { |
53 list->Push(block); | 71 MutexLocker ml(mutex_); |
| 72 full_.Push(block); |
| 73 } else if (block->IsEmpty()) { |
| 74 MutexLocker ml(global_mutex_); |
| 75 global_empty_->Push(block); |
| 76 TrimGlobalEmpty(); |
| 77 } else { |
| 78 MutexLocker ml(mutex_); |
| 79 partial_.Push(block); |
| 80 } |
54 if (check_threshold) { | 81 if (check_threshold) { |
55 CheckThreshold(); | 82 MutexLocker ml(mutex_); |
| 83 CheckThresholdNonEmpty(); |
56 } | 84 } |
57 } | 85 } |
58 | 86 |
59 | 87 |
60 StoreBufferBlock* StoreBuffer::PopBlock() { | 88 StoreBufferBlock* StoreBuffer::PopBlock() { |
61 MutexLocker ml(mutex_); | 89 { |
62 return (!partial_.IsEmpty()) ? partial_.Pop() : PopEmptyBlock(); | 90 MutexLocker ml(mutex_); |
| 91 if (!partial_.IsEmpty()) { |
| 92 return partial_.Pop(); |
| 93 } |
| 94 } |
| 95 return PopEmptyBlock(); |
63 } | 96 } |
64 | 97 |
65 | 98 |
66 StoreBufferBlock* StoreBuffer::PopEmptyBlock() { | 99 StoreBufferBlock* StoreBuffer::PopEmptyBlock() { |
67 // TODO(koda): Reuse and share empty blocks between isolates. | 100 { |
| 101 MutexLocker ml(global_mutex_); |
| 102 if (!global_empty_->IsEmpty()) { |
| 103 global_empty_->Pop(); |
| 104 } |
| 105 } |
68 return new StoreBufferBlock(); | 106 return new StoreBufferBlock(); |
69 } | 107 } |
70 | 108 |
71 | 109 |
72 StoreBuffer::List::~List() { | 110 StoreBuffer::List::~List() { |
73 while (!IsEmpty()) { | 111 while (!IsEmpty()) { |
74 delete Pop(); | 112 delete Pop(); |
75 } | 113 } |
76 } | 114 } |
77 | 115 |
78 | 116 |
79 StoreBufferBlock* StoreBuffer::List::Pop() { | 117 StoreBufferBlock* StoreBuffer::List::Pop() { |
80 StoreBufferBlock* result = head_; | 118 StoreBufferBlock* result = head_; |
81 head_ = head_->next_; | 119 head_ = head_->next_; |
82 --length_; | 120 --length_; |
83 result->next_ = NULL; | 121 result->next_ = NULL; |
84 return result; | 122 return result; |
85 } | 123 } |
86 | 124 |
87 | 125 |
88 StoreBufferBlock* StoreBuffer::List::PopAll() { | 126 StoreBufferBlock* StoreBuffer::List::PopAll() { |
89 StoreBufferBlock* result = head_; | 127 StoreBufferBlock* result = head_; |
90 head_ = NULL; | 128 head_ = NULL; |
91 length_ = 0; | 129 length_ = 0; |
92 return result; | 130 return result; |
93 } | 131 } |
94 | 132 |
95 | 133 |
96 void StoreBuffer::List::Push(StoreBufferBlock* block) { | 134 void StoreBuffer::List::Push(StoreBufferBlock* block) { |
| 135 ASSERT(block->next_ == NULL); |
97 block->next_ = head_; | 136 block->next_ = head_; |
98 head_ = block; | 137 head_ = block; |
99 ++length_; | 138 ++length_; |
100 } | 139 } |
101 | 140 |
102 | 141 |
103 void StoreBuffer::CheckThreshold() { | 142 void StoreBuffer::CheckThresholdNonEmpty() { |
104 // Schedule an interrupt if we have run over the max number of | 143 DEBUG_ASSERT(mutex_->IsOwnedByCurrentThread()); |
105 // StoreBufferBlocks. | 144 if (full_.length() + partial_.length() > kMaxNonEmpty) { |
106 // TODO(koda): Pass threshold and callback in constructor. Cap total? | 145 Isolate* isolate = Isolate::Current(); |
107 if (full_.length() > 100) { | 146 // Sanity check: it makes no sense to schedule the GC in another isolate. |
108 Isolate::Current()->ScheduleInterrupts(Isolate::kStoreBufferInterrupt); | 147 // (If Isolate ever gets multiple store buffers, we should avoid this |
| 148 // coupling by passing in an explicit callback+parameter at construction.) |
| 149 ASSERT(isolate->store_buffer() == this); |
| 150 isolate->ScheduleInterrupts(Isolate::kStoreBufferInterrupt); |
109 } | 151 } |
110 } | 152 } |
111 | 153 |
| 154 |
| 155 void StoreBuffer::TrimGlobalEmpty() { |
| 156 DEBUG_ASSERT(global_mutex_->IsOwnedByCurrentThread()); |
| 157 while (global_empty_->length() > kMaxGlobalEmpty) { |
| 158 delete global_empty_->Pop(); |
| 159 } |
| 160 } |
| 161 |
112 } // namespace dart | 162 } // namespace dart |
OLD | NEW |