mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:03:19 +00:00
Use OOB messages to schedule compaction for Dart_NotifyLowMemory.
This ensures any finalizers that run during compaction happen on a thread the embedder knows about. Bug: https://github.com/dart-lang/sdk/issues/31662 Change-Id: If1ca39fe72937ad2bc4607f7cacc4dc82e49be01 Reviewed-on: https://dart-review.googlesource.com/30040 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Siva Annamalai <asiva@google.com>
This commit is contained in:
parent
0b5300acf3
commit
8fba2ea2b6
|
@ -486,11 +486,12 @@ DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object);
|
||||||
* calling Dart_DeleteWeakPersistentHandle.
|
* calling Dart_DeleteWeakPersistentHandle.
|
||||||
*
|
*
|
||||||
* If the object becomes unreachable the callback is invoked with the weak
|
* If the object becomes unreachable the callback is invoked with the weak
|
||||||
* persistent handle and the peer as arguments. This gives the native code the
|
* persistent handle and the peer as arguments. The callback is invoked on the
|
||||||
* ability to cleanup data associated with the object and clear out any cached
|
* thread that has entered the isolate at the time of garbage collection. This
|
||||||
* references to the handle. All references to this handle after the callback
|
* gives the embedder the ability to cleanup data associated with the object and
|
||||||
* will be invalid. It is illegal to call into the VM from the callback.
|
* clear out any cached references to the handle. All references to this handle
|
||||||
* If the handle is deleted before the object becomes unreachable,
|
* after the callback will be invalid. It is illegal to call into the VM from
|
||||||
|
* the callback. If the handle is deleted before the object becomes unreachable,
|
||||||
* the callback is never invoked.
|
* the callback is never invoked.
|
||||||
*
|
*
|
||||||
* Requires there to be a current isolate.
|
* Requires there to be a current isolate.
|
||||||
|
|
|
@ -1240,7 +1240,7 @@ class GCTestHelper : public AllStatic {
|
||||||
Thread* thread = Thread::Current();
|
Thread* thread = Thread::Current();
|
||||||
PageSpace* old_space = thread->isolate()->heap()->old_space();
|
PageSpace* old_space = thread->isolate()->heap()->old_space();
|
||||||
MonitorLocker ml(old_space->tasks_lock());
|
MonitorLocker ml(old_space->tasks_lock());
|
||||||
while (old_space->sweeper_tasks() > 0) {
|
while (old_space->tasks() > 0) {
|
||||||
ml.WaitWithSafepointCheck(thread);
|
ml.WaitWithSafepointCheck(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,7 +115,7 @@ class SweeperTask : public ThreadPool::Task {
|
||||||
ASSERT(last_ != NULL);
|
ASSERT(last_ != NULL);
|
||||||
ASSERT(freelist_ != NULL);
|
ASSERT(freelist_ != NULL);
|
||||||
MonitorLocker ml(old_space_->tasks_lock());
|
MonitorLocker ml(old_space_->tasks_lock());
|
||||||
old_space_->set_sweeper_tasks(old_space_->sweeper_tasks() + 1);
|
old_space_->set_tasks(old_space_->tasks() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Run() {
|
virtual void Run() {
|
||||||
|
@ -155,7 +155,7 @@ class SweeperTask : public ThreadPool::Task {
|
||||||
// This sweeper task is done. Notify the original isolate.
|
// This sweeper task is done. Notify the original isolate.
|
||||||
{
|
{
|
||||||
MonitorLocker ml(old_space_->tasks_lock());
|
MonitorLocker ml(old_space_->tasks_lock());
|
||||||
old_space_->set_sweeper_tasks(old_space_->sweeper_tasks() - 1);
|
old_space_->set_tasks(old_space_->tasks() - 1);
|
||||||
ml.NotifyAll();
|
ml.NotifyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,14 +205,14 @@ HeapIterationScope::HeapIterationScope(Thread* thread, bool writable)
|
||||||
// We currently don't support nesting of HeapIterationScopes.
|
// We currently don't support nesting of HeapIterationScopes.
|
||||||
ASSERT(old_space_->iterating_thread_ != thread);
|
ASSERT(old_space_->iterating_thread_ != thread);
|
||||||
#endif
|
#endif
|
||||||
while (old_space_->sweeper_tasks() > 0) {
|
while (old_space_->tasks() > 0) {
|
||||||
ml.WaitWithSafepointCheck(thread);
|
ml.WaitWithSafepointCheck(thread);
|
||||||
}
|
}
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
ASSERT(old_space_->iterating_thread_ == NULL);
|
ASSERT(old_space_->iterating_thread_ == NULL);
|
||||||
old_space_->iterating_thread_ = thread;
|
old_space_->iterating_thread_ = thread;
|
||||||
#endif
|
#endif
|
||||||
old_space_->set_sweeper_tasks(1);
|
old_space_->set_tasks(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
isolate()->safepoint_handler()->SafepointThreads(thread);
|
isolate()->safepoint_handler()->SafepointThreads(thread);
|
||||||
|
@ -234,8 +234,8 @@ HeapIterationScope::~HeapIterationScope() {
|
||||||
ASSERT(old_space_->iterating_thread_ == thread());
|
ASSERT(old_space_->iterating_thread_ == thread());
|
||||||
old_space_->iterating_thread_ = NULL;
|
old_space_->iterating_thread_ = NULL;
|
||||||
#endif
|
#endif
|
||||||
ASSERT(old_space_->sweeper_tasks() == 1);
|
ASSERT(old_space_->tasks() == 1);
|
||||||
old_space_->set_sweeper_tasks(0);
|
old_space_->set_tasks(0);
|
||||||
ml.NotifyAll();
|
ml.NotifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,49 +365,8 @@ void Heap::NotifyIdle(int64_t deadline) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LowMemoryTask : public ThreadPool::Task {
|
|
||||||
public:
|
|
||||||
explicit LowMemoryTask(Isolate* isolate) : task_isolate_(isolate) {}
|
|
||||||
|
|
||||||
virtual void Run() {
|
|
||||||
bool result =
|
|
||||||
Thread::EnterIsolateAsHelper(task_isolate_, Thread::kLowMemoryTask);
|
|
||||||
ASSERT(result);
|
|
||||||
Heap* heap = task_isolate_->heap();
|
|
||||||
{
|
|
||||||
TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "LowMemoryTask");
|
|
||||||
heap->CollectAllGarbage(Heap::kLowMemory);
|
|
||||||
}
|
|
||||||
// Exit isolate cleanly *before* notifying it, to avoid shutdown race.
|
|
||||||
Thread::ExitIsolateAsHelper();
|
|
||||||
// This compactor task is done. Notify the original isolate.
|
|
||||||
{
|
|
||||||
MonitorLocker ml(heap->old_space()->tasks_lock());
|
|
||||||
heap->old_space()->set_low_memory_tasks(
|
|
||||||
heap->old_space()->low_memory_tasks() - 1);
|
|
||||||
ml.NotifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Isolate* task_isolate_;
|
|
||||||
};
|
|
||||||
|
|
||||||
void Heap::NotifyLowMemory() {
|
void Heap::NotifyLowMemory() {
|
||||||
{
|
CollectAllGarbage(kLowMemory);
|
||||||
MonitorLocker ml(old_space_.tasks_lock());
|
|
||||||
if (old_space_.low_memory_tasks() > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
old_space_.set_low_memory_tasks(old_space_.low_memory_tasks() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = Dart::thread_pool()->Run(new LowMemoryTask(isolate()));
|
|
||||||
if (!success) {
|
|
||||||
MonitorLocker ml(old_space_.tasks_lock());
|
|
||||||
old_space_.set_low_memory_tasks(old_space_.low_memory_tasks() - 1);
|
|
||||||
ml.NotifyAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Heap::EvacuateNewSpace(Thread* thread, GCReason reason) {
|
void Heap::EvacuateNewSpace(Thread* thread, GCReason reason) {
|
||||||
|
@ -503,7 +462,7 @@ void Heap::CollectAllGarbage(GCReason reason) {
|
||||||
|
|
||||||
void Heap::WaitForSweeperTasks(Thread* thread) {
|
void Heap::WaitForSweeperTasks(Thread* thread) {
|
||||||
MonitorLocker ml(old_space_.tasks_lock());
|
MonitorLocker ml(old_space_.tasks_lock());
|
||||||
while (old_space_.sweeper_tasks() > 0) {
|
while (old_space_.tasks() > 0) {
|
||||||
ml.WaitWithSafepointCheck(thread);
|
ml.WaitWithSafepointCheck(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -418,6 +418,10 @@ RawError* IsolateMessageHandler::HandleLibMessage(const Array& message) {
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Isolate::kLowMemoryMsg: {
|
||||||
|
I->heap()->NotifyLowMemory();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Isolate::kAddExitMsg:
|
case Isolate::kAddExitMsg:
|
||||||
case Isolate::kDelExitMsg:
|
case Isolate::kDelExitMsg:
|
||||||
|
@ -1700,21 +1704,7 @@ class FinalizeWeakPersistentHandlesVisitor : public HandleVisitor {
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void Isolate::NotifyLowMemory() {
|
void Isolate::NotifyLowMemory() {
|
||||||
MonitorLocker ml(isolates_list_monitor_);
|
Isolate::KillAllIsolates(Isolate::kLowMemoryMsg);
|
||||||
if (!creation_enabled_) {
|
|
||||||
return; // VM is shutting down
|
|
||||||
}
|
|
||||||
for (Isolate* isolate = isolates_list_head_; isolate != NULL;
|
|
||||||
isolate = isolate->next_) {
|
|
||||||
if (isolate == Dart::vm_isolate()) {
|
|
||||||
// Nothing to compact / isolate structure not completely initialized.
|
|
||||||
} else if (isolate == Isolate::Current()) {
|
|
||||||
isolate->heap()->NotifyLowMemory();
|
|
||||||
} else {
|
|
||||||
MutexLocker ml(isolate->mutex_);
|
|
||||||
isolate->heap()->NotifyLowMemory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Isolate::LowLevelShutdown() {
|
void Isolate::LowLevelShutdown() {
|
||||||
|
@ -1846,9 +1836,8 @@ void Isolate::Shutdown() {
|
||||||
// TODO(koda): Support faster sweeper shutdown (e.g., after current page).
|
// TODO(koda): Support faster sweeper shutdown (e.g., after current page).
|
||||||
PageSpace* old_space = heap_->old_space();
|
PageSpace* old_space = heap_->old_space();
|
||||||
MonitorLocker ml(old_space->tasks_lock());
|
MonitorLocker ml(old_space->tasks_lock());
|
||||||
while (old_space->sweeper_tasks() > 0 ||
|
while (old_space->tasks() > 0) {
|
||||||
old_space->low_memory_tasks() > 0) {
|
ml.Wait();
|
||||||
ml.WaitWithSafepointCheck(thread);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1871,8 +1860,7 @@ void Isolate::Shutdown() {
|
||||||
if (heap_ != NULL) {
|
if (heap_ != NULL) {
|
||||||
PageSpace* old_space = heap_->old_space();
|
PageSpace* old_space = heap_->old_space();
|
||||||
MonitorLocker ml(old_space->tasks_lock());
|
MonitorLocker ml(old_space->tasks_lock());
|
||||||
ASSERT(old_space->sweeper_tasks() == 0);
|
ASSERT(old_space->tasks() == 0);
|
||||||
ASSERT(old_space->low_memory_tasks() == 0);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,7 @@ class Isolate : public BaseIsolate {
|
||||||
// Internal message ids.
|
// Internal message ids.
|
||||||
kInterruptMsg = 10, // Break in the debugger.
|
kInterruptMsg = 10, // Break in the debugger.
|
||||||
kInternalKillMsg = 11, // Like kill, but does not run exit listeners, etc.
|
kInternalKillMsg = 11, // Like kill, but does not run exit listeners, etc.
|
||||||
|
kLowMemoryMsg = 12, // Run compactor, etc.
|
||||||
};
|
};
|
||||||
// The different Isolate API message priorities for ping and kill messages.
|
// The different Isolate API message priorities for ping and kill messages.
|
||||||
enum LibMsgPriority {
|
enum LibMsgPriority {
|
||||||
|
|
|
@ -183,8 +183,7 @@ PageSpace::PageSpace(Heap* heap,
|
||||||
max_capacity_in_words_(max_capacity_in_words),
|
max_capacity_in_words_(max_capacity_in_words),
|
||||||
max_external_in_words_(max_external_in_words),
|
max_external_in_words_(max_external_in_words),
|
||||||
tasks_lock_(new Monitor()),
|
tasks_lock_(new Monitor()),
|
||||||
sweeper_tasks_(0),
|
tasks_(0),
|
||||||
low_memory_tasks_(0),
|
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
iterating_thread_(NULL),
|
iterating_thread_(NULL),
|
||||||
#endif
|
#endif
|
||||||
|
@ -203,7 +202,7 @@ PageSpace::PageSpace(Heap* heap,
|
||||||
PageSpace::~PageSpace() {
|
PageSpace::~PageSpace() {
|
||||||
{
|
{
|
||||||
MonitorLocker ml(tasks_lock());
|
MonitorLocker ml(tasks_lock());
|
||||||
while (sweeper_tasks() > 0) {
|
while (tasks() > 0) {
|
||||||
ml.Wait();
|
ml.Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -851,7 +850,7 @@ bool PageSpace::ShouldPerformIdleMarkSweep(int64_t deadline) {
|
||||||
|
|
||||||
{
|
{
|
||||||
MonitorLocker locker(tasks_lock());
|
MonitorLocker locker(tasks_lock());
|
||||||
if (sweeper_tasks() > 0) {
|
if (tasks() > 0) {
|
||||||
// A concurrent sweeper is running. If we start a mark sweep now
|
// A concurrent sweeper is running. If we start a mark sweep now
|
||||||
// we'll have to wait for it, and this wait time is not included in
|
// we'll have to wait for it, and this wait time is not included in
|
||||||
// mark_sweep_words_per_micro_.
|
// mark_sweep_words_per_micro_.
|
||||||
|
@ -875,10 +874,10 @@ void PageSpace::CollectGarbage(bool compact) {
|
||||||
// Wait for pending tasks to complete and then account for the driver task.
|
// Wait for pending tasks to complete and then account for the driver task.
|
||||||
{
|
{
|
||||||
MonitorLocker locker(tasks_lock());
|
MonitorLocker locker(tasks_lock());
|
||||||
while (sweeper_tasks() > 0) {
|
while (tasks() > 0) {
|
||||||
locker.WaitWithSafepointCheck(thread);
|
locker.WaitWithSafepointCheck(thread);
|
||||||
}
|
}
|
||||||
set_sweeper_tasks(1);
|
set_tasks(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int64_t pre_safe_point = OS::GetCurrentMonotonicMicros();
|
const int64_t pre_safe_point = OS::GetCurrentMonotonicMicros();
|
||||||
|
@ -1036,7 +1035,7 @@ void PageSpace::CollectGarbage(bool compact) {
|
||||||
// Done, reset the task count.
|
// Done, reset the task count.
|
||||||
{
|
{
|
||||||
MonitorLocker ml(tasks_lock());
|
MonitorLocker ml(tasks_lock());
|
||||||
set_sweeper_tasks(sweeper_tasks() - 1);
|
set_tasks(tasks() - 1);
|
||||||
ml.NotifyAll();
|
ml.NotifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -330,15 +330,10 @@ class PageSpace {
|
||||||
}
|
}
|
||||||
|
|
||||||
Monitor* tasks_lock() const { return tasks_lock_; }
|
Monitor* tasks_lock() const { return tasks_lock_; }
|
||||||
intptr_t sweeper_tasks() const { return sweeper_tasks_; }
|
intptr_t tasks() const { return tasks_; }
|
||||||
void set_sweeper_tasks(intptr_t val) {
|
void set_tasks(intptr_t val) {
|
||||||
ASSERT(val >= 0);
|
ASSERT(val >= 0);
|
||||||
sweeper_tasks_ = val;
|
tasks_ = val;
|
||||||
}
|
|
||||||
intptr_t low_memory_tasks() const { return low_memory_tasks_; }
|
|
||||||
void set_low_memory_tasks(intptr_t val) {
|
|
||||||
ASSERT(val >= 0);
|
|
||||||
low_memory_tasks_ = val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to allocate from bump block rather than normal freelist.
|
// Attempt to allocate from bump block rather than normal freelist.
|
||||||
|
@ -439,8 +434,7 @@ class PageSpace {
|
||||||
|
|
||||||
// Keep track of running MarkSweep tasks.
|
// Keep track of running MarkSweep tasks.
|
||||||
Monitor* tasks_lock_;
|
Monitor* tasks_lock_;
|
||||||
int32_t sweeper_tasks_;
|
intptr_t tasks_;
|
||||||
int32_t low_memory_tasks_;
|
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
Thread* iterating_thread_;
|
Thread* iterating_thread_;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -498,7 +498,7 @@ void Scavenger::Epilogue(Isolate* isolate, SemiSpace* from) {
|
||||||
{
|
{
|
||||||
PageSpace* page_space = heap_->old_space();
|
PageSpace* page_space = heap_->old_space();
|
||||||
MonitorLocker ml(page_space->tasks_lock());
|
MonitorLocker ml(page_space->tasks_lock());
|
||||||
if (page_space->sweeper_tasks() == 0) {
|
if (page_space->tasks() == 0) {
|
||||||
VerifyStoreBufferPointerVisitor verify_store_buffer_visitor(isolate, to_);
|
VerifyStoreBufferPointerVisitor verify_store_buffer_visitor(isolate, to_);
|
||||||
heap_->old_space()->VisitObjectPointers(&verify_store_buffer_visitor);
|
heap_->old_space()->VisitObjectPointers(&verify_store_buffer_visitor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,7 +169,6 @@ class Thread : public BaseThread {
|
||||||
kMarkerTask = 0x4,
|
kMarkerTask = 0x4,
|
||||||
kSweeperTask = 0x8,
|
kSweeperTask = 0x8,
|
||||||
kCompactorTask = 0x10,
|
kCompactorTask = 0x10,
|
||||||
kLowMemoryTask = 0x20,
|
|
||||||
};
|
};
|
||||||
// Converts a TaskKind to its corresponding C-String name.
|
// Converts a TaskKind to its corresponding C-String name.
|
||||||
static const char* TaskKindToCString(TaskKind kind);
|
static const char* TaskKindToCString(TaskKind kind);
|
||||||
|
|
Loading…
Reference in a new issue