- 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 _PING = 3;
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,
@ -367,15 +372,28 @@ patch class Isolate {
}
/* 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) {
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) {
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]) {
@ -397,11 +415,19 @@ patch class Isolate {
}
/* 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) {
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() {

View file

@ -126,13 +126,21 @@ class IsolateMessageHandler : public MessageHandler {
virtual Isolate* isolate() const { return isolate_; }
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 {
kPauseMsg = 1,
kResumeMsg = 2,
kPingMsg = 3,
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,
kBeforeNextEventAction = 1,
kAsEventAction = 2
@ -167,8 +175,8 @@ bool IsolateMessageHandler::HandleLibMessage(const Array& message) {
if (message.Length() < 2) return true;
const Object& type = Object::Handle(I, message.At(1));
if (!type.IsSmi()) return true;
const Smi& msg_type = Smi::Cast(type);
switch (msg_type.Value()) {
const intptr_t msg_type = Smi::Cast(type).Value();
switch (msg_type) {
case kPauseMsg: {
// [ OOB, kPauseMsg, pause capability, resume capability ]
if (message.Length() != 4) return true;
@ -255,6 +263,45 @@ bool IsolateMessageHandler::HandleLibMessage(const Array& message) {
}
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)
// Malformed OOB messages are silently ignored in release builds.
default:
@ -438,8 +485,35 @@ bool IsolateMessageHandler::ProcessUnhandledException(const Error& result) {
Dart_ExitScope();
}
I->object_store()->set_sticky_error(result);
return false;
// Generate the error and stacktrace strings for the error message.
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),
pause_capability_(0),
terminate_capability_(0),
errors_fatal_(true),
heap_(NULL),
object_store_(NULL),
top_exit_frame_info_(0),
@ -535,6 +610,7 @@ Isolate::Isolate(Isolate* original)
main_port_(0),
pause_capability_(0),
terminate_capability_(0),
errors_fatal_(true),
heap_(NULL),
object_store_(NULL),
top_exit_frame_info_(0),
@ -890,7 +966,7 @@ bool Isolate::VerifyTerminateCapability(const Object& capability) const {
bool Isolate::AddResumeCapability(const Capability& capability) {
// 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(
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) {
ASSERT(obj.IsError());
isolate->object_store()->set_sticky_error(Error::Cast(obj));
@ -1170,6 +1388,11 @@ void Isolate::Shutdown() {
StackZone stack_zone(this);
HandleScope handle_scope(this);
// Notify exit listeners that this isolate is shutting down.
if (object_store() != NULL) {
NotifyExitListeners();
}
// Clean up debugger resources.
debugger()->Shutdown();

View file

@ -74,6 +74,7 @@ class RawFloat32x4;
class RawInt32x4;
class RawUserTag;
class SampleBuffer;
class SendPort;
class Simulator;
class StackResource;
class StackZone;
@ -410,6 +411,17 @@ class Isolate : public BaseIsolate {
bool AddResumeCapability(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_; }
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.
uint64_t pause_capability_;
uint64_t terminate_capability_;
bool errors_fatal_;
Heap* heap_;
ObjectStore* object_store_;
uword top_exit_frame_info_;

View file

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

View file

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

View file

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

View file

@ -92,7 +92,7 @@ main(){
case 2:
Expect.equals(new RangeError.value(37).toString(), "$error");
state2++;
reply.close();
reply2.close();
isolate2.removeErrorListener(errorPort2.sendPort);
errorPort2.close();
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 ]
compile_time_error_test/01: Skip # Issue 12587
ondone_test: Fail # Not implemented yet
ping_test: Skip # Resolve test issues
ping_pause_test: Skip # Resolve test issues
kill_test: Fail # Not implemented yet
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
kill3_test: Pass, Fail # Bad test: expects total message order
message3_test/constList_identical: RuntimeError # Issue 21816
message3_test/constMap: RuntimeError # Issue 21816