Files
sdk/runtime/lib/isolate.cc
T
turnidge@google.com cf23dbad75 Add a mid-sized integration test for the Dart Embedding Api which
demonstrates how to create a custom isolate abstraction.  In this
test, we use an event queue to share a single thread among our custom
isolates.

Add a callback which allows embedders to see when a port is created.
Not sure if I should keep this or not.

New apis:
  Dart_CreatePort() -- allocates a port id and adds a port->isolate mapping.
  Dart_IsolateHasActivePorts() -- does the current isolate have open ports?
  (this name a bit awkward...)

Bail out of PortMap::ClosePorts() early if there are no open ports.
This suppresses calls to the close_port_callback when there are no
open ports.

Use DART_CHECK_VALID to provide better error output when the test lib
has errors.

-------------------------
Sample output of the test:

-- (isolate=0x815600) Constructing isolate
-- Enter: CustomIsolateImpl_start --
-- Adding port (7111) -> isolate (0x830800) --
-- Adding StartEvent to queue --
-- Exit: CustomIsolateImpl_start --
-- Adding port (7112) -> isolate (0x815600) --
-- Posting message dest(7111) reply(0) --
-- Adding MessageEvent to queue --
-- Starting event loop --
>> StartEvent with isolate(0x830800)--
-- (isolate=0x830800) Running isolateMain
$$ MessageEvent with dest port 7111--
-- (isolate=0x830800) Received: 42
-- Posting message dest(7112) reply(0) --
-- Adding MessageEvent to queue --
$$ MessageEvent with dest port 7112--
-- Closing port (7112) --
-- Adding ShutdownEvent to queue --
-- (isolate=0x815600) Received: 43
<< ShutdownEvent with isolate(0x815600)--
-- Finished event loop --
Review URL: http://codereview.chromium.org//8588040

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@1906 260f80e4-7a28-3924-810f-c04153c831b5
2011-11-29 21:55:13 +00:00

360 lines
13 KiB
C++

