1 | // Copyright (c) 2019, 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 | // This file contains test functions for the dart:ffi test cases. |
6 | |
7 | #include <stddef.h> |
8 | #include <stdlib.h> |
9 | #include <sys/types.h> |
10 | #include <csignal> |
11 | |
12 | #include "platform/globals.h" |
13 | #if defined(HOST_OS_WINDOWS) |
14 | #include <psapi.h> |
15 | #include <windows.h> |
16 | #else |
17 | #include <unistd.h> |
18 | #endif |
19 | |
20 | // Only OK to use here because this is test code. |
21 | #include <condition_variable> // NOLINT(build/c++11) |
22 | #include <functional> // NOLINT(build/c++11) |
23 | #include <mutex> // NOLINT(build/c++11) |
24 | #include <queue> // NOLINT(build/c++11) |
25 | #include <thread> // NOLINT(build/c++11) |
26 | |
27 | #include <setjmp.h> // NOLINT |
28 | #include <signal.h> // NOLINT |
29 | #include <iostream> |
30 | #include <limits> |
31 | |
32 | // TODO(dartbug.com/40579): This requires static linking to either link |
33 | // dart.exe or dart_precompiled_runtime.exe on Windows. |
34 | // The sample currently fails on Windows in AOT mode. |
35 | #include "include/dart_api.h" |
36 | #include "include/dart_native_api.h" |
37 | |
38 | #include "include/dart_api_dl.h" |
39 | |
40 | namespace dart { |
41 | |
42 | #define CHECK(X) \ |
43 | if (!(X)) { \ |
44 | fprintf(stderr, "%s\n", "Check failed: " #X); \ |
45 | return 1; \ |
46 | } |
47 | |
48 | #define CHECK_EQ(X, Y) CHECK((X) == (Y)) |
49 | |
50 | //////////////////////////////////////////////////////////////////////////////// |
51 | // Functions for stress-testing. |
52 | |
53 | DART_EXPORT int64_t MinInt64() { |
54 | Dart_ExecuteInternalCommand("gc-on-nth-allocation" , |
55 | reinterpret_cast<void*>(1)); |
56 | return 0x8000000000000000; |
57 | } |
58 | |
59 | DART_EXPORT int64_t MinInt32() { |
60 | Dart_ExecuteInternalCommand("gc-on-nth-allocation" , |
61 | reinterpret_cast<void*>(1)); |
62 | return 0x80000000; |
63 | } |
64 | |
65 | DART_EXPORT double SmallDouble() { |
66 | Dart_ExecuteInternalCommand("gc-on-nth-allocation" , |
67 | reinterpret_cast<void*>(1)); |
68 | return 0x80000000 * -1.0; |
69 | } |
70 | |
71 | // Requires boxing on 32-bit and 64-bit systems, even if the top 32-bits are |
72 | // truncated. |
73 | DART_EXPORT void* LargePointer() { |
74 | Dart_ExecuteInternalCommand("gc-on-nth-allocation" , |
75 | reinterpret_cast<void*>(1)); |
76 | uint64_t origin = 0x8100000082000000; |
77 | return reinterpret_cast<void*>(origin); |
78 | } |
79 | |
80 | DART_EXPORT void TriggerGC(uint64_t count) { |
81 | Dart_ExecuteInternalCommand("gc-now" , nullptr); |
82 | } |
83 | |
84 | DART_EXPORT void CollectOnNthAllocation(intptr_t num_allocations) { |
85 | Dart_ExecuteInternalCommand("gc-on-nth-allocation" , |
86 | reinterpret_cast<void*>(num_allocations)); |
87 | } |
88 | |
89 | // Triggers GC. Has 11 dummy arguments as unboxed odd integers which should be |
90 | // ignored by GC. |
91 | DART_EXPORT void Regress37069(uint64_t a, |
92 | uint64_t b, |
93 | uint64_t c, |
94 | uint64_t d, |
95 | uint64_t e, |
96 | uint64_t f, |
97 | uint64_t g, |
98 | uint64_t h, |
99 | uint64_t i, |
100 | uint64_t j, |
101 | uint64_t k) { |
102 | Dart_ExecuteInternalCommand("gc-now" , nullptr); |
103 | } |
104 | |
105 | #if !defined(HOST_OS_WINDOWS) |
106 | DART_EXPORT void* UnprotectCodeOtherThread(void* isolate, |
107 | std::condition_variable* var, |
108 | std::mutex* mut) { |
109 | std::function<void()> callback = [&]() { |
110 | mut->lock(); |
111 | var->notify_all(); |
112 | mut->unlock(); |
113 | |
114 | // Wait for mutator thread to continue (and block) before leaving the |
115 | // safepoint. |
116 | while (Dart_ExecuteInternalCommand("is-mutator-in-native" , isolate) != |
117 | nullptr) { |
118 | usleep(10 * 1000 /*10 ms*/); |
119 | } |
120 | }; |
121 | |
122 | struct { |
123 | void* isolate; |
124 | std::function<void()>* callback; |
125 | } args = {.isolate = isolate, .callback = &callback}; |
126 | |
127 | Dart_ExecuteInternalCommand("run-in-safepoint-and-rw-code" , &args); |
128 | return nullptr; |
129 | } |
130 | |
131 | struct HelperThreadState { |
132 | std::mutex mutex; |
133 | std::condition_variable cvar; |
134 | std::unique_ptr<std::thread> helper; |
135 | }; |
136 | |
137 | DART_EXPORT void* TestUnprotectCode(void (*fn)(void*)) { |
138 | HelperThreadState* state = new HelperThreadState; |
139 | |
140 | { |
141 | std::unique_lock<std::mutex> lock(state->mutex); // locks the mutex |
142 | state->helper.reset(new std::thread(UnprotectCodeOtherThread, |
143 | Dart_CurrentIsolate(), &state->cvar, |
144 | &state->mutex)); |
145 | |
146 | state->cvar.wait(lock); |
147 | } |
148 | |
149 | if (fn != nullptr) { |
150 | fn(state); |
151 | return nullptr; |
152 | } else { |
153 | return state; |
154 | } |
155 | } |
156 | |
157 | DART_EXPORT void WaitForHelper(HelperThreadState* helper) { |
158 | helper->helper->join(); |
159 | delete helper; |
160 | } |
161 | #else |
162 | // Our version of VSC++ doesn't support std::thread yet. |
163 | DART_EXPORT void WaitForHelper(void* helper) {} |
164 | DART_EXPORT void* TestUnprotectCode(void (*fn)(void)) { |
165 | return nullptr; |
166 | } |
167 | #endif |
168 | |
169 | // Defined in ffi_test_functions.S. |
170 | // |
171 | // Clobbers some registers with special meaning in Dart before re-entry, for |
172 | // stress-testing. Not used on 32-bit Windows due to complications with Windows |
173 | // "safeseh". |
174 | #if defined(TARGET_OS_WINDOWS) && defined(HOST_ARCH_IA32) |
175 | void ClobberAndCall(void (*fn)()) { |
176 | fn(); |
177 | } |
178 | #else |
179 | extern "C" void ClobberAndCall(void (*fn)()); |
180 | #endif |
181 | |
182 | DART_EXPORT intptr_t TestGC(void (*do_gc)()) { |
183 | ClobberAndCall(do_gc); |
184 | return 0; |
185 | } |
186 | |
187 | struct CallbackTestData { |
188 | intptr_t success; |
189 | void (*callback)(); |
190 | }; |
191 | |
192 | #if defined(TARGET_OS_LINUX) |
193 | |
194 | thread_local sigjmp_buf buf; |
195 | void CallbackTestSignalHandler(int) { |
196 | siglongjmp(buf, 1); |
197 | } |
198 | |
199 | intptr_t ExpectAbort(void (*fn)()) { |
200 | fprintf(stderr, "**** EXPECT STACKTRACE TO FOLLOW. THIS IS OK. ****\n" ); |
201 | |
202 | struct sigaction old_action = {}; |
203 | intptr_t result = __sigsetjmp(buf, /*savesigs=*/1); |
204 | if (result == 0) { |
205 | // Install signal handler. |
206 | struct sigaction handler = {}; |
207 | handler.sa_handler = CallbackTestSignalHandler; |
208 | sigemptyset(&handler.sa_mask); |
209 | handler.sa_flags = 0; |
210 | |
211 | sigaction(SIGABRT, &handler, &old_action); |
212 | |
213 | fn(); |
214 | } else { |
215 | // Caught the setjmp. |
216 | sigaction(SIGABRT, &old_action, NULL); |
217 | exit(0); |
218 | } |
219 | fprintf(stderr, "Expected abort!!!\n" ); |
220 | exit(1); |
221 | } |
222 | |
223 | void* TestCallbackOnThreadOutsideIsolate(void* parameter) { |
224 | CallbackTestData* data = reinterpret_cast<CallbackTestData*>(parameter); |
225 | data->success = ExpectAbort(data->callback); |
226 | return NULL; |
227 | } |
228 | |
229 | intptr_t TestCallbackOtherThreadHelper(void* (*tester)(void*), void (*fn)()) { |
230 | CallbackTestData data = {1, fn}; |
231 | pthread_attr_t attr; |
232 | intptr_t result = pthread_attr_init(&attr); |
233 | CHECK_EQ(result, 0); |
234 | |
235 | pthread_t tid; |
236 | result = pthread_create(&tid, &attr, tester, &data); |
237 | CHECK_EQ(result, 0); |
238 | |
239 | result = pthread_attr_destroy(&attr); |
240 | CHECK_EQ(result, 0); |
241 | |
242 | void* retval; |
243 | result = pthread_join(tid, &retval); |
244 | |
245 | // Doesn't actually return because the other thread will exit when the test is |
246 | // finished. |
247 | return 1; |
248 | } |
249 | |
250 | // Run a callback on another thread and verify that it triggers SIGABRT. |
251 | DART_EXPORT intptr_t TestCallbackWrongThread(void (*fn)()) { |
252 | return TestCallbackOtherThreadHelper(&TestCallbackOnThreadOutsideIsolate, fn); |
253 | } |
254 | |
255 | // Verify that we get SIGABRT when invoking a native callback outside an |
256 | // isolate. |
257 | DART_EXPORT intptr_t TestCallbackOutsideIsolate(void (*fn)()) { |
258 | Dart_Isolate current = Dart_CurrentIsolate(); |
259 | |
260 | Dart_ExitIsolate(); |
261 | CallbackTestData data = {1, fn}; |
262 | TestCallbackOnThreadOutsideIsolate(&data); |
263 | Dart_EnterIsolate(current); |
264 | |
265 | return data.success; |
266 | } |
267 | |
268 | DART_EXPORT intptr_t TestCallbackWrongIsolate(void (*fn)()) { |
269 | return ExpectAbort(fn); |
270 | } |
271 | |
272 | #endif // defined(TARGET_OS_LINUX) |
273 | |
274 | //////////////////////////////////////////////////////////////////////////////// |
275 | // Initialize `dart_api_dl.h` |
276 | DART_EXPORT intptr_t InitDartApiDL(void* data) { |
277 | return Dart_InitializeApiDL(data); |
278 | } |
279 | |
280 | //////////////////////////////////////////////////////////////////////////////// |
281 | // Functions for async callbacks example. |
282 | // |
283 | // sample_async_callback.dart |
284 | |
285 | void Fatal(char const* file, int line, char const* error) { |
286 | printf("FATAL %s:%i\n" , file, line); |
287 | printf("%s\n" , error); |
288 | Dart_DumpNativeStackTrace(NULL); |
289 | Dart_PrepareToAbort(); |
290 | abort(); |
291 | } |
292 | |
293 | #define FATAL(error) Fatal(__FILE__, __LINE__, error) |
294 | |
295 | void SleepOnAnyOS(intptr_t seconds) { |
296 | #if defined(HOST_OS_WINDOWS) |
297 | Sleep(1000 * seconds); |
298 | #else |
299 | sleep(seconds); |
300 | #endif |
301 | } |
302 | |
303 | intptr_t (*my_callback_blocking_fp_)(intptr_t); |
304 | Dart_Port my_callback_blocking_send_port_; |
305 | |
306 | void (*my_callback_non_blocking_fp_)(intptr_t); |
307 | Dart_Port my_callback_non_blocking_send_port_; |
308 | |
309 | typedef std::function<void()> Work; |
310 | |
311 | // Notify Dart through a port that the C lib has pending async callbacks. |
312 | // |
313 | // Expects heap allocated `work` so delete can be called on it. |
314 | // |
315 | // The `send_port` should be from the isolate which registered the callback. |
316 | void NotifyDart(Dart_Port send_port, const Work* work) { |
317 | const intptr_t work_addr = reinterpret_cast<intptr_t>(work); |
318 | printf("C : Posting message (port: %" Px64 ", work: %" Px ").\n" , |
319 | send_port, work_addr); |
320 | |
321 | Dart_CObject dart_object; |
322 | dart_object.type = Dart_CObject_kInt64; |
323 | dart_object.value.as_int64 = work_addr; |
324 | |
325 | const bool result = Dart_PostCObject_DL(send_port, &dart_object); |
326 | if (!result) { |
327 | FATAL("C : Posting message to port failed." ); |
328 | } |
329 | } |
330 | |
331 | // Do a callback to Dart in a blocking way, being interested in the result. |
332 | // |
333 | // Dart returns `a + 3`. |
334 | intptr_t MyCallbackBlocking(intptr_t a) { |
335 | std::mutex mutex; |
336 | std::unique_lock<std::mutex> lock(mutex); |
337 | intptr_t result; |
338 | auto callback = my_callback_blocking_fp_; // Define storage duration. |
339 | std::condition_variable cv; |
340 | bool notified = false; |
341 | const Work work = [a, &result, callback, &cv, ¬ified]() { |
342 | result = callback(a); |
343 | printf("C Da: Notify result ready.\n" ); |
344 | notified = true; |
345 | cv.notify_one(); |
346 | }; |
347 | const Work* work_ptr = new Work(work); // Copy to heap. |
348 | NotifyDart(my_callback_blocking_send_port_, work_ptr); |
349 | printf("C : Waiting for result.\n" ); |
350 | while (!notified) { |
351 | cv.wait(lock); |
352 | } |
353 | printf("C : Received result.\n" ); |
354 | return result; |
355 | } |
356 | |
357 | // Do a callback to Dart in a non-blocking way. |
358 | // |
359 | // Dart sums all numbers posted to it. |
360 | void MyCallbackNonBlocking(intptr_t a) { |
361 | auto callback = my_callback_non_blocking_fp_; // Define storage duration. |
362 | const Work work = [a, callback]() { callback(a); }; |
363 | // Copy to heap to make it outlive the function scope. |
364 | const Work* work_ptr = new Work(work); |
365 | NotifyDart(my_callback_non_blocking_send_port_, work_ptr); |
366 | } |
367 | |
368 | // Simulated work for Thread #1. |
369 | // |
370 | // Simulates heavy work with sleeps. |
371 | void Work1() { |
372 | printf("C T1: Work1 Start.\n" ); |
373 | SleepOnAnyOS(1); |
374 | const intptr_t val1 = 3; |
375 | printf("C T1: MyCallbackBlocking(%" Pd ").\n" , val1); |
376 | const intptr_t val2 = MyCallbackBlocking(val1); // val2 = 6. |
377 | printf("C T1: MyCallbackBlocking returned %" Pd ".\n" , val2); |
378 | SleepOnAnyOS(1); |
379 | const intptr_t val3 = val2 - 1; // val3 = 5. |
380 | printf("C T1: MyCallbackNonBlocking(%" Pd ").\n" , val3); |
381 | MyCallbackNonBlocking(val3); // Post 5 to Dart. |
382 | printf("C T1: Work1 Done.\n" ); |
383 | } |
384 | |
385 | // Simulated work for Thread #2. |
386 | // |
387 | // Simulates lighter work, no sleeps. |
388 | void Work2() { |
389 | printf("C T2: Work2 Start.\n" ); |
390 | const intptr_t val1 = 5; |
391 | printf("C T2: MyCallbackNonBlocking(%" Pd ").\n" , val1); |
392 | MyCallbackNonBlocking(val1); // Post 5 to Dart. |
393 | const intptr_t val2 = 1; |
394 | printf("C T2: MyCallbackBlocking(%" Pd ").\n" , val2); |
395 | const intptr_t val3 = MyCallbackBlocking(val2); // val3 = 4. |
396 | printf("C T2: MyCallbackBlocking returned %" Pd ".\n" , val3); |
397 | printf("C T2: MyCallbackNonBlocking(%" Pd ").\n" , val3); |
398 | MyCallbackNonBlocking(val3); // Post 4 to Dart. |
399 | printf("C T2: Work2 Done.\n" ); |
400 | } |
401 | |
402 | // Simulator that simulates concurrent work with multiple threads. |
403 | class SimulateWork { |
404 | public: |
405 | static void StartWorkSimulator() { |
406 | running_work_simulator_ = new SimulateWork(); |
407 | running_work_simulator_->Start(); |
408 | } |
409 | |
410 | static void StopWorkSimulator() { |
411 | running_work_simulator_->Stop(); |
412 | delete running_work_simulator_; |
413 | running_work_simulator_ = nullptr; |
414 | } |
415 | |
416 | private: |
417 | static SimulateWork* running_work_simulator_; |
418 | |
419 | void Start() { |
420 | printf("C Da: Starting SimulateWork.\n" ); |
421 | printf("C Da: Starting worker threads.\n" ); |
422 | thread1 = new std::thread(Work1); |
423 | thread2 = new std::thread(Work2); |
424 | printf("C Da: Started SimulateWork.\n" ); |
425 | } |
426 | |
427 | void Stop() { |
428 | printf("C Da: Stopping SimulateWork.\n" ); |
429 | printf("C Da: Waiting for worker threads to finish.\n" ); |
430 | thread1->join(); |
431 | thread2->join(); |
432 | delete thread1; |
433 | delete thread2; |
434 | printf("C Da: Stopped SimulateWork.\n" ); |
435 | } |
436 | |
437 | std::thread* thread1; |
438 | std::thread* thread2; |
439 | }; |
440 | SimulateWork* SimulateWork::running_work_simulator_ = 0; |
441 | |
442 | DART_EXPORT void RegisterMyCallbackBlocking(Dart_Port send_port, |
443 | intptr_t (*callback1)(intptr_t)) { |
444 | my_callback_blocking_fp_ = callback1; |
445 | my_callback_blocking_send_port_ = send_port; |
446 | } |
447 | |
448 | DART_EXPORT void RegisterMyCallbackNonBlocking(Dart_Port send_port, |
449 | void (*callback)(intptr_t)) { |
450 | my_callback_non_blocking_fp_ = callback; |
451 | my_callback_non_blocking_send_port_ = send_port; |
452 | } |
453 | |
454 | DART_EXPORT void StartWorkSimulator() { |
455 | SimulateWork::StartWorkSimulator(); |
456 | } |
457 | |
458 | DART_EXPORT void StopWorkSimulator() { |
459 | SimulateWork::StopWorkSimulator(); |
460 | } |
461 | |
462 | DART_EXPORT void ExecuteCallback(Work* work_ptr) { |
463 | printf("C Da: ExecuteCallback(%" Pp ").\n" , |
464 | reinterpret_cast<intptr_t>(work_ptr)); |
465 | const Work work = *work_ptr; |
466 | work(); |
467 | delete work_ptr; |
468 | printf("C Da: ExecuteCallback done.\n" ); |
469 | } |
470 | |
471 | //////////////////////////////////////////////////////////////////////////////// |
472 | // Functions for async callbacks example. |
473 | // |
474 | // sample_native_port_call.dart |
475 | |
476 | Dart_Port send_port_; |
477 | |
478 | static void FreeFinalizer(void*, Dart_WeakPersistentHandle, void* value) { |
479 | free(value); |
480 | } |
481 | |
482 | class PendingCall { |
483 | public: |
484 | PendingCall(void** buffer, size_t* length) |
485 | : response_buffer_(buffer), response_length_(length) { |
486 | receive_port_ = |
487 | Dart_NewNativePort_DL("cpp-response" , &PendingCall::HandleResponse, |
488 | /*handle_concurrently=*/false); |
489 | } |
490 | ~PendingCall() { Dart_CloseNativePort_DL(receive_port_); } |
491 | |
492 | Dart_Port port() const { return receive_port_; } |
493 | |
494 | void PostAndWait(Dart_Port port, Dart_CObject* object) { |
495 | std::unique_lock<std::mutex> lock(mutex); |
496 | const bool success = Dart_PostCObject_DL(send_port_, object); |
497 | if (!success) FATAL("Failed to send message, invalid port or isolate died" ); |
498 | |
499 | printf("C : Waiting for result.\n" ); |
500 | while (!notified) { |
501 | cv.wait(lock); |
502 | } |
503 | } |
504 | |
505 | static void HandleResponse(Dart_Port p, Dart_CObject* message) { |
506 | if (message->type != Dart_CObject_kArray) { |
507 | FATAL("C : Wrong Data: message->type != Dart_CObject_kArray.\n" ); |
508 | } |
509 | Dart_CObject** c_response_args = message->value.as_array.values; |
510 | Dart_CObject* c_pending_call = c_response_args[0]; |
511 | Dart_CObject* c_message = c_response_args[1]; |
512 | printf("C : HandleResponse (call: %" Px ", message: %" Px ").\n" , |
513 | reinterpret_cast<intptr_t>(c_pending_call), |
514 | reinterpret_cast<intptr_t>(c_message)); |
515 | |
516 | auto pending_call = reinterpret_cast<PendingCall*>( |
517 | c_pending_call->type == Dart_CObject_kInt64 |
518 | ? c_pending_call->value.as_int64 |
519 | : c_pending_call->value.as_int32); |
520 | |
521 | pending_call->ResolveCall(c_message); |
522 | } |
523 | |
524 | private: |
525 | static bool NonEmptyBuffer(void** value) { return *value != nullptr; } |
526 | |
527 | void ResolveCall(Dart_CObject* bytes) { |
528 | assert(bytes->type == Dart_CObject_kTypedData); |
529 | if (bytes->type != Dart_CObject_kTypedData) { |
530 | FATAL("C : Wrong Data: bytes->type != Dart_CObject_kTypedData.\n" ); |
531 | } |
532 | const intptr_t response_length = bytes->value.as_typed_data.length; |
533 | const uint8_t* response_buffer = bytes->value.as_typed_data.values; |
534 | printf("C : ResolveCall(length: %" Pd ", buffer: %" Px ").\n" , |
535 | response_length, reinterpret_cast<intptr_t>(response_buffer)); |
536 | |
537 | void* buffer = malloc(response_length); |
538 | memmove(buffer, response_buffer, response_length); |
539 | |
540 | *response_buffer_ = buffer; |
541 | *response_length_ = response_length; |
542 | |
543 | printf("C : Notify result ready.\n" ); |
544 | notified = true; |
545 | cv.notify_one(); |
546 | } |
547 | |
548 | std::mutex mutex; |
549 | std::condition_variable cv; |
550 | bool notified = false; |
551 | |
552 | Dart_Port receive_port_; |
553 | void** response_buffer_; |
554 | size_t* response_length_; |
555 | }; |
556 | |
557 | // Do a callback to Dart in a blocking way, being interested in the result. |
558 | // |
559 | // Dart returns `a + 3`. |
560 | uint8_t MyCallback1(uint8_t a) { |
561 | const char* methodname = "myCallback1" ; |
562 | size_t request_length = sizeof(uint8_t) * 1; |
563 | void* request_buffer = malloc(request_length); // FreeFinalizer. |
564 | reinterpret_cast<uint8_t*>(request_buffer)[0] = a; // Populate buffer. |
565 | void* response_buffer = nullptr; |
566 | size_t response_length = 0; |
567 | |
568 | PendingCall pending_call(&response_buffer, &response_length); |
569 | |
570 | Dart_CObject c_send_port; |
571 | c_send_port.type = Dart_CObject_kSendPort; |
572 | c_send_port.value.as_send_port.id = pending_call.port(); |
573 | c_send_port.value.as_send_port.origin_id = ILLEGAL_PORT; |
574 | |
575 | Dart_CObject c_pending_call; |
576 | c_pending_call.type = Dart_CObject_kInt64; |
577 | c_pending_call.value.as_int64 = reinterpret_cast<int64_t>(&pending_call); |
578 | |
579 | Dart_CObject c_method_name; |
580 | c_method_name.type = Dart_CObject_kString; |
581 | c_method_name.value.as_string = const_cast<char*>(methodname); |
582 | |
583 | Dart_CObject c_request_data; |
584 | c_request_data.type = Dart_CObject_kExternalTypedData; |
585 | c_request_data.value.as_external_typed_data.type = Dart_TypedData_kUint8; |
586 | c_request_data.value.as_external_typed_data.length = request_length; |
587 | c_request_data.value.as_external_typed_data.data = |
588 | static_cast<uint8_t*>(request_buffer); |
589 | c_request_data.value.as_external_typed_data.peer = request_buffer; |
590 | c_request_data.value.as_external_typed_data.callback = FreeFinalizer; |
591 | |
592 | Dart_CObject* c_request_arr[] = {&c_send_port, &c_pending_call, |
593 | &c_method_name, &c_request_data}; |
594 | Dart_CObject c_request; |
595 | c_request.type = Dart_CObject_kArray; |
596 | c_request.value.as_array.values = c_request_arr; |
597 | c_request.value.as_array.length = |
598 | sizeof(c_request_arr) / sizeof(c_request_arr[0]); |
599 | |
600 | printf("C : Dart_PostCObject_(request: %" Px ", call: %" Px ").\n" , |
601 | reinterpret_cast<intptr_t>(&c_request), |
602 | reinterpret_cast<intptr_t>(&c_pending_call)); |
603 | pending_call.PostAndWait(send_port_, &c_request); |
604 | printf("C : Received result.\n" ); |
605 | |
606 | const intptr_t result = reinterpret_cast<uint8_t*>(response_buffer)[0]; |
607 | free(response_buffer); |
608 | |
609 | return result; |
610 | } |
611 | |
612 | // Do a callback to Dart in a non-blocking way. |
613 | // |
614 | // Dart sums all numbers posted to it. |
615 | void MyCallback2(uint8_t a) { |
616 | const char* methodname = "myCallback2" ; |
617 | void* request_buffer = malloc(sizeof(uint8_t) * 1); // FreeFinalizer. |
618 | reinterpret_cast<uint8_t*>(request_buffer)[0] = a; // Populate buffer. |
619 | const size_t request_length = sizeof(uint8_t) * 1; |
620 | |
621 | Dart_CObject c_send_port; |
622 | c_send_port.type = Dart_CObject_kNull; |
623 | |
624 | Dart_CObject c_pending_call; |
625 | c_pending_call.type = Dart_CObject_kNull; |
626 | |
627 | Dart_CObject c_method_name; |
628 | c_method_name.type = Dart_CObject_kString; |
629 | c_method_name.value.as_string = const_cast<char*>(methodname); |
630 | |
631 | Dart_CObject c_request_data; |
632 | c_request_data.type = Dart_CObject_kExternalTypedData; |
633 | c_request_data.value.as_external_typed_data.type = Dart_TypedData_kUint8; |
634 | c_request_data.value.as_external_typed_data.length = request_length; |
635 | c_request_data.value.as_external_typed_data.data = |
636 | static_cast<uint8_t*>(request_buffer); |
637 | c_request_data.value.as_external_typed_data.peer = request_buffer; |
638 | c_request_data.value.as_external_typed_data.callback = FreeFinalizer; |
639 | |
640 | Dart_CObject* c_request_arr[] = {&c_send_port, &c_pending_call, |
641 | &c_method_name, &c_request_data}; |
642 | Dart_CObject c_request; |
643 | c_request.type = Dart_CObject_kArray; |
644 | c_request.value.as_array.values = c_request_arr; |
645 | c_request.value.as_array.length = |
646 | sizeof(c_request_arr) / sizeof(c_request_arr[0]); |
647 | |
648 | printf("C : Dart_PostCObject_(request: %" Px ", call: %" Px ").\n" , |
649 | reinterpret_cast<intptr_t>(&c_request), |
650 | reinterpret_cast<intptr_t>(&c_pending_call)); |
651 | Dart_PostCObject_DL(send_port_, &c_request); |
652 | } |
653 | |
654 | // Simulated work for Thread #1. |
655 | // |
656 | // Simulates heavy work with sleeps. |
657 | void Work1_2() { |
658 | printf("C T1: Work1 Start.\n" ); |
659 | SleepOnAnyOS(1); |
660 | const intptr_t val1 = 3; |
661 | printf("C T1: MyCallback1(%" Pd ").\n" , val1); |
662 | const intptr_t val2 = MyCallback1(val1); // val2 = 6. |
663 | printf("C T1: MyCallback1 returned %" Pd ".\n" , val2); |
664 | SleepOnAnyOS(1); |
665 | const intptr_t val3 = val2 - 1; // val3 = 5. |
666 | printf("C T1: MyCallback2(%" Pd ").\n" , val3); |
667 | MyCallback2(val3); // Post 5 to Dart. |
668 | printf("C T1: Work1 Done.\n" ); |
669 | } |
670 | |
671 | // Simulated work for Thread #2. |
672 | // |
673 | // Simulates lighter work, no sleeps. |
674 | void Work2_2() { |
675 | printf("C T2: Work2 Start.\n" ); |
676 | const intptr_t val1 = 5; |
677 | printf("C T2: MyCallback2(%" Pd ").\n" , val1); |
678 | MyCallback2(val1); // Post 5 to Dart. |
679 | const intptr_t val2 = 1; |
680 | printf("C T2: MyCallback1(%" Pd ").\n" , val2); |
681 | const intptr_t val3 = MyCallback1(val2); // val3 = 4. |
682 | printf("C T2: MyCallback1 returned %" Pd ".\n" , val3); |
683 | printf("C T2: MyCallback2(%" Pd ").\n" , val3); |
684 | MyCallback2(val3); // Post 4 to Dart. |
685 | printf("C T2: Work2 Done.\n" ); |
686 | } |
687 | |
688 | // Simulator that simulates concurrent work with multiple threads. |
689 | class SimulateWork2 { |
690 | public: |
691 | static void StartWorkSimulator() { |
692 | running_work_simulator_ = new SimulateWork2(); |
693 | running_work_simulator_->Start(); |
694 | } |
695 | |
696 | static void StopWorkSimulator() { |
697 | running_work_simulator_->Stop(); |
698 | delete running_work_simulator_; |
699 | running_work_simulator_ = nullptr; |
700 | } |
701 | |
702 | private: |
703 | static SimulateWork2* running_work_simulator_; |
704 | |
705 | void Start() { |
706 | printf("C Da: Starting SimulateWork.\n" ); |
707 | printf("C Da: Starting worker threads.\n" ); |
708 | thread1 = new std::thread(Work1_2); |
709 | thread2 = new std::thread(Work2_2); |
710 | printf("C Da: Started SimulateWork.\n" ); |
711 | } |
712 | |
713 | void Stop() { |
714 | printf("C Da: Stopping SimulateWork.\n" ); |
715 | printf("C Da: Waiting for worker threads to finish.\n" ); |
716 | thread1->join(); |
717 | thread2->join(); |
718 | delete thread1; |
719 | delete thread2; |
720 | printf("C Da: Stopped SimulateWork.\n" ); |
721 | } |
722 | |
723 | std::thread* thread1; |
724 | std::thread* thread2; |
725 | }; |
726 | SimulateWork2* SimulateWork2::running_work_simulator_ = 0; |
727 | |
728 | DART_EXPORT void RegisterSendPort(Dart_Port send_port) { |
729 | send_port_ = send_port; |
730 | } |
731 | |
732 | DART_EXPORT void StartWorkSimulator2() { |
733 | SimulateWork2::StartWorkSimulator(); |
734 | } |
735 | |
736 | DART_EXPORT void StopWorkSimulator2() { |
737 | SimulateWork2::StopWorkSimulator(); |
738 | } |
739 | |
740 | //////////////////////////////////////////////////////////////////////////////// |
741 | // Helpers used for lightweight isolate tests. |
742 | //////////////////////////////////////////////////////////////////////////////// |
743 | |
744 | DART_EXPORT void ThreadPoolTest_BarrierSync( |
745 | Dart_Isolate (*dart_current_isolate)(), |
746 | void (*dart_enter_isolate)(Dart_Isolate), |
747 | void (*dart_exit_isolate)(), |
748 | intptr_t num_threads) { |
749 | // Guaranteed to be initialized exactly once (no race between multiple |
750 | // threads). |
751 | static std::mutex mutex; |
752 | static std::condition_variable cvar; |
753 | static intptr_t thread_count = 0; |
754 | |
755 | const Dart_Isolate isolate = dart_current_isolate(); |
756 | dart_exit_isolate(); |
757 | { |
758 | std::unique_lock<std::mutex> lock(mutex); |
759 | |
760 | ++thread_count; |
761 | if (thread_count < num_threads) { |
762 | while (thread_count < num_threads) { |
763 | cvar.wait(lock); |
764 | } |
765 | } else { |
766 | if (thread_count != num_threads) FATAL("bug" ); |
767 | cvar.notify_all(); |
768 | } |
769 | } |
770 | dart_enter_isolate(isolate); |
771 | } |
772 | |
773 | //////////////////////////////////////////////////////////////////////////////// |
774 | // Functions for handle tests. |
775 | // |
776 | // vmspecific_handle_test.dart (statically linked). |
777 | // vmspecific_handle_dynamically_linked_test.dart (dynamically linked). |
778 | |
779 | static void RunFinalizer(void* isolate_callback_data, |
780 | Dart_WeakPersistentHandle handle, |
781 | void* peer) { |
782 | printf("Running finalizer for weak handle.\n" ); |
783 | } |
784 | |
785 | // Tests that passing handles through FFI calls works, and that the FFI call |
786 | // sets up the VM state etc. correctly so that the handle API calls work. |
787 | DART_EXPORT Dart_Handle PassObjectToC(Dart_Handle h) { |
788 | // Can use "h" until this function returns. |
789 | |
790 | // A persistent handle which outlives this call. Lifetime managed in C. |
791 | auto persistent_handle = Dart_NewPersistentHandle(h); |
792 | |
793 | Dart_Handle handle_2 = Dart_HandleFromPersistent(persistent_handle); |
794 | Dart_DeletePersistentHandle(persistent_handle); |
795 | if (Dart_IsError(handle_2)) { |
796 | Dart_PropagateError(handle_2); |
797 | } |
798 | |
799 | Dart_Handle return_value; |
800 | if (!Dart_IsNull(h)) { |
801 | // A weak handle which outlives this call. Lifetime managed in C. |
802 | auto weak_handle = Dart_NewWeakPersistentHandle( |
803 | h, reinterpret_cast<void*>(0x1234), 64, RunFinalizer); |
804 | return_value = Dart_HandleFromWeakPersistent(weak_handle); |
805 | |
806 | // Deleting a weak handle is not required, it deletes itself on |
807 | // finalization. |
808 | // Deleting a weak handle cancels the finalizer. |
809 | Dart_DeleteWeakPersistentHandle(weak_handle); |
810 | } else { |
811 | return_value = h; |
812 | } |
813 | |
814 | return return_value; |
815 | } |
816 | |
817 | DART_EXPORT void ClosureCallbackThroughHandle(void (*callback)(Dart_Handle), |
818 | Dart_Handle closureHandle) { |
819 | printf("ClosureCallbackThroughHandle %p %p\n" , callback, closureHandle); |
820 | callback(closureHandle); |
821 | } |
822 | |
823 | DART_EXPORT Dart_Handle ReturnHandleInCallback(Dart_Handle (*callback)()) { |
824 | printf("ReturnHandleInCallback %p\n" , callback); |
825 | Dart_Handle handle = callback(); |
826 | if (Dart_IsError(handle)) { |
827 | printf("callback() returned an error, propagating error\n" ); |
828 | // Do C/C++ resource cleanup if needed, before propagating error. |
829 | Dart_PropagateError(handle); |
830 | } |
831 | return handle; |
832 | } |
833 | |
834 | // Recurses til `i` reaches 0. Throws some Dart_Invoke in there as well. |
835 | DART_EXPORT Dart_Handle HandleRecursion(Dart_Handle object, |
836 | Dart_Handle (*callback)(int64_t), |
837 | int64_t i) { |
838 | printf("HandleRecursion %" Pd64 "\n" , i); |
839 | const bool do_invoke = i % 3 == 0; |
840 | const bool do_gc = i % 7 == 3; |
841 | if (do_gc) { |
842 | Dart_ExecuteInternalCommand("gc-now" , nullptr); |
843 | } |
844 | Dart_Handle result; |
845 | if (do_invoke) { |
846 | Dart_Handle method_name = Dart_NewStringFromCString("a" ); |
847 | if (Dart_IsError(method_name)) { |
848 | Dart_PropagateError(method_name); |
849 | } |
850 | Dart_Handle arg = Dart_NewInteger(i - 1); |
851 | if (Dart_IsError(arg)) { |
852 | Dart_PropagateError(arg); |
853 | } |
854 | printf("Dart_Invoke\n" ); |
855 | result = Dart_Invoke(object, method_name, 1, &arg); |
856 | } else { |
857 | printf("callback\n" ); |
858 | result = callback(i - 1); |
859 | } |
860 | if (do_gc) { |
861 | Dart_ExecuteInternalCommand("gc-now" , nullptr); |
862 | } |
863 | if (Dart_IsError(result)) { |
864 | // Do C/C++ resource cleanup if needed, before propagating error. |
865 | printf("Dart_PropagateError %" Pd64 "\n" , i); |
866 | Dart_PropagateError(result); |
867 | } |
868 | printf("return %" Pd64 "\n" , i); |
869 | return result; |
870 | } |
871 | |
872 | DART_EXPORT int64_t HandleReadFieldValue(Dart_Handle handle) { |
873 | printf("HandleReadFieldValue\n" ); |
874 | Dart_Handle field_name = Dart_NewStringFromCString("a" ); |
875 | if (Dart_IsError(field_name)) { |
876 | printf("Dart_PropagateError(field_name)\n" ); |
877 | Dart_PropagateError(field_name); |
878 | } |
879 | Dart_Handle field_value = Dart_GetField(handle, field_name); |
880 | if (Dart_IsError(field_value)) { |
881 | printf("Dart_PropagateError(field_value)\n" ); |
882 | Dart_PropagateError(field_value); |
883 | } |
884 | int64_t value; |
885 | Dart_Handle err = Dart_IntegerToInt64(field_value, &value); |
886 | if (Dart_IsError(err)) { |
887 | Dart_PropagateError(err); |
888 | } |
889 | return value; |
890 | } |
891 | |
892 | // Does not have a handle in it's own signature, so does not enter and exit |
893 | // scope in the trampoline. |
894 | DART_EXPORT int64_t PropagateErrorWithoutHandle(Dart_Handle (*callback)()) { |
895 | Dart_EnterScope(); |
896 | Dart_Handle result = callback(); |
897 | if (Dart_IsError(result)) { |
898 | Dart_PropagateError(result); |
899 | } |
900 | Dart_ExitScope(); |
901 | return 0; |
902 | } |
903 | |
904 | DART_EXPORT Dart_Handle TrueHandle() { |
905 | return Dart_True(); |
906 | } |
907 | |
908 | DART_EXPORT Dart_Handle PassObjectToCUseDynamicLinking(Dart_Handle h) { |
909 | auto persistent_handle = Dart_NewPersistentHandle_DL(h); |
910 | |
911 | Dart_Handle handle_2 = Dart_HandleFromPersistent_DL(persistent_handle); |
912 | Dart_SetPersistentHandle_DL(persistent_handle, h); |
913 | Dart_DeletePersistentHandle_DL(persistent_handle); |
914 | |
915 | auto weak_handle = Dart_NewWeakPersistentHandle_DL( |
916 | handle_2, reinterpret_cast<void*>(0x1234), 64, RunFinalizer); |
917 | Dart_Handle return_value = Dart_HandleFromWeakPersistent_DL(weak_handle); |
918 | |
919 | Dart_DeleteWeakPersistentHandle_DL(weak_handle); |
920 | |
921 | return return_value; |
922 | } |
923 | |
924 | //////////////////////////////////////////////////////////////////////////////// |
925 | // Example for doing closure callbacks with help of `dart_api.h`. |
926 | // |
927 | // sample_ffi_functions_callbacks_closures.dart |
928 | |
929 | void (*callback_)(Dart_Handle); |
930 | Dart_PersistentHandle closure_to_callback_; |
931 | |
932 | DART_EXPORT void RegisterClosureCallbackFP(void (*callback)(Dart_Handle)) { |
933 | callback_ = callback; |
934 | } |
935 | |
936 | DART_EXPORT void RegisterClosureCallback(Dart_Handle h) { |
937 | closure_to_callback_ = Dart_NewPersistentHandle_DL(h); |
938 | } |
939 | |
940 | DART_EXPORT void InvokeClosureCallback() { |
941 | Dart_Handle closure_handle = |
942 | Dart_HandleFromPersistent_DL(closure_to_callback_); |
943 | callback_(closure_handle); |
944 | } |
945 | |
946 | DART_EXPORT void ReleaseClosureCallback() { |
947 | Dart_DeletePersistentHandle_DL(closure_to_callback_); |
948 | } |
949 | |
950 | } // namespace dart |
951 | |