1 | // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "include/dart_api.h" |
6 | #include "include/dart_native_api.h" |
7 | |
8 | #include "vm/unit_test.h" |
9 | |
10 | // Custom Isolate Test. |
11 | // |
12 | // This mid-size test uses the Dart Embedding Api to create a custom |
13 | // isolate abstraction. Instead of having a dedicated thread for each |
14 | // isolate, as is the case normally, this implementation shares a |
15 | // single thread among the isolates using an event queue. |
16 | |
17 | namespace dart { |
18 | |
19 | DECLARE_FLAG(bool, trace_shutdown); |
20 | |
21 | static void native_echo(Dart_NativeArguments args); |
22 | static void CustomIsolateImpl_start(Dart_NativeArguments args); |
23 | static Dart_NativeFunction NativeLookup(Dart_Handle name, |
24 | int argc, |
25 | bool* auto_setup_scope); |
26 | |
27 | static const char* kCustomIsolateScriptChars = |
28 | "import 'dart:isolate';\n" |
29 | "\n" |
30 | "final RawReceivePort mainPort = new RawReceivePort();\n" |
31 | "final SendPort mainSendPort = mainPort.sendPort;\n" |
32 | "\n" |
33 | "echo(arg) native \"native_echo\";\n" |
34 | "\n" |
35 | "class CustomIsolateImpl implements CustomIsolate {\n" |
36 | " CustomIsolateImpl(String entry) : _entry = entry{\n" |
37 | " echo('Constructing isolate');\n" |
38 | " }\n" |
39 | "\n" |
40 | " SendPort spawn() {\n" |
41 | " return _start(_entry);\n" |
42 | " }\n" |
43 | "\n" |
44 | " static SendPort _start(entry)\n" |
45 | " native \"CustomIsolateImpl_start\";\n" |
46 | "\n" |
47 | " String _entry;\n" |
48 | "}\n" |
49 | "\n" |
50 | "abstract class CustomIsolate {\n" |
51 | " factory CustomIsolate(String entry) = CustomIsolateImpl;\n" |
52 | "\n" |
53 | " SendPort spawn();\n" |
54 | "}\n" |
55 | "\n" |
56 | "isolateMain() {\n" |
57 | " echo('Running isolateMain');\n" |
58 | " mainPort.handler = (message) {\n" |
59 | " var data = message[0];\n" |
60 | " var replyTo = message[1];\n" |
61 | " echo('Received: $data');\n" |
62 | " replyTo.send(data + 1);\n" |
63 | " mainPort.close();\n" |
64 | " };\n" |
65 | "}\n" |
66 | "\n" |
67 | "main() {\n" |
68 | " var isolate = new CustomIsolate(\"isolateMain\");\n" |
69 | " var receivePort = new RawReceivePort();\n" |
70 | " SendPort port = isolate.spawn();\n" |
71 | " port.send([42, receivePort.sendPort]);\n" |
72 | " receivePort.handler = (message) {\n" |
73 | " receivePort.close();\n" |
74 | " echo('Received: $message');\n" |
75 | " };\n" |
76 | " return 'success';\n" |
77 | "}\n" ; |
78 | |
79 | // An entry in our event queue. |
80 | class Event { |
81 | protected: |
82 | explicit Event(Dart_Isolate isolate) : isolate_(isolate), next_(NULL) {} |
83 | |
84 | public: |
85 | virtual ~Event() {} |
86 | virtual void Process() = 0; |
87 | |
88 | Dart_Isolate isolate() const { return isolate_; } |
89 | |
90 | private: |
91 | friend class EventQueue; |
92 | Dart_Isolate isolate_; |
93 | Event* next_; |
94 | }; |
95 | |
96 | // A simple event queue for our test. |
97 | class EventQueue { |
98 | public: |
99 | EventQueue() { head_ = NULL; } |
100 | |
101 | void Add(Event* event) { |
102 | if (head_ == NULL) { |
103 | head_ = event; |
104 | tail_ = event; |
105 | } else { |
106 | tail_->next_ = event; |
107 | tail_ = event; |
108 | } |
109 | } |
110 | |
111 | Event* Get() { |
112 | if (head_ == NULL) { |
113 | return NULL; |
114 | } |
115 | Event* tmp = head_; |
116 | head_ = head_->next_; |
117 | if (head_ == NULL) { |
118 | // Not necessary, but why not. |
119 | tail_ = NULL; |
120 | } |
121 | |
122 | return tmp; |
123 | } |
124 | |
125 | void RemoveEventsForIsolate(Dart_Isolate isolate) { |
126 | Event* cur = head_; |
127 | Event* prev = NULL; |
128 | while (cur != NULL) { |
129 | Event* next = cur->next_; |
130 | if (cur->isolate() == isolate) { |
131 | // Remove matching event. |
132 | if (prev != NULL) { |
133 | prev->next_ = next; |
134 | } else { |
135 | head_ = next; |
136 | } |
137 | delete cur; |
138 | } else { |
139 | // Advance. |
140 | prev = cur; |
141 | } |
142 | cur = next; |
143 | } |
144 | tail_ = prev; |
145 | } |
146 | |
147 | private: |
148 | Event* head_; |
149 | Event* tail_; |
150 | }; |
151 | EventQueue* event_queue; |
152 | |
153 | // Start an isolate. |
154 | class StartEvent : public Event { |
155 | public: |
156 | StartEvent(Dart_Isolate isolate, const char* main) |
157 | : Event(isolate), main_(main) {} |
158 | |
159 | virtual void Process(); |
160 | |
161 | private: |
162 | const char* main_; |
163 | }; |
164 | |
165 | void StartEvent::Process() { |
166 | OS::PrintErr(">> StartEvent with isolate(%p)--\n" , isolate()); |
167 | Dart_EnterIsolate(isolate()); |
168 | Dart_EnterScope(); |
169 | Dart_Handle result; |
170 | |
171 | Dart_Handle lib = Dart_LookupLibrary(NewString(TestCase::url())); |
172 | EXPECT_VALID(lib); |
173 | |
174 | result = Dart_Invoke(lib, NewString(main_), 0, NULL); |
175 | EXPECT_VALID(result); |
176 | free(const_cast<char*>(main_)); |
177 | main_ = NULL; |
178 | |
179 | Dart_SetMessageNotifyCallback(NULL); |
180 | Dart_ExitScope(); |
181 | Dart_ExitIsolate(); |
182 | } |
183 | |
184 | // Notify an isolate of a pending message. |
185 | class MessageEvent : public Event { |
186 | public: |
187 | explicit MessageEvent(Dart_Isolate isolate) : Event(isolate) {} |
188 | |
189 | ~MessageEvent() {} |
190 | |
191 | virtual void Process(); |
192 | }; |
193 | |
194 | void MessageEvent::Process() { |
195 | OS::PrintErr("$$ MessageEvent with isolate(%p)\n" , isolate()); |
196 | Dart_EnterIsolate(isolate()); |
197 | Dart_EnterScope(); |
198 | |
199 | Dart_Handle result = Dart_HandleMessage(); |
200 | EXPECT_VALID(result); |
201 | |
202 | if (!Dart_HasLivePorts()) { |
203 | OS::PrintErr("<< Shutting down isolate(%p)\n" , isolate()); |
204 | event_queue->RemoveEventsForIsolate(isolate()); |
205 | Dart_SetMessageNotifyCallback(NULL); |
206 | Dart_ExitScope(); |
207 | Dart_ShutdownIsolate(); |
208 | } else { |
209 | Dart_ExitScope(); |
210 | Dart_ExitIsolate(); |
211 | } |
212 | ASSERT(Dart_CurrentIsolate() == NULL); |
213 | } |
214 | |
215 | static void NotifyMessage(Dart_Isolate dest_isolate) { |
216 | OS::PrintErr("-- Notify isolate(%p) of pending message --\n" , dest_isolate); |
217 | OS::PrintErr("-- Adding MessageEvent to queue --\n" ); |
218 | event_queue->Add(new MessageEvent(dest_isolate)); |
219 | } |
220 | |
221 | static Dart_NativeFunction NativeLookup(Dart_Handle name, |
222 | int argc, |
223 | bool* auto_setup_scope) { |
224 | ASSERT(auto_setup_scope != NULL); |
225 | *auto_setup_scope = true; |
226 | const char* name_str = NULL; |
227 | EXPECT(Dart_IsString(name)); |
228 | EXPECT_VALID(Dart_StringToCString(name, &name_str)); |
229 | if (strcmp(name_str, "native_echo" ) == 0) { |
230 | return &native_echo; |
231 | } else if (strcmp(name_str, "CustomIsolateImpl_start" ) == 0) { |
232 | return &CustomIsolateImpl_start; |
233 | } |
234 | return NULL; |
235 | } |
236 | |
237 | char* saved_echo = NULL; |
238 | static void native_echo(Dart_NativeArguments args) { |
239 | Dart_EnterScope(); |
240 | Dart_Handle arg = Dart_GetNativeArgument(args, 0); |
241 | Dart_Handle toString = Dart_ToString(arg); |
242 | EXPECT_VALID(toString); |
243 | const char* c_str = NULL; |
244 | EXPECT_VALID(Dart_StringToCString(toString, &c_str)); |
245 | if (saved_echo != nullptr) { |
246 | free(saved_echo); |
247 | } |
248 | saved_echo = Utils::StrDup(c_str); |
249 | OS::PrintErr("-- (isolate=%p) %s\n" , Dart_CurrentIsolate(), c_str); |
250 | Dart_ExitScope(); |
251 | } |
252 | |
253 | static void CustomIsolateImpl_start(Dart_NativeArguments args) { |
254 | OS::PrintErr("-- Enter: CustomIsolateImpl_start --\n" ); |
255 | |
256 | // We would probably want to pass in the this pointer too, so we |
257 | // could associate the CustomIsolateImpl instance with the |
258 | // Dart_Isolate by storing it in a native field. |
259 | EXPECT_EQ(1, Dart_GetNativeArgumentCount(args)); |
260 | Dart_Handle param = Dart_GetNativeArgument(args, 0); |
261 | EXPECT_VALID(param); |
262 | EXPECT(Dart_IsString(param)); |
263 | const char* isolate_main = NULL; |
264 | EXPECT_VALID(Dart_StringToCString(param, &isolate_main)); |
265 | isolate_main = Utils::StrDup(isolate_main); |
266 | |
267 | // Save current isolate. |
268 | Dart_Isolate saved_isolate = Dart_CurrentIsolate(); |
269 | Dart_ExitIsolate(); |
270 | |
271 | // Create a new Dart_Isolate. |
272 | Dart_Isolate new_isolate = TestCase::CreateTestIsolate(); |
273 | EXPECT(new_isolate != NULL); |
274 | Dart_SetMessageNotifyCallback(&NotifyMessage); |
275 | Dart_EnterScope(); |
276 | // Reload all the test classes here. |
277 | // |
278 | // TODO(turnidge): Use the create isolate callback instead? |
279 | Dart_Handle lib = |
280 | TestCase::LoadTestScript(kCustomIsolateScriptChars, NativeLookup); |
281 | EXPECT_VALID(lib); |
282 | |
283 | Dart_Handle main_send_port = Dart_GetField(lib, NewString("mainSendPort" )); |
284 | EXPECT_VALID(main_send_port); |
285 | Dart_Port main_port_id; |
286 | Dart_Handle err = Dart_SendPortGetId(main_send_port, &main_port_id); |
287 | EXPECT_VALID(err); |
288 | |
289 | OS::PrintErr("-- Adding StartEvent to queue --\n" ); |
290 | event_queue->Add(new StartEvent(new_isolate, isolate_main)); |
291 | |
292 | // Restore the original isolate. |
293 | Dart_ExitScope(); |
294 | Dart_ExitIsolate(); |
295 | Dart_EnterIsolate(saved_isolate); |
296 | Dart_EnterScope(); |
297 | |
298 | Dart_Handle send_port = Dart_NewSendPort(main_port_id); |
299 | EXPECT_VALID(send_port); |
300 | Dart_SetReturnValue(args, send_port); |
301 | |
302 | OS::PrintErr("-- Exit: CustomIsolateImpl_start --\n" ); |
303 | Dart_ExitScope(); |
304 | } |
305 | |
306 | VM_UNIT_TEST_CASE(CustomIsolates) { |
307 | bool saved_flag = FLAG_trace_shutdown; |
308 | FLAG_trace_shutdown = true; |
309 | FLAG_verify_handles = true; |
310 | event_queue = new EventQueue(); |
311 | |
312 | Dart_Isolate dart_isolate = TestCase::CreateTestIsolate(); |
313 | EXPECT(dart_isolate != NULL); |
314 | Dart_SetMessageNotifyCallback(&NotifyMessage); |
315 | Dart_EnterScope(); |
316 | Dart_Handle result; |
317 | |
318 | // Create a test library. |
319 | Dart_Handle lib = |
320 | TestCase::LoadTestScript(kCustomIsolateScriptChars, NativeLookup); |
321 | EXPECT_VALID(lib); |
322 | |
323 | // Run main. |
324 | result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
325 | EXPECT_VALID(result); |
326 | EXPECT(Dart_IsString(result)); |
327 | const char* result_str = NULL; |
328 | EXPECT_VALID(Dart_StringToCString(result, &result_str)); |
329 | EXPECT_STREQ("success" , result_str); |
330 | |
331 | Dart_ExitScope(); |
332 | Dart_ExitIsolate(); |
333 | |
334 | OS::PrintErr("-- Starting event loop --\n" ); |
335 | Event* event = event_queue->Get(); |
336 | while (event != nullptr) { |
337 | event->Process(); |
338 | delete event; |
339 | event = event_queue->Get(); |
340 | } |
341 | OS::PrintErr("-- Finished event loop --\n" ); |
342 | EXPECT_STREQ("Received: 43" , saved_echo); |
343 | free(saved_echo); |
344 | |
345 | delete event_queue; |
346 | event_queue = NULL; |
347 | FLAG_trace_shutdown = saved_flag; |
348 | } |
349 | |
350 | } // namespace dart |
351 | |