dart-sdk/runtime/vm/service_isolate.cc

610 lines
18 KiB
C++
Raw Normal View History

// 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"
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;
Second attempt to reland "[vm/concurrency] Introduce concept of Isolate Groups" This reverts commit 3d14b75f972ca287cd892ff907cf04b22839af59. An Isolate Group (IG) is a collection of isolates which were spawned from the same source. This allows the VM to: * have a guarantee that all isolates within one IG can safely exchange structured objects (currently we rely on embedder for this guarantee) * hot-reload all isolates together (currently we only reload one isolate, leaving same-source isolates in inconsistent state) * make a shared heap for all isolates from the same IG, which paves the way for faster communication and sharing of immutable objects. All isolates within one IG will share the same IsolateGroupSource. **Embedder changes** This change makes breaking embedder API changes to support this new concept of Isolate Groups: The existing isolate lifecycle callbacks given to Dart_Initialize will become Isolate Group lifecycle callbacks. A new callback `initialize_isolate` callback will be added which can initialize a new isolate within an existing IG. Existing embedders can be updated by performing the following renames Dart_CreateIsolate -> Dart_CreateIsolateGroup Dart_IsolateCreateCallback -> Dart_IsolateGroupCreateCallback Dart_IsolateCleanupCallback -> Dart_IsolateGroupShutdownCallback Dart_CreateIsolateFromKernel -> Dart_CreateIsolateGroupFromKernel Dart_CurrentIsolateData -> Dart_CurrentIsolateGroupData Dart_IsolateData -> Dart_IsolateGroupData Dart_GetNativeIsolateData -> Dart_GetNativeIsolateGroupData Dart_InitializeParams.create -> Dart_InitializeParams.create_group Dart_InitializeParams.cleanup -> Dart_InitializeParams.shutdown_group Dart_InitializeParams.shutdown -> Dart_InitializeParams.shutdown_isolate By default `Isolate.spawn` will cause the creation of a new IG. Though an embedder can opt-into supporting multiple isolates within one IG by providing a callback to the newly added `Dart_InitializeParams.initialize_isolate`. The responsibility of this new callback is to initialize an existing isolate (which was setup by re-using source code from the spawning isolate - i.e. the one which used `Isolate.spawn`) by setting native resolvers, initializing global state, etc. Issue https://github.com/dart-lang/sdk/issues/36648 Issue https://github.com/dart-lang/sdk/issues/36097 Original review: https://dart-review.googlesource.com/c/sdk/+/105241 Difference to original review: * Give each isolate it's own [Loader] (for now) * Sort classes during initialization for spawned isolates if app-jit is used (to match main isolate) * Fix IsolateData memory leak if isolate startup fails Difference to first reland(Patchset 2): * Fix typo where memory was freed twice. Change-Id: Ib1c83fe83b629cd50ae6af90ee99fdd44da882d4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108367 Commit-Queue: Alexander Aprelev <aam@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
2019-07-08 23:49:05 +00:00
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::load_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_;
}
Dart_Port ServiceIsolate::WaitForLoadPort() {
VMTagScope tagScope(Thread::Current(), VMTag::kLoadWaitTagId);
return WaitForLoadPortInternal();
}
Dart_Port ServiceIsolate::WaitForLoadPortInternal() {
MonitorLocker ml(monitor_);
while (state_ == kStarting && (load_port_ == ILLEGAL_PORT)) {
ml.Wait();
}
return load_port_;
}
Dart_Port ServiceIsolate::LoadPort() {
MonitorLocker ml(monitor_);
return load_port_;
}
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::WaitForLoadPortInternal();
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 (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 (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::SetLoadPort(Dart_Port port) {
MonitorLocker ml(monitor_);
load_port_ = port;
}
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)
TimelineDurationScope tds(Timeline::GetVMStream(), "ServiceIsolateStartup");
#endif // SUPPORT_TIMELINE
char* error = NULL;
Isolate* isolate = NULL;
Second attempt to reland "[vm/concurrency] Introduce concept of Isolate Groups" This reverts commit 3d14b75f972ca287cd892ff907cf04b22839af59. An Isolate Group (IG) is a collection of isolates which were spawned from the same source. This allows the VM to: * have a guarantee that all isolates within one IG can safely exchange structured objects (currently we rely on embedder for this guarantee) * hot-reload all isolates together (currently we only reload one isolate, leaving same-source isolates in inconsistent state) * make a shared heap for all isolates from the same IG, which paves the way for faster communication and sharing of immutable objects. All isolates within one IG will share the same IsolateGroupSource. **Embedder changes** This change makes breaking embedder API changes to support this new concept of Isolate Groups: The existing isolate lifecycle callbacks given to Dart_Initialize will become Isolate Group lifecycle callbacks. A new callback `initialize_isolate` callback will be added which can initialize a new isolate within an existing IG. Existing embedders can be updated by performing the following renames Dart_CreateIsolate -> Dart_CreateIsolateGroup Dart_IsolateCreateCallback -> Dart_IsolateGroupCreateCallback Dart_IsolateCleanupCallback -> Dart_IsolateGroupShutdownCallback Dart_CreateIsolateFromKernel -> Dart_CreateIsolateGroupFromKernel Dart_CurrentIsolateData -> Dart_CurrentIsolateGroupData Dart_IsolateData -> Dart_IsolateGroupData Dart_GetNativeIsolateData -> Dart_GetNativeIsolateGroupData Dart_InitializeParams.create -> Dart_InitializeParams.create_group Dart_InitializeParams.cleanup -> Dart_InitializeParams.shutdown_group Dart_InitializeParams.shutdown -> Dart_InitializeParams.shutdown_isolate By default `Isolate.spawn` will cause the creation of a new IG. Though an embedder can opt-into supporting multiple isolates within one IG by providing a callback to the newly added `Dart_InitializeParams.initialize_isolate`. The responsibility of this new callback is to initialize an existing isolate (which was setup by re-using source code from the spawning isolate - i.e. the one which used `Isolate.spawn`) by setting native resolvers, initializing global state, etc. Issue https://github.com/dart-lang/sdk/issues/36648 Issue https://github.com/dart-lang/sdk/issues/36097 Original review: https://dart-review.googlesource.com/c/sdk/+/105241 Difference to original review: * Give each isolate it's own [Loader] (for now) * Sort classes during initialization for spawned isolates if app-jit is used (to match main isolate) * Fix IsolateData memory leak if isolate startup fails Difference to first reland(Patchset 2): * Fix typo where memory was freed twice. Change-Id: Ib1c83fe83b629cd50ae6af90ee99fdd44da882d4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108367 Commit-Queue: Alexander Aprelev <aam@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
2019-07-08 23:49:05 +00:00
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*>(
Second attempt to reland "[vm/concurrency] Introduce concept of Isolate Groups" This reverts commit 3d14b75f972ca287cd892ff907cf04b22839af59. An Isolate Group (IG) is a collection of isolates which were spawned from the same source. This allows the VM to: * have a guarantee that all isolates within one IG can safely exchange structured objects (currently we rely on embedder for this guarantee) * hot-reload all isolates together (currently we only reload one isolate, leaving same-source isolates in inconsistent state) * make a shared heap for all isolates from the same IG, which paves the way for faster communication and sharing of immutable objects. All isolates within one IG will share the same IsolateGroupSource. **Embedder changes** This change makes breaking embedder API changes to support this new concept of Isolate Groups: The existing isolate lifecycle callbacks given to Dart_Initialize will become Isolate Group lifecycle callbacks. A new callback `initialize_isolate` callback will be added which can initialize a new isolate within an existing IG. Existing embedders can be updated by performing the following renames Dart_CreateIsolate -> Dart_CreateIsolateGroup Dart_IsolateCreateCallback -> Dart_IsolateGroupCreateCallback Dart_IsolateCleanupCallback -> Dart_IsolateGroupShutdownCallback Dart_CreateIsolateFromKernel -> Dart_CreateIsolateGroupFromKernel Dart_CurrentIsolateData -> Dart_CurrentIsolateGroupData Dart_IsolateData -> Dart_IsolateGroupData Dart_GetNativeIsolateData -> Dart_GetNativeIsolateGroupData Dart_InitializeParams.create -> Dart_InitializeParams.create_group Dart_InitializeParams.cleanup -> Dart_InitializeParams.shutdown_group Dart_InitializeParams.shutdown -> Dart_InitializeParams.shutdown_isolate By default `Isolate.spawn` will cause the creation of a new IG. Though an embedder can opt-into supporting multiple isolates within one IG by providing a callback to the newly added `Dart_InitializeParams.initialize_isolate`. The responsibility of this new callback is to initialize an existing isolate (which was setup by re-using source code from the spawning isolate - i.e. the one which used `Isolate.spawn`) by setting native resolvers, initializing global state, etc. Issue https://github.com/dart-lang/sdk/issues/36648 Issue https://github.com/dart-lang/sdk/issues/36097 Original review: https://dart-review.googlesource.com/c/sdk/+/105241 Difference to original review: * Give each isolate it's own [Loader] (for now) * Sort classes during initialization for spawned isolates if app-jit is used (to match main isolate) * Fix IsolateData memory leak if isolate startup fails Difference to first reland(Patchset 2): * Fix typo where memory was freed twice. Change-Id: Ib1c83fe83b629cd50ae6af90ee99fdd44da882d4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108367 Commit-Queue: Alexander Aprelev <aam@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
2019-07-08 23:49:05 +00:00
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::SetServiceIsolate(NULL);
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());
}
Revert "[vm] Cleanup Dart_Get/Set/HasStickyError API and use isolate's sticky error only as a backup for thread's sticky error" This reverts commit b10f17960890b053a5c1ac9303e0f9332fc8bd17. Reason for revert: failed service/* tests Original change's description: > [vm] Cleanup Dart_Get/Set/HasStickyError API and use isolate's sticky error only as a backup for thread's sticky error > > Both thread and isolate have sticky error fields. Dart API was using > isolate's sticky error, while Dart code was using thread's sticky error. > There was a one-way move of a thread's sticky error into isolate > when thread was unscheduled. > > This causes a problem as error in the isolate may go unnoticed and > repeated unscheduling/re-scheduling might end up overwriting the error > in the isolate (which triggers the assertion). > > To solve this problem, this CL: > * Cleans up Dart API which manipulates isolate's sticky error, so > isolate's sticky error is never set directly. > * When sceduling an isolate to a thread, sticky error is moved back from > isolate (if any). > > With this changes, thread's sticky error is always used if thread is running, > and isolate's sticky error is only used to hold sticky error while > isolate has no thread. > > Fixes https://github.com/dart-lang/sdk/issues/35590 > > Change-Id: I99b128cac363ca2df75f6e64c083b1ec36c866ce > Reviewed-on: https://dart-review.googlesource.com/c/89442 > Reviewed-by: Ryan Macnak <rmacnak@google.com> > Commit-Queue: Alexander Markov <alexmarkov@google.com> TBR=rmacnak@google.com,alexmarkov@google.com,zra@google.com,asiva@google.com Change-Id: I15874575b6b8ddca618741c59c21d4e692c4dcab No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://dart-review.googlesource.com/c/90127 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
2019-01-18 01:41:18 +00:00
error = I->sticky_error();
if (!error.IsNull() && !error.IsUnwindError()) {
OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME ": Error: %s\n",
error.ToErrorCString());
Revert "[vm] Cleanup Dart_Get/Set/HasStickyError API and use isolate's sticky error only as a backup for thread's sticky error" This reverts commit b10f17960890b053a5c1ac9303e0f9332fc8bd17. Reason for revert: failed service/* tests Original change's description: > [vm] Cleanup Dart_Get/Set/HasStickyError API and use isolate's sticky error only as a backup for thread's sticky error > > Both thread and isolate have sticky error fields. Dart API was using > isolate's sticky error, while Dart code was using thread's sticky error. > There was a one-way move of a thread's sticky error into isolate > when thread was unscheduled. > > This causes a problem as error in the isolate may go unnoticed and > repeated unscheduling/re-scheduling might end up overwriting the error > in the isolate (which triggers the assertion). > > To solve this problem, this CL: > * Cleans up Dart API which manipulates isolate's sticky error, so > isolate's sticky error is never set directly. > * When sceduling an isolate to a thread, sticky error is moved back from > isolate (if any). > > With this changes, thread's sticky error is always used if thread is running, > and isolate's sticky error is only used to hold sticky error while > isolate has no thread. > > Fixes https://github.com/dart-lang/sdk/issues/35590 > > Change-Id: I99b128cac363ca2df75f6e64c083b1ec36c866ce > Reviewed-on: https://dart-review.googlesource.com/c/89442 > Reviewed-by: Ryan Macnak <rmacnak@google.com> > Commit-Queue: Alexander Markov <alexmarkov@google.com> TBR=rmacnak@google.com,alexmarkov@google.com,zra@google.com,asiva@google.com Change-Id: I15874575b6b8ddca618741c59c21d4e692c4dcab No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://dart-review.googlesource.com/c/90127 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
2019-01-18 01:41:18 +00:00
}
Dart::RunShutdownCallback();
}
ASSERT(ServiceIsolate::IsServiceIsolate(I));
ServiceIsolate::SetServiceIsolate(NULL);
ServiceIsolate::SetServicePort(ILLEGAL_PORT);
// 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 return the loadScriptPort.
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()));
ASSERT(!result.IsNull());
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;
}
ASSERT(result.IsReceivePort());
const ReceivePort& rp = ReceivePort::Cast(result);
ServiceIsolate::SetLoadPort(rp.Id());
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.
Second attempt to reland "[vm/concurrency] Introduce concept of Isolate Groups" This reverts commit 3d14b75f972ca287cd892ff907cf04b22839af59. An Isolate Group (IG) is a collection of isolates which were spawned from the same source. This allows the VM to: * have a guarantee that all isolates within one IG can safely exchange structured objects (currently we rely on embedder for this guarantee) * hot-reload all isolates together (currently we only reload one isolate, leaving same-source isolates in inconsistent state) * make a shared heap for all isolates from the same IG, which paves the way for faster communication and sharing of immutable objects. All isolates within one IG will share the same IsolateGroupSource. **Embedder changes** This change makes breaking embedder API changes to support this new concept of Isolate Groups: The existing isolate lifecycle callbacks given to Dart_Initialize will become Isolate Group lifecycle callbacks. A new callback `initialize_isolate` callback will be added which can initialize a new isolate within an existing IG. Existing embedders can be updated by performing the following renames Dart_CreateIsolate -> Dart_CreateIsolateGroup Dart_IsolateCreateCallback -> Dart_IsolateGroupCreateCallback Dart_IsolateCleanupCallback -> Dart_IsolateGroupShutdownCallback Dart_CreateIsolateFromKernel -> Dart_CreateIsolateGroupFromKernel Dart_CurrentIsolateData -> Dart_CurrentIsolateGroupData Dart_IsolateData -> Dart_IsolateGroupData Dart_GetNativeIsolateData -> Dart_GetNativeIsolateGroupData Dart_InitializeParams.create -> Dart_InitializeParams.create_group Dart_InitializeParams.cleanup -> Dart_InitializeParams.shutdown_group Dart_InitializeParams.shutdown -> Dart_InitializeParams.shutdown_isolate By default `Isolate.spawn` will cause the creation of a new IG. Though an embedder can opt-into supporting multiple isolates within one IG by providing a callback to the newly added `Dart_InitializeParams.initialize_isolate`. The responsibility of this new callback is to initialize an existing isolate (which was setup by re-using source code from the spawning isolate - i.e. the one which used `Isolate.spawn`) by setting native resolvers, initializing global state, etc. Issue https://github.com/dart-lang/sdk/issues/36648 Issue https://github.com/dart-lang/sdk/issues/36097 Original review: https://dart-review.googlesource.com/c/sdk/+/105241 Difference to original review: * Give each isolate it's own [Loader] (for now) * Sort classes during initialization for spawned isolates if app-jit is used (to match main isolate) * Fix IsolateData memory leak if isolate startup fails Difference to first reland(Patchset 2): * Fix typo where memory was freed twice. Change-Id: Ib1c83fe83b629cd50ae6af90ee99fdd44da882d4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108367 Commit-Queue: Alexander Aprelev <aam@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
2019-07-08 23:49:05 +00:00
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