500eb6d79d
Add infrastructure to enable all threads in an isolate to rendezvous at GC-safe points, and exercise it through unit tests. * Use existing interrupt mechanism for Dart threads, but a cooperative approach for non-Dart threads (concurrent compiler, etc.): they must periodically call CheckSafepoint (may block). * Add unit tests to exercise and verify various scenarios: organizing the rendezvous from helper/main thread, with/without Dart code executing, etc. The next step is to use this interface in old-space allocation and garbage collection, respectively, to allow helper threads to perform allocations that potentially trigger GC (needed for concurrent compiler). Limitations: * A thread that is executing a long-running native function is currently not considered at a safepoint, and will delay the rendezvous until it returns to Dart or indirectly calls CheckSafepoint. In a future CL, we can use the saved stack pointer to detect and support this case, and intercept the thread if/when it re-enters Dart or the VM. BUG= R=iposva@google.com Review URL: https://codereview.chromium.org//1259223005 .
136 lines
3.3 KiB
C++
136 lines
3.3 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef VM_STORE_BUFFER_H_
|
|
#define VM_STORE_BUFFER_H_
|
|
|
|
#include "platform/assert.h"
|
|
#include "vm/globals.h"
|
|
|
|
namespace dart {
|
|
|
|
// Forward declarations.
|
|
class Isolate;
|
|
class Mutex;
|
|
class RawObject;
|
|
|
|
// A set of RawObject*. Must be emptied before destruction (using Pop/Reset).
|
|
class StoreBufferBlock {
|
|
public:
|
|
// Each full block contains kSize pointers.
|
|
static const int32_t kSize = 1024;
|
|
|
|
void Reset() {
|
|
top_ = 0;
|
|
next_ = NULL;
|
|
}
|
|
|
|
StoreBufferBlock* next() const { return next_; }
|
|
|
|
intptr_t Count() const { return top_; }
|
|
bool IsFull() const { return Count() == kSize; }
|
|
bool IsEmpty() const { return Count() == 0; }
|
|
|
|
void Push(RawObject* obj) {
|
|
ASSERT(!IsFull());
|
|
pointers_[top_++] = obj;
|
|
}
|
|
|
|
RawObject* Pop() {
|
|
ASSERT(!IsEmpty());
|
|
return pointers_[--top_];
|
|
}
|
|
|
|
#if defined(TESTING)
|
|
bool Contains(RawObject* obj) const {
|
|
for (intptr_t i = 0; i < Count(); i++) {
|
|
if (pointers_[i] == obj) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
#endif // TESTING
|
|
|
|
static intptr_t top_offset() { return OFFSET_OF(StoreBufferBlock, top_); }
|
|
static intptr_t pointers_offset() {
|
|
return OFFSET_OF(StoreBufferBlock, pointers_);
|
|
}
|
|
|
|
private:
|
|
StoreBufferBlock() : next_(NULL), top_(0) {}
|
|
~StoreBufferBlock() {
|
|
ASSERT(IsEmpty()); // Guard against unintentionally discarding pointers.
|
|
}
|
|
|
|
StoreBufferBlock* next_;
|
|
int32_t top_;
|
|
RawObject* pointers_[kSize];
|
|
|
|
friend class StoreBuffer;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(StoreBufferBlock);
|
|
};
|
|
|
|
|
|
class StoreBuffer {
|
|
public:
|
|
StoreBuffer();
|
|
~StoreBuffer();
|
|
static void InitOnce();
|
|
|
|
// Interrupt when crossing this threshold of non-empty blocks in the buffer.
|
|
static const intptr_t kMaxNonEmpty = 100;
|
|
|
|
// Adds and transfers ownership of the block to the buffer.
|
|
void PushBlock(StoreBufferBlock* block, bool check_threshold = true);
|
|
// Partially filled blocks can be reused, and there is an "inifite" supply
|
|
// of empty blocks (reused or newly allocated). In any case, the caller
|
|
// takes ownership of the returned block.
|
|
StoreBufferBlock* PopBlock();
|
|
StoreBufferBlock* PopEmptyBlock();
|
|
|
|
// Pops and returns all non-empty blocks as a linked list (owned by caller).
|
|
StoreBufferBlock* Blocks();
|
|
|
|
// Discards the contents of this store buffer.
|
|
void Reset();
|
|
|
|
// Check whether non-empty blocks have exceeded kMaxNonEmpty.
|
|
bool Overflowed();
|
|
|
|
private:
|
|
class List {
|
|
public:
|
|
List() : head_(NULL), length_(0) {}
|
|
~List();
|
|
void Push(StoreBufferBlock* block);
|
|
StoreBufferBlock* Pop();
|
|
intptr_t length() const { return length_; }
|
|
bool IsEmpty() const { return head_ == NULL; }
|
|
StoreBufferBlock* PopAll();
|
|
private:
|
|
StoreBufferBlock* head_;
|
|
intptr_t length_;
|
|
DISALLOW_COPY_AND_ASSIGN(List);
|
|
};
|
|
|
|
// If needed, trims the the global cache of empty blocks.
|
|
static void TrimGlobalEmpty();
|
|
|
|
List full_;
|
|
List partial_;
|
|
Mutex* mutex_;
|
|
|
|
static const intptr_t kMaxGlobalEmpty = 100;
|
|
static List* global_empty_;
|
|
static Mutex* global_mutex_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(StoreBuffer);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // VM_STORE_BUFFER_H_
|