| // Copyright (c) 2019, 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. |
| |
| // This file contains test functions for the dart:ffi test cases. |
| |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <csignal> |
| |
| #include "platform/globals.h" |
| #if defined(HOST_OS_WINDOWS) |
| #include <psapi.h> |
| #include <windows.h> |
| #else |
| #include <unistd.h> |
| #endif |
| |
| // Only OK to use here because this is test code. |
| #include <condition_variable> // NOLINT(build/c++11) |
| #include <functional> // NOLINT(build/c++11) |
| #include <mutex> // NOLINT(build/c++11) |
| #include <queue> // NOLINT(build/c++11) |
| #include <thread> // NOLINT(build/c++11) |
| |
| #include <setjmp.h> // NOLINT |
| #include <signal.h> // NOLINT |
| #include <iostream> |
| #include <limits> |
| |
| // TODO(dartbug.com/40579): This requires static linking to either link |
| // dart.exe or dart_precompiled_runtime.exe on Windows. |
| // The sample currently fails on Windows in AOT mode. |
| #include "include/dart_api.h" |
| #include "include/dart_native_api.h" |
| |
| namespace dart { |
| |
| #define CHECK(X) \ |
| if (!(X)) { \ |
| fprintf(stderr, "%s\n", "Check failed: " #X); \ |
| return 1; \ |
| } |
| |
| #define CHECK_EQ(X, Y) CHECK((X) == (Y)) |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Functions for stress-testing. |
| |
| DART_EXPORT int64_t MinInt64() { |
| Dart_ExecuteInternalCommand("gc-on-nth-allocation", |
| reinterpret_cast<void*>(1)); |
| return 0x8000000000000000; |
| } |
| |
| DART_EXPORT int64_t MinInt32() { |
| Dart_ExecuteInternalCommand("gc-on-nth-allocation", |
| reinterpret_cast<void*>(1)); |
| return 0x80000000; |
| } |
| |
| DART_EXPORT double SmallDouble() { |
| Dart_ExecuteInternalCommand("gc-on-nth-allocation", |
| reinterpret_cast<void*>(1)); |
| return 0x80000000 * -1.0; |
| } |
| |
| // Requires boxing on 32-bit and 64-bit systems, even if the top 32-bits are |
| // truncated. |
| DART_EXPORT void* LargePointer() { |
| Dart_ExecuteInternalCommand("gc-on-nth-allocation", |
| reinterpret_cast<void*>(1)); |
| uint64_t origin = 0x8100000082000000; |
| return reinterpret_cast<void*>(origin); |
| } |
| |
| DART_EXPORT void TriggerGC(uint64_t count) { |
| Dart_ExecuteInternalCommand("gc-now", nullptr); |
| } |
| |
| DART_EXPORT void CollectOnNthAllocation(intptr_t num_allocations) { |
| Dart_ExecuteInternalCommand("gc-on-nth-allocation", |
| reinterpret_cast<void*>(num_allocations)); |
| } |
| |
| // Triggers GC. Has 11 dummy arguments as unboxed odd integers which should be |
| // ignored by GC. |
| DART_EXPORT void Regress37069(uint64_t a, |
| uint64_t b, |
| uint64_t c, |
| uint64_t d, |
| uint64_t e, |
| uint64_t f, |
| uint64_t g, |
| uint64_t h, |
| uint64_t i, |
| uint64_t j, |
| uint64_t k) { |
| Dart_ExecuteInternalCommand("gc-now", nullptr); |
| } |
| |
| #if !defined(HOST_OS_WINDOWS) |
| DART_EXPORT void* UnprotectCodeOtherThread(void* isolate, |
| std::condition_variable* var, |
| std::mutex* mut) { |
| std::function<void()> callback = [&]() { |
| mut->lock(); |
| var->notify_all(); |
| mut->unlock(); |
| |
| // Wait for mutator thread to continue (and block) before leaving the |
| // safepoint. |
| while (Dart_ExecuteInternalCommand("is-mutator-in-native", isolate) != |
| nullptr) { |
| usleep(10 * 1000 /*10 ms*/); |
| } |
| }; |
| |
| struct { |
| void* isolate; |
| std::function<void()>* callback; |
| } args = {.isolate = isolate, .callback = &callback}; |
| |
| Dart_ExecuteInternalCommand("run-in-safepoint-and-rw-code", &args); |
| return nullptr; |
| } |
| |
| struct HelperThreadState { |
| std::mutex mutex; |
| std::condition_variable cvar; |
| std::unique_ptr<std::thread> helper; |
| }; |
| |
| DART_EXPORT void* TestUnprotectCode(void (*fn)(void*)) { |
| HelperThreadState* state = new HelperThreadState; |
| |
| { |
| std::unique_lock<std::mutex> lock(state->mutex); // locks the mutex |
| state->helper.reset(new std::thread(UnprotectCodeOtherThread, |
| Dart_CurrentIsolate(), &state->cvar, |
| &state->mutex)); |
| |
| state->cvar.wait(lock); |
| } |
| |
| if (fn != nullptr) { |
| fn(state); |
| return nullptr; |
| } else { |
| return state; |
| } |
| } |
| |
| DART_EXPORT void WaitForHelper(HelperThreadState* helper) { |
| helper->helper->join(); |
| delete helper; |
| } |
| #else |
| // Our version of VSC++ doesn't support std::thread yet. |
| DART_EXPORT void WaitForHelper(void* helper) {} |
| DART_EXPORT void* TestUnprotectCode(void (*fn)(void)) { |
| return nullptr; |
| } |
| #endif |
| |
| // Defined in ffi_test_functions.S. |
| // |
| // Clobbers some registers with special meaning in Dart before re-entry, for |
| // stress-testing. Not used on 32-bit Windows due to complications with Windows |
| // "safeseh". |
| #if defined(TARGET_OS_WINDOWS) && defined(HOST_ARCH_IA32) |
| void ClobberAndCall(void (*fn)()) { |
| fn(); |
| } |
| #else |
| extern "C" void ClobberAndCall(void (*fn)()); |
| #endif |
| |
| DART_EXPORT intptr_t TestGC(void (*do_gc)()) { |
| ClobberAndCall(do_gc); |
| return 0; |
| } |
| |
| struct CallbackTestData { |
| intptr_t success; |
| void (*callback)(); |
| }; |
| |
| #if defined(TARGET_OS_LINUX) |
| |
| thread_local sigjmp_buf buf; |
| void CallbackTestSignalHandler(int) { |
| siglongjmp(buf, 1); |
| } |
| |
| intptr_t ExpectAbort(void (*fn)()) { |
| fprintf(stderr, "**** EXPECT STACKTRACE TO FOLLOW. THIS IS OK. ****\n"); |
| |
| struct sigaction old_action = {}; |
| intptr_t result = __sigsetjmp(buf, /*savesigs=*/1); |
| if (result == 0) { |
| // Install signal handler. |
| struct sigaction handler = {}; |
| handler.sa_handler = CallbackTestSignalHandler; |
| sigemptyset(&handler.sa_mask); |
| handler.sa_flags = 0; |
| |
| sigaction(SIGABRT, &handler, &old_action); |
| |
| fn(); |
| } else { |
| // Caught the setjmp. |
| sigaction(SIGABRT, &old_action, NULL); |
| exit(0); |
| } |
| fprintf(stderr, "Expected abort!!!\n"); |
| exit(1); |
| } |
| |
| void* TestCallbackOnThreadOutsideIsolate(void* parameter) { |
| CallbackTestData* data = reinterpret_cast<CallbackTestData*>(parameter); |
| data->success = ExpectAbort(data->callback); |
| return NULL; |
| } |
| |
| intptr_t TestCallbackOtherThreadHelper(void* (*tester)(void*), void (*fn)()) { |
| CallbackTestData data = {1, fn}; |
| pthread_attr_t attr; |
| intptr_t result = pthread_attr_init(&attr); |
| CHECK_EQ(result, 0); |
| |
| pthread_t tid; |
| result = pthread_create(&tid, &attr, tester, &data); |
| CHECK_EQ(result, 0); |
| |
| result = pthread_attr_destroy(&attr); |
| CHECK_EQ(result, 0); |
| |
| void* retval; |
| result = pthread_join(tid, &retval); |
| |
| // Doesn't actually return because the other thread will exit when the test is |
| // finished. |
| return 1; |
| } |
| |
| // Run a callback on another thread and verify that it triggers SIGABRT. |
| DART_EXPORT intptr_t TestCallbackWrongThread(void (*fn)()) { |
| return TestCallbackOtherThreadHelper(&TestCallbackOnThreadOutsideIsolate, fn); |
| } |
| |
| // Verify that we get SIGABRT when invoking a native callback outside an |
| // isolate. |
| DART_EXPORT intptr_t TestCallbackOutsideIsolate(void (*fn)()) { |
| Dart_Isolate current = Dart_CurrentIsolate(); |
| |
| Dart_ExitIsolate(); |
| CallbackTestData data = {1, fn}; |
| TestCallbackOnThreadOutsideIsolate(&data); |
| Dart_EnterIsolate(current); |
| |
| return data.success; |
| } |
| |
| DART_EXPORT intptr_t TestCallbackWrongIsolate(void (*fn)()) { |
| return ExpectAbort(fn); |
| } |
| |
| #endif // defined(TARGET_OS_LINUX) |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Dynamic linking of dart_native_api.h for the next two samples. |
| typedef bool (*Dart_PostCObjectType)(Dart_Port port_id, Dart_CObject* message); |
| Dart_PostCObjectType Dart_PostCObject_ = nullptr; |
| |
| DART_EXPORT void RegisterDart_PostCObject( |
| Dart_PostCObjectType function_pointer) { |
| Dart_PostCObject_ = function_pointer; |
| } |
| |
| typedef Dart_Port (*Dart_NewNativePortType)(const char* name, |
| Dart_NativeMessageHandler handler, |
| bool handle_concurrently); |
| Dart_NewNativePortType Dart_NewNativePort_ = nullptr; |
| |
| DART_EXPORT void RegisterDart_NewNativePort( |
| Dart_NewNativePortType function_pointer) { |
| Dart_NewNativePort_ = function_pointer; |
| } |
| |
| typedef bool (*Dart_CloseNativePortType)(Dart_Port native_port_id); |
| Dart_CloseNativePortType Dart_CloseNativePort_ = nullptr; |
| |
| DART_EXPORT void RegisterDart_CloseNativePort( |
| Dart_CloseNativePortType function_pointer) { |
| Dart_CloseNativePort_ = function_pointer; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Functions for async callbacks example. |
| // |
| // sample_async_callback.dart |
| |
| void Fatal(char const* file, int line, char const* error) { |
| printf("FATAL %s:%i\n", file, line); |
| printf("%s\n", error); |
| Dart_DumpNativeStackTrace(NULL); |
| Dart_PrepareToAbort(); |
| abort(); |
| } |
| |
| #define FATAL(error) Fatal(__FILE__, __LINE__, error) |
| |
| void SleepOnAnyOS(intptr_t seconds) { |
| #if defined(HOST_OS_WINDOWS) |
| Sleep(1000 * seconds); |
| #else |
| sleep(seconds); |
| #endif |
| } |
| |
| intptr_t (*my_callback_blocking_fp_)(intptr_t); |
| Dart_Port my_callback_blocking_send_port_; |
| |
| void (*my_callback_non_blocking_fp_)(intptr_t); |
| Dart_Port my_callback_non_blocking_send_port_; |
| |
| typedef std::function<void()> Work; |
| |
| // Notify Dart through a port that the C lib has pending async callbacks. |
| // |
| // Expects heap allocated `work` so delete can be called on it. |
| // |
| // The `send_port` should be from the isolate which registered the callback. |
| void NotifyDart(Dart_Port send_port, const Work* work) { |
| const intptr_t work_addr = reinterpret_cast<intptr_t>(work); |
| printf("C : Posting message (port: %" Px64 ", work: %" Px ").\n", |
| send_port, work_addr); |
| |
| Dart_CObject dart_object; |
| dart_object.type = Dart_CObject_kInt64; |
| dart_object.value.as_int64 = work_addr; |
| |
| const bool result = Dart_PostCObject_(send_port, &dart_object); |
| if (!result) { |
| FATAL("C : Posting message to port failed."); |
| } |
| } |
| |
| // Do a callback to Dart in a blocking way, being interested in the result. |
| // |
| // Dart returns `a + 3`. |
| intptr_t MyCallbackBlocking(intptr_t a) { |
| std::mutex mutex; |
| std::unique_lock<std::mutex> lock(mutex); |
| intptr_t result; |
| auto callback = my_callback_blocking_fp_; // Define storage duration. |
| std::condition_variable cv; |
| bool notified = false; |
| const Work work = [a, &result, callback, &cv, ¬ified]() { |
| result = callback(a); |
| printf("C Da: Notify result ready.\n"); |
| notified = true; |
| cv.notify_one(); |
| }; |
| const Work* work_ptr = new Work(work); // Copy to heap. |
| NotifyDart(my_callback_blocking_send_port_, work_ptr); |
| printf("C : Waiting for result.\n"); |
| while (!notified) { |
| cv.wait(lock); |
| } |
| printf("C : Received result.\n"); |
| return result; |
| } |
| |
| // Do a callback to Dart in a non-blocking way. |
| // |
| // Dart sums all numbers posted to it. |
| void MyCallbackNonBlocking(intptr_t a) { |
| auto callback = my_callback_non_blocking_fp_; // Define storage duration. |
| const Work work = [a, callback]() { callback(a); }; |
| // Copy to heap to make it outlive the function scope. |
| const Work* work_ptr = new Work(work); |
| NotifyDart(my_callback_non_blocking_send_port_, work_ptr); |
| } |
| |
| // Simulated work for Thread #1. |
| // |
| // Simulates heavy work with sleeps. |
| void Work1() { |
| printf("C T1: Work1 Start.\n"); |
| SleepOnAnyOS(1); |
| const intptr_t val1 = 3; |
| printf("C T1: MyCallbackBlocking(%" Pd ").\n", val1); |
| const intptr_t val2 = MyCallbackBlocking(val1); // val2 = 6. |
| printf("C T1: MyCallbackBlocking returned %" Pd ".\n", val2); |
| SleepOnAnyOS(1); |
| const intptr_t val3 = val2 - 1; // val3 = 5. |
| printf("C T1: MyCallbackNonBlocking(%" Pd ").\n", val3); |
| MyCallbackNonBlocking(val3); // Post 5 to Dart. |
| printf("C T1: Work1 Done.\n"); |
| } |
| |
| // Simulated work for Thread #2. |
| // |
| // Simulates lighter work, no sleeps. |
| void Work2() { |
| printf("C T2: Work2 Start.\n"); |
| const intptr_t val1 = 5; |
| printf("C T2: MyCallbackNonBlocking(%" Pd ").\n", val1); |
| MyCallbackNonBlocking(val1); // Post 5 to Dart. |
| const intptr_t val2 = 1; |
| printf("C T2: MyCallbackBlocking(%" Pd ").\n", val2); |
| const intptr_t val3 = MyCallbackBlocking(val2); // val3 = 4. |
| printf("C T2: MyCallbackBlocking returned %" Pd ".\n", val3); |
| printf("C T2: MyCallbackNonBlocking(%" Pd ").\n", val3); |
| MyCallbackNonBlocking(val3); // Post 4 to Dart. |
| printf("C T2: Work2 Done.\n"); |
| } |
| |
| // Simulator that simulates concurrent work with multiple threads. |
| class SimulateWork { |
| public: |
| static void StartWorkSimulator() { |
| running_work_simulator_ = new SimulateWork(); |
| running_work_simulator_->Start(); |
| } |
| |
| static void StopWorkSimulator() { |
| running_work_simulator_->Stop(); |
| delete running_work_simulator_; |
| running_work_simulator_ = nullptr; |
| } |
| |
| private: |
| static SimulateWork* running_work_simulator_; |
| |
| void Start() { |
| printf("C Da: Starting SimulateWork.\n"); |
| printf("C Da: Starting worker threads.\n"); |
| thread1 = new std::thread(Work1); |
| thread2 = new std::thread(Work2); |
| printf("C Da: Started SimulateWork.\n"); |
| } |
| |
| void Stop() { |
| printf("C Da: Stopping SimulateWork.\n"); |
| printf("C Da: Waiting for worker threads to finish.\n"); |
| thread1->join(); |
| thread2->join(); |
| delete thread1; |
| delete thread2; |
| printf("C Da: Stopped SimulateWork.\n"); |
| } |
| |
| std::thread* thread1; |
| std::thread* thread2; |
| }; |
| SimulateWork* SimulateWork::running_work_simulator_ = 0; |
| |
| DART_EXPORT void RegisterMyCallbackBlocking(Dart_Port send_port, |
| intptr_t (*callback1)(intptr_t)) { |
| my_callback_blocking_fp_ = callback1; |
| my_callback_blocking_send_port_ = send_port; |
| } |
| |
| DART_EXPORT void RegisterMyCallbackNonBlocking(Dart_Port send_port, |
| void (*callback)(intptr_t)) { |
| my_callback_non_blocking_fp_ = callback; |
| my_callback_non_blocking_send_port_ = send_port; |
| } |
| |
| DART_EXPORT void StartWorkSimulator() { |
| SimulateWork::StartWorkSimulator(); |
| } |
| |
| DART_EXPORT void StopWorkSimulator() { |
| SimulateWork::StopWorkSimulator(); |
| } |
| |
| DART_EXPORT void ExecuteCallback(Work* work_ptr) { |
| printf("C Da: ExecuteCallback(%" Pp ").\n", |
| reinterpret_cast<intptr_t>(work_ptr)); |
| const Work work = *work_ptr; |
| work(); |
| delete work_ptr; |
| printf("C Da: ExecuteCallback done.\n"); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Functions for async callbacks example. |
| // |
| // sample_native_port_call.dart |
| |
| Dart_Port send_port_; |
| |
| static void FreeFinalizer(void*, Dart_WeakPersistentHandle, void* value) { |
| free(value); |
| } |
| |
| class PendingCall { |
| public: |
| PendingCall(void** buffer, size_t* length) |
| : response_buffer_(buffer), response_length_(length) { |
| receive_port_ = |
| Dart_NewNativePort_("cpp-response", &PendingCall::HandleResponse, |
| /*handle_concurrently=*/false); |
| } |
| ~PendingCall() { Dart_CloseNativePort_(receive_port_); } |
| |
| Dart_Port port() const { return receive_port_; } |
| |
| void PostAndWait(Dart_Port port, Dart_CObject* object) { |
| std::unique_lock<std::mutex> lock(mutex); |
| const bool success = Dart_PostCObject_(send_port_, object); |
| if (!success) FATAL("Failed to send message, invalid port or isolate died"); |
| |
| printf("C : Waiting for result.\n"); |
| while (!notified) { |
| cv.wait(lock); |
| } |
| } |
| |
| static void HandleResponse(Dart_Port p, Dart_CObject* message) { |
| if (message->type != Dart_CObject_kArray) { |
| FATAL("C : Wrong Data: message->type != Dart_CObject_kArray.\n"); |
| } |
| Dart_CObject** c_response_args = message->value.as_array.values; |
| Dart_CObject* c_pending_call = c_response_args[0]; |
| Dart_CObject* c_message = c_response_args[1]; |
| printf("C : HandleResponse (call: %" Px ", message: %" Px ").\n", |
| reinterpret_cast<intptr_t>(c_pending_call), |
| reinterpret_cast<intptr_t>(c_message)); |
| |
| auto pending_call = reinterpret_cast<PendingCall*>( |
| c_pending_call->type == Dart_CObject_kInt64 |
| ? c_pending_call->value.as_int64 |
| : c_pending_call->value.as_int32); |
| |
| pending_call->ResolveCall(c_message); |
| } |
| |
| private: |
| static bool NonEmptyBuffer(void** value) { return *value != nullptr; } |
| |
| void ResolveCall(Dart_CObject* bytes) { |
| assert(bytes->type == Dart_CObject_kTypedData); |
| if (bytes->type != Dart_CObject_kTypedData) { |
| FATAL("C : Wrong Data: bytes->type != Dart_CObject_kTypedData.\n"); |
| } |
| const intptr_t response_length = bytes->value.as_typed_data.length; |
| const uint8_t* response_buffer = bytes->value.as_typed_data.values; |
| printf("C : ResolveCall(length: %" Pd ", buffer: %" Px ").\n", |
| response_length, reinterpret_cast<intptr_t>(response_buffer)); |
| |
| void* buffer = malloc(response_length); |
| memmove(buffer, response_buffer, response_length); |
| |
| *response_buffer_ = buffer; |
| *response_length_ = response_length; |
| |
| printf("C : Notify result ready.\n"); |
| notified = true; |
| cv.notify_one(); |
| } |
| |
| std::mutex mutex; |
| std::condition_variable cv; |
| bool notified = false; |
| |
| Dart_Port receive_port_; |
| void** response_buffer_; |
| size_t* response_length_; |
| }; |
| |
| // Do a callback to Dart in a blocking way, being interested in the result. |
| // |
| // Dart returns `a + 3`. |
| uint8_t MyCallback1(uint8_t a) { |
| const char* methodname = "myCallback1"; |
| size_t request_length = sizeof(uint8_t) * 1; |
| void* request_buffer = malloc(request_length); // FreeFinalizer. |
| reinterpret_cast<uint8_t*>(request_buffer)[0] = a; // Populate buffer. |
| void* response_buffer = nullptr; |
| size_t response_length = 0; |
| |
| PendingCall pending_call(&response_buffer, &response_length); |
| |
| Dart_CObject c_send_port; |
| c_send_port.type = Dart_CObject_kSendPort; |
| c_send_port.value.as_send_port.id = pending_call.port(); |
| c_send_port.value.as_send_port.origin_id = ILLEGAL_PORT; |
| |
| Dart_CObject c_pending_call; |
| c_pending_call.type = Dart_CObject_kInt64; |
| c_pending_call.value.as_int64 = reinterpret_cast<int64_t>(&pending_call); |
| |
| Dart_CObject c_method_name; |
| c_method_name.type = Dart_CObject_kString; |
| c_method_name.value.as_string = const_cast<char*>(methodname); |
| |
| Dart_CObject c_request_data; |
| c_request_data.type = Dart_CObject_kExternalTypedData; |
| c_request_data.value.as_external_typed_data.type = Dart_TypedData_kUint8; |
| c_request_data.value.as_external_typed_data.length = request_length; |
| c_request_data.value.as_external_typed_data.data = |
| static_cast<uint8_t*>(request_buffer); |
| c_request_data.value.as_external_typed_data.peer = request_buffer; |
| c_request_data.value.as_external_typed_data.callback = FreeFinalizer; |
| |
| Dart_CObject* c_request_arr[] = {&c_send_port, &c_pending_call, |
| &c_method_name, &c_request_data}; |
| Dart_CObject c_request; |
| c_request.type = Dart_CObject_kArray; |
| c_request.value.as_array.values = c_request_arr; |
| c_request.value.as_array.length = |
| sizeof(c_request_arr) / sizeof(c_request_arr[0]); |
| |
| printf("C : Dart_PostCObject_(request: %" Px ", call: %" Px ").\n", |
| reinterpret_cast<intptr_t>(&c_request), |
| reinterpret_cast<intptr_t>(&c_pending_call)); |
| pending_call.PostAndWait(send_port_, &c_request); |
| printf("C : Received result.\n"); |
| |
| const intptr_t result = reinterpret_cast<uint8_t*>(response_buffer)[0]; |
| free(response_buffer); |
| |
| return result; |
| } |
| |
| // Do a callback to Dart in a non-blocking way. |
| // |
| // Dart sums all numbers posted to it. |
| void MyCallback2(uint8_t a) { |
| const char* methodname = "myCallback2"; |
| void* request_buffer = malloc(sizeof(uint8_t) * 1); // FreeFinalizer. |
| reinterpret_cast<uint8_t*>(request_buffer)[0] = a; // Populate buffer. |
| const size_t request_length = sizeof(uint8_t) * 1; |
| |
| Dart_CObject c_send_port; |
| c_send_port.type = Dart_CObject_kNull; |
| |
| Dart_CObject c_pending_call; |
| c_pending_call.type = Dart_CObject_kNull; |
| |
| Dart_CObject c_method_name; |
| c_method_name.type = Dart_CObject_kString; |
| c_method_name.value.as_string = const_cast<char*>(methodname); |
| |
| Dart_CObject c_request_data; |
| c_request_data.type = Dart_CObject_kExternalTypedData; |
| c_request_data.value.as_external_typed_data.type = Dart_TypedData_kUint8; |
| c_request_data.value.as_external_typed_data.length = request_length; |
| c_request_data.value.as_external_typed_data.data = |
| static_cast<uint8_t*>(request_buffer); |
| c_request_data.value.as_external_typed_data.peer = request_buffer; |
| c_request_data.value.as_external_typed_data.callback = FreeFinalizer; |
| |
| Dart_CObject* c_request_arr[] = {&c_send_port, &c_pending_call, |
| &c_method_name, &c_request_data}; |
| Dart_CObject c_request; |
| c_request.type = Dart_CObject_kArray; |
| c_request.value.as_array.values = c_request_arr; |
| c_request.value.as_array.length = |
| sizeof(c_request_arr) / sizeof(c_request_arr[0]); |
| |
| printf("C : Dart_PostCObject_(request: %" Px ", call: %" Px ").\n", |
| reinterpret_cast<intptr_t>(&c_request), |
| reinterpret_cast<intptr_t>(&c_pending_call)); |
| Dart_PostCObject_(send_port_, &c_request); |
| } |
| |
| // Simulated work for Thread #1. |
| // |
| // Simulates heavy work with sleeps. |
| void Work1_2() { |
| printf("C T1: Work1 Start.\n"); |
| SleepOnAnyOS(1); |
| const intptr_t val1 = 3; |
| printf("C T1: MyCallback1(%" Pd ").\n", val1); |
| const intptr_t val2 = MyCallback1(val1); // val2 = 6. |
| printf("C T1: MyCallback1 returned %" Pd ".\n", val2); |
| SleepOnAnyOS(1); |
| const intptr_t val3 = val2 - 1; // val3 = 5. |
| printf("C T1: MyCallback2(%" Pd ").\n", val3); |
| MyCallback2(val3); // Post 5 to Dart. |
| printf("C T1: Work1 Done.\n"); |
| } |
| |
| // Simulated work for Thread #2. |
| // |
| // Simulates lighter work, no sleeps. |
| void Work2_2() { |
| printf("C T2: Work2 Start.\n"); |
| const intptr_t val1 = 5; |
| printf("C T2: MyCallback2(%" Pd ").\n", val1); |
| MyCallback2(val1); // Post 5 to Dart. |
| const intptr_t val2 = 1; |
| printf("C T2: MyCallback1(%" Pd ").\n", val2); |
| const intptr_t val3 = MyCallback1(val2); // val3 = 4. |
| printf("C T2: MyCallback1 returned %" Pd ".\n", val3); |
| printf("C T2: MyCallback2(%" Pd ").\n", val3); |
| MyCallback2(val3); // Post 4 to Dart. |
| printf("C T2: Work2 Done.\n"); |
| } |
| |
| // Simulator that simulates concurrent work with multiple threads. |
| class SimulateWork2 { |
| public: |
| static void StartWorkSimulator() { |
| running_work_simulator_ = new SimulateWork2(); |
| running_work_simulator_->Start(); |
| } |
| |
| static void StopWorkSimulator() { |
| running_work_simulator_->Stop(); |
| delete running_work_simulator_; |
| running_work_simulator_ = nullptr; |
| } |
| |
| private: |
| static SimulateWork2* running_work_simulator_; |
| |
| void Start() { |
| printf("C Da: Starting SimulateWork.\n"); |
| printf("C Da: Starting worker threads.\n"); |
| thread1 = new std::thread(Work1_2); |
| thread2 = new std::thread(Work2_2); |
| printf("C Da: Started SimulateWork.\n"); |
| } |
| |
| void Stop() { |
| printf("C Da: Stopping SimulateWork.\n"); |
| printf("C Da: Waiting for worker threads to finish.\n"); |
| thread1->join(); |
| thread2->join(); |
| delete thread1; |
| delete thread2; |
| printf("C Da: Stopped SimulateWork.\n"); |
| } |
| |
| std::thread* thread1; |
| std::thread* thread2; |
| }; |
| SimulateWork2* SimulateWork2::running_work_simulator_ = 0; |
| |
| DART_EXPORT void RegisterSendPort(Dart_Port send_port) { |
| send_port_ = send_port; |
| } |
| |
| DART_EXPORT void StartWorkSimulator2() { |
| SimulateWork2::StartWorkSimulator(); |
| } |
| |
| DART_EXPORT void StopWorkSimulator2() { |
| SimulateWork2::StopWorkSimulator(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Helpers used for lightweight isolate tests. |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| DART_EXPORT void ThreadPoolTest_BarrierSync( |
| Dart_Isolate (*dart_current_isolate)(), |
| void (*dart_enter_isolate)(Dart_Isolate), |
| void (*dart_exit_isolate)(), |
| intptr_t num_threads) { |
| // Guaranteed to be initialized exactly once (no race between multiple |
| // threads). |
| static std::mutex mutex; |
| static std::condition_variable cvar; |
| static intptr_t thread_count = 0; |
| |
| const Dart_Isolate isolate = dart_current_isolate(); |
| dart_exit_isolate(); |
| { |
| std::unique_lock<std::mutex> lock(mutex); |
| |
| ++thread_count; |
| if (thread_count < num_threads) { |
| while (thread_count < num_threads) { |
| cvar.wait(lock); |
| } |
| } else { |
| if (thread_count != num_threads) FATAL("bug"); |
| cvar.notify_all(); |
| } |
| } |
| dart_enter_isolate(isolate); |
| } |
| |
| } // namespace dart |