// Copyright (c) 2011, 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/bootstrap_natives.h"
#include "vm/assert.h"
#include "vm/class_finalizer.h"
#include "vm/dart.h"
#include "vm/dart_entry.h"
#include "vm/exceptions.h"
#include "vm/longjump.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/port.h"
#include "vm/resolver.h"
#include "vm/snapshot.h"
#include "vm/thread.h"
namespace dart {
class IsolateStartData {
public:
IsolateStartData(Isolate* isolate,
char* library_url,
char* class_name,
intptr_t port_id)
: isolate_(isolate),
library_url_(library_url),
class_name_(class_name),
port_id_(port_id) {}
Isolate* isolate_;
char* library_url_;
char* class_name_;
intptr_t port_id_;
};
static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) {
void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size);
return reinterpret_cast<uint8_t*>(new_ptr);
}
static void* SerializeObject(const Instance& obj) {
uint8_t* result = NULL;
SnapshotWriter writer(false, &result, &allocator);
writer.WriteObject(obj.raw());
writer.FinalizeBuffer();
return result;
}
static void ProcessUnhandledException(const UnhandledException& uhe) {
const Instance& exception = Instance::Handle(uhe.exception());
Instance& string = Instance::Handle(DartLibraryCalls::ToString(exception));
const char* str = string.ToCString();
fprintf(stderr, "%s\n", str);
const Instance& stack = Instance::Handle(uhe.stacktrace());
string = DartLibraryCalls::ToString(stack);
str = string.ToCString();
fprintf(stderr, "%s\n", str);
exit(255);
}
static void ThrowErrorException(Exceptions::ExceptionType type,
const char* error_msg,
const char* library_url,
const char* class_name) {
String& str = String::Handle();
String& name = String::Handle();
str ^= String::New(error_msg);
name ^= String::NewSymbol(library_url);
str ^= String::Concat(str, name);
name ^= String::New(":");
str ^= String::Concat(str, name);
name ^= String::NewSymbol(class_name);
str ^= String::Concat(str, name);
GrowableArray<const Object*> arguments(1);
arguments.Add(&str);
Exceptions::ThrowByType(type, arguments);
}
RawInstance* ReceivePortCreate(intptr_t port_id) {
const String& class_name =
String::Handle(String::NewSymbol("ReceivePortImpl"));
const String& function_name =
String::Handle(String::NewSymbol("_get_or_create"));
const int kNumArguments = 1;
const Array& kNoArgumentNames = Array::Handle();
const Function& function = Function::Handle(
Resolver::ResolveStatic(Library::Handle(Library::CoreLibrary()),
class_name,
function_name,
kNumArguments,
kNoArgumentNames,
Resolver::kIsQualified));
GrowableArray<const Object*> arguments(kNumArguments);
arguments.Add(&Integer::Handle(Integer::New(port_id)));
const Instance& result = Instance::Handle(
DartEntry::InvokeStatic(function, arguments, kNoArgumentNames));
if (result.IsUnhandledException()) {
UnhandledException& uhe = UnhandledException::Handle();
uhe ^= result.raw();
ProcessUnhandledException(uhe);
} else {
PortMap::SetLive(port_id);
}
return result.raw();
}
static RawInstance* SendPortCreate(intptr_t port_id) {
const String& class_name = String::Handle(String::NewSymbol("SendPortImpl"));
const String& function_name = String::Handle(String::NewSymbol("_create"));
const int kNumArguments = 1;
const Array& kNoArgumentNames = Array::Handle();
const Function& function = Function::Handle(
Resolver::ResolveStatic(Library::Handle(Library::CoreLibrary()),
class_name,
function_name,
kNumArguments,
kNoArgumentNames,
Resolver::kIsQualified));
GrowableArray<const Object*> arguments(kNumArguments);
arguments.Add(&Integer::Handle(Integer::New(port_id)));
const Instance& result = Instance::Handle(
DartEntry::InvokeStatic(function, arguments, kNoArgumentNames));
return result.raw();
}
static void RunIsolate(uword parameter) {
IsolateStartData* data = reinterpret_cast<IsolateStartData*>(parameter);
Isolate* isolate = data->isolate_;
char* library_url = data->library_url_;
char* class_name = data->class_name_;
intptr_t port_id = data->port_id_;
delete data;
Isolate::SetCurrent(isolate);
// Intialize stack limit in case we are running isolate in a
// different thread than in which it was initialized.
isolate->SetStackLimitFromCurrentTOS(reinterpret_cast<uword>(&isolate));
LongJump* base = isolate->long_jump_base();
LongJump jump;
isolate->set_long_jump_base(&jump);
if (setjmp(*jump.Set()) == 0) {
Zone zone(isolate);
HandleScope handle_scope(isolate);
ASSERT(ClassFinalizer::FinalizePendingClasses());
// Lookup the target class by name, create an instance and call the run
// method.
const String& lib_name = String::Handle(String::NewSymbol(library_url));
const Library& lib = Library::Handle(Library::LookupLibrary(lib_name));
ASSERT(!lib.IsNull());
const String& cls_name = String::Handle(String::NewSymbol(class_name));
const Class& target_class = Class::Handle(lib.LookupClass(cls_name));
// TODO(iposva): Deserialize or call the constructor after allocating.
// For now, we only support a non-parameterized or raw target class.
const Instance& target = Instance::Handle(Instance::New(target_class));
Instance& result = Instance::Handle();
// Invoke the default constructor.
const String& period = String::Handle(String::New("."));
String& constructor_name = String::Handle(String::Concat(cls_name, period));
const Function& default_constructor =
Function::Handle(target_class.LookupConstructor(constructor_name));
if (!default_constructor.IsNull()) {
GrowableArray<const Object*> arguments(1);
arguments.Add(&target);
arguments.Add(&Smi::Handle(Smi::New(Function::kCtorPhaseAll)));
const Array& kNoArgumentNames = Array::Handle();
result = DartEntry::InvokeStatic(default_constructor,
arguments,
kNoArgumentNames);
if (result.IsUnhandledException()) {
UnhandledException& uhe = UnhandledException::Handle();
uhe ^= result.raw();
ProcessUnhandledException(uhe);
}
ASSERT(result.IsNull());
}
// Invoke the "_run" method.
const Function& target_function = Function::Handle(Resolver::ResolveDynamic(
target, String::Handle(String::NewSymbol("_run")), 2, 0));
// TODO(iposva): Proper error checking here.
ASSERT(!target_function.IsNull());
// TODO(iposva): Allocate the proper port number here.
const Instance& local_port = Instance::Handle(ReceivePortCreate(port_id));
GrowableArray<const Object*> arguments(1);
arguments.Add(&local_port);
const Array& kNoArgumentNames = Array::Handle();
result = DartEntry::InvokeDynamic(target,
target_function,
arguments,
kNoArgumentNames);
if (result.IsUnhandledException()) {
UnhandledException& uhe = UnhandledException::Handle();
uhe ^= result.raw();
ProcessUnhandledException(uhe);
}
ASSERT(result.IsNull());
free(class_name);
isolate->StandardRunLoop();
} else {
Zone zone(isolate);
HandleScope handle_scope(isolate);
const String& error = String::Handle(
Isolate::Current()->object_store()->sticky_error());
const char* errmsg = error.ToCString();
fprintf(stderr, "%s\n", errmsg);
exit(255);
}
isolate->set_long_jump_base(base);
Dart::ShutdownIsolate();
}
static bool CheckArguments(const char* library_url, const char* class_name) {
Isolate* isolate = Isolate::Current();
Zone zone(isolate);
HandleScope handle_scope(isolate);
String& name = String::Handle();
if (!ClassFinalizer::FinalizePendingClasses()) {
return false;
}
// Lookup the target class by name, create an instance and call the run
// method.
name ^= String::NewSymbol(library_url);
const Library& lib = Library::Handle(Library::LookupLibrary(name));
if (lib.IsNull()) {
const String& error = String::Handle(
String::New("Error starting Isolate, library not loaded : "));
Isolate::Current()->object_store()->set_sticky_error(error);
return false;
}
name ^= String::NewSymbol(class_name);
const Class& target_class = Class::Handle(lib.LookupClass(name));
if (target_class.IsNull()) {
const String& error = String::Handle(
String::New("Error starting Isolate, class not loaded : "));
Isolate::Current()->object_store()->set_sticky_error(error);
return false;
}
return true; // No errors.
}
DEFINE_NATIVE_ENTRY(IsolateNatives_start, 2) {
Isolate* preserved_isolate = Isolate::Current();
const Instance& runnable = Instance::CheckedHandle(arguments->At(0));
const Class& runnable_class = Class::Handle(runnable.clazz());
const char* class_name = String::Handle(runnable_class.Name()).ToCString();
const Library& library = Library::Handle(runnable_class.library());
ASSERT(!library.IsNull());
const char* library_url = String::Handle(library.url()).ToCString();
intptr_t port_id = 0;
const char* error_msg = NULL;
LongJump jump;
bool init_successful = true;
Isolate* spawned_isolate = Dart::CreateIsolate();
if (spawned_isolate != NULL) {
// First initialize the spawned isolate.
LongJump* base = spawned_isolate->long_jump_base();
spawned_isolate->set_long_jump_base(&jump);
if (setjmp(*jump.Set()) == 0) {
Dart::InitializeIsolate(NULL, preserved_isolate->init_callback_data());
} else {
init_successful = false;
}
spawned_isolate->set_long_jump_base(base);
// Check arguments to see if the specified library and classes are
// loaded, this check will throw an exception if they are not loaded.
if (init_successful && CheckArguments(library_url, class_name)) {
port_id = spawned_isolate->main_port();
uword data = reinterpret_cast<uword>(
new IsolateStartData(spawned_isolate,
strdup(library_url),
strdup(class_name),
port_id));
new Thread(RunIsolate, data);
} else {
// Error spawning the isolate, maybe due to initialization errors or
// errors while loading the application into spawned isolate, shut
// it down and report error.
// Make sure to grab the error message out of the isolate before it has
// been shutdown and to allocate it in the preserved isolates zone.
{
Zone zone(spawned_isolate);
HandleScope scope(spawned_isolate);
const String& error = String::Handle(
spawned_isolate->object_store()->sticky_error());
const char* temp_error_msg = error.ToCString();
intptr_t err_len = strlen(temp_error_msg) + 1;
Zone* preserved_zone = preserved_isolate->current_zone();
error_msg = reinterpret_cast<char*>(preserved_zone->Allocate(err_len));
OS::SNPrint(
const_cast<char*>(error_msg), err_len, "%s", temp_error_msg);
}
Dart::ShutdownIsolate();
spawned_isolate = NULL;
}
} else {
error_msg = "Creation of Isolate failed : ";
}
// Switch back to the original isolate and return.
Isolate::SetCurrent(preserved_isolate);
if (spawned_isolate == NULL) {
// Unable to spawn isolate correctly, throw exception.
ASSERT(error_msg != NULL);
ThrowErrorException(Exceptions::kIllegalArgument,
error_msg,
library_url,
class_name);
}
const Instance& port = Instance::Handle(SendPortCreate(port_id));
if (port.IsUnhandledException()) {
ThrowErrorException(Exceptions::kInternalError,
"Unable to create send port to isolate",
library_url,
class_name);
}
arguments->SetReturn(port);
}
DEFINE_NATIVE_ENTRY(ReceivePortImpl_factory, 1) {
ASSERT(TypeArguments::CheckedHandle(arguments->At(0)).IsNull());
intptr_t port_id = PortMap::CreatePort();
const Instance& port = Instance::Handle(ReceivePortCreate(port_id));
arguments->SetReturn(port);
}
DEFINE_NATIVE_ENTRY(ReceivePortImpl_closeInternal, 1) {
intptr_t id = Smi::CheckedHandle(arguments->At(0)).Value();
PortMap::ClosePort(id);
}
DEFINE_NATIVE_ENTRY(SendPortImpl_sendInternal_, 3) {
intptr_t send_id = Smi::CheckedHandle(arguments->At(0)).Value();
intptr_t reply_id = Smi::CheckedHandle(arguments->At(1)).Value();
// TODO(iposva): Allow for arbitrary messages to be sent.
void* data = SerializeObject(Instance::CheckedHandle(arguments->At(2)));
// TODO(turnidge): Throw an exception when the return value is false?
PortMap::PostMessage(send_id, reply_id, data);
}
} // namespace dart