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_; | |
Ivan Posva
2015/06/16 21:48:19
This will lead to statically destructed objects wh
koda
2015/06/18 17:40:22
Done.
| |
20 Mutex* StoreBuffer::global_mutex_ = NULL; | |
21 | |
22 | |
23 void StoreBuffer::InitOnce() { | |
24 global_mutex_ = new Mutex(); | |
25 } | |
26 | |
27 | |
19 StoreBuffer::StoreBuffer() : mutex_(new Mutex()) { | 28 StoreBuffer::StoreBuffer() : mutex_(new Mutex()) { |
20 } | 29 } |
21 | 30 |
22 | 31 |
23 StoreBuffer::~StoreBuffer() { | 32 StoreBuffer::~StoreBuffer() { |
24 Reset(); | 33 Reset(); |
25 delete mutex_; | 34 delete mutex_; |
26 } | 35 } |
27 | 36 |
28 | 37 |
29 void StoreBuffer::Reset() { | 38 void StoreBuffer::Reset() { |
30 MutexLocker ml(mutex_); | 39 MutexLocker local_mutex_locker(mutex_); |
31 // TODO(koda): Reuse and share empty blocks between isolates. | 40 { |
32 while (!full_.IsEmpty()) { | 41 // Empty all blocks and move them to the global cache. |
33 delete full_.Pop(); | 42 MutexLocker global_mutex_locker(global_mutex_); |
34 } | 43 while (!full_.IsEmpty()) { |
35 while (!partial_.IsEmpty()) { | 44 StoreBufferBlock* block = full_.Pop(); |
36 delete partial_.Pop(); | 45 block->Reset(); |
46 global_empty_.Push(block); | |
47 } | |
48 while (!partial_.IsEmpty()) { | |
49 StoreBufferBlock* block = partial_.Pop(); | |
50 block->Reset(); | |
51 global_empty_.Push(block); | |
52 } | |
53 CheckThresholdGlobalEmpty(); | |
37 } | 54 } |
38 } | 55 } |
39 | 56 |
40 | 57 |
41 StoreBufferBlock* StoreBuffer::Blocks() { | 58 StoreBufferBlock* StoreBuffer::Blocks() { |
42 MutexLocker ml(mutex_); | 59 MutexLocker ml(mutex_); |
43 while (!partial_.IsEmpty()) { | 60 while (!partial_.IsEmpty()) { |
44 full_.Push(partial_.Pop()); | 61 full_.Push(partial_.Pop()); |
45 } | 62 } |
46 return full_.PopAll(); | 63 return full_.PopAll(); |
47 } | 64 } |
48 | 65 |
49 | 66 |
50 void StoreBuffer::PushBlock(StoreBufferBlock* block, bool check_threshold) { | 67 void StoreBuffer::PushBlock(StoreBufferBlock* block, bool check_threshold) { |
51 MutexLocker ml(mutex_); | 68 MutexLocker ml(mutex_); |
Ivan Posva
2015/06/16 21:48:19
ditto from below: Reduce mutex area.
koda
2015/06/18 17:40:23
Done.
| |
52 List* list = block->IsFull() ? &full_ : &partial_; | 69 ASSERT(block->next() == NULL); // Should be just a single block. |
53 list->Push(block); | 70 if (block->IsFull()) { |
71 full_.Push(block); | |
72 } else if (block->IsEmpty()) { | |
73 MutexLocker ml(global_mutex_); | |
74 global_empty_.Push(block); | |
75 CheckThresholdGlobalEmpty(); | |
76 } else { | |
77 partial_.Push(block); | |
78 } | |
54 if (check_threshold) { | 79 if (check_threshold) { |
55 CheckThreshold(); | 80 CheckThresholdNonEmpty(); |
56 } | 81 } |
57 } | 82 } |
58 | 83 |
59 | 84 |
60 StoreBufferBlock* StoreBuffer::PopBlock() { | 85 StoreBufferBlock* StoreBuffer::PopBlock() { |
61 MutexLocker ml(mutex_); | 86 MutexLocker ml(mutex_); |
62 return (!partial_.IsEmpty()) ? partial_.Pop() : PopEmptyBlock(); | 87 return partial_.IsEmpty() ? PopEmptyBlock() : partial_.Pop(); |
Ivan Posva
2015/06/16 21:48:19
The mutex does not need to be held around the popp
koda
2015/06/18 17:40:22
Done.
| |
63 } | 88 } |
64 | 89 |
65 | 90 |
66 StoreBufferBlock* StoreBuffer::PopEmptyBlock() { | 91 StoreBufferBlock* StoreBuffer::PopEmptyBlock() { |
67 // TODO(koda): Reuse and share empty blocks between isolates. | 92 MutexLocker ml(global_mutex_); |
68 return new StoreBufferBlock(); | 93 return global_empty_.IsEmpty() ? new StoreBufferBlock() : global_empty_.Pop(); |
Ivan Posva
2015/06/16 21:48:19
The mutex does not need to be held around the mall
koda
2015/06/18 17:40:23
Done.
| |
69 } | 94 } |
70 | 95 |
71 | 96 |
72 StoreBuffer::List::~List() { | 97 StoreBuffer::List::~List() { |
73 while (!IsEmpty()) { | 98 while (!IsEmpty()) { |
74 delete Pop(); | 99 delete Pop(); |
75 } | 100 } |
76 } | 101 } |
77 | 102 |
78 | 103 |
79 StoreBufferBlock* StoreBuffer::List::Pop() { | 104 StoreBufferBlock* StoreBuffer::List::Pop() { |
80 StoreBufferBlock* result = head_; | 105 StoreBufferBlock* result = head_; |
81 head_ = head_->next_; | 106 head_ = head_->next_; |
82 --length_; | 107 --length_; |
83 result->next_ = NULL; | 108 result->next_ = NULL; |
84 return result; | 109 return result; |
85 } | 110 } |
86 | 111 |
87 | 112 |
88 StoreBufferBlock* StoreBuffer::List::PopAll() { | 113 StoreBufferBlock* StoreBuffer::List::PopAll() { |
89 StoreBufferBlock* result = head_; | 114 StoreBufferBlock* result = head_; |
90 head_ = NULL; | 115 head_ = NULL; |
91 length_ = 0; | 116 length_ = 0; |
92 return result; | 117 return result; |
93 } | 118 } |
94 | 119 |
95 | 120 |
96 void StoreBuffer::List::Push(StoreBufferBlock* block) { | 121 void StoreBuffer::List::Push(StoreBufferBlock* block) { |
122 ASSERT(block->next_ == NULL); | |
97 block->next_ = head_; | 123 block->next_ = head_; |
98 head_ = block; | 124 head_ = block; |
99 ++length_; | 125 ++length_; |
100 } | 126 } |
101 | 127 |
102 | 128 |
103 void StoreBuffer::CheckThreshold() { | 129 void StoreBuffer::CheckThresholdNonEmpty() { |
104 // Schedule an interrupt if we have run over the max number of | 130 DEBUG_ASSERT(mutex_->IsOwnedByCurrentThread()); |
105 // StoreBufferBlocks. | 131 if (full_.length() + partial_.length() > kMaxNonEmpty) { |
106 // TODO(koda): Pass threshold and callback in constructor. Cap total? | 132 Isolate* isolate = Isolate::Current(); |
107 if (full_.length() > 100) { | 133 // Sanity check: it makes no sense to schedule the GC in another isolate. |
108 Isolate::Current()->ScheduleInterrupts(Isolate::kStoreBufferInterrupt); | 134 // (If Isolate ever gets multiple store buffers, we should avoid this |
135 // coupling by passing in an explicit callback+parameter at construction.) | |
136 ASSERT(isolate->store_buffer() == this); | |
137 isolate->ScheduleInterrupts(Isolate::kStoreBufferInterrupt); | |
109 } | 138 } |
110 } | 139 } |
111 | 140 |
141 | |
142 void StoreBuffer::CheckThresholdGlobalEmpty() { | |
143 DEBUG_ASSERT(global_mutex_->IsOwnedByCurrentThread()); | |
144 while (global_empty_.length() > kMaxGlobalEmpty) { | |
145 delete global_empty_.Pop(); | |
146 } | |
147 } | |
148 | |
112 } // namespace dart | 149 } // namespace dart |
OLD | NEW |