blob: 543f0fe69ee5bfedc3902bb748cfa79f3092a115 [file] [log] [blame]
// 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, &notified]() {
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