Chromium Code Reviews| Index: runtime/vm/store_buffer.cc |
| diff --git a/runtime/vm/store_buffer.cc b/runtime/vm/store_buffer.cc |
| index 99a1371a909c10a51ca220d247d10e178617b70e..0745886fd224054eda13ef1d6bd343b9756ad069 100644 |
| --- a/runtime/vm/store_buffer.cc |
| +++ b/runtime/vm/store_buffer.cc |
| @@ -16,6 +16,15 @@ DEFINE_LEAF_RUNTIME_ENTRY(void, StoreBufferBlockProcess, 1, Thread* thread) { |
| END_LEAF_RUNTIME_ENTRY |
| +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.
|
| +Mutex* StoreBuffer::global_mutex_ = NULL; |
| + |
| + |
| +void StoreBuffer::InitOnce() { |
| + global_mutex_ = new Mutex(); |
| +} |
| + |
| + |
| StoreBuffer::StoreBuffer() : mutex_(new Mutex()) { |
| } |
| @@ -27,13 +36,21 @@ StoreBuffer::~StoreBuffer() { |
| void StoreBuffer::Reset() { |
| - MutexLocker ml(mutex_); |
| - // TODO(koda): Reuse and share empty blocks between isolates. |
| - while (!full_.IsEmpty()) { |
| - delete full_.Pop(); |
| - } |
| - while (!partial_.IsEmpty()) { |
| - delete partial_.Pop(); |
| + MutexLocker local_mutex_locker(mutex_); |
| + { |
| + // Empty all blocks and move them to the global cache. |
| + MutexLocker global_mutex_locker(global_mutex_); |
| + while (!full_.IsEmpty()) { |
| + StoreBufferBlock* block = full_.Pop(); |
| + block->Reset(); |
| + global_empty_.Push(block); |
| + } |
| + while (!partial_.IsEmpty()) { |
| + StoreBufferBlock* block = partial_.Pop(); |
| + block->Reset(); |
| + global_empty_.Push(block); |
| + } |
| + CheckThresholdGlobalEmpty(); |
| } |
| } |
| @@ -49,23 +66,31 @@ StoreBufferBlock* StoreBuffer::Blocks() { |
| void StoreBuffer::PushBlock(StoreBufferBlock* block, bool check_threshold) { |
| 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.
|
| - List* list = block->IsFull() ? &full_ : &partial_; |
| - list->Push(block); |
| + ASSERT(block->next() == NULL); // Should be just a single block. |
| + if (block->IsFull()) { |
| + full_.Push(block); |
| + } else if (block->IsEmpty()) { |
| + MutexLocker ml(global_mutex_); |
| + global_empty_.Push(block); |
| + CheckThresholdGlobalEmpty(); |
| + } else { |
| + partial_.Push(block); |
| + } |
| if (check_threshold) { |
| - CheckThreshold(); |
| + CheckThresholdNonEmpty(); |
| } |
| } |
| StoreBufferBlock* StoreBuffer::PopBlock() { |
| MutexLocker ml(mutex_); |
| - return (!partial_.IsEmpty()) ? partial_.Pop() : PopEmptyBlock(); |
| + 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.
|
| } |
| StoreBufferBlock* StoreBuffer::PopEmptyBlock() { |
| - // TODO(koda): Reuse and share empty blocks between isolates. |
| - return new StoreBufferBlock(); |
| + MutexLocker ml(global_mutex_); |
| + 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.
|
| } |
| @@ -94,18 +119,30 @@ StoreBufferBlock* StoreBuffer::List::PopAll() { |
| void StoreBuffer::List::Push(StoreBufferBlock* block) { |
| + ASSERT(block->next_ == NULL); |
| block->next_ = head_; |
| head_ = block; |
| ++length_; |
| } |
| -void StoreBuffer::CheckThreshold() { |
| - // Schedule an interrupt if we have run over the max number of |
| - // StoreBufferBlocks. |
| - // TODO(koda): Pass threshold and callback in constructor. Cap total? |
| - if (full_.length() > 100) { |
| - Isolate::Current()->ScheduleInterrupts(Isolate::kStoreBufferInterrupt); |
| +void StoreBuffer::CheckThresholdNonEmpty() { |
| + DEBUG_ASSERT(mutex_->IsOwnedByCurrentThread()); |
| + if (full_.length() + partial_.length() > kMaxNonEmpty) { |
| + Isolate* isolate = Isolate::Current(); |
| + // Sanity check: it makes no sense to schedule the GC in another isolate. |
| + // (If Isolate ever gets multiple store buffers, we should avoid this |
| + // coupling by passing in an explicit callback+parameter at construction.) |
| + ASSERT(isolate->store_buffer() == this); |
| + isolate->ScheduleInterrupts(Isolate::kStoreBufferInterrupt); |
| + } |
| +} |
| + |
| + |
| +void StoreBuffer::CheckThresholdGlobalEmpty() { |
| + DEBUG_ASSERT(global_mutex_->IsOwnedByCurrentThread()); |
| + while (global_empty_.length() > kMaxGlobalEmpty) { |
| + delete global_empty_.Pop(); |
| } |
| } |