54a541706d
Move SpawnCount logic into SpawnIsolateTask, where it logically makes more sense. This eliminates some code and also prevents IsolateSpawnState from having lingering pointers to the parent Isolate's fields after it might have been reclaimed. Change-Id: If8d827f299ee90dd54faf896609a7acdfbb87608 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101628 Reviewed-by: Siva Annamalai <asiva@google.com> Commit-Queue: Matthew Dempsky <mdempsky@google.com>
438 lines
16 KiB
C++
438 lines
16 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.
|
|
|
|
#include "include/dart_native_api.h"
|
|
#include "platform/assert.h"
|
|
#include "platform/unicode.h"
|
|
#include "vm/bootstrap_natives.h"
|
|
#include "vm/class_finalizer.h"
|
|
#include "vm/dart.h"
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/dart_api_message.h"
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/exceptions.h"
|
|
#include "vm/lockers.h"
|
|
#include "vm/longjump.h"
|
|
#include "vm/message_handler.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/port.h"
|
|
#include "vm/resolver.h"
|
|
#include "vm/service.h"
|
|
#include "vm/snapshot.h"
|
|
#include "vm/symbols.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_NATIVE_ENTRY(CapabilityImpl_factory, 0, 1) {
|
|
ASSERT(
|
|
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
|
|
uint64_t id = isolate->random()->NextUInt64();
|
|
return Capability::New(id);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(CapabilityImpl_equals, 0, 2) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Capability, recv, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Capability, other, arguments->NativeArgAt(1));
|
|
return (recv.Id() == other.Id()) ? Bool::True().raw() : Bool::False().raw();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(CapabilityImpl_get_hashcode, 0, 1) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Capability, cap, arguments->NativeArgAt(0));
|
|
int64_t id = cap.Id();
|
|
int32_t hi = static_cast<int32_t>(id >> 32);
|
|
int32_t lo = static_cast<int32_t>(id);
|
|
int32_t hash = (hi ^ lo) & kSmiMax;
|
|
return Smi::New(hash);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RawReceivePortImpl_factory, 0, 1) {
|
|
ASSERT(
|
|
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
|
|
Dart_Port port_id = PortMap::CreatePort(isolate->message_handler());
|
|
return ReceivePort::New(port_id, false /* not control port */);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RawReceivePortImpl_get_id, 0, 1) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0));
|
|
return Integer::New(port.Id());
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RawReceivePortImpl_get_sendport, 0, 1) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0));
|
|
return port.send_port();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(RawReceivePortImpl_closeInternal, 0, 1) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0));
|
|
Dart_Port id = port.Id();
|
|
PortMap::ClosePort(id);
|
|
return Integer::New(id);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(SendPortImpl_get_id, 0, 1) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
|
|
return Integer::New(port.Id());
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(SendPortImpl_get_hashcode, 0, 1) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
|
|
int64_t id = port.Id();
|
|
int32_t hi = static_cast<int32_t>(id >> 32);
|
|
int32_t lo = static_cast<int32_t>(id);
|
|
int32_t hash = (hi ^ lo) & kSmiMax;
|
|
return Smi::New(hash);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(SendPortImpl_sendInternal_, 0, 2) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
|
|
// TODO(iposva): Allow for arbitrary messages to be sent.
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(1));
|
|
|
|
const Dart_Port destination_port_id = port.Id();
|
|
const bool can_send_any_object = isolate->origin_id() == port.origin_id();
|
|
|
|
if (ApiObjectConverter::CanConvert(obj.raw())) {
|
|
PortMap::PostMessage(
|
|
Message::New(destination_port_id, obj.raw(), Message::kNormalPriority));
|
|
} else {
|
|
MessageWriter writer(can_send_any_object);
|
|
// TODO(turnidge): Throw an exception when the return value is false?
|
|
PortMap::PostMessage(writer.WriteMessage(obj, destination_port_id,
|
|
Message::kNormalPriority));
|
|
}
|
|
return Object::null();
|
|
}
|
|
|
|
static void ThrowIsolateSpawnException(const String& message) {
|
|
const Array& args = Array::Handle(Array::New(1));
|
|
args.SetAt(0, message);
|
|
Exceptions::ThrowByType(Exceptions::kIsolateSpawn, args);
|
|
}
|
|
|
|
class SpawnIsolateTask : public ThreadPool::Task {
|
|
public:
|
|
SpawnIsolateTask(Isolate* parent_isolate, IsolateSpawnState* state)
|
|
: parent_isolate_(parent_isolate), state_(state) {
|
|
parent_isolate->IncrementSpawnCount();
|
|
}
|
|
|
|
~SpawnIsolateTask() override {
|
|
if (parent_isolate_) {
|
|
parent_isolate_->DecrementSpawnCount();
|
|
}
|
|
}
|
|
|
|
void Run() override {
|
|
// Create a new isolate.
|
|
char* error = NULL;
|
|
Dart_IsolateCreateCallback callback = Isolate::CreateCallback();
|
|
if (callback == NULL) {
|
|
ReportError(
|
|
"Isolate spawn is not supported by this Dart implementation\n");
|
|
delete state_;
|
|
state_ = NULL;
|
|
return;
|
|
}
|
|
|
|
// Make a copy of the state's isolate flags and hand it to the callback.
|
|
Dart_IsolateFlags api_flags = *(state_->isolate_flags());
|
|
const char* name = (state_->debug_name() == NULL) ? state_->function_name()
|
|
: state_->debug_name();
|
|
ASSERT(name != NULL);
|
|
|
|
Isolate* isolate = reinterpret_cast<Isolate*>((callback)(
|
|
state_->script_url(), name, nullptr, state_->package_config(),
|
|
&api_flags, parent_isolate_->init_callback_data(), &error));
|
|
parent_isolate_->DecrementSpawnCount();
|
|
parent_isolate_ = nullptr;
|
|
if (isolate == NULL) {
|
|
ReportError(error);
|
|
delete state_;
|
|
state_ = NULL;
|
|
free(error);
|
|
return;
|
|
}
|
|
|
|
if (state_->origin_id() != ILLEGAL_PORT) {
|
|
// For isolates spawned using spawnFunction we set the origin_id
|
|
// to the origin_id of the parent isolate.
|
|
isolate->set_origin_id(state_->origin_id());
|
|
}
|
|
MutexLocker ml(isolate->mutex());
|
|
state_->set_isolate(isolate);
|
|
isolate->set_spawn_state(state_);
|
|
state_ = NULL;
|
|
if (isolate->is_runnable()) {
|
|
isolate->Run();
|
|
}
|
|
}
|
|
|
|
private:
|
|
void ReportError(const char* error) {
|
|
Dart_CObject error_cobj;
|
|
error_cobj.type = Dart_CObject_kString;
|
|
error_cobj.value.as_string = const_cast<char*>(error);
|
|
if (!Dart_PostCObject(state_->parent_port(), &error_cobj)) {
|
|
// Perhaps the parent isolate died or closed the port before we
|
|
// could report the error. Ignore.
|
|
}
|
|
}
|
|
|
|
Isolate* parent_isolate_;
|
|
IsolateSpawnState* state_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SpawnIsolateTask);
|
|
};
|
|
|
|
static const char* String2UTF8(const String& str) {
|
|
intptr_t len = Utf8::Length(str);
|
|
char* result = new char[len + 1];
|
|
str.ToUTF8(reinterpret_cast<uint8_t*>(result), len);
|
|
result[len] = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 11) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(String, script_uri, arguments->NativeArgAt(1));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, closure, arguments->NativeArgAt(2));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(3));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Bool, paused, arguments->NativeArgAt(4));
|
|
GET_NATIVE_ARGUMENT(Bool, fatalErrors, arguments->NativeArgAt(5));
|
|
GET_NATIVE_ARGUMENT(SendPort, onExit, arguments->NativeArgAt(6));
|
|
GET_NATIVE_ARGUMENT(SendPort, onError, arguments->NativeArgAt(7));
|
|
GET_NATIVE_ARGUMENT(String, packageRoot, arguments->NativeArgAt(8));
|
|
GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(9));
|
|
GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(10));
|
|
|
|
if (closure.IsClosure()) {
|
|
Function& func = Function::Handle();
|
|
func = Closure::Cast(closure).function();
|
|
if (func.IsImplicitClosureFunction() && func.is_static()) {
|
|
#if defined(DEBUG)
|
|
Context& ctx = Context::Handle();
|
|
ctx = Closure::Cast(closure).context();
|
|
ASSERT(ctx.IsNull());
|
|
#endif
|
|
// Get the parent function so that we get the right function name.
|
|
func = func.parent_function();
|
|
|
|
bool fatal_errors = fatalErrors.IsNull() ? true : fatalErrors.value();
|
|
Dart_Port on_exit_port = onExit.IsNull() ? ILLEGAL_PORT : onExit.Id();
|
|
Dart_Port on_error_port = onError.IsNull() ? ILLEGAL_PORT : onError.Id();
|
|
|
|
// We first try to serialize the message. In case the message is not
|
|
// serializable this will throw an exception.
|
|
SerializedObjectBuffer message_buffer;
|
|
{
|
|
MessageWriter writer(/* can_send_any_object = */ true);
|
|
message_buffer.set_message(writer.WriteMessage(
|
|
message, ILLEGAL_PORT, Message::kNormalPriority));
|
|
}
|
|
|
|
const char* utf8_package_config =
|
|
packageConfig.IsNull() ? NULL : String2UTF8(packageConfig);
|
|
const char* utf8_debug_name =
|
|
debugName.IsNull() ? NULL : String2UTF8(debugName);
|
|
|
|
IsolateSpawnState* state = new IsolateSpawnState(
|
|
port.Id(), isolate->origin_id(), String2UTF8(script_uri), func,
|
|
&message_buffer, utf8_package_config, paused.value(), fatal_errors,
|
|
on_exit_port, on_error_port, utf8_debug_name);
|
|
|
|
// Since this is a call to Isolate.spawn, copy the parent isolate's code.
|
|
state->isolate_flags()->copy_parent_code = true;
|
|
|
|
ThreadPool::Task* spawn_task = new SpawnIsolateTask(isolate, state);
|
|
|
|
if (!Dart::thread_pool()->Run(spawn_task)) {
|
|
// Running on the thread pool failed. Clean up everything.
|
|
delete state;
|
|
state = NULL;
|
|
delete spawn_task;
|
|
spawn_task = NULL;
|
|
}
|
|
return Object::null();
|
|
}
|
|
}
|
|
const String& msg = String::Handle(String::New(
|
|
"Isolate.spawn expects to be passed a static or top-level function"));
|
|
Exceptions::ThrowArgumentError(msg);
|
|
return Object::null();
|
|
}
|
|
|
|
static const char* CanonicalizeUri(Thread* thread,
|
|
const Library& library,
|
|
const String& uri,
|
|
char** error) {
|
|
const char* result = NULL;
|
|
Zone* zone = thread->zone();
|
|
Isolate* isolate = thread->isolate();
|
|
if (isolate->HasTagHandler()) {
|
|
const Object& obj = Object::Handle(
|
|
isolate->CallTagHandler(Dart_kCanonicalizeUrl, library, uri));
|
|
if (obj.IsString()) {
|
|
result = String2UTF8(String::Cast(obj));
|
|
} else if (obj.IsError()) {
|
|
Error& error_obj = Error::Handle();
|
|
error_obj ^= obj.raw();
|
|
*error = zone->PrintToString("Unable to canonicalize uri '%s': %s",
|
|
uri.ToCString(), error_obj.ToErrorCString());
|
|
} else {
|
|
*error = zone->PrintToString(
|
|
"Unable to canonicalize uri '%s': "
|
|
"library tag handler returned wrong type",
|
|
uri.ToCString());
|
|
}
|
|
} else {
|
|
*error = zone->PrintToString(
|
|
"Unable to canonicalize uri '%s': no library tag handler found.",
|
|
uri.ToCString());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Isolate_spawnUri, 0, 13) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(String, uri, arguments->NativeArgAt(1));
|
|
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, args, arguments->NativeArgAt(2));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(3));
|
|
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Bool, paused, arguments->NativeArgAt(4));
|
|
GET_NATIVE_ARGUMENT(SendPort, onExit, arguments->NativeArgAt(5));
|
|
GET_NATIVE_ARGUMENT(SendPort, onError, arguments->NativeArgAt(6));
|
|
|
|
GET_NATIVE_ARGUMENT(Bool, fatalErrors, arguments->NativeArgAt(7));
|
|
GET_NATIVE_ARGUMENT(Bool, checked, arguments->NativeArgAt(8));
|
|
|
|
GET_NATIVE_ARGUMENT(Array, environment, arguments->NativeArgAt(9));
|
|
|
|
GET_NATIVE_ARGUMENT(String, packageRoot, arguments->NativeArgAt(10));
|
|
GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(11));
|
|
|
|
GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(12));
|
|
|
|
if (Dart::vm_snapshot_kind() == Snapshot::kFullAOT) {
|
|
const Array& args = Array::Handle(Array::New(1));
|
|
args.SetAt(
|
|
0,
|
|
String::Handle(String::New(
|
|
"Isolate.spawnUri is not supported when using AOT compilation")));
|
|
Exceptions::ThrowByType(Exceptions::kUnsupported, args);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
bool fatal_errors = fatalErrors.IsNull() ? true : fatalErrors.value();
|
|
Dart_Port on_exit_port = onExit.IsNull() ? ILLEGAL_PORT : onExit.Id();
|
|
Dart_Port on_error_port = onError.IsNull() ? ILLEGAL_PORT : onError.Id();
|
|
|
|
// We first try to serialize the arguments and the message. In case the
|
|
// arguments or the message are not serializable this will throw an exception.
|
|
SerializedObjectBuffer arguments_buffer;
|
|
SerializedObjectBuffer message_buffer;
|
|
{
|
|
MessageWriter writer(/* can_send_any_object = */ false);
|
|
arguments_buffer.set_message(
|
|
writer.WriteMessage(args, ILLEGAL_PORT, Message::kNormalPriority));
|
|
}
|
|
{
|
|
MessageWriter writer(/* can_send_any_object = */ false);
|
|
message_buffer.set_message(
|
|
writer.WriteMessage(message, ILLEGAL_PORT, Message::kNormalPriority));
|
|
}
|
|
|
|
// Canonicalize the uri with respect to the current isolate.
|
|
const Library& root_lib =
|
|
Library::Handle(isolate->object_store()->root_library());
|
|
char* error = NULL;
|
|
const char* canonical_uri = CanonicalizeUri(thread, root_lib, uri, &error);
|
|
if (canonical_uri == NULL) {
|
|
const String& msg = String::Handle(String::New(error));
|
|
ThrowIsolateSpawnException(msg);
|
|
}
|
|
|
|
const char* utf8_package_config =
|
|
packageConfig.IsNull() ? NULL : String2UTF8(packageConfig);
|
|
const char* utf8_debug_name =
|
|
debugName.IsNull() ? NULL : String2UTF8(debugName);
|
|
|
|
IsolateSpawnState* state = new IsolateSpawnState(
|
|
port.Id(), canonical_uri, utf8_package_config, &arguments_buffer,
|
|
&message_buffer, paused.value(), fatal_errors, on_exit_port,
|
|
on_error_port, utf8_debug_name);
|
|
|
|
// If we were passed a value then override the default flags state for
|
|
// checked mode.
|
|
if (!checked.IsNull()) {
|
|
Dart_IsolateFlags* flags = state->isolate_flags();
|
|
flags->enable_asserts = checked.value();
|
|
}
|
|
|
|
// Since this is a call to Isolate.spawnUri, don't copy the parent's code.
|
|
state->isolate_flags()->copy_parent_code = false;
|
|
|
|
ThreadPool::Task* spawn_task = new SpawnIsolateTask(isolate, state);
|
|
|
|
if (!Dart::thread_pool()->Run(spawn_task)) {
|
|
// Running on the thread pool failed. Clean up everything.
|
|
delete state;
|
|
state = NULL;
|
|
delete spawn_task;
|
|
spawn_task = NULL;
|
|
}
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Isolate_getDebugName, 0, 1) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
|
|
Isolate* isolate_lookup = Isolate::LookupIsolateByPort(port.Id());
|
|
if (isolate_lookup == NULL) {
|
|
return String::null();
|
|
}
|
|
return String::New(isolate_lookup->name());
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Isolate_getPortAndCapabilitiesOfCurrentIsolate, 0, 0) {
|
|
const Array& result = Array::Handle(Array::New(3));
|
|
result.SetAt(0, SendPort::Handle(SendPort::New(isolate->main_port())));
|
|
result.SetAt(
|
|
1, Capability::Handle(Capability::New(isolate->pause_capability())));
|
|
result.SetAt(
|
|
2, Capability::Handle(Capability::New(isolate->terminate_capability())));
|
|
return result.raw();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Isolate_getCurrentRootUriStr, 0, 0) {
|
|
const Library& root_lib =
|
|
Library::Handle(zone, isolate->object_store()->root_library());
|
|
return root_lib.url();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Isolate_sendOOB, 0, 2) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Array, msg, arguments->NativeArgAt(1));
|
|
|
|
// Make sure to route this request to the isolate library OOB mesage handler.
|
|
msg.SetAt(0, Smi::Handle(Smi::New(Message::kIsolateLibOOBMsg)));
|
|
|
|
MessageWriter writer(false);
|
|
PortMap::PostMessage(
|
|
writer.WriteMessage(msg, port.Id(), Message::kOOBPriority));
|
|
|
|
// Drain interrupts before running so any IMMEDIATE operations on the current
|
|
// isolate happen synchronously.
|
|
const Error& error = Error::Handle(thread->HandleInterrupts());
|
|
if (!error.IsNull()) {
|
|
Exceptions::PropagateError(error);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
return Object::null();
|
|
}
|
|
|
|
} // namespace dart
|