OLD | NEW |
---|---|
(Empty) | |
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 | |
7 #include "vm/unit_test.h" | |
8 | |
9 // Custom Isolate Test. | |
10 // | |
11 // This mid-size test uses the Dart Embedding Api to create a custom | |
12 // isolate abstraction. Instead of having a dedicated thread for each | |
13 // isolate, as is the case normally, this implementation shares a | |
14 // single thread among the isolates using an event queue. | |
15 | |
16 namespace dart { | |
17 | |
18 #if defined(TARGET_ARCH_IA32) // only ia32 can run execution tests. | |
19 | |
20 static void native_echo(Dart_NativeArguments args); | |
21 static void CustomIsolateImpl_start(Dart_NativeArguments args); | |
22 static Dart_NativeFunction NativeLookup(Dart_Handle name, int argc); | |
23 | |
24 | |
25 static const char* kCustomIsolateScriptChars = | |
26 "class GlobalsHack {\n" | |
27 " static ReceivePort _receivePort;\n" | |
28 "}\n" | |
29 "\n" | |
30 "ReceivePort get receivePort() {\n" | |
31 " return GlobalsHack._receivePort;\n" | |
32 "}\n" | |
33 "\n" | |
34 "echo(arg) native \"native_echo\";\n" | |
35 "\n" | |
36 "class CustomIsolateImpl implements CustomIsolate {\n" | |
37 " CustomIsolateImpl(String entry) : _entry = entry{\n" | |
38 " echo('Constructing isolate');\n" | |
39 " }\n" | |
40 "\n" | |
41 " Future<SendPort> spawn() {\n" | |
42 " Completer<SendPort> completer = new Completer<SendPort>();\n" | |
43 " SendPort port = _start(_entry);\n" | |
44 " completer.complete(port);\n" | |
45 " return completer.future;\n" | |
46 " }\n" | |
47 "\n" | |
48 " static SendPort _start(entry)\n" | |
49 " native \"CustomIsolateImpl_start\";\n" | |
50 "\n" | |
51 " String _entry;\n" | |
52 "}\n" | |
53 "\n" | |
54 "interface CustomIsolate factory CustomIsolateImpl {\n" | |
55 " CustomIsolate(String entry);\n" | |
56 "\n" | |
57 " Future<SendPort> spawn();\n" | |
58 "}\n" | |
59 "\n" | |
60 "isolateMain() {\n" | |
61 " echo('Running isolateMain');\n" | |
62 " receivePort.receive((message, SendPort replyTo) {\n" | |
63 " echo('Received: ' + message);\n" | |
64 " replyTo.send((message + 1), null);\n" | |
65 " });\n" | |
66 "}\n" | |
67 "\n" | |
68 "main() {\n" | |
69 " Isolate isolate = new CustomIsolate(\"isolateMain\");\n" | |
70 " isolate.spawn().then((SendPort port) {\n" | |
71 " port.call(42).receive((message, replyTo) {\n" | |
72 " echo('Received: ' + message);\n" | |
73 " });\n" | |
74 " });\n" | |
75 " return 'success';\n" | |
76 "}\n"; | |
77 | |
78 | |
79 // An entry in our event queue. | |
80 class Event { | |
81 protected: | |
82 Event() : next_(NULL) {} | |
83 | |
84 public: | |
85 virtual ~Event() {} | |
86 virtual void Process() = 0; | |
87 | |
88 virtual bool matches(Dart_Isolate isolate, Dart_Port port) { | |
89 return false; | |
90 } | |
91 | |
92 private: | |
93 friend class EventQueue; | |
94 Event* next_; | |
95 }; | |
96 | |
97 | |
98 // Start an isolate. | |
99 class StartEvent : public Event { | |
100 public: | |
101 StartEvent(Dart_Isolate isolate, const char* main) | |
102 : isolate_(isolate), main_(main) {} | |
103 | |
104 virtual void Process(); | |
105 private: | |
106 Dart_Isolate isolate_; | |
107 const char* main_; | |
108 }; | |
109 | |
110 | |
111 void StartEvent::Process() { | |
112 OS::Print(">> StartEvent with isolate(%p)--\n", isolate_); | |
113 Dart_EnterIsolate(isolate_); | |
114 Dart_EnterScope(); | |
115 Dart_Handle result; | |
116 | |
117 // Reload all the test classes here. | |
118 // | |
119 // TODO(turnidge): Use the create isolate callback instead? | |
120 Dart_Handle lib = TestCase::LoadTestScript(kCustomIsolateScriptChars, | |
121 NativeLookup); | |
122 EXPECT_VALID(lib); | |
123 | |
124 Dart_Port port = Dart_GetMainPort(); | |
Anton Muhin
2011/11/23 19:28:14
do you need this port here?
turnidge
2011/11/23 21:45:37
The variable is used, but only once. I have combi
| |
125 Dart_Handle recv_port = Dart_NewReceivePort(port); | |
126 EXPECT_VALID(recv_port); | |
127 | |
128 // TODO(turnidge): Provide a way to set a top-level variable from | |
129 // the dart embedding api. | |
130 Dart_Handle hidden = Dart_GetClass(lib, Dart_NewString("GlobalsHack")); | |
131 EXPECT_VALID(hidden); | |
132 result = Dart_SetStaticField(hidden, Dart_NewString("_receivePort"), | |
133 recv_port); | |
134 EXPECT_VALID(result); | |
135 | |
136 result = Dart_InvokeStatic(lib, | |
137 Dart_NewString(""), | |
138 Dart_NewString(main_), | |
139 0, | |
140 NULL); | |
141 EXPECT_VALID(result); | |
142 free(const_cast<char*>(main_)); | |
143 main_ = NULL; | |
144 | |
145 Dart_ExitScope(); | |
146 Dart_ExitIsolate(); | |
147 } | |
148 | |
149 | |
150 // Shutdown an isolate. | |
151 class ShutdownEvent : public Event { | |
152 public: | |
153 explicit ShutdownEvent(Dart_Isolate isolate) : isolate_(isolate) {} | |
154 | |
155 virtual void Process(); | |
156 private: | |
157 Dart_Isolate isolate_; | |
158 }; | |
159 | |
160 | |
161 void ShutdownEvent::Process() { | |
162 OS::Print("<< ShutdownEvent with isolate(%p)--\n", isolate_); | |
163 Dart_EnterIsolate(isolate_); | |
164 Dart_ShutdownIsolate(); | |
165 } | |
166 | |
167 | |
168 // Deliver a message to an isolate. | |
169 class MessageEvent : public Event { | |
170 public: | |
171 MessageEvent(Dart_Isolate isolate, Dart_Port dest, Dart_Port reply, | |
172 Dart_Message msg) | |
173 : isolate_(isolate), dest_(dest), reply_(reply), msg_(msg) {} | |
174 | |
175 ~MessageEvent() { | |
176 free(msg_); | |
177 msg_ = NULL; | |
178 } | |
179 | |
180 virtual bool matches(Dart_Isolate isolate, Dart_Port port) { | |
181 return isolate == isolate_ && (port == kCloseAllPorts || port == dest_); | |
182 } | |
183 | |
184 virtual void Process(); | |
185 private: | |
186 Dart_Isolate isolate_; | |
187 Dart_Port dest_; | |
188 Dart_Port reply_; | |
189 Dart_Message msg_; | |
190 }; | |
191 | |
192 | |
193 void MessageEvent::Process() { | |
194 OS::Print("$$ MessageEvent with dest port %lld--\n", dest_); | |
195 Dart_EnterIsolate(isolate_); | |
196 Dart_EnterScope(); | |
197 | |
198 Dart_Handle result = Dart_HandleMessage(dest_, reply_, msg_); | |
199 EXPECT_VALID(result); | |
200 | |
201 Dart_ExitScope(); | |
202 Dart_ExitIsolate(); | |
203 } | |
204 | |
205 | |
206 // A simple event queue for our test. | |
207 class EventQueue { | |
208 public: | |
209 EventQueue() { | |
210 head_ = NULL; | |
211 } | |
212 | |
213 void Add(Event* event) { | |
214 if (head_ == NULL) { | |
215 head_ = event; | |
216 tail_ = event; | |
217 } else { | |
218 tail_->next_ = event; | |
219 tail_ = event; | |
220 } | |
221 } | |
222 | |
223 Event* Get() { | |
224 if (head_ == NULL) { | |
225 return NULL; | |
226 } | |
227 Event* tmp = head_; | |
228 head_ = head_->next_; | |
229 if (head_ == NULL) { | |
230 tail_ = NULL; | |
231 } | |
232 | |
233 return tmp; | |
234 } | |
235 | |
236 void ClosePort(Dart_Isolate isolate, Dart_Port port) { | |
237 Event* cur = head_; | |
238 Event* prev = NULL; | |
239 while (cur != NULL) { | |
240 Event* next = cur->next_; | |
241 if (cur->matches(isolate, port)) { | |
242 // Remove matching event. | |
243 if (prev != NULL) { | |
244 prev->next_ = next; | |
245 } else { | |
246 head_ = next; | |
247 } | |
248 delete cur; | |
249 } else { | |
250 // Advance. | |
251 prev = cur; | |
252 } | |
253 cur = next; | |
254 } | |
255 tail_ = prev; | |
256 } | |
257 | |
258 private: | |
259 Event* head_; | |
260 Event* tail_; | |
261 }; | |
262 EventQueue* event_queue; | |
263 | |
264 | |
265 static bool PostMessage(Dart_Isolate dest_isolate, | |
266 Dart_Port dest_port, | |
267 Dart_Port reply_port, | |
268 Dart_Message message) { | |
269 OS::Print("-- Posting message dest(%d) reply(%d) --\n", | |
270 dest_port, reply_port); | |
271 OS::Print("-- Adding MessageEvent to queue --\n"); | |
272 event_queue->Add( | |
273 new MessageEvent(dest_isolate, dest_port, reply_port, message)); | |
274 } | |
275 | |
276 | |
277 static void ClosePort(Dart_Isolate isolate, | |
278 Dart_Port port) { | |
279 OS::Print("-- Closing port (%lld) --\n", port); | |
280 | |
281 // Flush any pending events for the isolate/port. | |
282 event_queue->ClosePort(isolate, port); | |
283 | |
284 Dart_Isolate current = Dart_CurrentIsolate(); | |
285 if (current) { | |
286 Dart_ExitIsolate(); | |
287 } | |
288 Dart_EnterIsolate(isolate); | |
289 if (!Dart_IsolateHasActivePorts()) { | |
290 OS::Print("-- Adding ShutdownEvent to queue --\n"); | |
291 event_queue->Add(new ShutdownEvent(isolate)); | |
292 } | |
293 Dart_ExitIsolate(); | |
294 if (current) { | |
295 Dart_EnterIsolate(current); | |
296 } | |
297 } | |
298 | |
299 | |
300 static Dart_NativeFunction NativeLookup(Dart_Handle name, int argc) { | |
301 const char* name_str = NULL; | |
302 EXPECT(Dart_IsString(name)); | |
303 EXPECT_VALID(Dart_StringToCString(name, &name_str)); | |
304 if (strcmp(name_str, "native_echo") == 0) { | |
305 return &native_echo; | |
306 } else if (strcmp(name_str, "CustomIsolateImpl_start") == 0) { | |
307 return &CustomIsolateImpl_start; | |
308 } | |
309 return NULL; | |
310 } | |
311 | |
312 | |
313 const char* saved_echo = NULL; | |
314 static void native_echo(Dart_NativeArguments args) { | |
315 Dart_EnterScope(); | |
316 Dart_Handle arg = Dart_GetNativeArgument(args, 0); | |
317 Dart_Handle toString = Dart_ToString(arg); | |
318 EXPECT_VALID(toString); | |
319 const char* c_str = NULL; | |
320 EXPECT_VALID(Dart_StringToCString(toString, &c_str)); | |
321 if (saved_echo) { | |
322 free(const_cast<char*>(saved_echo)); | |
323 } | |
324 saved_echo = strdup(c_str); | |
325 OS::Print("-- (isolate=%p) %s\n", Dart_CurrentIsolate(), c_str); | |
326 Dart_ExitScope(); | |
327 } | |
328 | |
329 | |
330 static void CustomIsolateImpl_start(Dart_NativeArguments args) { | |
331 OS::Print("-- Enter: CustomIsolateImpl_start --\n"); | |
332 Dart_Handle result; | |
333 | |
334 // We would probably want to pass in the this pointer too, so we | |
335 // could associate the CustomIsolateImpl instance with the | |
336 // Dart_Isolate by storing it in a native field. | |
337 EXPECT_EQ(1, Dart_GetNativeArgumentCount(args)); | |
338 Dart_Handle param = Dart_GetNativeArgument(args, 0); | |
339 EXPECT_VALID(param); | |
340 EXPECT(Dart_IsString(param)); | |
341 const char* isolate_main = NULL; | |
342 EXPECT_VALID(Dart_StringToCString(param, &isolate_main)); | |
343 isolate_main = strdup(isolate_main); | |
344 | |
345 // Save current isolate. | |
346 Dart_Isolate saved_isolate = Dart_CurrentIsolate(); | |
347 Dart_ExitIsolate(); | |
348 | |
349 // Create a new Dart_Isolate. | |
350 Dart_Isolate new_isolate = Dart_CreateIsolate(NULL, NULL); | |
351 Dart_SetMessageCallbacks(&PostMessage, &ClosePort); | |
352 Dart_Port new_port = Dart_GetMainPort(); | |
353 | |
354 OS::Print("-- Adding StartEvent to queue --\n"); | |
355 event_queue->Add(new StartEvent(new_isolate, isolate_main)); | |
356 | |
357 // Restore the original isolate. | |
358 Dart_ExitIsolate(); | |
359 Dart_EnterIsolate(saved_isolate); | |
360 Dart_EnterScope(); | |
361 | |
362 Dart_Handle send_port = Dart_NewSendPort(new_port); | |
363 EXPECT_VALID(send_port); | |
364 Dart_SetReturnValue(args, send_port); | |
365 | |
366 OS::Print("-- Exit: CustomIsolateImpl_start --\n"); | |
367 Dart_ExitScope(); | |
368 } | |
369 | |
370 | |
371 UNIT_TEST_CASE(CustomIsolates) { | |
372 event_queue = new EventQueue(); | |
373 | |
374 Dart_Isolate main_isolate = Dart_CreateIsolate(NULL, NULL); | |
375 Dart_SetMessageCallbacks(&PostMessage, &ClosePort); | |
376 Dart_EnterScope(); | |
377 Dart_Handle result; | |
378 | |
379 // Create a test library. | |
380 Dart_Handle lib = TestCase::LoadTestScript(kCustomIsolateScriptChars, | |
381 NativeLookup); | |
382 EXPECT_VALID(lib); | |
383 | |
384 // Run main. | |
385 result = Dart_InvokeStatic(lib, | |
386 Dart_NewString(""), | |
387 Dart_NewString("main"), | |
388 0, | |
389 NULL); | |
390 EXPECT_VALID(result); | |
391 EXPECT(Dart_IsString(result)); | |
392 const char* result_str = NULL; | |
393 EXPECT_VALID(Dart_StringToCString(result, &result_str)); | |
394 EXPECT_STREQ("success", result_str); | |
395 | |
396 Dart_ExitScope(); | |
397 Dart_ExitIsolate(); | |
398 | |
399 OS::Print("-- Starting event loop --\n"); | |
400 Event* event = event_queue->Get(); | |
401 while (event) { | |
402 event->Process(); | |
403 delete event; | |
404 event = event_queue->Get(); | |
405 } | |
406 OS::Print("-- Finished event loop --\n"); | |
407 EXPECT_STREQ("Received: 43", saved_echo); | |
408 free(const_cast<char*>(saved_echo)); | |
409 | |
410 delete event_queue; | |
411 } | |
412 | |
413 #endif // TARGET_ARCH_IA32. | |
414 | |
415 } // namespace dart | |
OLD | NEW |