Chromium Code Reviews| 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 |