Files
sdk/runtime/vm/custom_isolate_test.cc
T
turnidge@google.com 8920623dc5 OOB messages and general message refactor.
(1)
No longer give embedders direct access to inter-isolate messages.
Instead, keep these messages in an internal queue and rely on the
embedder only to provide notifications.  This simplifies the api in
numerous ways:

 - post message callback drops parameters and becomes message notify callback.
 - close port callback goes away
 - Dart_Message type goes away
 - Dart_HandleMessage gets a simpler signature

Updated the custom_isolate_test to show what use of the new apis looks like.

(2)
Begin to add support for out-of-band (OOB) messages.  These messages
supercede regular messages in the queue.  We will attempt to deliver
these messages even while code is running, by using the isolate
interrupt mechanism.  These messages are not user-visible.  They will
be used by the runtime internally to implement things like reflection.

Renamed PortMessage to Message.

Refactored message sending apis in PortMap and Isolate.

Modified MessageQueue to be aware of multiple priorities.

Modify Dart_HandleMessage to process multiple OOB messages
Review URL: https://chromiumcodereview.appspot.com//9182001

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@3562 260f80e4-7a28-3924-810f-c04153c831b5
2012-01-24 19:31:01 +00:00

365 lines
9.5 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 "include/dart_api.h"
#include "vm/unit_test.h"
// Custom Isolate Test.
//
// This mid-size test uses the Dart Embedding Api to create a custom
// isolate abstraction. Instead of having a dedicated thread for each
// isolate, as is the case normally, this implementation shares a
// single thread among the isolates using an event queue.
namespace dart {
// Only ia32 and x64 can run execution tests.
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
static void native_echo(Dart_NativeArguments args);
static void CustomIsolateImpl_start(Dart_NativeArguments args);
static Dart_NativeFunction NativeLookup(Dart_Handle name, int argc);
static const char* kCustomIsolateScriptChars =
"class GlobalsHack {\n"
" static ReceivePort _receivePort;\n"
"}\n"
"\n"
"ReceivePort get receivePort() {\n"
" return GlobalsHack._receivePort;\n"
"}\n"
"\n"
"echo(arg) native \"native_echo\";\n"
"\n"
"class CustomIsolateImpl implements CustomIsolate {\n"
" CustomIsolateImpl(String entry) : _entry = entry{\n"
" echo('Constructing isolate');\n"
" }\n"
"\n"
" Future<SendPort> spawn() {\n"
" Completer<SendPort> completer = new Completer<SendPort>();\n"
" SendPort port = _start(_entry);\n"
" completer.complete(port);\n"
" return completer.future;\n"
" }\n"
"\n"
" static SendPort _start(entry)\n"
" native \"CustomIsolateImpl_start\";\n"
"\n"
" String _entry;\n"
"}\n"
"\n"
"interface CustomIsolate default CustomIsolateImpl {\n"
" CustomIsolate(String entry);\n"
"\n"
" Future<SendPort> spawn();\n"
"}\n"
"\n"
"isolateMain() {\n"
" echo('Running isolateMain');\n"
" receivePort.receive((message, SendPort replyTo) {\n"
" echo('Received: ' + message);\n"
" replyTo.send((message + 1), null);\n"
" });\n"
"}\n"
"\n"
"main() {\n"
" var isolate = new CustomIsolate(\"isolateMain\");\n"
" isolate.spawn().then((SendPort port) {\n"
" port.call(42).receive((message, replyTo) {\n"
" echo('Received: ' + message);\n"
" });\n"
" });\n"
" return 'success';\n"
"}\n";
// An entry in our event queue.
class Event {
protected:
explicit Event(Dart_Isolate isolate) : isolate_(isolate), next_(NULL) {}
public:
virtual ~Event() {}
virtual void Process() = 0;
Dart_Isolate isolate() const { return isolate_; }
private:
friend class EventQueue;
Dart_Isolate isolate_;
Event* next_;
};
// A simple event queue for our test.
class EventQueue {
public:
EventQueue() {
head_ = NULL;
}
void Add(Event* event) {
if (head_ == NULL) {
head_ = event;
tail_ = event;
} else {
tail_->next_ = event;
tail_ = event;
}
}
Event* Get() {
if (head_ == NULL) {
return NULL;
}
Event* tmp = head_;
head_ = head_->next_;
if (head_ == NULL) {
// Not necessary, but why not.
tail_ = NULL;
}
return tmp;
}
void RemoveEventsForIsolate(Dart_Isolate isolate) {
Event* cur = head_;
Event* prev = NULL;
while (cur != NULL) {
Event* next = cur->next_;
if (cur->isolate() == isolate) {
// Remove matching event.
if (prev != NULL) {
prev->next_ = next;
} else {
head_ = next;
}
delete cur;
} else {
// Advance.
prev = cur;
}
cur = next;
}
tail_ = prev;
}
private:
Event* head_;
Event* tail_;
};
EventQueue* event_queue;
// Start an isolate.
class StartEvent : public Event {
public:
StartEvent(Dart_Isolate isolate, const char* main)
: Event(isolate), main_(main) {}
virtual void Process();
private:
const char* main_;
};
void StartEvent::Process() {
OS::Print(">> StartEvent with isolate(%p)--\n", isolate());
Dart_EnterIsolate(isolate());
Dart_EnterScope();
Dart_Handle result;
// Reload all the test classes here.
//
// TODO(turnidge): Use the create isolate callback instead?
Dart_Handle lib = TestCase::LoadTestScript(kCustomIsolateScriptChars,
NativeLookup);
EXPECT_VALID(lib);
EXPECT_VALID(Dart_CompileAll());
Dart_Handle recv_port = Dart_GetReceivePort(Dart_GetMainPortId());
EXPECT_VALID(recv_port);
// TODO(turnidge): Provide a way to set a top-level variable from
// the dart embedding api.
Dart_Handle hidden = Dart_GetClass(lib, Dart_NewString("GlobalsHack"));
EXPECT_VALID(hidden);
result = Dart_SetStaticField(hidden, Dart_NewString("_receivePort"),
recv_port);
EXPECT_VALID(result);
result = Dart_InvokeStatic(lib,
Dart_NewString(""),
Dart_NewString(main_),
0,
NULL);
EXPECT_VALID(result);
free(const_cast<char*>(main_));
main_ = NULL;
Dart_ExitScope();
Dart_ExitIsolate();
}
// Notify an isolate of a pending message.
class MessageEvent : public Event {
public:
explicit MessageEvent(Dart_Isolate isolate) : Event(isolate) {}
~MessageEvent() {
}
virtual void Process();
};
void MessageEvent::Process() {
OS::Print("$$ MessageEvent with isolate(%p)\n", isolate());
Dart_EnterIsolate(isolate());
Dart_EnterScope();
Dart_Handle result = Dart_HandleMessage();
EXPECT_VALID(result);
if (!Dart_HasLivePorts()) {
OS::Print("<< Shutting down isolate(%p)\n", isolate());
event_queue->RemoveEventsForIsolate(isolate());
Dart_ShutdownIsolate();
} else {
Dart_ExitScope();
Dart_ExitIsolate();
}
ASSERT(Dart_CurrentIsolate() == NULL);
}
static void NotifyMessage(Dart_Isolate dest_isolate) {
OS::Print("-- Notify isolate(%p) of pending message --\n", dest_isolate);
OS::Print("-- Adding MessageEvent to queue --\n");
event_queue->Add(new MessageEvent(dest_isolate));
}
static Dart_NativeFunction NativeLookup(Dart_Handle name, int argc) {
const char* name_str = NULL;
EXPECT(Dart_IsString(name));
EXPECT_VALID(Dart_StringToCString(name, &name_str));
if (strcmp(name_str, "native_echo") == 0) {
return &native_echo;
} else if (strcmp(name_str, "CustomIsolateImpl_start") == 0) {
return &CustomIsolateImpl_start;
}
return NULL;
}
const char* saved_echo = NULL;
static void native_echo(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle arg = Dart_GetNativeArgument(args, 0);
Dart_Handle toString = Dart_ToString(arg);
EXPECT_VALID(toString);
const char* c_str = NULL;
EXPECT_VALID(Dart_StringToCString(toString, &c_str));
if (saved_echo) {
free(const_cast<char*>(saved_echo));
}
saved_echo = strdup(c_str);
OS::Print("-- (isolate=%p) %s\n", Dart_CurrentIsolate(), c_str);
Dart_ExitScope();
}
static void CustomIsolateImpl_start(Dart_NativeArguments args) {
OS::Print("-- Enter: CustomIsolateImpl_start --\n");
// We would probably want to pass in the this pointer too, so we
// could associate the CustomIsolateImpl instance with the
// Dart_Isolate by storing it in a native field.
EXPECT_EQ(1, Dart_GetNativeArgumentCount(args));
Dart_Handle param = Dart_GetNativeArgument(args, 0);
EXPECT_VALID(param);
EXPECT(Dart_IsString(param));
const char* isolate_main = NULL;
EXPECT_VALID(Dart_StringToCString(param, &isolate_main));
isolate_main = strdup(isolate_main);
// Save current isolate.
Dart_Isolate saved_isolate = Dart_CurrentIsolate();
Dart_ExitIsolate();
// Create a new Dart_Isolate.
Dart_Isolate new_isolate = TestCase::CreateTestIsolate();
EXPECT(new_isolate != NULL);
Dart_SetMessageNotifyCallback(&NotifyMessage);
Dart_Port new_port = Dart_GetMainPortId();
OS::Print("-- Adding StartEvent to queue --\n");
event_queue->Add(new StartEvent(new_isolate, isolate_main));
// Restore the original isolate.
Dart_ExitIsolate();
Dart_EnterIsolate(saved_isolate);
Dart_EnterScope();
Dart_Handle send_port = Dart_NewSendPort(new_port);
EXPECT_VALID(send_port);
Dart_SetReturnValue(args, send_port);
OS::Print("-- Exit: CustomIsolateImpl_start --\n");
Dart_ExitScope();
}
UNIT_TEST_CASE(CustomIsolates) {
event_queue = new EventQueue();
Dart_Isolate dart_isolate = TestCase::CreateTestIsolate();
EXPECT(dart_isolate != NULL);
Dart_SetMessageNotifyCallback(&NotifyMessage);
Dart_EnterScope();
Dart_Handle result;
// Create a test library.
Dart_Handle lib = TestCase::LoadTestScript(kCustomIsolateScriptChars,
NativeLookup);
EXPECT_VALID(lib);
// Run main.
result = Dart_InvokeStatic(lib,
Dart_NewString(""),
Dart_NewString("main"),
0,
NULL);
EXPECT_VALID(result);
EXPECT(Dart_IsString(result));
const char* result_str = NULL;
EXPECT_VALID(Dart_StringToCString(result, &result_str));
EXPECT_STREQ("success", result_str);
Dart_ExitScope();
Dart_ExitIsolate();
OS::Print("-- Starting event loop --\n");
Event* event = event_queue->Get();
while (event) {
event->Process();
delete event;
event = event_queue->Get();
}
OS::Print("-- Finished event loop --\n");
EXPECT_STREQ("Received: 43", saved_echo);
free(const_cast<char*>(saved_echo));
delete event_queue;
}
#endif // defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64).
} // namespace dart