- Implement some more missing pieces of the Isolate API.

Added:
    * addOnExitListener, removeOnExitListener
    * setErrorsFatal
    * addOnErrorListener, removeOnErrorListener

R=asiva@google.com

Review URL: https://codereview.chromium.org//881373002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@44307 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
iposva@google.com 2015-03-07 01:04:19 +00:00
parent 87790063a4
commit 91e105d8c6
8 changed files with 290 additions and 22 deletions

View file

@ -335,6 +335,11 @@ patch class Isolate {
static const _RESUME = 2; static const _RESUME = 2;
static const _PING = 3; static const _PING = 3;
static const _KILL = 4; static const _KILL = 4;
static const _ADD_EXIT = 5;
static const _DEL_EXIT = 6;
static const _ADD_ERROR = 7;
static const _DEL_ERROR = 8;
static const _ERROR_FATAL = 9;
static void _spawnFunction(SendPort readyPort, Function topLevelFunction, static void _spawnFunction(SendPort readyPort, Function topLevelFunction,
@ -367,15 +372,28 @@ patch class Isolate {
} }
/* patch */ void addOnExitListener(SendPort responsePort) { /* patch */ void addOnExitListener(SendPort responsePort) {
throw new UnsupportedError("addOnExitListener"); var msg = new List(3)
..[0] = 0 // Make room for OOB message type.
..[1] = _ADD_EXIT
..[2] = responsePort;
_sendOOB(controlPort, msg);
} }
/* patch */ void removeOnExitListener(SendPort responsePort) { /* patch */ void removeOnExitListener(SendPort responsePort) {
throw new UnsupportedError("removeOnExitListener"); var msg = new List(3)
..[0] = 0 // Make room for OOB message type.
..[1] = _DEL_EXIT
..[2] = responsePort;
_sendOOB(controlPort, msg);
} }
/* patch */ void setErrorsFatal(bool errorsAreFatal) { /* patch */ void setErrorsFatal(bool errorsAreFatal) {
throw new UnsupportedError("setErrorsFatal"); var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _ERROR_FATAL
..[2] = terminateCapability
..[3] = errorsAreFatal;
_sendOOB(controlPort, msg);
} }
/* patch */ void kill([int priority = BEFORE_NEXT_EVENT]) { /* patch */ void kill([int priority = BEFORE_NEXT_EVENT]) {
@ -397,11 +415,19 @@ patch class Isolate {
} }
/* patch */ void addErrorListener(SendPort port) { /* patch */ void addErrorListener(SendPort port) {
throw new UnsupportedError("addErrorListener"); var msg = new List(3)
..[0] = 0 // Make room for OOB message type.
..[1] = _ADD_ERROR
..[2] = port;
_sendOOB(controlPort, msg);
} }
/* patch */ void removeErrorListener(SendPort port) { /* patch */ void removeErrorListener(SendPort port) {
throw new UnsupportedError("removeErrorListener"); var msg = new List(3)
..[0] = 0 // Make room for OOB message type.
..[1] = _DEL_ERROR
..[2] = port;
_sendOOB(controlPort, msg);
} }
static Isolate _getCurrentIsolate() { static Isolate _getCurrentIsolate() {

View file

@ -126,13 +126,21 @@ class IsolateMessageHandler : public MessageHandler {
virtual Isolate* isolate() const { return isolate_; } virtual Isolate* isolate() const { return isolate_; }
private: private:
// Keep in sync with isolate_patch.dart. // Keep both these enums in sync with isolate_patch.dart.
// The different Isolate API message types.
enum { enum {
kPauseMsg = 1, kPauseMsg = 1,
kResumeMsg = 2, kResumeMsg = 2,
kPingMsg = 3, kPingMsg = 3,
kKillMsg = 4, kKillMsg = 4,
kAddExitMsg = 5,
kDelExitMsg = 6,
kAddErrorMsg = 7,
kDelErrorMsg = 8,
kErrorFatalMsg = 9,
};
// The different Isolate API message priorities for ping and kill messages.
enum {
kImmediateAction = 0, kImmediateAction = 0,
kBeforeNextEventAction = 1, kBeforeNextEventAction = 1,
kAsEventAction = 2 kAsEventAction = 2
@ -167,8 +175,8 @@ bool IsolateMessageHandler::HandleLibMessage(const Array& message) {
if (message.Length() < 2) return true; if (message.Length() < 2) return true;
const Object& type = Object::Handle(I, message.At(1)); const Object& type = Object::Handle(I, message.At(1));
if (!type.IsSmi()) return true; if (!type.IsSmi()) return true;
const Smi& msg_type = Smi::Cast(type); const intptr_t msg_type = Smi::Cast(type).Value();
switch (msg_type.Value()) { switch (msg_type) {
case kPauseMsg: { case kPauseMsg: {
// [ OOB, kPauseMsg, pause capability, resume capability ] // [ OOB, kPauseMsg, pause capability, resume capability ]
if (message.Length() != 4) return true; if (message.Length() != 4) return true;
@ -255,6 +263,45 @@ bool IsolateMessageHandler::HandleLibMessage(const Array& message) {
} }
break; break;
} }
case kAddExitMsg:
case kDelExitMsg:
case kAddErrorMsg:
case kDelErrorMsg: {
// [ OOB, msg, listener port ]
if (message.Length() != 3) return true;
const Object& obj = Object::Handle(I, message.At(2));
if (!obj.IsSendPort()) return true;
const SendPort& listener = SendPort::Cast(obj);
switch (msg_type) {
case kAddExitMsg:
I->AddExitListener(listener);
break;
case kDelExitMsg:
I->RemoveExitListener(listener);
break;
case kAddErrorMsg:
I->AddErrorListener(listener);
break;
case kDelErrorMsg:
I->RemoveErrorListener(listener);
break;
default:
UNREACHABLE();
}
break;
}
case kErrorFatalMsg: {
// [ OOB, kErrorFatalMsg, terminate capability, val ]
if (message.Length() != 4) return true;
// Check that the terminate capability has been passed correctly.
Object& obj = Object::Handle(I, message.At(2));
if (!I->VerifyTerminateCapability(obj)) return true;
// Get the value to be set.
obj = message.At(3);
if (!obj.IsBool()) return true;
I->SetErrorsFatal(Bool::Cast(obj).value());
break;
}
#if defined(DEBUG) #if defined(DEBUG)
// Malformed OOB messages are silently ignored in release builds. // Malformed OOB messages are silently ignored in release builds.
default: default:
@ -438,8 +485,35 @@ bool IsolateMessageHandler::ProcessUnhandledException(const Error& result) {
Dart_ExitScope(); Dart_ExitScope();
} }
I->object_store()->set_sticky_error(result); // Generate the error and stacktrace strings for the error message.
return false; String& exc_str = String::Handle(I);
String& stacktrace_str = String::Handle(I);
if (result.IsUnhandledException()) {
const UnhandledException& uhe = UnhandledException::Cast(result);
const Instance& exception = Instance::Handle(I, uhe.exception());
Object& tmp = Object::Handle(I);
tmp = DartLibraryCalls::ToString(exception);
if (!tmp.IsString()) {
tmp = String::New(exception.ToCString());
}
exc_str ^= tmp.raw();
const Instance& stacktrace = Instance::Handle(I, uhe.stacktrace());
tmp = DartLibraryCalls::ToString(stacktrace);
if (!tmp.IsString()) {
tmp = String::New(stacktrace.ToCString());
}
stacktrace_str ^= tmp.raw();;
} else {
exc_str = String::New(result.ToErrorCString());
}
I->NotifyErrorListeners(exc_str, stacktrace_str);
if (I->ErrorsFatal()) {
I->object_store()->set_sticky_error(result);
return false;
}
return true;
} }
@ -470,6 +544,7 @@ Isolate::Isolate()
origin_id_(0), origin_id_(0),
pause_capability_(0), pause_capability_(0),
terminate_capability_(0), terminate_capability_(0),
errors_fatal_(true),
heap_(NULL), heap_(NULL),
object_store_(NULL), object_store_(NULL),
top_exit_frame_info_(0), top_exit_frame_info_(0),
@ -535,6 +610,7 @@ Isolate::Isolate(Isolate* original)
main_port_(0), main_port_(0),
pause_capability_(0), pause_capability_(0),
terminate_capability_(0), terminate_capability_(0),
errors_fatal_(true),
heap_(NULL), heap_(NULL),
object_store_(NULL), object_store_(NULL),
top_exit_frame_info_(0), top_exit_frame_info_(0),
@ -890,7 +966,7 @@ bool Isolate::VerifyTerminateCapability(const Object& capability) const {
bool Isolate::AddResumeCapability(const Capability& capability) { bool Isolate::AddResumeCapability(const Capability& capability) {
// Ensure a limit for the number of resume capabilities remembered. // Ensure a limit for the number of resume capabilities remembered.
static const intptr_t kMaxResumeCapabilities = kSmiMax / (6*kWordSize); static const intptr_t kMaxResumeCapabilities = kSmiMax / (6 * kWordSize);
const GrowableObjectArray& caps = GrowableObjectArray::Handle( const GrowableObjectArray& caps = GrowableObjectArray::Handle(
this, object_store()->resume_capabilities()); this, object_store()->resume_capabilities());
@ -938,6 +1014,148 @@ bool Isolate::RemoveResumeCapability(const Capability& capability) {
} }
// TODO(iposva): Remove duplicated code and start using some hash based
// structure instead of these linear lookups.
void Isolate::AddExitListener(const SendPort& listener) {
// Ensure a limit for the number of listeners remembered.
static const intptr_t kMaxListeners = kSmiMax / (6 * kWordSize);
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
this, object_store()->exit_listeners());
SendPort& current = SendPort::Handle(this);
intptr_t insertion_index = -1;
for (intptr_t i = 0; i < listeners.Length(); i++) {
current ^= listeners.At(i);
if (current.IsNull()) {
if (insertion_index < 0) {
insertion_index = i;
}
} else if (current.Id() == listener.Id()) {
return;
}
}
if (insertion_index < 0) {
if (listeners.Length() >= kMaxListeners) {
// Cannot grow the array of listeners beyond its max. Additional
// listeners are ignored. In practice will never happen as we will
// run out of memory beforehand.
return;
}
listeners.Add(listener);
} else {
listeners.SetAt(insertion_index, listener);
}
}
void Isolate::RemoveExitListener(const SendPort& listener) {
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
this, object_store()->exit_listeners());
SendPort& current = SendPort::Handle(this);
for (intptr_t i = 0; i < listeners.Length(); i++) {
current ^= listeners.At(i);
if (!current.IsNull() && (current.Id() == listener.Id())) {
// Remove the matching listener from the list.
current = SendPort::null();
listeners.SetAt(i, current);
return;
}
}
}
void Isolate::NotifyExitListeners() {
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
this, this->object_store()->exit_listeners());
if (listeners.IsNull()) return;
SendPort& listener = SendPort::Handle(this);
for (intptr_t i = 0; i < listeners.Length(); i++) {
listener ^= listeners.At(i);
if (!listener.IsNull()) {
Dart_Port port_id = listener.Id();
uint8_t* data = NULL;
intptr_t len = 0;
SerializeObject(Object::null_instance(), &data, &len, false);
Message* msg = new Message(port_id, data, len, Message::kNormalPriority);
PortMap::PostMessage(msg);
}
}
}
void Isolate::AddErrorListener(const SendPort& listener) {
// Ensure a limit for the number of listeners remembered.
static const intptr_t kMaxListeners = kSmiMax / (6 * kWordSize);
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
this, object_store()->error_listeners());
SendPort& current = SendPort::Handle(this);
intptr_t insertion_index = -1;
for (intptr_t i = 0; i < listeners.Length(); i++) {
current ^= listeners.At(i);
if (current.IsNull()) {
if (insertion_index < 0) {
insertion_index = i;
}
} else if (current.Id() == listener.Id()) {
return;
}
}
if (insertion_index < 0) {
if (listeners.Length() >= kMaxListeners) {
// Cannot grow the array of listeners beyond its max. Additional
// listeners are ignored. In practice will never happen as we will
// run out of memory beforehand.
return;
}
listeners.Add(listener);
} else {
listeners.SetAt(insertion_index, listener);
}
}
void Isolate::RemoveErrorListener(const SendPort& listener) {
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
this, object_store()->error_listeners());
SendPort& current = SendPort::Handle(this);
for (intptr_t i = 0; i < listeners.Length(); i++) {
current ^= listeners.At(i);
if (!current.IsNull() && (current.Id() == listener.Id())) {
// Remove the matching listener from the list.
current = SendPort::null();
listeners.SetAt(i, current);
return;
}
}
}
void Isolate::NotifyErrorListeners(const String& msg,
const String& stacktrace) {
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
this, this->object_store()->error_listeners());
if (listeners.IsNull()) return;
const Array& arr = Array::Handle(this, Array::New(2));
arr.SetAt(0, msg);
arr.SetAt(1, stacktrace);
SendPort& listener = SendPort::Handle(this);
for (intptr_t i = 0; i < listeners.Length(); i++) {
listener ^= listeners.At(i);
if (!listener.IsNull()) {
Dart_Port port_id = listener.Id();
uint8_t* data = NULL;
intptr_t len = 0;
SerializeObject(arr, &data, &len, false);
Message* msg = new Message(port_id, data, len, Message::kNormalPriority);
PortMap::PostMessage(msg);
}
}
}
static void StoreError(Isolate* isolate, const Object& obj) { static void StoreError(Isolate* isolate, const Object& obj) {
ASSERT(obj.IsError()); ASSERT(obj.IsError());
isolate->object_store()->set_sticky_error(Error::Cast(obj)); isolate->object_store()->set_sticky_error(Error::Cast(obj));
@ -1170,6 +1388,11 @@ void Isolate::Shutdown() {
StackZone stack_zone(this); StackZone stack_zone(this);
HandleScope handle_scope(this); HandleScope handle_scope(this);
// Notify exit listeners that this isolate is shutting down.
if (object_store() != NULL) {
NotifyExitListeners();
}
// Clean up debugger resources. // Clean up debugger resources.
debugger()->Shutdown(); debugger()->Shutdown();

View file

@ -74,6 +74,7 @@ class RawFloat32x4;
class RawInt32x4; class RawInt32x4;
class RawUserTag; class RawUserTag;
class SampleBuffer; class SampleBuffer;
class SendPort;
class Simulator; class Simulator;
class StackResource; class StackResource;
class StackZone; class StackZone;
@ -410,6 +411,17 @@ class Isolate : public BaseIsolate {
bool AddResumeCapability(const Capability& capability); bool AddResumeCapability(const Capability& capability);
bool RemoveResumeCapability(const Capability& capability); bool RemoveResumeCapability(const Capability& capability);
void AddExitListener(const SendPort& listener);
void RemoveExitListener(const SendPort& listener);
void NotifyExitListeners();
void AddErrorListener(const SendPort& listener);
void RemoveErrorListener(const SendPort& listener);
void NotifyErrorListeners(const String& msg, const String& stacktrace);
bool ErrorsFatal() const { return errors_fatal_; }
void SetErrorsFatal(bool val) { errors_fatal_ = val; }
Random* random() { return &random_; } Random* random() { return &random_; }
Simulator* simulator() const { return simulator_; } Simulator* simulator() const { return simulator_; }
@ -674,6 +686,7 @@ class Isolate : public BaseIsolate {
Dart_Port origin_id_; // Isolates created by spawnFunc have some origin id. Dart_Port origin_id_; // Isolates created by spawnFunc have some origin id.
uint64_t pause_capability_; uint64_t pause_capability_;
uint64_t terminate_capability_; uint64_t terminate_capability_;
bool errors_fatal_;
Heap* heap_; Heap* heap_;
ObjectStore* object_store_; ObjectStore* object_store_;
uword top_exit_frame_info_; uword top_exit_frame_info_;

View file

@ -75,6 +75,8 @@ ObjectStore::ObjectStore()
pending_functions_(GrowableObjectArray::null()), pending_functions_(GrowableObjectArray::null()),
pending_deferred_loads_(GrowableObjectArray::null()), pending_deferred_loads_(GrowableObjectArray::null()),
resume_capabilities_(GrowableObjectArray::null()), resume_capabilities_(GrowableObjectArray::null()),
exit_listeners_(GrowableObjectArray::null()),
error_listeners_(GrowableObjectArray::null()),
sticky_error_(Error::null()), sticky_error_(Error::null()),
empty_context_(Context::null()), empty_context_(Context::null()),
stack_overflow_(Instance::null()), stack_overflow_(Instance::null()),
@ -125,6 +127,8 @@ bool ObjectStore::PreallocateObjects() {
this->pending_deferred_loads_ = GrowableObjectArray::New(); this->pending_deferred_loads_ = GrowableObjectArray::New();
this->resume_capabilities_ = GrowableObjectArray::New(); this->resume_capabilities_ = GrowableObjectArray::New();
this->exit_listeners_ = GrowableObjectArray::New();
this->error_listeners_ = GrowableObjectArray::New();
Object& result = Object::Handle(); Object& result = Object::Handle();
const Library& library = Library::Handle(Library::CoreLibrary()); const Library& library = Library::Handle(Library::CoreLibrary());

View file

@ -342,6 +342,14 @@ class ObjectStore {
return resume_capabilities_; return resume_capabilities_;
} }
RawGrowableObjectArray* exit_listeners() const {
return exit_listeners_;
}
RawGrowableObjectArray* error_listeners() const {
return error_listeners_;
}
RawError* sticky_error() const { return sticky_error_; } RawError* sticky_error() const { return sticky_error_; }
void set_sticky_error(const Error& value) { void set_sticky_error(const Error& value) {
ASSERT(!value.IsNull()); ASSERT(!value.IsNull());
@ -488,6 +496,8 @@ class ObjectStore {
RawGrowableObjectArray* pending_functions_; RawGrowableObjectArray* pending_functions_;
RawGrowableObjectArray* pending_deferred_loads_; RawGrowableObjectArray* pending_deferred_loads_;
RawGrowableObjectArray* resume_capabilities_; RawGrowableObjectArray* resume_capabilities_;
RawGrowableObjectArray* exit_listeners_;
RawGrowableObjectArray* error_listeners_;
RawError* sticky_error_; RawError* sticky_error_;
RawContext* empty_context_; RawContext* empty_context_;
RawInstance* stack_overflow_; RawInstance* stack_overflow_;

View file

@ -47,7 +47,6 @@ Future spawn(entry) {
main(){ main(){
asyncStart(); asyncStart();
RawReceivePort reply = new RawReceivePort(null); RawReceivePort reply = new RawReceivePort(null);
RawReceivePort reply2 = new RawReceivePort(null);
// Create two isolates waiting for commands, with errors non-fatal. // Create two isolates waiting for commands, with errors non-fatal.
Future iso1 = spawn(isomain1); Future iso1 = spawn(isomain1);
Future iso2 = spawn(isomain1); Future iso2 = spawn(isomain1);

View file

@ -92,7 +92,7 @@ main(){
case 2: case 2:
Expect.equals(new RangeError.value(37).toString(), "$error"); Expect.equals(new RangeError.value(37).toString(), "$error");
state2++; state2++;
reply.close(); reply2.close();
isolate2.removeErrorListener(errorPort2.sendPort); isolate2.removeErrorListener(errorPort2.sendPort);
errorPort2.close(); errorPort2.close();
asyncEnd(); asyncEnd();

View file

@ -11,16 +11,9 @@ mandel_isolate_test: Skip # Uses 600 MB Ram on our 1 GB test device.
[ $compiler == none || $compiler == dart2dart ] [ $compiler == none || $compiler == dart2dart ]
compile_time_error_test/01: Skip # Issue 12587 compile_time_error_test/01: Skip # Issue 12587
ondone_test: Fail # Not implemented yet
ping_test: Skip # Resolve test issues ping_test: Skip # Resolve test issues
ping_pause_test: Skip # Resolve test issues ping_pause_test: Skip # Resolve test issues
kill_test: Fail # Not implemented yet kill3_test: Pass, Fail # Bad test: expects total message order
kill2_test: Fail # Not implemented yet
kill3_test: Fail # Not implemented yet
kill_self_test: Fail # Not implemented yet
handle_error_test: Fail # Not implemented yet
handle_error2_test: Fail # Not implemented yet
handle_error3_test: Fail # Not implemented yet
message3_test/constList_identical: RuntimeError # Issue 21816 message3_test/constList_identical: RuntimeError # Issue 21816
message3_test/constMap: RuntimeError # Issue 21816 message3_test/constMap: RuntimeError # Issue 21816