Files
sdk/runtime/vm/service_isolate.cc
T
Martin Kustermann 355d443b5f Reland "[vm/concurrency] Remove redundant isolates list, ensure shutdown procedure waits until the isolates actually got deleted"
The memory leak as well as OOM seems to be unrelated to this CL, see
discussion on https://github.com/dart-lang/sdk/issues/40627

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

Change-Id: I95cc27370b816fdc79eba1a35b11fd6ca072a4e5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/136225
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
2020-02-20 20:52:00 +00:00

589 lines
18 KiB
C++

// Copyright (c) 2015, 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.
#include "vm/service_isolate.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_api_message.h"
#include "vm/dart_entry.h"
#include "vm/isolate.h"
#include "vm/lockers.h"
#include "vm/message.h"
#include "vm/message_handler.h"
#include "vm/native_arguments.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/port.h"
#include "vm/service.h"
#include "vm/symbols.h"
#include "vm/thread_pool.h"
#include "vm/timeline.h"
#if !defined(PRODUCT)
namespace dart {
#define Z (T->zone())
DEFINE_FLAG(bool, trace_service, false, "Trace VM service requests.");
DEFINE_FLAG(bool,
trace_service_pause_events,
false,
"Trace VM service isolate pause events.");
DEFINE_FLAG(bool,
trace_service_verbose,
false,
"Provide extra service tracing information.");
// These must be kept in sync with service/constants.dart
#define VM_SERVICE_ISOLATE_EXIT_MESSAGE_ID 0
#define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1
#define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2
#define VM_SERVICE_WEB_SERVER_CONTROL_MESSAGE_ID 3
#define VM_SERVICE_SERVER_INFO_MESSAGE_ID 4
#define VM_SERVICE_METHOD_CALL_FROM_NATIVE 5
static RawArray* MakeServiceControlMessage(Dart_Port port_id,
intptr_t code,
const String& name) {
const Array& list = Array::Handle(Array::New(4));
ASSERT(!list.IsNull());
const Integer& code_int = Integer::Handle(Integer::New(code));
const Integer& port_int = Integer::Handle(Integer::New(port_id));
const SendPort& send_port = SendPort::Handle(SendPort::New(port_id));
list.SetAt(0, code_int);
list.SetAt(1, port_int);
list.SetAt(2, send_port);
list.SetAt(3, name);
return list.raw();
}
static RawArray* MakeServerControlMessage(const SendPort& sp,
intptr_t code,
bool enable = false) {
const Array& list = Array::Handle(Array::New(3));
ASSERT(!list.IsNull());
list.SetAt(0, Integer::Handle(Integer::New(code)));
list.SetAt(1, sp);
list.SetAt(2, Bool::Get(enable));
return list.raw();
}
const char* ServiceIsolate::kName = DART_VM_SERVICE_ISOLATE_NAME;
Dart_IsolateGroupCreateCallback ServiceIsolate::create_group_callback_ = NULL;
Monitor* ServiceIsolate::monitor_ = new Monitor();
ServiceIsolate::State ServiceIsolate::state_ = ServiceIsolate::kStopped;
Isolate* ServiceIsolate::isolate_ = NULL;
Dart_Port ServiceIsolate::port_ = ILLEGAL_PORT;
Dart_Port ServiceIsolate::origin_ = ILLEGAL_PORT;
char* ServiceIsolate::server_address_ = NULL;
char* ServiceIsolate::startup_failure_reason_ = nullptr;
void ServiceIsolate::RequestServerInfo(const SendPort& sp) {
const Array& message = Array::Handle(MakeServerControlMessage(
sp, VM_SERVICE_SERVER_INFO_MESSAGE_ID, false /* ignored */));
ASSERT(!message.IsNull());
MessageWriter writer(false);
PortMap::PostMessage(
writer.WriteMessage(message, port_, Message::kNormalPriority));
}
void ServiceIsolate::ControlWebServer(const SendPort& sp, bool enable) {
const Array& message = Array::Handle(MakeServerControlMessage(
sp, VM_SERVICE_WEB_SERVER_CONTROL_MESSAGE_ID, enable));
ASSERT(!message.IsNull());
MessageWriter writer(false);
PortMap::PostMessage(
writer.WriteMessage(message, port_, Message::kNormalPriority));
}
void ServiceIsolate::SetServerAddress(const char* address) {
if (server_address_ != NULL) {
free(server_address_);
server_address_ = NULL;
}
if (address == NULL) {
return;
}
server_address_ = strdup(address);
}
bool ServiceIsolate::NameEquals(const char* name) {
ASSERT(name != NULL);
return strcmp(name, kName) == 0;
}
bool ServiceIsolate::Exists() {
MonitorLocker ml(monitor_);
return isolate_ != NULL;
}
bool ServiceIsolate::IsRunning() {
MonitorLocker ml(monitor_);
return (port_ != ILLEGAL_PORT) && (isolate_ != NULL);
}
bool ServiceIsolate::IsServiceIsolate(const Isolate* isolate) {
MonitorLocker ml(monitor_);
return isolate != nullptr && isolate == isolate_;
}
bool ServiceIsolate::IsServiceIsolateDescendant(const Isolate* isolate) {
MonitorLocker ml(monitor_);
return isolate->origin_id() == origin_;
}
Dart_Port ServiceIsolate::Port() {
MonitorLocker ml(monitor_);
return port_;
}
void ServiceIsolate::WaitForServiceIsolateStartup() {
MonitorLocker ml(monitor_);
while (state_ == kStarting) {
ml.Wait();
}
}
bool ServiceIsolate::SendServiceRpc(uint8_t* request_json,
intptr_t request_json_length,
Dart_Port reply_port,
char** error) {
// Keep in sync with "sdk/lib/vmservice/vmservice.dart:_handleNativeRpcCall".
Dart_CObject opcode;
opcode.type = Dart_CObject_kInt32;
opcode.value.as_int32 = VM_SERVICE_METHOD_CALL_FROM_NATIVE;
Dart_CObject message;
message.type = Dart_CObject_kTypedData;
message.value.as_typed_data.type = Dart_TypedData_kUint8;
message.value.as_typed_data.length = request_json_length;
message.value.as_typed_data.values = request_json;
Dart_CObject send_port;
send_port.type = Dart_CObject_kSendPort;
send_port.value.as_send_port.id = reply_port;
send_port.value.as_send_port.origin_id = ILLEGAL_PORT;
Dart_CObject* request_array[] = {
&opcode,
&message,
&send_port,
};
Dart_CObject request;
request.type = Dart_CObject_kArray;
request.value.as_array.values = request_array;
request.value.as_array.length = ARRAY_SIZE(request_array);
ServiceIsolate::WaitForServiceIsolateStartup();
Dart_Port service_port = ServiceIsolate::Port();
const bool success = Dart_PostCObject(service_port, &request);
if (!success && error != nullptr) {
if (service_port == ILLEGAL_PORT) {
if (startup_failure_reason_ != nullptr) {
*error = OS::SCreate(/*zone=*/nullptr,
"Service isolate failed to start up: %s.",
startup_failure_reason_);
} else {
*error = strdup("No service isolate port was found.");
}
} else {
*error = strdup("Was unable to post message to service isolate.");
}
}
return success;
}
bool ServiceIsolate::SendIsolateStartupMessage() {
if (!IsRunning()) {
return false;
}
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
if (!FLAG_show_invisible_isolates && Isolate::IsVMInternalIsolate(isolate)) {
return false;
}
ASSERT(isolate != NULL);
HANDLESCOPE(thread);
const String& name = String::Handle(String::New(isolate->name()));
ASSERT(!name.IsNull());
const Array& list = Array::Handle(MakeServiceControlMessage(
Dart_GetMainPortId(), VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID, name));
ASSERT(!list.IsNull());
MessageWriter writer(false);
if (FLAG_trace_service) {
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Isolate %s %" Pd64
" registered.\n",
name.ToCString(), Dart_GetMainPortId());
}
return PortMap::PostMessage(
writer.WriteMessage(list, port_, Message::kNormalPriority));
}
bool ServiceIsolate::SendIsolateShutdownMessage() {
if (!IsRunning()) {
return false;
}
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
if (!FLAG_show_invisible_isolates && Isolate::IsVMInternalIsolate(isolate)) {
return false;
}
ASSERT(isolate != NULL);
HANDLESCOPE(thread);
const String& name = String::Handle(String::New(isolate->name()));
ASSERT(!name.IsNull());
const Array& list = Array::Handle(MakeServiceControlMessage(
Dart_GetMainPortId(), VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID, name));
ASSERT(!list.IsNull());
MessageWriter writer(false);
if (FLAG_trace_service) {
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Isolate %s %" Pd64
" deregistered.\n",
name.ToCString(), Dart_GetMainPortId());
}
return PortMap::PostMessage(
writer.WriteMessage(list, port_, Message::kNormalPriority));
}
void ServiceIsolate::SendServiceExitMessage() {
if (!IsRunning()) {
return;
}
if (FLAG_trace_service) {
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
": sending service exit message.\n");
}
Dart_CObject code;
code.type = Dart_CObject_kInt32;
code.value.as_int32 = VM_SERVICE_ISOLATE_EXIT_MESSAGE_ID;
Dart_CObject* values[1] = {&code};
Dart_CObject message;
message.type = Dart_CObject_kArray;
message.value.as_array.length = 1;
message.value.as_array.values = values;
ApiMessageWriter writer;
PortMap::PostMessage(
writer.WriteCMessage(&message, port_, Message::kNormalPriority));
}
void ServiceIsolate::SetServicePort(Dart_Port port) {
MonitorLocker ml(monitor_);
port_ = port;
}
void ServiceIsolate::SetServiceIsolate(Isolate* isolate) {
MonitorLocker ml(monitor_);
isolate_ = isolate;
if (isolate_ != NULL) {
isolate_->set_is_service_isolate(true);
origin_ = isolate_->origin_id();
}
}
void ServiceIsolate::MaybeMakeServiceIsolate(Isolate* I) {
Thread* T = Thread::Current();
ASSERT(I == T->isolate());
ASSERT(I != NULL);
ASSERT(I->name() != NULL);
if (!ServiceIsolate::NameEquals(I->name())) {
// Not service isolate.
return;
}
if (Exists()) {
// Service isolate already exists.
return;
}
SetServiceIsolate(I);
}
void ServiceIsolate::FinishedExiting() {
MonitorLocker ml(monitor_);
ASSERT(state_ == kStarted || state_ == kStopping);
state_ = kStopped;
ml.NotifyAll();
}
void ServiceIsolate::FinishedInitializing() {
MonitorLocker ml(monitor_);
ASSERT(state_ == kStarting);
state_ = kStarted;
ml.NotifyAll();
}
void ServiceIsolate::InitializingFailed(char* error) {
MonitorLocker ml(monitor_);
ASSERT(state_ == kStarting);
state_ = kStopped;
startup_failure_reason_ = error;
ml.NotifyAll();
}
class RunServiceTask : public ThreadPool::Task {
public:
virtual void Run() {
ASSERT(Isolate::Current() == NULL);
#if defined(SUPPORT_TIMELINE)
TimelineBeginEndScope tbes(Timeline::GetVMStream(),
"ServiceIsolateStartup");
#endif // SUPPORT_TIMELINE
char* error = NULL;
Isolate* isolate = NULL;
const auto create_group_callback = ServiceIsolate::create_group_callback();
ASSERT(create_group_callback != NULL);
Dart_IsolateFlags api_flags;
Isolate::FlagsInitialize(&api_flags);
isolate = reinterpret_cast<Isolate*>(
create_group_callback(ServiceIsolate::kName, ServiceIsolate::kName,
NULL, NULL, &api_flags, NULL, &error));
if (isolate == NULL) {
if (FLAG_trace_service) {
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
": Isolate creation error: %s\n",
error);
}
char* formatted_error = OS::SCreate(
/*zone=*/nullptr, "Invoking the 'create_group' failed with: '%s'",
error);
free(error);
error = nullptr;
ServiceIsolate::InitializingFailed(formatted_error);
return;
}
bool got_unwind;
{
ASSERT(Isolate::Current() == NULL);
StartIsolateScope start_scope(isolate);
got_unwind = RunMain(isolate);
}
// FinishedInitializing should be called irrespective of whether
// running main caused an error or not. Otherwise, other isolates
// waiting for service isolate to come up will deadlock.
ServiceIsolate::FinishedInitializing();
if (got_unwind) {
ShutdownIsolate(reinterpret_cast<uword>(isolate));
return;
}
isolate->message_handler()->Run(Dart::thread_pool(), NULL, ShutdownIsolate,
reinterpret_cast<uword>(isolate));
}
protected:
static void ShutdownIsolate(uword parameter) {
if (FLAG_trace_service) {
OS::PrintErr("vm-service: ShutdownIsolate\n");
}
Isolate* I = reinterpret_cast<Isolate*>(parameter);
ASSERT(ServiceIsolate::IsServiceIsolate(I));
{
// Print the error if there is one. This may execute dart code to
// print the exception object, so we need to use a StartIsolateScope.
ASSERT(Isolate::Current() == NULL);
StartIsolateScope start_scope(I);
Thread* T = Thread::Current();
ASSERT(I == T->isolate());
I->WaitForOutstandingSpawns();
StackZone zone(T);
HandleScope handle_scope(T);
Error& error = Error::Handle(Z);
error = T->sticky_error();
if (!error.IsNull() && !error.IsUnwindError()) {
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Error: %s\n",
error.ToErrorCString());
}
error = I->sticky_error();
if (!error.IsNull() && !error.IsUnwindError()) {
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Error: %s\n",
error.ToErrorCString());
}
Dart::RunShutdownCallback();
}
// Shut the isolate down.
Dart::ShutdownIsolate(I);
if (FLAG_trace_service) {
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Shutdown.\n");
}
ServiceIsolate::FinishedExiting();
}
bool RunMain(Isolate* I) {
Thread* T = Thread::Current();
ASSERT(I == T->isolate());
StackZone zone(T);
HANDLESCOPE(T);
// Invoke main which will set up the service port.
const Library& root_library =
Library::Handle(Z, I->object_store()->root_library());
if (root_library.IsNull()) {
if (FLAG_trace_service) {
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
": Embedder did not install a script.");
}
// Service isolate is not supported by embedder.
return false;
}
ASSERT(!root_library.IsNull());
const String& entry_name = String::Handle(Z, String::New("main"));
ASSERT(!entry_name.IsNull());
const Function& entry = Function::Handle(
Z, root_library.LookupFunctionAllowPrivate(entry_name));
if (entry.IsNull()) {
// Service isolate is not supported by embedder.
if (FLAG_trace_service) {
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
": Embedder did not provide a main function.");
}
return false;
}
ASSERT(!entry.IsNull());
const Object& result = Object::Handle(
Z, DartEntry::InvokeFunction(entry, Object::empty_array()));
if (result.IsError()) {
// Service isolate did not initialize properly.
if (FLAG_trace_service) {
const Error& error = Error::Cast(result);
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
": Calling main resulted in an error: %s",
error.ToErrorCString());
}
if (result.IsUnwindError()) {
return true;
}
return false;
}
return false;
}
};
void ServiceIsolate::Run() {
{
MonitorLocker ml(monitor_);
ASSERT(state_ == kStopped);
state_ = kStarting;
ml.NotifyAll();
}
// Grab the isolate create callback here to avoid race conditions with tests
// that change this after Dart_Initialize returns.
create_group_callback_ = Isolate::CreateGroupCallback();
if (create_group_callback_ == NULL) {
ServiceIsolate::InitializingFailed(
strdup("The 'create_group' callback was not provided"));
return;
}
bool task_started = Dart::thread_pool()->Run<RunServiceTask>();
ASSERT(task_started);
}
void ServiceIsolate::KillServiceIsolate() {
{
MonitorLocker ml(monitor_);
if (state_ == kStopped) {
return;
}
ASSERT(state_ == kStarted);
state_ = kStopping;
ml.NotifyAll();
}
Isolate::KillIfExists(isolate_, Isolate::kInternalKillMsg);
{
MonitorLocker ml(monitor_);
while (state_ == kStopping) {
ml.Wait();
}
ASSERT(state_ == kStopped);
}
}
void ServiceIsolate::Shutdown() {
{
MonitorLocker ml(monitor_);
while (state_ == kStarting) {
ml.Wait();
}
}
if (IsRunning()) {
{
MonitorLocker ml(monitor_);
ASSERT(state_ == kStarted);
state_ = kStopping;
ml.NotifyAll();
}
SendServiceExitMessage();
{
MonitorLocker ml(monitor_);
while (state_ == kStopping) {
ml.Wait();
}
ASSERT(state_ == kStopped);
}
} else {
if (isolate_ != NULL) {
// TODO(johnmccutchan,turnidge) When it is possible to properly create
// the VMService object and set up its shutdown handler in the service
// isolate's main() function, this case will no longer be possible and
// can be removed.
KillServiceIsolate();
}
}
if (server_address_ != NULL) {
free(server_address_);
server_address_ = NULL;
}
if (startup_failure_reason_ != nullptr) {
free(startup_failure_reason_);
startup_failure_reason_ = nullptr;
}
}
void ServiceIsolate::BootVmServiceLibrary() {
Thread* thread = Thread::Current();
const Library& vmservice_library =
Library::Handle(Library::LookupLibrary(thread, Symbols::DartVMService()));
ASSERT(!vmservice_library.IsNull());
const String& boot_function_name = String::Handle(String::New("boot"));
const Function& boot_function = Function::Handle(
vmservice_library.LookupFunctionAllowPrivate(boot_function_name));
ASSERT(!boot_function.IsNull());
const Object& result = Object::Handle(
DartEntry::InvokeFunction(boot_function, Object::empty_array()));
ASSERT(!result.IsNull());
if (result.IsUnwindError() || result.IsUnhandledException()) {
Exceptions::PropagateError(Error::Cast(result));
}
Dart_Port port = ILLEGAL_PORT;
if (result.IsReceivePort()) {
port = ReceivePort::Cast(result).Id();
}
ASSERT(port != ILLEGAL_PORT);
ServiceIsolate::SetServicePort(port);
}
void ServiceIsolate::VisitObjectPointers(ObjectPointerVisitor* visitor) {}
} // namespace dart
#endif // !defined(PRODUCT)