| Index: runtime/vm/custom_isolate_test.cc
 | 
| ===================================================================
 | 
| --- runtime/vm/custom_isolate_test.cc	(revision 0)
 | 
| +++ runtime/vm/custom_isolate_test.cc	(revision 0)
 | 
| @@ -0,0 +1,427 @@
 | 
| +// 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 {
 | 
| +
 | 
| +#if defined(TARGET_ARCH_IA32)  // only ia32 can run execution tests.
 | 
| +
 | 
| +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 factory 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"
 | 
| +    "  Isolate 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:
 | 
| +  Event() : next_(NULL) {}
 | 
| +
 | 
| + public:
 | 
| +  virtual ~Event() {}
 | 
| +  virtual void Process() = 0;
 | 
| +
 | 
| +  virtual bool IsShutdownEvent(Dart_Isolate isolate) {
 | 
| +    return false;
 | 
| +  }
 | 
| +  virtual bool IsMessageEvent(Dart_Isolate isolate, Dart_Port port) {
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  friend class EventQueue;
 | 
| +  Event* next_;
 | 
| +};
 | 
| +
 | 
| +
 | 
| +// Start an isolate.
 | 
| +class StartEvent : public Event {
 | 
| + public:
 | 
| +  StartEvent(Dart_Isolate isolate, const char* main)
 | 
| +      : isolate_(isolate), main_(main) {}
 | 
| +
 | 
| +  virtual void Process();
 | 
| + private:
 | 
| +  Dart_Isolate isolate_;
 | 
| +  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();
 | 
| +}
 | 
| +
 | 
| +
 | 
| +// Shutdown an isolate.
 | 
| +class ShutdownEvent : public Event {
 | 
| + public:
 | 
| +  explicit ShutdownEvent(Dart_Isolate isolate) : isolate_(isolate) {}
 | 
| +
 | 
| +  virtual bool IsShutdownEvent(Dart_Isolate isolate) {
 | 
| +    return isolate == isolate_;
 | 
| +  }
 | 
| +
 | 
| +  virtual void Process();
 | 
| + private:
 | 
| +  Dart_Isolate isolate_;
 | 
| +};
 | 
| +
 | 
| +
 | 
| +void ShutdownEvent::Process() {
 | 
| +  OS::Print("<< ShutdownEvent with isolate(%p)--\n", isolate_);
 | 
| +  Dart_EnterIsolate(isolate_);
 | 
| +  Dart_ShutdownIsolate();
 | 
| +}
 | 
| +
 | 
| +
 | 
| +// Deliver a message to an isolate.
 | 
| +class MessageEvent : public Event {
 | 
| + public:
 | 
| +  MessageEvent(Dart_Isolate isolate, Dart_Port dest, Dart_Port reply,
 | 
| +               Dart_Message msg)
 | 
| +      : isolate_(isolate), dest_(dest), reply_(reply), msg_(msg) {}
 | 
| +
 | 
| +  ~MessageEvent() {
 | 
| +    free(msg_);
 | 
| +    msg_ = NULL;
 | 
| +  }
 | 
| +
 | 
| +  virtual bool IsMessageEvent(Dart_Isolate isolate, Dart_Port port) {
 | 
| +    return isolate == isolate_ && (port == kCloseAllPorts || port == dest_);
 | 
| +  }
 | 
| +
 | 
| +  virtual void Process();
 | 
| + private:
 | 
| +  Dart_Isolate isolate_;
 | 
| +  Dart_Port dest_;
 | 
| +  Dart_Port reply_;
 | 
| +  Dart_Message msg_;
 | 
| +};
 | 
| +
 | 
| +
 | 
| +void MessageEvent::Process() {
 | 
| +  OS::Print("$$ MessageEvent with dest port %lld--\n", dest_);
 | 
| +  Dart_EnterIsolate(isolate_);
 | 
| +  Dart_EnterScope();
 | 
| +
 | 
| +  Dart_Handle result = Dart_HandleMessage(dest_, reply_, msg_);
 | 
| +  EXPECT_VALID(result);
 | 
| +
 | 
| +  Dart_ExitScope();
 | 
| +  Dart_ExitIsolate();
 | 
| +}
 | 
| +
 | 
| +
 | 
| +// 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) {
 | 
| +      tail_ = NULL;
 | 
| +    }
 | 
| +
 | 
| +    return tmp;
 | 
| +  }
 | 
| +
 | 
| +  void ClosePort(Dart_Isolate isolate, Dart_Port port) {
 | 
| +    Event* cur = head_;
 | 
| +    Event* prev = NULL;
 | 
| +    while (cur != NULL) {
 | 
| +      Event* next = cur->next_;
 | 
| +      if (cur->IsMessageEvent(isolate, port)) {
 | 
| +        // 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;
 | 
| +Event* current_event;
 | 
| +
 | 
| +static bool PostMessage(Dart_Isolate dest_isolate,
 | 
| +                        Dart_Port dest_port,
 | 
| +                        Dart_Port reply_port,
 | 
| +                        Dart_Message message) {
 | 
| +  OS::Print("-- Posting message dest(%d) reply(%d) --\n",
 | 
| +            dest_port, reply_port);
 | 
| +  OS::Print("-- Adding MessageEvent to queue --\n");
 | 
| +  event_queue->Add(
 | 
| +      new MessageEvent(dest_isolate, dest_port, reply_port, message));
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void ClosePort(Dart_Isolate isolate,
 | 
| +                      Dart_Port port) {
 | 
| +  OS::Print("-- Closing port (%lld) for isolate(%p) --\n",
 | 
| +            port, isolate);
 | 
| +
 | 
| +  // Remove any pending events for the isolate/port.
 | 
| +  event_queue->ClosePort(isolate, port);
 | 
| +
 | 
| +  Dart_Isolate current = Dart_CurrentIsolate();
 | 
| +  if (current) {
 | 
| +    Dart_ExitIsolate();
 | 
| +  }
 | 
| +  Dart_EnterIsolate(isolate);
 | 
| +  if (!Dart_HasLivePorts() &&
 | 
| +      (current_event == NULL || !current_event->IsShutdownEvent(isolate))) {
 | 
| +    OS::Print("-- Adding ShutdownEvent to queue --\n");
 | 
| +    event_queue->Add(new ShutdownEvent(isolate));
 | 
| +  }
 | 
| +  Dart_ExitIsolate();
 | 
| +  if (current) {
 | 
| +    Dart_EnterIsolate(current);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
| +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");
 | 
| +  Dart_Handle result;
 | 
| +
 | 
| +  // 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 = Dart_CreateIsolate(NULL, NULL);
 | 
| +  Dart_SetMessageCallbacks(&PostMessage, &ClosePort);
 | 
| +  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();
 | 
| +  current_event = NULL;
 | 
| +
 | 
| +  Dart_Isolate main_isolate = Dart_CreateIsolate(NULL, NULL);
 | 
| +  Dart_SetMessageCallbacks(&PostMessage, &ClosePort);
 | 
| +  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) {
 | 
| +    current_event = event;
 | 
| +    event->Process();
 | 
| +    current_event = NULL;
 | 
| +    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  // TARGET_ARCH_IA32.
 | 
| +
 | 
| +}  // namespace dart
 | 
| 
 |