| // 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 "include/dart_native_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 { | |
| DECLARE_FLAG(bool, trace_shutdown); | |
| static void native_echo(Dart_NativeArguments args); | |
| static void CustomIsolateImpl_start(Dart_NativeArguments args); | |
| static Dart_NativeFunction NativeLookup(Dart_Handle name, | |
| int argc, | |
| bool* auto_setup_scope); | |
| static const char* kCustomIsolateScriptChars = | |
| R"( | |
| import 'dart:isolate'; | |
| final RawReceivePort mainPort = new RawReceivePort(); | |
| @pragma('vm:entry-point', 'get') | |
| final SendPort mainSendPort = mainPort.sendPort; | |
| @pragma('vm:external-name', 'native_echo') | |
| external void echo(arg); | |
| class CustomIsolateImpl implements CustomIsolate { | |
| CustomIsolateImpl(String entry) : _entry = entry { | |
| echo('Constructing isolate'); | |
| } | |
| SendPort spawn() { | |
| return _start(_entry); | |
| } | |
| @pragma('vm:external-name', 'CustomIsolateImpl_start') | |
| external static SendPort _start(entry); | |
| String _entry; | |
| } | |
| abstract class CustomIsolate { | |
| factory CustomIsolate(String entry) = CustomIsolateImpl; | |
| SendPort spawn(); | |
| } | |
| @pragma('vm:entry-point', 'call') | |
| isolateMain() { | |
| echo('Running isolateMain'); | |
| mainPort.handler = (message) { | |
| var data = message[0]; | |
| var replyTo = message[1]; | |
| echo('Received: $data'); | |
| replyTo.send(data + 1); | |
| mainPort.close(); | |
| }; | |
| } | |
| main() { | |
| var isolate = new CustomIsolate("isolateMain"); | |
| var receivePort = new RawReceivePort(); | |
| SendPort port = isolate.spawn(); | |
| port.send([42, receivePort.sendPort]); | |
| receivePort.handler = (message) { | |
| receivePort.close(); | |
| echo('Received: $message'); | |
| }; | |
| return 'success'; | |
| } | |
| )"; | |
| // An entry in our event queue. | |
| class Event { | |
| protected: | |
| explicit Event(Dart_Isolate isolate) : isolate_(isolate), next_(nullptr) {} | |
| 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_ = nullptr; } | |
| void Add(Event* event) { | |
| if (head_ == nullptr) { | |
| head_ = event; | |
| tail_ = event; | |
| } else { | |
| tail_->next_ = event; | |
| tail_ = event; | |
| } | |
| } | |
| Event* Get() { | |
| if (head_ == nullptr) { | |
| return nullptr; | |
| } | |
| Event* tmp = head_; | |
| head_ = head_->next_; | |
| if (head_ == nullptr) { | |
| // Not necessary, but why not. | |
| tail_ = nullptr; | |
| } | |
| return tmp; | |
| } | |
| void RemoveEventsForIsolate(Dart_Isolate isolate) { | |
| Event* cur = head_; | |
| Event* prev = nullptr; | |
| while (cur != nullptr) { | |
| Event* next = cur->next_; | |
| if (cur->isolate() == isolate) { | |
| // Remove matching event. | |
| if (prev != nullptr) { | |
| 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::PrintErr(">> StartEvent with isolate(%p)--\n", isolate()); | |
| Dart_EnterIsolate(isolate()); | |
| Dart_EnterScope(); | |
| Dart_Handle result; | |
| Dart_Handle lib = Dart_LookupLibrary(NewString(TestCase::url())); | |
| EXPECT_VALID(lib); | |
| result = Dart_Invoke(lib, NewString(main_), 0, nullptr); | |
| EXPECT_VALID(result); | |
| free(const_cast<char*>(main_)); | |
| main_ = nullptr; | |
| Dart_SetMessageNotifyCallback(nullptr); | |
| 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::PrintErr("$$ MessageEvent with isolate(%p)\n", isolate()); | |
| Dart_EnterIsolate(isolate()); | |
| Dart_EnterScope(); | |
| Dart_Handle result = Dart_HandleMessage(); | |
| EXPECT_VALID(result); | |
| if (!Dart_HasLivePorts()) { | |
| OS::PrintErr("<< Shutting down isolate(%p)\n", isolate()); | |
| event_queue->RemoveEventsForIsolate(isolate()); | |
| Dart_SetMessageNotifyCallback(nullptr); | |
| Dart_ExitScope(); | |
| Dart_ShutdownIsolate(); | |
| } else { | |
| Dart_ExitScope(); | |
| Dart_ExitIsolate(); | |
| } | |
| ASSERT(Dart_CurrentIsolate() == nullptr); | |
| } | |
| static void NotifyMessage(Dart_Isolate dest_isolate) { | |
| OS::PrintErr("-- Notify isolate(%p) of pending message --\n", dest_isolate); | |
| OS::PrintErr("-- Adding MessageEvent to queue --\n"); | |
| event_queue->Add(new MessageEvent(dest_isolate)); | |
| } | |
| static Dart_NativeFunction NativeLookup(Dart_Handle name, | |
| int argc, | |
| bool* auto_setup_scope) { | |
| ASSERT(auto_setup_scope != nullptr); | |
| *auto_setup_scope = true; | |
| const char* name_str = nullptr; | |
| 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 nullptr; | |
| } | |
| char* saved_echo = nullptr; | |
| 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 = nullptr; | |
| EXPECT_VALID(Dart_StringToCString(toString, &c_str)); | |
| if (saved_echo != nullptr) { | |
| free(saved_echo); | |
| } | |
| saved_echo = Utils::StrDup(c_str); | |
| OS::PrintErr("-- (isolate=%p) %s\n", Dart_CurrentIsolate(), c_str); | |
| Dart_ExitScope(); | |
| } | |
| static void CustomIsolateImpl_start(Dart_NativeArguments args) { | |
| OS::PrintErr("-- 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 = nullptr; | |
| EXPECT_VALID(Dart_StringToCString(param, &isolate_main)); | |
| isolate_main = Utils::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 != nullptr); | |
| Dart_SetMessageNotifyCallback(&NotifyMessage); | |
| Dart_EnterScope(); | |
| // Reload all the test classes here. | |
| // | |
| // TODO(turnidge): Use the create isolate callback instead? | |
| Dart_Handle lib = | |
| TestCase::LoadTestScript(kCustomIsolateScriptChars, NativeLookup); | |
| EXPECT_VALID(lib); | |
| Dart_Handle main_send_port = Dart_GetField(lib, NewString("mainSendPort")); | |
| EXPECT_VALID(main_send_port); | |
| Dart_Port main_port_id; | |
| Dart_Handle err = Dart_SendPortGetId(main_send_port, &main_port_id); | |
| EXPECT_VALID(err); | |
| OS::PrintErr("-- Adding StartEvent to queue --\n"); | |
| event_queue->Add(new StartEvent(new_isolate, isolate_main)); | |
| // Restore the original isolate. | |
| Dart_ExitScope(); | |
| Dart_ExitIsolate(); | |
| Dart_EnterIsolate(saved_isolate); | |
| Dart_EnterScope(); | |
| Dart_Handle send_port = Dart_NewSendPort(main_port_id); | |
| EXPECT_VALID(send_port); | |
| Dart_SetReturnValue(args, send_port); | |
| OS::PrintErr("-- Exit: CustomIsolateImpl_start --\n"); | |
| Dart_ExitScope(); | |
| } | |
| VM_UNIT_TEST_CASE(CustomIsolates) { | |
| bool saved_flag = FLAG_trace_shutdown; | |
| FLAG_trace_shutdown = true; | |
| event_queue = new EventQueue(); | |
| Dart_Isolate dart_isolate = TestCase::CreateTestIsolate(); | |
| EXPECT(dart_isolate != nullptr); | |
| 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_Invoke(lib, NewString("main"), 0, nullptr); | |
| EXPECT_VALID(result); | |
| EXPECT(Dart_IsString(result)); | |
| const char* result_str = nullptr; | |
| EXPECT_VALID(Dart_StringToCString(result, &result_str)); | |
| EXPECT_STREQ("success", result_str); | |
| Dart_ExitScope(); | |
| Dart_ExitIsolate(); | |
| OS::PrintErr("-- Starting event loop --\n"); | |
| Event* event = event_queue->Get(); | |
| while (event != nullptr) { | |
| event->Process(); | |
| delete event; | |
| event = event_queue->Get(); | |
| } | |
| OS::PrintErr("-- Finished event loop --\n"); | |
| EXPECT_STREQ("Received: 43", saved_echo); | |
| free(saved_echo); | |
| delete event_queue; | |
| event_queue = nullptr; | |
| FLAG_trace_shutdown = saved_flag; | |
| } | |
| } // namespace dart |