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
40namespace 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
53DART_EXPORT int64_t MinInt64() {
54 Dart_ExecuteInternalCommand("gc-on-nth-allocation",
55 reinterpret_cast<void*>(1));
56 return 0x8000000000000000;
57}
58
59DART_EXPORT int64_t MinInt32() {
60 Dart_ExecuteInternalCommand("gc-on-nth-allocation",
61 reinterpret_cast<void*>(1));
62 return 0x80000000;
63}
64
65DART_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.
73DART_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
80DART_EXPORT void TriggerGC(uint64_t count) {
81 Dart_ExecuteInternalCommand("gc-now", nullptr);
82}
83
84DART_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.
91DART_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)
106DART_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
131struct HelperThreadState {
132 std::mutex mutex;
133 std::condition_variable cvar;
134 std::unique_ptr<std::thread> helper;
135};
136
137DART_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
157DART_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.
163DART_EXPORT void WaitForHelper(void* helper) {}
164DART_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)
175void ClobberAndCall(void (*fn)()) {
176 fn();
177}
178#else
179extern "C" void ClobberAndCall(void (*fn)());
180#endif
181
182DART_EXPORT intptr_t TestGC(void (*do_gc)()) {
183 ClobberAndCall(do_gc);
184 return 0;
185}
186
187struct CallbackTestData {
188 intptr_t success;
189 void (*callback)();
190};
191
192#if defined(TARGET_OS_LINUX)
193
194thread_local sigjmp_buf buf;
195void CallbackTestSignalHandler(int) {
196 siglongjmp(buf, 1);
197}
198
199intptr_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
223void* TestCallbackOnThreadOutsideIsolate(void* parameter) {
224 CallbackTestData* data = reinterpret_cast<CallbackTestData*>(parameter);
225 data->success = ExpectAbort(data->callback);
226 return NULL;
227}
228
229intptr_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.
251DART_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.
257DART_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
268DART_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`
276DART_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
285void 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
295void SleepOnAnyOS(intptr_t seconds) {
296#if defined(HOST_OS_WINDOWS)
297 Sleep(1000 * seconds);
298#else
299 sleep(seconds);
300#endif
301}
302
303intptr_t (*my_callback_blocking_fp_)(intptr_t);
304Dart_Port my_callback_blocking_send_port_;
305
306void (*my_callback_non_blocking_fp_)(intptr_t);
307Dart_Port my_callback_non_blocking_send_port_;
308
309typedef 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.
316void 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`.
334intptr_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, &notified]() {
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.
360void 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.
371void 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.
388void 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.
403class 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};
440SimulateWork* SimulateWork::running_work_simulator_ = 0;
441
442DART_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
448DART_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
454DART_EXPORT void StartWorkSimulator() {
455 SimulateWork::StartWorkSimulator();
456}
457
458DART_EXPORT void StopWorkSimulator() {
459 SimulateWork::StopWorkSimulator();
460}
461
462DART_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
476Dart_Port send_port_;
477
478static void FreeFinalizer(void*, Dart_WeakPersistentHandle, void* value) {
479 free(value);
480}
481
482class 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`.
560uint8_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.
615void 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.
657void 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.
674void 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.
689class 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};
726SimulateWork2* SimulateWork2::running_work_simulator_ = 0;
727
728DART_EXPORT void RegisterSendPort(Dart_Port send_port) {
729 send_port_ = send_port;
730}
731
732DART_EXPORT void StartWorkSimulator2() {
733 SimulateWork2::StartWorkSimulator();
734}
735
736DART_EXPORT void StopWorkSimulator2() {
737 SimulateWork2::StopWorkSimulator();
738}
739
740////////////////////////////////////////////////////////////////////////////////
741// Helpers used for lightweight isolate tests.
742////////////////////////////////////////////////////////////////////////////////
743
744DART_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
779static 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.
787DART_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
817DART_EXPORT void ClosureCallbackThroughHandle(void (*callback)(Dart_Handle),
818 Dart_Handle closureHandle) {
819 printf("ClosureCallbackThroughHandle %p %p\n", callback, closureHandle);
820 callback(closureHandle);
821}
822
823DART_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.
835DART_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
872DART_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.
894DART_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
904DART_EXPORT Dart_Handle TrueHandle() {
905 return Dart_True();
906}
907
908DART_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
929void (*callback_)(Dart_Handle);
930Dart_PersistentHandle closure_to_callback_;
931
932DART_EXPORT void RegisterClosureCallbackFP(void (*callback)(Dart_Handle)) {
933 callback_ = callback;
934}
935
936DART_EXPORT void RegisterClosureCallback(Dart_Handle h) {
937 closure_to_callback_ = Dart_NewPersistentHandle_DL(h);
938}
939
940DART_EXPORT void InvokeClosureCallback() {
941 Dart_Handle closure_handle =
942 Dart_HandleFromPersistent_DL(closure_to_callback_);
943 callback_(closure_handle);
944}
945
946DART_EXPORT void ReleaseClosureCallback() {
947 Dart_DeletePersistentHandle_DL(closure_to_callback_);
948}
949
950} // namespace dart
951