[vm/concurrency] Remove redundant isolates list, ensure shutdown procedure waits until the isolates actually got deleted

Issue https://github.com/dart-lang/sdk/issues/36097

Change-Id: If24affbb838eff8d80e5d448eac7455b3ffcb3a1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135062
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
This commit is contained in:
Martin Kustermann 2020-02-11 15:45:33 +00:00
parent 61b2d6a9a7
commit 40eaf81834
5 changed files with 141 additions and 125 deletions

View file

@ -246,12 +246,12 @@ char* Dart::Init(const uint8_t* vm_isolate_snapshot,
// really an isolate itself - it acts more as a container for VM-global // really an isolate itself - it acts more as a container for VM-global
// objects. // objects.
std::unique_ptr<IsolateGroupSource> source( std::unique_ptr<IsolateGroupSource> source(
new IsolateGroupSource(nullptr, "vm-isolate", vm_isolate_snapshot, new IsolateGroupSource(nullptr, kVmIsolateName, vm_isolate_snapshot,
instructions_snapshot, nullptr, -1, api_flags)); instructions_snapshot, nullptr, -1, api_flags));
auto group = new IsolateGroup(std::move(source), /*embedder_data=*/nullptr); auto group = new IsolateGroup(std::move(source), /*embedder_data=*/nullptr);
IsolateGroup::RegisterIsolateGroup(group); IsolateGroup::RegisterIsolateGroup(group);
vm_isolate_ = vm_isolate_ =
Isolate::InitIsolate("vm-isolate", group, api_flags, is_vm_isolate); Isolate::InitIsolate(kVmIsolateName, group, api_flags, is_vm_isolate);
group->set_initial_spawn_successful(); group->set_initial_spawn_successful();
// Verify assumptions about executing in the VM isolate. // Verify assumptions about executing in the VM isolate.
@ -405,31 +405,43 @@ char* Dart::Init(const uint8_t* vm_isolate_snapshot,
return NULL; return NULL;
} }
bool Dart::HasApplicationIsolateLocked() { static void DumpAliveIsolates(intptr_t num_attempts,
for (Isolate* isolate = Isolate::isolates_list_head_; isolate != NULL; bool only_aplication_isolates) {
isolate = isolate->next_) { IsolateGroup::ForEach([&](IsolateGroup* group) {
if (!Isolate::IsVMInternalIsolate(isolate)) return true; group->ForEachIsolate([&](Isolate* isolate) {
} if (!only_aplication_isolates || !Isolate::IsVMInternalIsolate(isolate)) {
return false; OS::PrintErr("Attempt:%" Pd " waiting for isolate %s to check in\n",
num_attempts, isolate->name());
}
});
});
}
static bool OnlyVmIsolateLeft() {
intptr_t count = 0;
bool found_vm_isolate = false;
IsolateGroup::ForEach([&](IsolateGroup* group) {
group->ForEachIsolate([&](Isolate* isolate) {
count++;
if (isolate == Dart::vm_isolate()) {
found_vm_isolate = true;
}
});
});
return count == 1 && found_vm_isolate;
} }
// This waits until only the VM, service and kernel isolates are in the list. // This waits until only the VM, service and kernel isolates are in the list.
void Dart::WaitForApplicationIsolateShutdown() { void Dart::WaitForApplicationIsolateShutdown() {
ASSERT(!Isolate::creation_enabled_); ASSERT(!Isolate::creation_enabled_);
MonitorLocker ml(Isolate::isolates_list_monitor_); MonitorLocker ml(Isolate::isolate_creation_monitor_);
intptr_t num_attempts = 0; intptr_t num_attempts = 0;
while (HasApplicationIsolateLocked()) { while (Isolate::application_isolates_count_ > 1) {
Monitor::WaitResult retval = ml.Wait(1000); Monitor::WaitResult retval = ml.Wait(1000);
if (retval == Monitor::kTimedOut) { if (retval == Monitor::kTimedOut) {
num_attempts += 1; num_attempts += 1;
if (num_attempts > 10) { if (num_attempts > 10) {
for (Isolate* isolate = Isolate::isolates_list_head_; isolate != NULL; DumpAliveIsolates(num_attempts, /*only_application_isolates=*/true);
isolate = isolate->next_) {
if (!Isolate::IsVMInternalIsolate(isolate)) {
OS::PrintErr("Attempt:%" Pd " waiting for isolate %s to check in\n",
num_attempts, isolate->name_);
}
}
} }
} }
} }
@ -438,22 +450,19 @@ void Dart::WaitForApplicationIsolateShutdown() {
// This waits until only the VM isolate remains in the list. // This waits until only the VM isolate remains in the list.
void Dart::WaitForIsolateShutdown() { void Dart::WaitForIsolateShutdown() {
ASSERT(!Isolate::creation_enabled_); ASSERT(!Isolate::creation_enabled_);
MonitorLocker ml(Isolate::isolates_list_monitor_); MonitorLocker ml(Isolate::isolate_creation_monitor_);
intptr_t num_attempts = 0; intptr_t num_attempts = 0;
while ((Isolate::isolates_list_head_ != NULL) && while (Isolate::total_isolates_count_ > 1) {
(Isolate::isolates_list_head_->next_ != NULL)) {
Monitor::WaitResult retval = ml.Wait(1000); Monitor::WaitResult retval = ml.Wait(1000);
if (retval == Monitor::kTimedOut) { if (retval == Monitor::kTimedOut) {
num_attempts += 1; num_attempts += 1;
if (num_attempts > 10) { if (num_attempts > 10) {
for (Isolate* isolate = Isolate::isolates_list_head_; isolate != NULL; DumpAliveIsolates(num_attempts, /*only_application_isolates=*/false);
isolate = isolate->next_)
OS::PrintErr("Attempt:%" Pd " waiting for isolate %s to check in\n",
num_attempts, isolate->name_);
} }
} }
} }
ASSERT(Isolate::isolates_list_head_ == Dart::vm_isolate());
ASSERT(OnlyVmIsolateLeft());
} }
char* Dart::Cleanup() { char* Dart::Cleanup() {
@ -940,11 +949,22 @@ void Dart::ShutdownIsolate(Isolate* isolate) {
void Dart::ShutdownIsolate() { void Dart::ShutdownIsolate() {
Isolate* isolate = Isolate::Current(); Isolate* isolate = Isolate::Current();
const bool is_application_isolate = !Isolate::IsVMInternalIsolate(isolate);
isolate->Shutdown(); isolate->Shutdown();
if (KernelIsolate::IsKernelIsolate(isolate)) { if (KernelIsolate::IsKernelIsolate(isolate)) {
KernelIsolate::SetKernelIsolate(NULL); KernelIsolate::SetKernelIsolate(NULL);
} }
delete isolate; delete isolate;
// Only now do we know for sure that the isolate and all it's resources have
// been deleted. So we can let any potential Dart::Cleanup() know it's safe to
// proceed shutdown of the VM.
Isolate::MarkIsolateDead(is_application_isolate);
}
bool Dart::VmIsolateNameEquals(const char* name) {
ASSERT(name != NULL);
return (strcmp(name, kVmIsolateName) == 0);
} }
int64_t Dart::UptimeMicros() { int64_t Dart::UptimeMicros() {

View file

@ -66,6 +66,7 @@ class Dart : public AllStatic {
static Isolate* vm_isolate() { return vm_isolate_; } static Isolate* vm_isolate() { return vm_isolate_; }
static ThreadPool* thread_pool() { return thread_pool_; } static ThreadPool* thread_pool() { return thread_pool_; }
static bool VmIsolateNameEquals(const char* name);
static int64_t UptimeMicros(); static int64_t UptimeMicros();
static int64_t UptimeMillis() { static int64_t UptimeMillis() {
@ -129,9 +130,10 @@ class Dart : public AllStatic {
static bool non_nullable_flag() { return true; } static bool non_nullable_flag() { return true; }
private: private:
static constexpr const char* kVmIsolateName = "vm-isolate";
static void WaitForIsolateShutdown(); static void WaitForIsolateShutdown();
static void WaitForApplicationIsolateShutdown(); static void WaitForApplicationIsolateShutdown();
static bool HasApplicationIsolateLocked();
static Isolate* vm_isolate_; static Isolate* vm_isolate_;
static int64_t start_time_micros_; static int64_t start_time_micros_;

View file

@ -1380,10 +1380,10 @@ void Isolate::InitVM() {
shutdown_callback_ = nullptr; shutdown_callback_ = nullptr;
cleanup_callback_ = nullptr; cleanup_callback_ = nullptr;
cleanup_group_callback_ = nullptr; cleanup_group_callback_ = nullptr;
if (isolates_list_monitor_ == nullptr) { if (isolate_creation_monitor_ == nullptr) {
isolates_list_monitor_ = new Monitor(); isolate_creation_monitor_ = new Monitor();
} }
ASSERT(isolates_list_monitor_ != nullptr); ASSERT(isolate_creation_monitor_ != nullptr);
EnableIsolateCreation(); EnableIsolateCreation();
} }
@ -1392,6 +1392,7 @@ Isolate* Isolate::InitIsolate(const char* name_prefix,
const Dart_IsolateFlags& api_flags, const Dart_IsolateFlags& api_flags,
bool is_vm_isolate) { bool is_vm_isolate) {
Isolate* result = new Isolate(isolate_group, api_flags); Isolate* result = new Isolate(isolate_group, api_flags);
result->BuildName(name_prefix);
ASSERT(result != nullptr); ASSERT(result != nullptr);
#if !defined(PRODUCT) #if !defined(PRODUCT)
@ -1458,7 +1459,6 @@ Isolate* Isolate::InitIsolate(const char* name_prefix,
result->set_pause_capability(result->random()->NextUInt64()); result->set_pause_capability(result->random()->NextUInt64());
result->set_terminate_capability(result->random()->NextUInt64()); result->set_terminate_capability(result->random()->NextUInt64());
result->BuildName(name_prefix);
#if !defined(PRODUCT) #if !defined(PRODUCT)
result->debugger_ = new Debugger(result); result->debugger_ = new Debugger(result);
#endif #endif
@ -1476,7 +1476,7 @@ Isolate* Isolate::InitIsolate(const char* name_prefix,
#endif // !PRODUCT #endif // !PRODUCT
// Add to isolate list. Shutdown and delete the isolate on failure. // Add to isolate list. Shutdown and delete the isolate on failure.
if (!AddIsolateToList(result)) { if (!TryMarkIsolateReady(result)) {
result->LowLevelShutdown(); result->LowLevelShutdown();
Thread::ExitIsolate(); Thread::ExitIsolate();
if (KernelIsolate::IsKernelIsolate(result)) { if (KernelIsolate::IsKernelIsolate(result)) {
@ -2224,12 +2224,7 @@ void Isolate::Shutdown() {
// Don't allow anymore dart code to execution on this isolate. // Don't allow anymore dart code to execution on this isolate.
thread->ClearStackLimit(); thread->ClearStackLimit();
// Remove this isolate from the list *before* we start tearing it down, to
// avoid exposing it in a state of decay.
RemoveIsolateFromList(this);
{ {
// After removal from isolate list. Before tearing down the heap.
StackZone zone(thread); StackZone zone(thread);
HandleScope handle_scope(thread); HandleScope handle_scope(thread);
ServiceIsolate::SendIsolateShutdownMessage(); ServiceIsolate::SendIsolateShutdownMessage();
@ -2265,6 +2260,7 @@ void Isolate::Shutdown() {
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
// Then, proceed with low-level teardown. // Then, proceed with low-level teardown.
Isolate::UnMarkIsolateReady(this);
LowLevelShutdown(); LowLevelShutdown();
#if defined(DEBUG) #if defined(DEBUG)
@ -2295,8 +2291,9 @@ Dart_IsolateShutdownCallback Isolate::shutdown_callback_ = nullptr;
Dart_IsolateCleanupCallback Isolate::cleanup_callback_ = nullptr; Dart_IsolateCleanupCallback Isolate::cleanup_callback_ = nullptr;
Dart_IsolateGroupCleanupCallback Isolate::cleanup_group_callback_ = nullptr; Dart_IsolateGroupCleanupCallback Isolate::cleanup_group_callback_ = nullptr;
Monitor* Isolate::isolates_list_monitor_ = nullptr; Monitor* Isolate::isolate_creation_monitor_ = nullptr;
Isolate* Isolate::isolates_list_head_ = nullptr; intptr_t Isolate::application_isolates_count_ = 0;
intptr_t Isolate::total_isolates_count_ = 0;
bool Isolate::creation_enabled_ = false; bool Isolate::creation_enabled_ = false;
RwLock* IsolateGroup::isolate_groups_rwlock_ = nullptr; RwLock* IsolateGroup::isolate_groups_rwlock_ = nullptr;
@ -3026,113 +3023,99 @@ void Isolate::VisitIsolates(IsolateVisitor* visitor) {
if (visitor == nullptr) { if (visitor == nullptr) {
return; return;
} }
// The visitor could potentially run code that could safepoint so use IsolateGroup::ForEach([&](IsolateGroup* group) {
// SafepointMonitorLocker to ensure the lock has safepoint checks. group->ForEachIsolate(
SafepointMonitorLocker ml(isolates_list_monitor_); [&](Isolate* isolate) { visitor->VisitIsolate(isolate); });
Isolate* current = isolates_list_head_; });
while (current != nullptr) {
visitor->VisitIsolate(current);
current = current->next_;
}
} }
intptr_t Isolate::IsolateListLength() { intptr_t Isolate::IsolateListLength() {
MonitorLocker ml(isolates_list_monitor_); MonitorLocker ml(isolate_creation_monitor_);
intptr_t count = 0; return total_isolates_count_;
Isolate* current = isolates_list_head_;
while (current != nullptr) {
count++;
current = current->next_;
}
return count;
} }
Isolate* Isolate::LookupIsolateByPort(Dart_Port port) { Isolate* Isolate::LookupIsolateByPort(Dart_Port port) {
MonitorLocker ml(isolates_list_monitor_); Isolate* match = nullptr;
Isolate* current = isolates_list_head_; IsolateGroup::ForEach([&](IsolateGroup* group) {
while (current != nullptr) { group->ForEachIsolate([&](Isolate* isolate) {
if (current->main_port() == port) { if (isolate->main_port() == port) {
return current; match = isolate;
} }
current = current->next_; });
} });
return nullptr; return match;
} }
std::unique_ptr<char[]> Isolate::LookupIsolateNameByPort(Dart_Port port) { std::unique_ptr<char[]> Isolate::LookupIsolateNameByPort(Dart_Port port) {
MonitorLocker ml(isolates_list_monitor_); MonitorLocker ml(isolate_creation_monitor_);
Isolate* current = isolates_list_head_; std::unique_ptr<char[]> result;
while (current != nullptr) { IsolateGroup::ForEach([&](IsolateGroup* group) {
if (current->main_port() == port) { group->ForEachIsolate([&](Isolate* isolate) {
const size_t len = strlen(current->name()) + 1; if (isolate->main_port() == port) {
auto result = std::unique_ptr<char[]>(new char[len]); const size_t len = strlen(isolate->name()) + 1;
strncpy(result.get(), current->name(), len); result = std::unique_ptr<char[]>(new char[len]);
return result; strncpy(result.get(), isolate->name(), len);
} }
current = current->next_; });
} });
return std::unique_ptr<char[]>(); return result;
} }
bool Isolate::AddIsolateToList(Isolate* isolate) { bool Isolate::TryMarkIsolateReady(Isolate* isolate) {
MonitorLocker ml(isolates_list_monitor_); MonitorLocker ml(isolate_creation_monitor_);
if (!creation_enabled_) { if (!creation_enabled_) {
return false; return false;
} }
ASSERT(isolate != nullptr); total_isolates_count_++;
ASSERT(isolate->next_ == nullptr); if (!Isolate::IsVMInternalIsolate(isolate)) {
isolate->next_ = isolates_list_head_; application_isolates_count_++;
isolates_list_head_ = isolate; }
isolate->accepts_messages_ = true;
return true; return true;
} }
void Isolate::RemoveIsolateFromList(Isolate* isolate) { void Isolate::UnMarkIsolateReady(Isolate* isolate) {
MonitorLocker ml(isolates_list_monitor_); MonitorLocker ml(isolate_creation_monitor_);
ASSERT(isolate != nullptr); ASSERT(total_isolates_count_ > 0);
if (isolate == isolates_list_head_) { isolate->accepts_messages_ = false;
isolates_list_head_ = isolate->next_; }
if (!creation_enabled_) {
ml.Notify(); void Isolate::MarkIsolateDead(bool is_application_isolate) {
} MonitorLocker ml(isolate_creation_monitor_);
return; ASSERT(total_isolates_count_ > 0);
total_isolates_count_--;
if (is_application_isolate) {
ASSERT(application_isolates_count_ > 0);
application_isolates_count_--;
} }
Isolate* previous = nullptr; if (!creation_enabled_) {
Isolate* current = isolates_list_head_; ml.Notify();
while (current != nullptr) {
if (current == isolate) {
ASSERT(previous != nullptr);
previous->next_ = current->next_;
if (!creation_enabled_) {
ml.Notify();
}
return;
}
previous = current;
current = current->next_;
} }
// If we are shutting down the VM, the isolate may not be in the list.
ASSERT(!creation_enabled_);
} }
void Isolate::DisableIsolateCreation() { void Isolate::DisableIsolateCreation() {
MonitorLocker ml(isolates_list_monitor_); MonitorLocker ml(isolate_creation_monitor_);
creation_enabled_ = false; creation_enabled_ = false;
} }
void Isolate::EnableIsolateCreation() { void Isolate::EnableIsolateCreation() {
MonitorLocker ml(isolates_list_monitor_); MonitorLocker ml(isolate_creation_monitor_);
creation_enabled_ = true; creation_enabled_ = true;
} }
bool Isolate::IsolateCreationEnabled() { bool Isolate::IsolateCreationEnabled() {
MonitorLocker ml(isolates_list_monitor_); MonitorLocker ml(isolate_creation_monitor_);
return creation_enabled_; return creation_enabled_;
} }
bool Isolate::IsVMInternalIsolate(const Isolate* isolate) { bool Isolate::IsVMInternalIsolate(const Isolate* isolate) {
return (isolate == Dart::vm_isolate()) || // We use a name comparison here because this method can be called during
ServiceIsolate::IsServiceIsolateDescendant(isolate) || // shutdown, where the actual isolate pointers might've already been cleared.
KernelIsolate::IsKernelIsolate(isolate); return Dart::VmIsolateNameEquals(isolate->name()) ||
#if !defined(DART_PRECOMPILED_RUNTIME)
KernelIsolate::NameEquals(isolate->name()) ||
#endif
ServiceIsolate::NameEquals(isolate->name());
} }
void Isolate::KillLocked(LibMsgId msg_id) { void Isolate::KillLocked(LibMsgId msg_id) {
@ -3189,7 +3172,9 @@ class IsolateKillerVisitor : public IsolateVisitor {
void VisitIsolate(Isolate* isolate) { void VisitIsolate(Isolate* isolate) {
ASSERT(isolate != nullptr); ASSERT(isolate != nullptr);
if (ShouldKill(isolate)) { if (ShouldKill(isolate)) {
isolate->KillLocked(msg_id_); if (isolate->AcceptsMessagesLocked()) {
isolate->KillLocked(msg_id_);
}
} }
} }
@ -3206,11 +3191,15 @@ class IsolateKillerVisitor : public IsolateVisitor {
}; };
void Isolate::KillAllIsolates(LibMsgId msg_id) { void Isolate::KillAllIsolates(LibMsgId msg_id) {
MonitorLocker ml(isolate_creation_monitor_);
IsolateKillerVisitor visitor(msg_id); IsolateKillerVisitor visitor(msg_id);
VisitIsolates(&visitor); VisitIsolates(&visitor);
} }
void Isolate::KillIfExists(Isolate* isolate, LibMsgId msg_id) { void Isolate::KillIfExists(Isolate* isolate, LibMsgId msg_id) {
MonitorLocker ml(isolate_creation_monitor_);
IsolateKillerVisitor visitor(isolate, msg_id); IsolateKillerVisitor visitor(isolate, msg_id);
VisitIsolates(&visitor); VisitIsolates(&visitor);
} }

View file

@ -1087,7 +1087,7 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
const Dart_IsolateFlags& api_flags, const Dart_IsolateFlags& api_flags,
bool is_vm_isolate = false); bool is_vm_isolate = false);
// The isolates_list_monitor_ should be held when calling Kill(). // The isolate_creation_monitor_ should be held when calling Kill().
void KillLocked(LibMsgId msg_id); void KillLocked(LibMsgId msg_id);
void LowLevelShutdown(); void LowLevelShutdown();
@ -1277,9 +1277,6 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
RawError* sticky_error_; RawError* sticky_error_;
// Isolate list next pointer.
Isolate* next_ = nullptr;
// Protect access to boxed_field_list_. // Protect access to boxed_field_list_.
Mutex field_list_mutex_; Mutex field_list_mutex_;
// List of fields that became boxed and that trigger deoptimization. // List of fields that became boxed and that trigger deoptimization.
@ -1303,6 +1300,11 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
std::unique_ptr<WeakTable> forward_table_new_; std::unique_ptr<WeakTable> forward_table_new_;
std::unique_ptr<WeakTable> forward_table_old_; std::unique_ptr<WeakTable> forward_table_old_;
// Signals whether the isolate can receive messages (e.g. KillAllIsolates can
// send a kill message).
// This is protected by [isolate_creation_monitor_].
bool accepts_messages_ = false;
static Dart_IsolateGroupCreateCallback create_group_callback_; static Dart_IsolateGroupCreateCallback create_group_callback_;
static Dart_InitializeIsolateCallback initialize_callback_; static Dart_InitializeIsolateCallback initialize_callback_;
static Dart_IsolateShutdownCallback shutdown_callback_; static Dart_IsolateShutdownCallback shutdown_callback_;
@ -1314,12 +1316,19 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
#endif #endif
// Manage list of existing isolates. // Manage list of existing isolates.
static bool AddIsolateToList(Isolate* isolate); static bool TryMarkIsolateReady(Isolate* isolate);
static void RemoveIsolateFromList(Isolate* isolate); static void UnMarkIsolateReady(Isolate* isolate);
static void MarkIsolateDead(bool is_application_isolate);
bool AcceptsMessagesLocked() {
ASSERT(isolate_creation_monitor_->IsOwnedByCurrentThread());
return accepts_messages_;
}
// This monitor protects isolates_list_head_, and creation_enabled_. // This monitor protects application_isolates_count_, total_isolates_count_,
static Monitor* isolates_list_monitor_; // creation_enabled_.
static Isolate* isolates_list_head_; static Monitor* isolate_creation_monitor_;
static intptr_t application_isolates_count_;
static intptr_t total_isolates_count_;
static bool creation_enabled_; static bool creation_enabled_;
#define REUSABLE_FRIEND_DECLARATION(name) \ #define REUSABLE_FRIEND_DECLARATION(name) \

View file

@ -363,7 +363,6 @@ class RunServiceTask : public ThreadPool::Task {
free(error); free(error);
error = nullptr; error = nullptr;
ServiceIsolate::SetServiceIsolate(NULL);
ServiceIsolate::InitializingFailed(formatted_error); ServiceIsolate::InitializingFailed(formatted_error);
return; return;
} }
@ -419,9 +418,6 @@ class RunServiceTask : public ThreadPool::Task {
} }
Dart::RunShutdownCallback(); Dart::RunShutdownCallback();
} }
ASSERT(ServiceIsolate::IsServiceIsolate(I));
ServiceIsolate::SetServiceIsolate(NULL);
ServiceIsolate::SetServicePort(ILLEGAL_PORT);
// Shut the isolate down. // Shut the isolate down.
Dart::ShutdownIsolate(I); Dart::ShutdownIsolate(I);