[vm] Native API: Make Dart_NewWeakPersistentHandle not auto delete

Changes Dart_NewWeakPersistentHandle to no longer auto delete the
weak persistent handle.

Changes the signatures of WeakPersistentHandleFinalizers to no longer
have access to the handle.

Flutter PR: https://github.com/flutter/engine/pull/19843
g3 presubmit: cl/318028238

Issue: https://github.com/dart-lang/sdk/issues/42312

TEST=runtime/vm/dart_api_impl_test.cc

Change-Id: I3f77db9954d9486759f903b78c03a494f73c68ba
Cq-Include-Trybots:dart/try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,app-kernel-linux-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-precomp-msan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,dart-sdk-linux-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,front-end-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-mac-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-nnbd-linux-debug-x64-try,analyzer-nnbd-linux-release-try,front-end-nnbd-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151525
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Daco Harkes 2020-11-03 10:27:44 +00:00 committed by commit-bot@chromium.org
parent 6cfd46e177
commit 5278383736
34 changed files with 1099 additions and 359 deletions

View file

@ -27,6 +27,16 @@ opted out of null safety by adding `// @dart=2.9` to the beginning of the file.
### Dart VM
* **Breaking Change** [#42312][]: `Dart_WeakPersistentHandle`s will no longer
auto-delete themselves when the referenced object is garbage collected to
avoid race conditions, but they are still automatically deleted when the
isolate group shuts down.
* **Breaking Change** [#42312][]: `Dart_WeakPersistentHandleFinalizer`
is renamed to `Dart_HandleFinalizer` and had its `handle` argument removed.
All api functions using that type have been updated.
[#42312]: https://github.com/dart-lang/sdk/issues/42312
### Dart2JS
* Removed `--no-defer-class-types` and `--no-new-deferred-split`.

View file

@ -905,11 +905,10 @@ Dart_CObject* CObject::NewUint32Array(intptr_t length) {
return cobject;
}
Dart_CObject* CObject::NewExternalUint8Array(
intptr_t length,
uint8_t* data,
void* peer,
Dart_WeakPersistentHandleFinalizer callback) {
Dart_CObject* CObject::NewExternalUint8Array(intptr_t length,
uint8_t* data,
void* peer,
Dart_HandleFinalizer callback) {
Dart_CObject* cobject = New(Dart_CObject_kExternalTypedData);
cobject->value.as_external_typed_data.type = Dart_TypedData_kUint8;
cobject->value.as_external_typed_data.length = length;
@ -937,7 +936,7 @@ Dart_CObject* CObject::NewIOBuffer(int64_t length) {
void CObject::FreeIOBufferData(Dart_CObject* cobject) {
ASSERT(cobject->type == Dart_CObject_kExternalTypedData);
cobject->value.as_external_typed_data.callback(
NULL, NULL, cobject->value.as_external_typed_data.peer);
NULL, cobject->value.as_external_typed_data.peer);
cobject->value.as_external_typed_data.data = NULL;
}

View file

@ -344,11 +344,10 @@ class CObject {
static Dart_CObject* NewArray(intptr_t length);
static Dart_CObject* NewUint8Array(intptr_t length);
static Dart_CObject* NewUint32Array(intptr_t length);
static Dart_CObject* NewExternalUint8Array(
intptr_t length,
uint8_t* data,
void* peer,
Dart_WeakPersistentHandleFinalizer callback);
static Dart_CObject* NewExternalUint8Array(intptr_t length,
uint8_t* data,
void* peer,
Dart_HandleFinalizer callback);
static Dart_CObject* NewIOBuffer(int64_t length);
static void FreeIOBufferData(Dart_CObject* object);
@ -571,7 +570,7 @@ class CObjectExternalUint8Array : public CObject {
}
uint8_t* Data() const { return cobject_->value.as_external_typed_data.data; }
void* Peer() const { return cobject_->value.as_external_typed_data.peer; }
Dart_WeakPersistentHandleFinalizer Callback() const {
Dart_HandleFinalizer Callback() const {
return cobject_->value.as_external_typed_data.callback;
}

View file

@ -475,7 +475,7 @@ DART_EXPORT void ExecuteCallback(Work* work_ptr) {
Dart_Port send_port_;
static void FreeFinalizer(void*, Dart_WeakPersistentHandle, void* value) {
static void FreeFinalizer(void*, void* value) {
free(value);
}
@ -776,9 +776,7 @@ DART_EXPORT void ThreadPoolTest_BarrierSync(
// vmspecific_handle_test.dart (statically linked).
// vmspecific_handle_dynamically_linked_test.dart (dynamically linked).
static void RunFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
static void RunFinalizer(void* isolate_callback_data, void* peer) {
printf("Running finalizer for weak handle.\n");
}

View file

@ -25,9 +25,7 @@ class IOBuffer {
static void Free(void* buffer) { free(buffer); }
// Function for finalizing external byte arrays used as IO buffers.
static void Finalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* buffer) {
static void Finalizer(void* isolate_callback_data, void* buffer) {
Free(buffer);
}

View file

@ -244,8 +244,9 @@ typedef struct _Dart_IsolateGroup* Dart_IsolateGroup;
* the object is garbage collected. It is never safe to use these handles
* unless you know the object is still reachable.
*
* WeakPersistentHandles are persistent handles which are auto deleted
* when the object is garbage collected.
* WeakPersistentHandles are persistent handles which are automatically set
* to point Dart_Null when the object is garbage collected. They are not auto
* deleted, so it is safe to use them after the object has become unreachable.
*/
typedef struct _Dart_Handle* Dart_Handle;
typedef Dart_Handle Dart_PersistentHandle;
@ -254,10 +255,6 @@ typedef struct _Dart_FinalizableHandle* Dart_FinalizableHandle;
// These structs are versioned by DART_API_DL_MAJOR_VERSION, bump the
// version when changing this struct.
typedef void (*Dart_WeakPersistentHandleFinalizer)(
void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer);
typedef void (*Dart_HandleFinalizer)(void* isolate_callback_data, void* peer);
/**
@ -423,6 +420,8 @@ DART_EXPORT Dart_Handle Dart_HandleFromPersistent(Dart_PersistentHandle object);
/**
* Allocates a handle in the current scope from a weak persistent handle.
*
* This will be a handle to Dart_Null if the object has been garbage collected.
*/
DART_EXPORT Dart_Handle
Dart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object);
@ -461,24 +460,18 @@ DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object);
/**
* Allocates a weak persistent handle for an object.
*
* This handle has the lifetime of the current isolate unless the object
* pointed to by the handle is garbage collected, in this case the VM
* automatically deletes the handle after invoking the callback associated
* with the handle. The handle can also be explicitly deallocated by
* calling Dart_DeleteWeakPersistentHandle.
* This handle has the lifetime of the current isolate. The handle can also be
* explicitly deallocated by calling Dart_DeleteWeakPersistentHandle.
*
* If the object becomes unreachable the callback is invoked with the weak
* persistent handle and the peer as arguments. The callback can be executed on
* any thread, will have an isolate group, but will not have a current isolate.
* The callback can only call Dart_DeletePersistentHandle or
* Dart_DeleteWeakPersistentHandle. The callback must not call
* Dart_DeleteWeakPersistentHandle for the handle being finalized, as it is
* automatically deleted by the VM after the callback returns. This gives the
* embedder the ability to cleanup data associated with the object and clear
* out any cached references to the handle. All references to this handle after
* the callback will be invalid. It is illegal to call into the VM with any
* other Dart_* functions from the callback. If the handle is deleted before
* the object becomes unreachable, the callback is never invoked.
* If the object becomes unreachable the callback is invoked with the peer as
* argument. The callback can be executed on any thread, will have a current
* isolate group, but will not have a current isolate. The callback can only
* call Dart_DeletePersistentHandle or Dart_DeleteWeakPersistentHandle. This
* gives the embedder the ability to cleanup data associated with the object.
* The handle will point to the Dart_Null object after the finalizer has been
* run. It is illegal to call into the VM with any other Dart_* functions from
* the callback. If the handle is deleted before the object becomes
* unreachable, the callback is never invoked.
*
* Requires there to be a current isolate.
*
@ -498,7 +491,7 @@ DART_EXPORT Dart_WeakPersistentHandle
Dart_NewWeakPersistentHandle(Dart_Handle object,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback);
Dart_HandleFinalizer callback);
/**
* Deletes the given weak persistent [object] handle.
@ -2046,7 +2039,7 @@ Dart_NewExternalLatin1String(const uint8_t* latin1_array,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback);
Dart_HandleFinalizer callback);
/**
* Returns a String which references an external array of UTF-16 encoded
@ -2067,7 +2060,7 @@ Dart_NewExternalUTF16String(const uint16_t* utf16_array,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback);
Dart_HandleFinalizer callback);
/**
* Gets the C string representation of a String.
@ -2440,13 +2433,13 @@ DART_EXPORT Dart_Handle Dart_NewExternalTypedData(Dart_TypedData_Type type,
* \return The TypedData object if no error occurs. Otherwise returns
* an error handle.
*/
DART_EXPORT Dart_Handle Dart_NewExternalTypedDataWithFinalizer(
Dart_TypedData_Type type,
void* data,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback);
DART_EXPORT Dart_Handle
Dart_NewExternalTypedDataWithFinalizer(Dart_TypedData_Type type,
void* data,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback);
/**
* Returns a ByteBuffer object for the typed data.

View file

@ -84,7 +84,7 @@ typedef void (*Dart_NativeMessageHandler_DL)(Dart_Port_DL dest_port_id,
F(Dart_DeletePersistentHandle, void, (Dart_PersistentHandle object)) \
F(Dart_NewWeakPersistentHandle, Dart_WeakPersistentHandle, \
(Dart_Handle object, void* peer, intptr_t external_allocation_size, \
Dart_WeakPersistentHandleFinalizer callback)) \
Dart_HandleFinalizer callback)) \
F(Dart_DeleteWeakPersistentHandle, void, (Dart_WeakPersistentHandle object)) \
F(Dart_UpdateExternalSize, void, \
(Dart_WeakPersistentHandle object, intptr_t external_allocation_size)) \

View file

@ -31,8 +31,7 @@
* The data for kTypedData is copied on message send and ownership remains with
* the caller. The ownership of data for kExternalTyped is passed to the VM on
* message send and returned when the VM invokes the
* Dart_WeakPersistentHandleFinalizer callback; a non-NULL callback must be
* provided.
* Dart_HandleFinalizer callback; a non-NULL callback must be provided.
*/
typedef enum {
Dart_CObject_kNull = 0,
@ -79,7 +78,7 @@ typedef struct _Dart_CObject {
intptr_t length;
uint8_t* data;
void* peer;
Dart_WeakPersistentHandleFinalizer callback;
Dart_HandleFinalizer callback;
} as_external_typed_data;
} value;
} Dart_CObject;

View file

@ -10,7 +10,7 @@
// On breaking changes the major version is increased.
// On backwards compatible changes the minor version is increased.
// The versioning covers the symbols exposed in dart_api_dl.h
#define DART_API_DL_MAJOR_VERSION 1
#define DART_API_DL_MINOR_VERSION 1
#define DART_API_DL_MAJOR_VERSION 2
#define DART_API_DL_MINOR_VERSION 0
#endif /* RUNTIME_INCLUDE_DART_VERSION_H_ */ /* NOLINT */

View file

@ -72,7 +72,7 @@ DEFINE_NATIVE_ENTRY(VMService_SendRootServiceMessage, 0, 1) {
DEFINE_NATIVE_ENTRY(VMService_SendObjectRootServiceMessage, 0, 1) {
#ifndef PRODUCT
GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(0));
return Service::HandleObjectRootMessage(message);
return Service::HandleObjectRootMessage(message);
#endif
return Object::null();
}
@ -307,16 +307,12 @@ class TarArchive {
DISALLOW_COPY_AND_ASSIGN(TarArchive);
};
static void ContentsFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
static void ContentsFinalizer(void* isolate_callback_data, void* peer) {
uint8_t* data = reinterpret_cast<uint8_t*>(peer);
delete[] data;
}
static void FilenameFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
static void FilenameFinalizer(void* isolate_callback_data, void* peer) {
char* filename = reinterpret_cast<char*>(peer);
delete[] filename;
}

View file

@ -201,9 +201,7 @@ BENCHMARK(UseDartApi) {
benchmark->set_score(elapsed_time);
}
static void NoopFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {}
static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
//
// Measure time accessing internal and external strings.

View file

@ -711,25 +711,22 @@ void FinalizablePersistentHandle::Finalize(
if (!handle->raw()->IsHeapObject()) {
return; // Free handle.
}
Dart_HandleFinalizer callback = handle->callback();
ASSERT(callback != NULL);
void* peer = handle->peer();
ApiState* state = isolate_group->api_state();
ASSERT(state != NULL);
ASSERT(handle->auto_delete());
if (handle->callback_signature_ == CallbackSignature::kHandleFinalizer) {
Dart_HandleFinalizer callback = handle->callback();
ASSERT(callback != NULL);
(*callback)(isolate_group->embedder_data(), peer);
} else {
Dart_WeakPersistentHandleFinalizer callback =
handle->CallbackWeakFinalizer();
ASSERT(callback != NULL);
Dart_WeakPersistentHandle object = handle->ApiWeakPersistentHandle();
(*callback)(isolate_group->embedder_data(), object, peer);
if (!handle->auto_delete()) {
// Clear handle before running finalizer, finalizer can free the handle.
state->ClearWeakPersistentHandle(handle);
}
state->FreeWeakPersistentHandle(handle);
(*callback)(isolate_group->embedder_data(), peer);
if (handle->auto_delete()) {
state->FreeWeakPersistentHandle(handle);
}
}
// --- Handles ---
@ -942,6 +939,9 @@ Dart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object) {
NoSafepointScope no_safepoint_scope;
FinalizablePersistentHandle* weak_ref =
FinalizablePersistentHandle::Cast(object);
if (weak_ref->IsFinalizedNotFreed()) {
return Dart_Null();
}
return Api::NewHandle(thread, weak_ref->raw());
}
@ -986,14 +986,14 @@ static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
const Object& ref,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback) {
Dart_HandleFinalizer callback) {
if (!ref.raw()->IsHeapObject()) {
return NULL;
}
FinalizablePersistentHandle* finalizable_ref =
FinalizablePersistentHandle::New(thread->isolate(), ref, peer, callback,
external_allocation_size,
/*auto_delete=*/true);
/*auto_delete=*/false);
return finalizable_ref->ApiWeakPersistentHandle();
}
@ -1002,7 +1002,7 @@ static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
Dart_Handle object,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback) {
Dart_HandleFinalizer callback) {
REUSABLE_OBJECT_HANDLESCOPE(thread);
Object& ref = thread->ObjectHandle();
ref = Api::UnwrapHandle(object);
@ -1044,7 +1044,7 @@ DART_EXPORT Dart_WeakPersistentHandle
Dart_NewWeakPersistentHandle(Dart_Handle object,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback) {
Dart_HandleFinalizer callback) {
Thread* thread = Thread::Current();
CHECK_ISOLATE(thread->isolate());
if (callback == NULL) {
@ -2961,7 +2961,7 @@ Dart_NewExternalLatin1String(const uint8_t* latin1_array,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback) {
Dart_HandleFinalizer callback) {
DARTSCOPE(Thread::Current());
API_TIMELINE_DURATION(T);
if (latin1_array == NULL && length != 0) {
@ -2983,7 +2983,7 @@ Dart_NewExternalUTF16String(const uint16_t* utf16_array,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback) {
Dart_HandleFinalizer callback) {
DARTSCOPE(Thread::Current());
if (utf16_array == NULL && length != 0) {
RETURN_NULL_ERROR(utf16_array);
@ -3933,14 +3933,13 @@ static Dart_Handle NewTypedData(Thread* thread, intptr_t cid, intptr_t length) {
return Api::NewHandle(thread, TypedData::New(cid, length));
}
static Dart_Handle NewExternalTypedData(
Thread* thread,
intptr_t cid,
void* data,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback) {
static Dart_Handle NewExternalTypedData(Thread* thread,
intptr_t cid,
void* data,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback) {
CHECK_LENGTH(length, ExternalTypedData::MaxElements(cid));
Zone* zone = thread->zone();
intptr_t bytes = length * ExternalTypedData::ElementSizeInBytes(cid);
@ -3952,19 +3951,18 @@ static Dart_Handle NewExternalTypedData(
result = ExternalTypedData::New(cid, reinterpret_cast<uint8_t*>(data), length,
thread->heap()->SpaceForExternal(bytes));
if (callback != nullptr) {
AllocateWeakPersistentHandle(thread, result, peer, external_allocation_size,
callback);
AllocateFinalizableHandle(thread, result, peer, external_allocation_size,
callback);
}
return Api::NewHandle(thread, result.raw());
}
static Dart_Handle NewExternalByteData(
Thread* thread,
void* data,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback) {
static Dart_Handle NewExternalByteData(Thread* thread,
void* data,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback) {
Zone* zone = thread->zone();
Dart_Handle ext_data =
NewExternalTypedData(thread, kExternalTypedDataUint8ArrayCid, data,
@ -4049,13 +4047,13 @@ DART_EXPORT Dart_Handle Dart_NewExternalTypedData(Dart_TypedData_Type type,
NULL);
}
DART_EXPORT Dart_Handle Dart_NewExternalTypedDataWithFinalizer(
Dart_TypedData_Type type,
void* data,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback) {
DART_EXPORT Dart_Handle
Dart_NewExternalTypedDataWithFinalizer(Dart_TypedData_Type type,
void* data,
intptr_t length,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback) {
DARTSCOPE(Thread::Current());
if (data == NULL && length != 0) {
RETURN_NULL_ERROR(data);

View file

@ -1430,9 +1430,7 @@ TEST_CASE(DartAPI_ArrayValues) {
}
}
static void NoopFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {}
static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
TEST_CASE(DartAPI_IsString) {
uint8_t latin1[] = {'o', 'n', 'e', 0xC2, 0xA2};
@ -1581,7 +1579,6 @@ TEST_CASE(DartAPI_MalformedStringToUTF8) {
}
static void ExternalStringCallbackFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
*static_cast<int*>(peer) *= 2;
}
@ -2258,9 +2255,7 @@ TEST_CASE(DartAPI_ExternalByteDataAccess) {
}
static bool byte_data_finalizer_run = false;
void ByteDataFinalizer(void* isolate_data,
Dart_WeakPersistentHandle handle,
void* peer) {
void ByteDataFinalizer(void* isolate_data, void* peer) {
ASSERT(!byte_data_finalizer_run);
free(peer);
byte_data_finalizer_run = true;
@ -2789,22 +2784,13 @@ TEST_CASE(DartAPI_ExternalUint8ClampedArrayAccess) {
EXPECT(value);
}
static void NopCallback(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {}
static void NopCallback(void* isolate_callback_data, void* peer) {}
static void UnreachedCallback(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
UNREACHABLE();
}
static void UnreachedCallback(void* isolate_callback_data, void* peer) {
UNREACHABLE();
}
static void ExternalTypedDataFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
*static_cast<int*>(peer) = 42;
}
@ -2855,22 +2841,24 @@ TEST_CASE(DartAPI_SlowFinalizer) {
EXPECT_EQ(20, count);
}
static void SlowWeakPersistentHandle(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
static void SlowWeakPersistentHandle(void* isolate_callback_data, void* peer) {
OS::Sleep(10);
intptr_t* count = reinterpret_cast<intptr_t*>(peer);
(*count)++;
}
TEST_CASE(DartAPI_SlowWeakPersistenhandle) {
Dart_WeakPersistentHandle handles[20];
intptr_t count = 0;
for (intptr_t i = 0; i < 10; i++) {
Dart_EnterScope();
Dart_Handle str1 = Dart_NewStringFromCString("Live fast");
Dart_NewWeakPersistentHandle(str1, &count, 0, SlowWeakPersistentHandle);
handles[i] =
Dart_NewWeakPersistentHandle(str1, &count, 0, SlowWeakPersistentHandle);
Dart_Handle str2 = Dart_NewStringFromCString("Die young");
Dart_NewWeakPersistentHandle(str2, &count, 0, SlowWeakPersistentHandle);
handles[i + 10] =
Dart_NewWeakPersistentHandle(str2, &count, 0, SlowWeakPersistentHandle);
Dart_ExitScope();
{
@ -2880,6 +2868,10 @@ TEST_CASE(DartAPI_SlowWeakPersistenhandle) {
}
EXPECT_EQ(20, count);
for (intptr_t i = 0; i < 20; i++) {
Dart_DeleteWeakPersistentHandle(handles[i]);
}
}
static void CheckFloat32x4Data(Dart_Handle obj) {
@ -3119,27 +3111,9 @@ static Dart_Handle AsHandle(Dart_WeakPersistentHandle weak) {
return Dart_HandleFromWeakPersistent(weak);
}
static Dart_WeakPersistentHandle weak_new_ref = NULL;
static Dart_WeakPersistentHandle weak_old_ref = NULL;
static void WeakPersistentHandleCallback(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
if (handle == weak_new_ref) {
weak_new_ref = NULL;
} else if (handle == weak_old_ref) {
weak_old_ref = NULL;
}
}
TEST_CASE(DartAPI_WeakPersistentHandle) {
Dart_Handle local_new_ref = Dart_Null();
weak_new_ref = Dart_NewWeakPersistentHandle(local_new_ref, NULL, 0,
WeakPersistentHandleCallback);
Dart_Handle local_old_ref = Dart_Null();
weak_old_ref = Dart_NewWeakPersistentHandle(local_old_ref, NULL, 0,
WeakPersistentHandleCallback);
Dart_WeakPersistentHandle weak_new_ref = nullptr;
Dart_WeakPersistentHandle weak_old_ref = nullptr;
{
Dart_EnterScope();
@ -3159,14 +3133,15 @@ TEST_CASE(DartAPI_WeakPersistentHandle) {
EXPECT_VALID(old_ref);
// Create a weak ref to the new space object.
weak_new_ref = Dart_NewWeakPersistentHandle(new_ref, NULL, 0,
WeakPersistentHandleCallback);
weak_new_ref =
Dart_NewWeakPersistentHandle(new_ref, nullptr, 0, NopCallback);
EXPECT_VALID(AsHandle(weak_new_ref));
EXPECT(!Dart_IsNull(AsHandle(weak_new_ref)));
// Create a weak ref to the old space object.
weak_old_ref = Dart_NewWeakPersistentHandle(old_ref, NULL, 0,
WeakPersistentHandleCallback);
weak_old_ref =
Dart_NewWeakPersistentHandle(old_ref, nullptr, 0, NopCallback);
EXPECT_VALID(AsHandle(weak_old_ref));
EXPECT(!Dart_IsNull(AsHandle(weak_old_ref)));
}
@ -3224,7 +3199,8 @@ TEST_CASE(DartAPI_WeakPersistentHandle) {
{
Dart_EnterScope();
// Weak ref to new space object should now be cleared.
EXPECT(weak_new_ref == NULL);
EXPECT_VALID(AsHandle(weak_new_ref));
EXPECT(Dart_IsNull(AsHandle(weak_new_ref)));
EXPECT_VALID(AsHandle(weak_old_ref));
EXPECT(!Dart_IsNull(AsHandle(weak_old_ref)));
Dart_ExitScope();
@ -3239,8 +3215,10 @@ TEST_CASE(DartAPI_WeakPersistentHandle) {
{
Dart_EnterScope();
// Weak ref to old space object should now be cleared.
EXPECT(weak_new_ref == NULL);
EXPECT(weak_old_ref == NULL);
EXPECT_VALID(AsHandle(weak_new_ref));
EXPECT(Dart_IsNull(AsHandle(weak_new_ref)));
EXPECT_VALID(AsHandle(weak_old_ref));
EXPECT(Dart_IsNull(AsHandle(weak_old_ref)));
Dart_ExitScope();
}
@ -3249,6 +3227,9 @@ TEST_CASE(DartAPI_WeakPersistentHandle) {
// Garbage collect one last time to revisit deleted handles.
GCTestHelper::CollectAllGarbage();
}
Dart_DeleteWeakPersistentHandle(weak_new_ref);
Dart_DeleteWeakPersistentHandle(weak_old_ref);
}
static Dart_FinalizableHandle finalizable_new_ref = nullptr;
@ -3384,7 +3365,7 @@ TEST_CASE(DartAPI_WeakPersistentHandleErrors) {
Dart_Handle obj2 = Dart_NewInteger(0);
EXPECT_VALID(obj2);
Dart_WeakPersistentHandle ref2 =
Dart_NewWeakPersistentHandle(obj2, NULL, 0, WeakPersistentHandleCallback);
Dart_NewWeakPersistentHandle(obj2, NULL, 0, NopCallback);
EXPECT_EQ(ref2, static_cast<void*>(NULL));
Dart_ExitScope();
@ -3416,17 +3397,12 @@ static Dart_WeakPersistentHandle weak_persistent_handle3;
static void WeakPersistentHandlePeerCleanupFinalizer(
void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
Dart_DeletePersistentHandle(persistent_handle1);
Dart_DeleteWeakPersistentHandle(weak_persistent_handle2);
*static_cast<int*>(peer) = 42;
}
static void WeakPersistentHandleNoopCallback(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {}
TEST_CASE(DartAPI_WeakPersistentHandleCleanupFinalizer) {
Heap* heap = Isolate::Current()->heap();
@ -3437,8 +3413,8 @@ TEST_CASE(DartAPI_WeakPersistentHandleCleanupFinalizer) {
persistent_handle1 = Dart_NewPersistentHandle(ref1);
Dart_Handle ref2 = Dart_NewStringFromCString(kTestString1);
int peer2 = 0;
weak_persistent_handle2 = Dart_NewWeakPersistentHandle(
ref2, &peer2, 0, WeakPersistentHandleNoopCallback);
weak_persistent_handle2 =
Dart_NewWeakPersistentHandle(ref2, &peer2, 0, NopCallback);
int peer3 = 0;
{
Dart_EnterScope();
@ -3454,6 +3430,8 @@ TEST_CASE(DartAPI_WeakPersistentHandleCleanupFinalizer) {
EXPECT(peer3 == 42);
}
Dart_ExitScope();
Dart_DeleteWeakPersistentHandle(weak_persistent_handle3);
}
static Dart_FinalizableHandle finalizable_handle3;
@ -3495,7 +3473,6 @@ TEST_CASE(DartAPI_FinalizableHandleCleanupFinalizer) {
}
static void WeakPersistentHandlePeerFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
*static_cast<int*>(peer) = 42;
}
@ -3520,6 +3497,7 @@ TEST_CASE(DartAPI_WeakPersistentHandleCallback) {
GCTestHelper::CollectNewSpace();
EXPECT(peer == 42);
}
Dart_DeleteWeakPersistentHandle(weak_ref);
}
static void FinalizableHandlePeerFinalizer(void* isolate_callback_data,
@ -3599,13 +3577,53 @@ TEST_CASE(DartAPI_FinalizableHandleNoCallback) {
Dart_WeakPersistentHandle delete_on_finalization;
static void DeleteWeakHandleOnFinalization(void* isolate_callback_data,
void* peer) {
*static_cast<int*>(peer) = 42;
Dart_DeleteWeakPersistentHandle(delete_on_finalization);
delete_on_finalization = nullptr;
}
static void DontDeleteWeakHandleOnFinalization(void* isolate_callback_data,
void* peer) {
*static_cast<int*>(peer) = 42;
delete_on_finalization = nullptr;
}
// Mimicking the old handle behavior by deleting the handle itself in the
// finalizer.
TEST_CASE(DartAPI_WeakPersistentHandleCallbackSelfDelete) {
int peer = 0;
{
Dart_EnterScope();
Dart_Handle obj = NewString("new string");
EXPECT_VALID(obj);
delete_on_finalization = Dart_NewWeakPersistentHandle(
obj, &peer, 0, DeleteWeakHandleOnFinalization);
EXPECT_VALID(AsHandle(delete_on_finalization));
EXPECT(peer == 0);
Dart_ExitScope();
}
{
TransitionNativeToVM transition(thread);
GCTestHelper::CollectOldSpace();
EXPECT(peer == 0);
GCTestHelper::CollectNewSpace();
EXPECT(peer == 42);
ASSERT(delete_on_finalization == nullptr);
}
}
// Checking that the finalizer gets run on shutdown, but that the delete
// handle does not get invoked. (The handles have already been deleted by
// releasing the LocalApiState.)
VM_UNIT_TEST_CASE(DartAPI_WeakPersistentHandlesCallbackShutdown) {
TestCase::CreateTestIsolate();
Dart_EnterScope();
Dart_Handle ref = Dart_True();
int peer = 1234;
Dart_NewWeakPersistentHandle(ref, &peer, 0,
WeakPersistentHandlePeerFinalizer);
delete_on_finalization = Dart_NewWeakPersistentHandle(
ref, &peer, 0, DontDeleteWeakHandleOnFinalization);
Dart_ExitScope();
Dart_ShutdownIsolate();
EXPECT(peer == 42);
@ -3668,6 +3686,8 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSize) {
GCTestHelper::CollectOldSpace();
EXPECT(heap->ExternalInWords(Heap::kOld) == 0);
}
Dart_DeleteWeakPersistentHandle(weak1);
Dart_DeleteWeakPersistentHandle(weak2);
}
TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSize) {
@ -3827,7 +3847,8 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOldspaceGC) {
}
// Large enough to trigger GC in old space. Not actually allocated.
const intptr_t kHugeExternalSize = (kWordSize == 4) ? 513 * MB : 1025 * MB;
Dart_NewWeakPersistentHandle(live, NULL, kHugeExternalSize, NopCallback);
Dart_WeakPersistentHandle weak2 =
Dart_NewWeakPersistentHandle(live, NULL, kHugeExternalSize, NopCallback);
{
TransitionNativeToVM transition(thread);
GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size.
@ -3836,6 +3857,8 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOldspaceGC) {
isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
}
Dart_ExitScope();
Dart_DeleteWeakPersistentHandle(weak);
Dart_DeleteWeakPersistentHandle(weak2);
}
TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeOldspaceGC) {
@ -3984,13 +4007,11 @@ static Dart_NativeFunction ExampleResourceNativeResolver(
}
struct ExampleResource {
Dart_WeakPersistentHandle self;
Dart_FinalizableHandle self;
void* lots_of_memory;
};
void ExampleResourceFinalizer(void* isolate_peer,
Dart_WeakPersistentHandle handle,
void* peer) {
void ExampleResourceFinalizer(void* isolate_peer, void* peer) {
ExampleResource* resource = reinterpret_cast<ExampleResource*>(peer);
free(resource->lots_of_memory);
delete resource;
@ -4001,8 +4022,8 @@ void FUNCTION_NAME(ExampleResource_Allocate)(Dart_NativeArguments native_args) {
intptr_t external_size = 10 * MB;
ExampleResource* resource = new ExampleResource();
resource->lots_of_memory = malloc(external_size);
resource->self = Dart_NewWeakPersistentHandle(
receiver, resource, external_size, ExampleResourceFinalizer);
resource->self = Dart_NewFinalizableHandle(receiver, resource, external_size,
ExampleResourceFinalizer);
EXPECT_VALID(Dart_SetNativeInstanceField(
receiver, 0, reinterpret_cast<intptr_t>(resource)));
// Some pretend resource initialization.
@ -4032,7 +4053,7 @@ void FUNCTION_NAME(ExampleResource_Dispose)(Dart_NativeArguments native_args) {
if (resource->lots_of_memory != nullptr) {
free(resource->lots_of_memory);
resource->lots_of_memory = nullptr;
Dart_UpdateExternalSize(resource->self, 0);
Dart_UpdateFinalizableExternalSize(resource->self, receiver, 0);
}
}
@ -4072,13 +4093,12 @@ static Dart_WeakPersistentHandle weak2 = NULL;
static Dart_WeakPersistentHandle weak3 = NULL;
static void ImplicitReferencesCallback(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
if (handle == weak1) {
if (peer == &weak1) {
weak1 = NULL;
} else if (handle == weak2) {
} else if (peer == &weak2) {
weak2 = NULL;
} else if (handle == weak3) {
} else if (peer == &weak3) {
weak3 = NULL;
}
}
@ -4103,19 +4123,19 @@ TEST_CASE(DartAPI_ImplicitReferencesOldSpace) {
weak1 =
Dart_NewWeakPersistentHandle(AllocateOldString("weakly reachable 1"),
NULL, 0, ImplicitReferencesCallback);
&weak1, 0, ImplicitReferencesCallback);
EXPECT(!Dart_IsNull(AsHandle(weak1)));
EXPECT_VALID(AsHandle(weak1));
weak2 =
Dart_NewWeakPersistentHandle(AllocateOldString("weakly reachable 2"),
NULL, 0, ImplicitReferencesCallback);
&weak2, 0, ImplicitReferencesCallback);
EXPECT(!Dart_IsNull(AsHandle(weak2)));
EXPECT_VALID(AsHandle(weak2));
weak3 =
Dart_NewWeakPersistentHandle(AllocateOldString("weakly reachable 3"),
NULL, 0, ImplicitReferencesCallback);
&weak3, 0, ImplicitReferencesCallback);
EXPECT(!Dart_IsNull(AsHandle(weak3)));
EXPECT_VALID(AsHandle(weak3));
}
@ -4144,6 +4164,11 @@ TEST_CASE(DartAPI_ImplicitReferencesOldSpace) {
EXPECT(!Dart_IsNull(AsHandle(weak3)));
Dart_ExitScope();
}
Dart_DeleteWeakPersistentHandle(strong_weak);
Dart_DeleteWeakPersistentHandle(weak1);
Dart_DeleteWeakPersistentHandle(weak2);
Dart_DeleteWeakPersistentHandle(weak3);
}
TEST_CASE(DartAPI_ImplicitReferencesNewSpace) {
@ -4166,19 +4191,19 @@ TEST_CASE(DartAPI_ImplicitReferencesNewSpace) {
weak1 =
Dart_NewWeakPersistentHandle(AllocateNewString("weakly reachable 1"),
NULL, 0, ImplicitReferencesCallback);
&weak1, 0, ImplicitReferencesCallback);
EXPECT(!Dart_IsNull(AsHandle(weak1)));
EXPECT_VALID(AsHandle(weak1));
weak2 =
Dart_NewWeakPersistentHandle(AllocateNewString("weakly reachable 2"),
NULL, 0, ImplicitReferencesCallback);
&weak2, 0, ImplicitReferencesCallback);
EXPECT(!Dart_IsNull(AsHandle(weak2)));
EXPECT_VALID(AsHandle(weak2));
weak3 =
Dart_NewWeakPersistentHandle(AllocateNewString("weakly reachable 3"),
NULL, 0, ImplicitReferencesCallback);
&weak3, 0, ImplicitReferencesCallback);
EXPECT(!Dart_IsNull(AsHandle(weak3)));
EXPECT_VALID(AsHandle(weak3));
}
@ -4206,6 +4231,11 @@ TEST_CASE(DartAPI_ImplicitReferencesNewSpace) {
EXPECT(!Dart_IsNull(AsHandle(weak3)));
Dart_ExitScope();
}
Dart_DeleteWeakPersistentHandle(strong_weak);
Dart_DeleteWeakPersistentHandle(weak1);
Dart_DeleteWeakPersistentHandle(weak2);
Dart_DeleteWeakPersistentHandle(weak3);
}
// Unit test for creating multiple scopes and local handles within them.
@ -7379,9 +7409,7 @@ TEST_CASE(DartAPI_IllegalPost) {
EXPECT(!success);
}
static void UnreachableFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
static void UnreachableFinalizer(void* isolate_callback_data, void* peer) {
UNREACHABLE();
}
@ -8970,10 +8998,7 @@ TEST_CASE(DartAPI_InvokeVMServiceMethod) {
Dart_Handle bytes = Dart_NewExternalTypedDataWithFinalizer(
Dart_TypedData_kUint8, response_json, response_json_length, response_json,
response_json_length,
[](void* ignored, Dart_WeakPersistentHandle handle, void* peer) {
free(peer);
});
response_json_length, [](void* ignored, void* peer) { free(peer); });
EXPECT_VALID(bytes);
// We don't have a C++ JSON decoder so we'll invoke dart to validate the

View file

@ -1124,7 +1124,7 @@ bool ApiMessageWriter::WriteCObjectInlined(Dart_CObject* object,
}
uint8_t* data = object->value.as_external_typed_data.data;
void* peer = object->value.as_external_typed_data.peer;
Dart_WeakPersistentHandleFinalizer callback =
Dart_HandleFinalizer callback =
object->value.as_external_typed_data.callback;
if (callback == NULL) {
return false;

View file

@ -190,23 +190,6 @@ class PersistentHandle {
// dart API.
class FinalizablePersistentHandle {
public:
// TODO(http://dartbug.com/42312): Delete this on migrating signature
// Dart_NewWeakPersistentHandle to Dart_HandleFinalizer.
enum class CallbackSignature {
// Uses a Dart_WeakPersistentHandleFinalizer.
kWeakPersistentHandleFinalizer = 0,
// Uses a Dart_HandleFinalizer.
kHandleFinalizer = 1,
};
static FinalizablePersistentHandle* New(
Isolate* isolate,
const Object& object,
void* peer,
Dart_WeakPersistentHandleFinalizer callback,
intptr_t external_size,
bool auto_delete);
static FinalizablePersistentHandle* New(Isolate* isolate,
const Object& object,
void* peer,
@ -221,18 +204,7 @@ class FinalizablePersistentHandle {
return OFFSET_OF(FinalizablePersistentHandle, raw_);
}
void* peer() const { return peer_; }
Dart_WeakPersistentHandleFinalizer CallbackWeakFinalizer() const {
ASSERT(callback_signature_ ==
CallbackSignature::kWeakPersistentHandleFinalizer);
return callback_.weak_persistent;
}
Dart_HandleFinalizer callback() const {
ASSERT(callback_signature_ == CallbackSignature::kHandleFinalizer);
return callback_.finalizable;
}
uword callback_address() const {
return reinterpret_cast<uword>(callback_.finalizable);
}
Dart_HandleFinalizer callback() const { return callback_; }
Dart_WeakPersistentHandle ApiWeakPersistentHandle() {
return reinterpret_cast<Dart_WeakPersistentHandle>(this);
}
@ -242,6 +214,10 @@ class FinalizablePersistentHandle {
bool auto_delete() const { return auto_delete_; }
bool IsFinalizedNotFreed() const {
return raw_ == static_cast<ObjectPtr>(reinterpret_cast<uword>(this));
}
intptr_t external_size() const {
return ExternalSizeInWordsBits::decode(external_data_) * kWordSize;
}
@ -298,15 +274,6 @@ class FinalizablePersistentHandle {
kExternalSizeBitsSize = (kBitsPerWord - 1),
};
union HandleFinalizer {
Dart_HandleFinalizer finalizable;
Dart_WeakPersistentHandleFinalizer weak_persistent;
HandleFinalizer(Dart_HandleFinalizer finalizer) : finalizable(finalizer) {}
HandleFinalizer(Dart_WeakPersistentHandleFinalizer finalizer)
: weak_persistent(finalizer) {}
HandleFinalizer() : finalizable(nullptr) {}
};
// This part of external_data_ is the number of externally allocated bytes.
class ExternalSizeInWordsBits : public BitField<uword,
intptr_t,
@ -320,10 +287,7 @@ class FinalizablePersistentHandle {
friend class FinalizablePersistentHandles;
FinalizablePersistentHandle()
: raw_(nullptr),
peer_(NULL),
external_data_(0),
callback_(HandleFinalizer()) {}
: raw_(nullptr), peer_(NULL), external_data_(0), callback_(NULL) {}
~FinalizablePersistentHandle() {}
static void Finalize(IsolateGroup* isolate_group,
@ -340,6 +304,11 @@ class FinalizablePersistentHandle {
ASSERT(!raw_->IsHeapObject());
}
void SetFinalizedNotFreed() {
// `handle->raw_ != Object::null()` or the GC will finalize again.
SetNext(this);
}
void FreeHandle(FinalizablePersistentHandle* free_list) {
Clear();
SetNext(free_list);
@ -347,11 +316,10 @@ class FinalizablePersistentHandle {
void Clear() {
raw_ = Object::null();
peer_ = NULL;
peer_ = nullptr;
external_data_ = 0;
callback_ = HandleFinalizer();
callback_ = nullptr;
auto_delete_ = false;
callback_signature_ = CallbackSignature::kWeakPersistentHandleFinalizer;
}
void set_raw(ObjectPtr raw) { raw_ = raw; }
@ -360,11 +328,7 @@ class FinalizablePersistentHandle {
void set_peer(void* peer) { peer_ = peer; }
void set_callback_signature(CallbackSignature callback_signature) {
callback_signature_ = callback_signature;
}
void set_callback(HandleFinalizer callback) { callback_ = callback; }
void set_callback(Dart_HandleFinalizer callback) { callback_ = callback; }
void set_auto_delete(bool auto_delete) { auto_delete_ = auto_delete; }
@ -396,9 +360,8 @@ class FinalizablePersistentHandle {
ObjectPtr raw_;
void* peer_;
uword external_data_;
HandleFinalizer callback_;
Dart_HandleFinalizer callback_;
bool auto_delete_;
CallbackSignature callback_signature_;
DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods.
DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandle);
@ -608,6 +571,11 @@ class FinalizablePersistentHandles
return handle;
}
void ClearHandle(FinalizablePersistentHandle* handle) {
handle->Clear();
handle->SetFinalizedNotFreed();
}
void FreeHandle(FinalizablePersistentHandle* handle) {
handle->FreeHandle(free_list());
set_free_list(handle);
@ -805,7 +773,10 @@ class ApiState {
MutexLocker ml(&mutex_);
return weak_persistent_handles_.AllocateHandle();
}
void ClearWeakPersistentHandle(FinalizablePersistentHandle* weak_ref) {
MutexLocker ml(&mutex_);
weak_persistent_handles_.ClearHandle(weak_ref);
}
void FreeWeakPersistentHandle(FinalizablePersistentHandle* weak_ref) {
MutexLocker ml(&mutex_);
weak_persistent_handles_.FreeHandle(weak_ref);
@ -892,28 +863,6 @@ class ApiState {
DISALLOW_COPY_AND_ASSIGN(ApiState);
};
inline FinalizablePersistentHandle* FinalizablePersistentHandle::New(
Isolate* isolate,
const Object& object,
void* peer,
Dart_WeakPersistentHandleFinalizer callback,
intptr_t external_size,
bool auto_delete) {
ApiState* state = isolate->group()->api_state();
ASSERT(state != NULL);
ASSERT(callback != NULL);
FinalizablePersistentHandle* ref = state->AllocateWeakPersistentHandle();
ref->set_raw(object);
ref->set_peer(peer);
ref->set_callback_signature(
CallbackSignature::kWeakPersistentHandleFinalizer);
ref->set_callback(HandleFinalizer(callback));
ref->set_auto_delete(auto_delete);
// This may trigger GC, so it must be called last.
ref->SetExternalSize(external_size, isolate->group());
return ref;
}
inline FinalizablePersistentHandle* FinalizablePersistentHandle::New(
Isolate* isolate,
const Object& object,
@ -926,8 +875,7 @@ inline FinalizablePersistentHandle* FinalizablePersistentHandle::New(
FinalizablePersistentHandle* ref = state->AllocateWeakPersistentHandle();
ref->set_raw(object);
ref->set_peer(peer);
ref->set_callback_signature(CallbackSignature::kHandleFinalizer);
ref->set_callback(HandleFinalizer(callback));
ref->set_callback(callback);
ref->set_auto_delete(auto_delete);
// This may trigger GC, so it must be called last.
ref->SetExternalSize(external_size, isolate->group());

View file

@ -14,8 +14,8 @@ namespace dart {
struct FinalizableData {
void* data;
void* peer;
Dart_WeakPersistentHandleFinalizer callback;
Dart_WeakPersistentHandleFinalizer successful_write_callback;
Dart_HandleFinalizer callback;
Dart_HandleFinalizer successful_write_callback;
};
class MessageFinalizableData {
@ -25,19 +25,18 @@ class MessageFinalizableData {
~MessageFinalizableData() {
for (intptr_t i = take_position_; i < records_.length(); i++) {
records_[i].callback(nullptr, nullptr, records_[i].peer);
records_[i].callback(nullptr, records_[i].peer);
}
}
/// If [successful_write_callback] is provided, it's invoked when message
/// was serialized successfully.
/// [callback] is invoked when serialization failed.
void Put(
intptr_t external_size,
void* data,
void* peer,
Dart_WeakPersistentHandleFinalizer callback,
Dart_WeakPersistentHandleFinalizer successful_write_callback = nullptr) {
void Put(intptr_t external_size,
void* data,
void* peer,
Dart_HandleFinalizer callback,
Dart_HandleFinalizer successful_write_callback = nullptr) {
FinalizableData finalizable_data;
finalizable_data.data = data;
finalizable_data.peer = peer;
@ -64,8 +63,7 @@ class MessageFinalizableData {
void SerializationSucceeded() {
for (intptr_t i = 0; i < records_.length(); i++) {
if (records_[i].successful_write_callback != nullptr) {
records_[i].successful_write_callback(nullptr, nullptr,
records_[i].peer);
records_[i].successful_write_callback(nullptr, records_[i].peer);
}
}
}

View file

@ -77,9 +77,7 @@ ISOLATE_UNIT_TEST_CASE(AllocateScopeHandle) {
EXPECT_EQ(handle_count, VMHandles::ScopedHandleCount());
}
static void NoopCallback(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {}
static void NoopCallback(void* isolate_callback_data, void* peer) {}
// Unit test for handle validity checks.
TEST_CASE(CheckHandleValidity) {

View file

@ -184,9 +184,7 @@ void JSONStream::PostNullReply(Dart_Port port) {
Message::New(port, Object::null(), Message::kNormalPriority));
}
static void Finalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* buffer) {
static void Finalizer(void* isolate_callback_data, void* buffer) {
free(buffer);
}

View file

@ -401,9 +401,7 @@ static void ReleaseFilesPairs(const Dart_CObject& files) {
delete[] files.value.as_array.values;
}
static void PassThroughFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {}
static void PassThroughFinalizer(void* isolate_callback_data, void* peer) {}
MallocGrowableArray<char*>* KernelIsolate::experimental_flags_ =
new MallocGrowableArray<char*>();

View file

@ -21845,7 +21845,7 @@ StringPtr String::NewExternal(const uint8_t* characters,
intptr_t len,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback,
Dart_HandleFinalizer callback,
Heap::Space space) {
return ExternalOneByteString::New(characters, len, peer,
external_allocation_size, callback, space);
@ -21855,7 +21855,7 @@ StringPtr String::NewExternal(const uint16_t* characters,
intptr_t len,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback,
Dart_HandleFinalizer callback,
Heap::Space space) {
return ExternalTwoByteString::New(characters, len, peer,
external_allocation_size, callback, space);
@ -22239,11 +22239,10 @@ void String::ToUTF8(uint8_t* utf8_array, intptr_t array_len) const {
Utf8::Encode(*this, reinterpret_cast<char*>(utf8_array), array_len);
}
static FinalizablePersistentHandle* AddFinalizer(
const Object& referent,
void* peer,
Dart_WeakPersistentHandleFinalizer callback,
intptr_t external_size) {
static FinalizablePersistentHandle* AddFinalizer(const Object& referent,
void* peer,
Dart_HandleFinalizer callback,
intptr_t external_size) {
ASSERT(callback != NULL);
return FinalizablePersistentHandle::New(Isolate::Current(), referent, peer,
callback, external_size,
@ -22856,7 +22855,7 @@ ExternalOneByteStringPtr ExternalOneByteString::New(
intptr_t len,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback,
Dart_HandleFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_one_byte_string_class() !=
Class::null());
@ -22885,7 +22884,7 @@ ExternalTwoByteStringPtr ExternalTwoByteString::New(
intptr_t len,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback,
Dart_HandleFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_two_byte_string_class() !=
Class::null());
@ -23658,7 +23657,7 @@ const char* TypedData::ToCString() const {
FinalizablePersistentHandle* ExternalTypedData::AddFinalizer(
void* peer,
Dart_WeakPersistentHandleFinalizer callback,
Dart_HandleFinalizer callback,
intptr_t external_size) const {
return dart::AddFinalizer(*this, peer, callback, external_size);
}
@ -23696,10 +23695,7 @@ ExternalTypedDataPtr ExternalTypedData::NewFinalizeWithFree(uint8_t* data,
ExternalTypedData& result = ExternalTypedData::Handle(ExternalTypedData::New(
kExternalTypedDataUint8ArrayCid, data, len, Heap::kOld));
result.AddFinalizer(
data,
[](void* isolate_callback_data, Dart_WeakPersistentHandle handle,
void* data) { free(data); },
len);
data, [](void* isolate_callback_data, void* data) { free(data); }, len);
return result.raw();
}

View file

@ -8765,7 +8765,7 @@ class String : public Instance {
intptr_t array_len,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback,
Dart_HandleFinalizer callback,
Heap::Space = Heap::kNew);
// Creates a new External String object using the specified array of
@ -8774,7 +8774,7 @@ class String : public Instance {
intptr_t array_len,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback,
Dart_HandleFinalizer callback,
Heap::Space = Heap::kNew);
static void Copy(const String& dst,
@ -9214,13 +9214,12 @@ class ExternalOneByteString : public AllStatic {
return String::RoundedAllocationSize(sizeof(ExternalOneByteStringLayout));
}
static ExternalOneByteStringPtr New(
const uint8_t* characters,
intptr_t len,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback,
Heap::Space space);
static ExternalOneByteStringPtr New(const uint8_t* characters,
intptr_t len,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback,
Heap::Space space);
static ExternalOneByteStringPtr null() {
return static_cast<ExternalOneByteStringPtr>(Object::null());
@ -9313,13 +9312,12 @@ class ExternalTwoByteString : public AllStatic {
return String::RoundedAllocationSize(sizeof(ExternalTwoByteStringLayout));
}
static ExternalTwoByteStringPtr New(
const uint16_t* characters,
intptr_t len,
void* peer,
intptr_t external_allocation_size,
Dart_WeakPersistentHandleFinalizer callback,
Heap::Space space = Heap::kNew);
static ExternalTwoByteStringPtr New(const uint16_t* characters,
intptr_t len,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback,
Heap::Space space = Heap::kNew);
static ExternalTwoByteStringPtr null() {
return static_cast<ExternalTwoByteStringPtr>(Object::null());
@ -10103,10 +10101,9 @@ class ExternalTypedData : public TypedDataBase {
#undef TYPED_GETTER_SETTER
FinalizablePersistentHandle* AddFinalizer(
void* peer,
Dart_WeakPersistentHandleFinalizer callback,
intptr_t external_size) const;
FinalizablePersistentHandle* AddFinalizer(void* peer,
Dart_HandleFinalizer callback,
intptr_t external_size) const;
static intptr_t data_offset() {
return OFFSET_OF(ExternalTypedDataLayout, data_);

View file

@ -990,7 +990,7 @@ class Pass2Visitor : public ObjectVisitor,
writer_->WriteUnsigned(weak_persistent_handle->external_size());
// Attempt to include a native symbol name.
auto const name = NativeSymbolResolver::LookupSymbolName(
weak_persistent_handle->callback_address(), nullptr);
reinterpret_cast<uword>(weak_persistent_handle->callback()), nullptr);
writer_->WriteUtf8((name == nullptr) ? "Unknown native function" : name);
if (name != nullptr) {
NativeSymbolResolver::FreeSymbolName(name);

View file

@ -142,9 +142,7 @@ ISOLATE_UNIT_TEST_CASE(ObjectGraph) {
}
}
static void WeakHandleFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {}
static void WeakHandleFinalizer(void* isolate_callback_data, void* peer) {}
ISOLATE_UNIT_TEST_CASE(RetainingPathGCRoot) {
Dart_PersistentHandle persistent_handle;

View file

@ -1624,9 +1624,7 @@ ISOLATE_UNIT_TEST_CASE(StringEqualsUTF32) {
EXPECT(!th_str.Equals(chars, 3));
}
static void NoopFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {}
static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
ISOLATE_UNIT_TEST_CASE(ExternalOneByteString) {
uint8_t characters[] = {0xF6, 0xF1, 0xE9};

View file

@ -1374,7 +1374,6 @@ ExternalTypedDataPtr ExternalTypedData::ReadFrom(SnapshotReader* reader,
// This function's name can appear in Observatory.
static void IsolateMessageTypedDataFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* buffer) {
free(buffer);
}
@ -1707,9 +1706,9 @@ void TransferableTypedDataLayout::WriteTo(SnapshotWriter* writer,
length, data, tpeer,
// Finalizer does nothing - in case of failure to serialize,
// [data] remains wrapped in sender's [TransferableTypedData].
[](void* data, Dart_WeakPersistentHandle handle, void* peer) {},
[](void* data, void* peer) {},
// This is invoked on successful serialization of the message
[](void* data, Dart_WeakPersistentHandle handle, void* peer) {
[](void* data, void* peer) {
TransferableTypedDataPeer* tpeer =
reinterpret_cast<TransferableTypedDataPeer*>(peer);
tpeer->handle()->EnsureFreedExternal(IsolateGroup::Current());

View file

@ -64,9 +64,7 @@ ISOLATE_UNIT_TEST_CASE(RegExp_TwoByteString) {
EXPECT_EQ(3, smi_2.Value());
}
static void NoopFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {}
static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
ISOLATE_UNIT_TEST_CASE(RegExp_ExternalOneByteString) {
uint8_t chars[] = {'a', 'b', 'c', 'b', 'a'};

View file

@ -974,9 +974,7 @@ ErrorPtr Service::HandleIsolateMessage(Isolate* isolate, const Array& msg) {
return MaybePause(isolate, error);
}
static void Finalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* buffer) {
static void Finalizer(void* isolate_callback_data, void* buffer) {
free(buffer);
}
@ -4356,11 +4354,12 @@ class PersistentHandleVisitor : public HandleVisitor {
obj.AddPropertyF(
"peer", "0x%" Px "",
reinterpret_cast<uintptr_t>(weak_persistent_handle->peer()));
obj.AddPropertyF("callbackAddress", "0x%" Px "",
weak_persistent_handle->callback_address());
obj.AddPropertyF(
"callbackAddress", "0x%" Px "",
reinterpret_cast<uintptr_t>(weak_persistent_handle->callback()));
// Attempt to include a native symbol name.
char* name = NativeSymbolResolver::LookupSymbolName(
weak_persistent_handle->callback_address(), nullptr);
reinterpret_cast<uword>(weak_persistent_handle->callback()), nullptr);
obj.AddProperty("callbackSymbolName", (name == nullptr) ? "" : name);
if (name != nullptr) {
NativeSymbolResolver::FreeSymbolName(name);

View file

@ -485,9 +485,7 @@ ISOLATE_UNIT_TEST_CASE(Service_LocalVarDescriptors) {
EXPECT_SUBSTRING("\"members\":[", handler.msg());
}
static void WeakHandleFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {}
static void WeakHandleFinalizer(void* isolate_callback_data, void* peer) {}
ISOLATE_UNIT_TEST_CASE(Service_PersistentHandles) {
const char* kScript =

View file

@ -28,8 +28,8 @@ main() async {
print("C T2 = Some C thread executing C.");
print("C = C T1 or C T2.");
print("Dart: Setup.");
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 1);
Expect.isTrue(NativeApi.majorVersion == 2);
Expect.isTrue(NativeApi.minorVersion >= 0);
final initializeApi = dl.lookupFunction<IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");
Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);

View file

@ -37,8 +37,8 @@ main() async {
print("C T2 = Some C thread executing C.");
print("C = C T1 or C T2.");
print("Dart: Setup.");
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 1);
Expect.isTrue(NativeApi.majorVersion == 2);
Expect.isTrue(NativeApi.minorVersion >= 0);
final initializeApi = dl.lookupFunction<IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");
Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);

View file

@ -55,8 +55,8 @@ final closureCallbackPointer =
Pointer.fromFunction<Void Function(Handle)>(doClosureCallback);
void doDynamicLinking() {
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 1);
Expect.isTrue(NativeApi.majorVersion == 2);
Expect.isTrue(NativeApi.minorVersion >= 0);
final initializeApi = testLibrary.lookupFunction<
IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");

View file

@ -16,8 +16,8 @@ void main() {
}
void doDynamicLinking() {
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 1);
Expect.isTrue(NativeApi.majorVersion == 2);
Expect.isTrue(NativeApi.minorVersion >= 0);
final initializeApi = testLibrary.lookupFunction<
IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");

View file

@ -16,8 +16,8 @@ void main() {
}
void doDynamicLinking() {
Expect.isTrue(NativeApi.majorVersion == 1);
Expect.isTrue(NativeApi.minorVersion >= 1);
Expect.isTrue(NativeApi.majorVersion == 2);
Expect.isTrue(NativeApi.minorVersion >= 0);
final initializeApi = testLibrary.lookupFunction<
IntPtr Function(Pointer<Void>),
int Function(Pointer<Void>)>("InitDartApiDL");

View file

@ -0,0 +1,804 @@
diff --git a/BUILD.gn b/BUILD.gn
index edbeb6803..e280ae901 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -98,6 +98,7 @@ group("flutter") {
"//flutter/shell/platform/embedder:embedder_proctable_unittests",
"//flutter/shell/platform/embedder:embedder_unittests",
"//flutter/testing:testing_unittests",
+ "//flutter/third_party/tonic/tests:tonic_unittests",
"//flutter/third_party/txt:txt_unittests",
]
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index f13271aa9..6b84f6a83 100755
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1463,6 +1463,8 @@ FILE: ../../../flutter/third_party/tonic/dart_persistent_value.cc
FILE: ../../../flutter/third_party/tonic/dart_persistent_value.h
FILE: ../../../flutter/third_party/tonic/dart_state.cc
FILE: ../../../flutter/third_party/tonic/dart_state.h
+FILE: ../../../flutter/third_party/tonic/dart_weak_persistent_value.cc
+FILE: ../../../flutter/third_party/tonic/dart_weak_persistent_value.h
FILE: ../../../flutter/third_party/tonic/dart_wrappable.cc
FILE: ../../../flutter/third_party/tonic/dart_wrappable.h
FILE: ../../../flutter/third_party/tonic/dart_wrapper_info.h
diff --git a/lib/ui/painting/image_decoder.cc b/lib/ui/painting/image_decoder.cc
index d6c4a668f..b6ffb20f5 100644
--- a/lib/ui/painting/image_decoder.cc
+++ b/lib/ui/painting/image_decoder.cc
@@ -224,12 +224,13 @@ void ImageDecoder::Decode(fml::RefPtr<ImageDescriptor> descriptor,
FML_DCHECK(callback);
FML_DCHECK(runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
- // Always service the callback on the UI thread.
- auto result = [callback, ui_runner = runners_.GetUITaskRunner()](
+ // Always service the callback (and cleanup the descriptor) on the UI thread.
+ auto result = [callback, descriptor, ui_runner = runners_.GetUITaskRunner()](
SkiaGPUObject<SkImage> image,
fml::tracing::TraceFlow flow) {
- ui_runner->PostTask(fml::MakeCopyable(
- [callback, image = std::move(image), flow = std::move(flow)]() mutable {
+ ui_runner->PostTask(
+ fml::MakeCopyable([callback, descriptor, image = std::move(image),
+ flow = std::move(flow)]() mutable {
// We are going to terminate the trace flow here. Flows cannot
// terminate without a base trace. Add one explicitly.
TRACE_EVENT0("flutter", "ImageDecodeCallback");
diff --git a/lib/ui/painting/image_encoding.cc b/lib/ui/painting/image_encoding.cc
index c17a784c5..8a7653f0b 100644
--- a/lib/ui/painting/image_encoding.cc
+++ b/lib/ui/painting/image_encoding.cc
@@ -35,9 +35,7 @@ enum ImageByteFormat {
kPNG,
};
-void FinalizeSkData(void* isolate_callback_data,
- Dart_WeakPersistentHandle handle,
- void* peer) {
+void FinalizeSkData(void* isolate_callback_data, void* peer) {
SkData* buffer = reinterpret_cast<SkData*>(peer);
buffer->unref();
}
diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc
index b834957a9..8a6215110 100644
--- a/runtime/dart_isolate.cc
+++ b/runtime/dart_isolate.cc
@@ -981,6 +981,11 @@ void DartIsolate::AddIsolateShutdownCallback(const fml::closure& closure) {
}
void DartIsolate::OnShutdownCallback() {
+ tonic::DartState* state = tonic::DartState::Current();
+ if (state != nullptr) {
+ state->SetIsShuttingDown();
+ }
+
{
tonic::DartApiScope api_scope;
Dart_Handle sticky_error = Dart_GetStickyError();
diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc
index ae9849a5a..b0c61a1b5 100644
--- a/shell/platform/embedder/embedder.cc
+++ b/shell/platform/embedder/embedder.cc
@@ -1922,8 +1922,7 @@ FlutterEngineResult FlutterEnginePostDartObject(
dart_object.value.as_external_typed_data.data = buffer;
dart_object.value.as_external_typed_data.peer = peer;
dart_object.value.as_external_typed_data.callback =
- +[](void* unused_isolate_callback_data,
- Dart_WeakPersistentHandle unused_handle, void* peer) {
+ +[](void* unused_isolate_callback_data, void* peer) {
auto typed_peer = reinterpret_cast<ExternalTypedDataPeer*>(peer);
typed_peer->trampoline(typed_peer->user_data);
delete typed_peer;
diff --git a/shell/platform/fuchsia/dart_runner/dart_runner.cc b/shell/platform/fuchsia/dart_runner/dart_runner.cc
index 76d85405a..415d2885c 100644
--- a/shell/platform/fuchsia/dart_runner/dart_runner.cc
+++ b/shell/platform/fuchsia/dart_runner/dart_runner.cc
@@ -86,6 +86,10 @@ void IsolateShutdownCallback(void* isolate_group_data, void* isolate_data) {
tonic::DartMicrotaskQueue::GetForCurrentThread()->Destroy();
async_loop_quit(loop);
}
+
+ auto state =
+ static_cast<std::shared_ptr<tonic::DartState>*>(isolate_group_data);
+ state->get()->SetIsShuttingDown();
}
void IsolateGroupCleanupCallback(void* isolate_group_data) {
diff --git a/testing/run_tests.py b/testing/run_tests.py
index 06b8256d0..8bdd114f4 100755
--- a/testing/run_tests.py
+++ b/testing/run_tests.py
@@ -132,6 +132,8 @@ def RunCCTests(build_dir, filter):
RunEngineExecutable(build_dir, 'runtime_unittests', filter, shuffle_flags)
+ RunEngineExecutable(build_dir, 'tonic_unittests', filter, shuffle_flags)
+
if not IsWindows():
# https://github.com/flutter/flutter/issues/36295
RunEngineExecutable(build_dir, 'shell_unittests', filter, shuffle_flags)
diff --git a/third_party/tonic/BUILD.gn b/third_party/tonic/BUILD.gn
index f5bedda8d..9a6abda8b 100644
--- a/third_party/tonic/BUILD.gn
+++ b/third_party/tonic/BUILD.gn
@@ -35,6 +35,8 @@ source_set("tonic") {
"dart_persistent_value.h",
"dart_state.cc",
"dart_state.h",
+ "dart_weak_persistent_value.cc",
+ "dart_weak_persistent_value.h",
"dart_wrappable.cc",
"dart_wrappable.h",
"dart_wrapper_info.h",
diff --git a/third_party/tonic/dart_state.cc b/third_party/tonic/dart_state.cc
index b711a2297..3f37685c5 100644
--- a/third_party/tonic/dart_state.cc
+++ b/third_party/tonic/dart_state.cc
@@ -27,7 +27,8 @@ DartState::DartState(int dirfd,
message_handler_(new DartMessageHandler()),
file_loader_(new FileLoader(dirfd)),
message_epilogue_(message_epilogue),
- has_set_return_code_(false) {}
+ has_set_return_code_(false),
+ is_shutting_down_(false) {}
DartState::~DartState() {}
diff --git a/third_party/tonic/dart_state.h b/third_party/tonic/dart_state.h
index 845914937..1984a66bf 100644
--- a/third_party/tonic/dart_state.h
+++ b/third_party/tonic/dart_state.h
@@ -5,6 +5,7 @@
#ifndef LIB_TONIC_DART_STATE_H_
#define LIB_TONIC_DART_STATE_H_
+#include <atomic>
#include <functional>
#include <memory>
@@ -68,6 +69,9 @@ class DartState : public std::enable_shared_from_this<DartState> {
void SetReturnCodeCallback(std::function<void(uint32_t)> callback);
bool has_set_return_code() const { return has_set_return_code_; }
+ void SetIsShuttingDown() { is_shutting_down_ = true; }
+ bool IsShuttingDown() { return is_shutting_down_; }
+
virtual void DidSetIsolate();
static Dart_Handle HandleLibraryTag(Dart_LibraryTag tag,
@@ -83,6 +87,7 @@ class DartState : public std::enable_shared_from_this<DartState> {
std::function<void(Dart_Handle)> message_epilogue_;
std::function<void(uint32_t)> set_return_code_callback_;
bool has_set_return_code_;
+ std::atomic<bool> is_shutting_down_;
protected:
TONIC_DISALLOW_COPY_AND_ASSIGN(DartState);
diff --git a/third_party/tonic/dart_weak_persistent_value.cc b/third_party/tonic/dart_weak_persistent_value.cc
new file mode 100644
index 000000000..a2664d3e0
--- /dev/null
+++ b/third_party/tonic/dart_weak_persistent_value.cc
@@ -0,0 +1,70 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tonic/dart_weak_persistent_value.h"
+
+#include "tonic/dart_state.h"
+#include "tonic/scopes/dart_isolate_scope.h"
+
+namespace tonic {
+
+DartWeakPersistentValue::DartWeakPersistentValue() : handle_(nullptr) {}
+
+DartWeakPersistentValue::~DartWeakPersistentValue() {
+ Clear();
+}
+
+void DartWeakPersistentValue::Set(DartState* dart_state,
+ Dart_Handle object,
+ void* peer,
+ intptr_t external_allocation_size,
+ Dart_HandleFinalizer callback) {
+ TONIC_DCHECK(is_empty());
+ dart_state_ = dart_state->GetWeakPtr();
+ handle_ = Dart_NewWeakPersistentHandle(object, peer, external_allocation_size,
+ callback);
+}
+
+void DartWeakPersistentValue::Clear() {
+ if (!handle_) {
+ return;
+ }
+
+ auto dart_state = dart_state_.lock();
+ if (!dart_state) {
+ // The DartVM that the handle used to belong to has been shut down and that
+ // handle has already been deleted.
+ handle_ = nullptr;
+ return;
+ }
+
+ // The DartVM frees the handles during shutdown and calls the finalizers.
+ // Freeing handles during shutdown would fail.
+ if (!dart_state->IsShuttingDown()) {
+ if (Dart_CurrentIsolateGroup()) {
+ Dart_DeleteWeakPersistentHandle(handle_);
+ } else {
+ // If we are not on the mutator thread, this will fail. The caller must
+ // ensure to be on the mutator thread.
+ DartIsolateScope scope(dart_state->isolate());
+ Dart_DeleteWeakPersistentHandle(handle_);
+ }
+ }
+ // If it's shutting down, the handle will be deleted already.
+
+ dart_state_.reset();
+ handle_ = nullptr;
+}
+
+Dart_Handle DartWeakPersistentValue::Get() {
+ auto dart_state = dart_state_.lock();
+ TONIC_DCHECK(dart_state);
+ TONIC_DCHECK(!dart_state->IsShuttingDown());
+ if (!handle_) {
+ return nullptr;
+ }
+ return Dart_HandleFromWeakPersistent(handle_);
+}
+
+} // namespace tonic
diff --git a/third_party/tonic/dart_weak_persistent_value.h b/third_party/tonic/dart_weak_persistent_value.h
new file mode 100644
index 000000000..5f8aed5ee
--- /dev/null
+++ b/third_party/tonic/dart_weak_persistent_value.h
@@ -0,0 +1,48 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIB_TONIC_DART_WEAK_PERSISTENT_VALUE_H_
+#define LIB_TONIC_DART_WEAK_PERSISTENT_VALUE_H_
+
+#include <memory>
+
+#include "third_party/dart/runtime/include/dart_api.h"
+#include "tonic/common/macros.h"
+
+namespace tonic {
+class DartState;
+
+// DartWeakPersistentValue is a bookkeeping class to help pair calls to
+// Dart_NewWeakPersistentHandle with Dart_DeleteWeakPersistentHandle even in
+// the case if IsolateGroup shutdown. Consider using this class instead of
+// holding a Dart_PersistentHandle directly so that you don't leak the
+// Dart_WeakPersistentHandle.
+class DartWeakPersistentValue {
+ public:
+ DartWeakPersistentValue();
+ ~DartWeakPersistentValue();
+
+ Dart_WeakPersistentHandle value() const { return handle_; }
+ bool is_empty() const { return handle_ == nullptr; }
+
+ void Set(DartState* dart_state,
+ Dart_Handle object,
+ void* peer,
+ intptr_t external_allocation_size,
+ Dart_HandleFinalizer callback);
+ void Clear();
+ Dart_Handle Get();
+
+ const std::weak_ptr<DartState>& dart_state() const { return dart_state_; }
+
+ private:
+ std::weak_ptr<DartState> dart_state_;
+ Dart_WeakPersistentHandle handle_;
+
+ TONIC_DISALLOW_COPY_AND_ASSIGN(DartWeakPersistentValue);
+};
+
+} // namespace tonic
+
+#endif // LIB_TONIC_DART_WEAK_PERSISTENT_VALUE_H_
diff --git a/third_party/tonic/dart_wrappable.cc b/third_party/tonic/dart_wrappable.cc
index 3bdfe3e6e..858215c11 100644
--- a/third_party/tonic/dart_wrappable.cc
+++ b/third_party/tonic/dart_wrappable.cc
@@ -12,12 +12,17 @@
namespace tonic {
DartWrappable::~DartWrappable() {
- TONIC_CHECK(!dart_wrapper_);
+ // Calls the destructor of dart_wrapper_ to delete WeakPersistentHandle.
}
// TODO(dnfield): Delete this. https://github.com/flutter/flutter/issues/50997
Dart_Handle DartWrappable::CreateDartWrapper(DartState* dart_state) {
- TONIC_DCHECK(!dart_wrapper_);
+ if (!dart_wrapper_.is_empty()) {
+ // Any previously given out wrapper must have been GCed.
+ TONIC_DCHECK(Dart_IsNull(dart_wrapper_.Get()));
+ dart_wrapper_.Clear();
+ }
+
const DartWrapperInfo& info = GetDartWrapperInfo();
Dart_PersistentHandle type = dart_state->class_library().GetClass(info);
@@ -36,14 +41,19 @@ Dart_Handle DartWrappable::CreateDartWrapper(DartState* dart_state) {
TONIC_DCHECK(!LogIfError(res));
this->RetainDartWrappableReference(); // Balanced in FinalizeDartWrapper.
- dart_wrapper_ = Dart_NewWeakPersistentHandle(
- wrapper, this, GetAllocationSize(), &FinalizeDartWrapper);
+ dart_wrapper_.Set(dart_state, wrapper, this, GetAllocationSize(),
+ &FinalizeDartWrapper);
return wrapper;
}
void DartWrappable::AssociateWithDartWrapper(Dart_Handle wrapper) {
- TONIC_DCHECK(!dart_wrapper_);
+ if (!dart_wrapper_.is_empty()) {
+ // Any previously given out wrapper must have been GCed.
+ TONIC_DCHECK(Dart_IsNull(dart_wrapper_.Get()));
+ dart_wrapper_.Clear();
+ }
+
TONIC_CHECK(!LogIfError(wrapper));
const DartWrapperInfo& info = GetDartWrapperInfo();
@@ -54,26 +64,25 @@ void DartWrappable::AssociateWithDartWrapper(Dart_Handle wrapper) {
wrapper, kWrapperInfoIndex, reinterpret_cast<intptr_t>(&info))));
this->RetainDartWrappableReference(); // Balanced in FinalizeDartWrapper.
- dart_wrapper_ = Dart_NewWeakPersistentHandle(
- wrapper, this, GetAllocationSize(), &FinalizeDartWrapper);
+
+ DartState* dart_state = DartState::Current();
+ dart_wrapper_.Set(dart_state, wrapper, this, GetAllocationSize(),
+ &FinalizeDartWrapper);
}
void DartWrappable::ClearDartWrapper() {
- TONIC_DCHECK(dart_wrapper_);
- Dart_Handle wrapper = Dart_HandleFromWeakPersistent(dart_wrapper_);
+ TONIC_DCHECK(!dart_wrapper_.is_empty());
+ Dart_Handle wrapper = dart_wrapper_.Get();
TONIC_CHECK(!LogIfError(Dart_SetNativeInstanceField(wrapper, kPeerIndex, 0)));
TONIC_CHECK(
!LogIfError(Dart_SetNativeInstanceField(wrapper, kWrapperInfoIndex, 0)));
- Dart_DeleteWeakPersistentHandle(dart_wrapper_);
- dart_wrapper_ = nullptr;
+ dart_wrapper_.Clear();
this->ReleaseDartWrappableReference();
}
void DartWrappable::FinalizeDartWrapper(void* isolate_callback_data,
- Dart_WeakPersistentHandle wrapper,
void* peer) {
DartWrappable* wrappable = reinterpret_cast<DartWrappable*>(peer);
- wrappable->dart_wrapper_ = nullptr;
wrappable->ReleaseDartWrappableReference(); // Balanced in CreateDartWrapper.
}
diff --git a/third_party/tonic/dart_wrappable.h b/third_party/tonic/dart_wrappable.h
index a036abb85..f944dacef 100644
--- a/third_party/tonic/dart_wrappable.h
+++ b/third_party/tonic/dart_wrappable.h
@@ -9,6 +9,7 @@
#include "tonic/common/macros.h"
#include "tonic/converter/dart_converter.h"
#include "tonic/dart_state.h"
+#include "tonic/dart_weak_persistent_value.h"
#include "tonic/dart_wrapper_info.h"
#include "tonic/logging/dart_error.h"
@@ -26,7 +27,7 @@ class DartWrappable {
kNumberOfNativeFields,
};
- DartWrappable() : dart_wrapper_(nullptr) {}
+ DartWrappable() : dart_wrapper_(DartWeakPersistentValue()) {}
// Subclasses that wish to expose a new interface must override this function
// and provide information about their wrapper. There is no need to call your
@@ -49,7 +50,9 @@ class DartWrappable {
Dart_Handle CreateDartWrapper(DartState* dart_state);
void AssociateWithDartWrapper(Dart_Handle wrappable);
void ClearDartWrapper(); // Warning: Might delete this.
- Dart_WeakPersistentHandle dart_wrapper() const { return dart_wrapper_; }
+ Dart_WeakPersistentHandle dart_wrapper() const {
+ return dart_wrapper_.value();
+ }
protected:
virtual ~DartWrappable();
@@ -59,11 +62,9 @@ class DartWrappable {
const tonic::DartWrapperInfo& wrapper_info);
private:
- static void FinalizeDartWrapper(void* isolate_callback_data,
- Dart_WeakPersistentHandle wrapper,
- void* peer);
+ static void FinalizeDartWrapper(void* isolate_callback_data, void* peer);
- Dart_WeakPersistentHandle dart_wrapper_;
+ DartWeakPersistentValue dart_wrapper_;
TONIC_DISALLOW_COPY_AND_ASSIGN(DartWrappable);
};
@@ -103,22 +104,36 @@ struct DartConverter<
typename std::enable_if<
std::is_convertible<T*, const DartWrappable*>::value>::type> {
static Dart_Handle ToDart(DartWrappable* val) {
- if (!val)
+ if (!val) {
return Dart_Null();
- if (Dart_WeakPersistentHandle wrapper = val->dart_wrapper())
- return Dart_HandleFromWeakPersistent(wrapper);
+ }
+ if (Dart_WeakPersistentHandle wrapper = val->dart_wrapper()) {
+ auto strong_handle = Dart_HandleFromWeakPersistent(wrapper);
+ if (!Dart_IsNull(strong_handle)) {
+ return strong_handle;
+ }
+ // After the weak referenced object has been GCed, the handle points to
+ // Dart_Null(). Fall through create a new wrapper object.
+ }
return val->CreateDartWrapper(DartState::Current());
}
static void SetReturnValue(Dart_NativeArguments args,
DartWrappable* val,
bool auto_scope = true) {
- if (!val)
+ if (!val) {
Dart_SetReturnValue(args, Dart_Null());
- else if (Dart_WeakPersistentHandle wrapper = val->dart_wrapper())
- Dart_SetWeakHandleReturnValue(args, wrapper);
- else
- Dart_SetReturnValue(args, val->CreateDartWrapper(DartState::Current()));
+ return;
+ } else if (Dart_WeakPersistentHandle wrapper = val->dart_wrapper()) {
+ auto strong_handle = Dart_HandleFromWeakPersistent(wrapper);
+ if (!Dart_IsNull(strong_handle)) {
+ Dart_SetReturnValue(args, strong_handle);
+ return;
+ }
+ // After the weak referenced object has been GCed, the handle points to
+ // Dart_Null(). Fall through create a new wrapper object.
+ }
+ Dart_SetReturnValue(args, val->CreateDartWrapper(DartState::Current()));
}
static T* FromDart(Dart_Handle handle) {
diff --git a/third_party/tonic/tests/BUILD.gn b/third_party/tonic/tests/BUILD.gn
new file mode 100644
index 000000000..359deb13b
--- /dev/null
+++ b/third_party/tonic/tests/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2013 The Flutter Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//flutter/testing/testing.gni")
+
+test_fixtures("tonic_fixtures") {
+ dart_main = "fixtures/tonic_test.dart"
+ fixtures = []
+}
+
+executable("tonic_unittests") {
+ testonly = true
+
+ public_configs = [ "//flutter:export_dynamic_symbols" ]
+
+ sources = [
+ "dart_state_unittest.cc",
+ "dart_weak_persistent_handle_unittest.cc",
+ ]
+
+ public_deps = [
+ ":tonic_fixtures",
+ "//flutter/runtime:libdart",
+ "//flutter/runtime:runtime",
+ "//flutter/testing",
+ "//flutter/testing:fixture_test",
+ "//third_party/dart/runtime:dart_api",
+ "//third_party/googletest:gtest",
+ ]
+
+ deps = [ "../:tonic" ]
+}
diff --git a/third_party/tonic/tests/dart_state_unittest.cc b/third_party/tonic/tests/dart_state_unittest.cc
new file mode 100644
index 000000000..3d053de40
--- /dev/null
+++ b/third_party/tonic/tests/dart_state_unittest.cc
@@ -0,0 +1,61 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/common/task_runners.h"
+#include "flutter/runtime/dart_vm_lifecycle.h"
+#include "flutter/runtime/isolate_configuration.h"
+#include "flutter/testing/fixture_test.h"
+
+namespace flutter {
+namespace testing {
+
+using DartState = FixtureTest;
+
+TEST_F(DartState, IsShuttingDown) {
+ ASSERT_FALSE(DartVMRef::IsInstanceRunning());
+ auto settings = CreateSettingsForFixture();
+ auto vm_ref = DartVMRef::Create(settings);
+ ASSERT_TRUE(vm_ref);
+ auto vm_data = vm_ref.GetVMData();
+ ASSERT_TRUE(vm_data);
+ TaskRunners task_runners(GetCurrentTestName(), //
+ GetCurrentTaskRunner(), //
+ GetCurrentTaskRunner(), //
+ GetCurrentTaskRunner(), //
+ GetCurrentTaskRunner() //
+ );
+ auto isolate_configuration =
+ IsolateConfiguration::InferFromSettings(settings);
+ auto weak_isolate = DartIsolate::CreateRunningRootIsolate(
+ vm_data->GetSettings(), // settings
+ vm_data->GetIsolateSnapshot(), // isolate snapshot
+ std::move(task_runners), // task runners
+ nullptr, // window
+ {}, // snapshot delegate
+ {}, // hint freed delegate
+ {}, // io manager
+ {}, // unref queue
+ {}, // image decoder
+ "main.dart", // advisory uri
+ "main", // advisory entrypoint
+ DartIsolate::Flags{}, // flags
+ settings.isolate_create_callback, // isolate create callback
+ settings.isolate_shutdown_callback, // isolate shutdown callback
+ "main", // dart entrypoint
+ std::nullopt, // dart entrypoint library
+ std::move(isolate_configuration) // isolate configuration
+ );
+ auto root_isolate = weak_isolate.lock();
+ ASSERT_TRUE(root_isolate);
+
+ tonic::DartState* dart_state = root_isolate.get();
+ ASSERT_FALSE(dart_state->IsShuttingDown());
+
+ ASSERT_TRUE(root_isolate->Shutdown());
+
+ ASSERT_TRUE(dart_state->IsShuttingDown());
+}
+
+} // namespace testing
+} // namespace flutter
diff --git a/third_party/tonic/tests/dart_weak_persistent_handle_unittest.cc b/third_party/tonic/tests/dart_weak_persistent_handle_unittest.cc
new file mode 100644
index 000000000..65d5e116c
--- /dev/null
+++ b/third_party/tonic/tests/dart_weak_persistent_handle_unittest.cc
@@ -0,0 +1,167 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/testing/dart_isolate_runner.h"
+#include "flutter/testing/fixture_test.h"
+
+namespace flutter {
+namespace testing {
+
+class DartWeakPersistentHandle : public FixtureTest {
+ public:
+ DartWeakPersistentHandle()
+ : settings_(CreateSettingsForFixture()),
+ vm_(DartVMRef::Create(settings_)) {}
+
+ ~DartWeakPersistentHandle() = default;
+
+ [[nodiscard]] bool RunWithEntrypoint(const std::string& entrypoint) {
+ if (running_isolate_) {
+ return false;
+ }
+ auto thread = CreateNewThread();
+ TaskRunners single_threaded_task_runner(GetCurrentTestName(), thread,
+ thread, thread, thread);
+ auto isolate =
+ RunDartCodeInIsolate(vm_, settings_, single_threaded_task_runner,
+ entrypoint, {}, GetFixturesPath());
+ if (!isolate || isolate->get()->GetPhase() != DartIsolate::Phase::Running) {
+ return false;
+ }
+
+ running_isolate_ = std::move(isolate);
+ return true;
+ }
+
+ [[nodiscard]] bool RunInIsolateScope(std::function<bool(void)> closure) {
+ return running_isolate_->RunInIsolateScope(closure);
+ }
+
+ private:
+ Settings settings_;
+ DartVMRef vm_;
+ std::unique_ptr<AutoIsolateShutdown> running_isolate_;
+ FML_DISALLOW_COPY_AND_ASSIGN(DartWeakPersistentHandle);
+};
+
+void NopFinalizer(void* isolate_callback_data, void* peer) {}
+
+TEST_F(DartWeakPersistentHandle, ClearImmediately) {
+ auto weak_persistent_value = tonic::DartWeakPersistentValue();
+
+ fml::AutoResetWaitableEvent event;
+
+ AddNativeCallback(
+ "GiveObjectToNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
+ auto handle = Dart_GetNativeArgument(args, 0);
+
+ auto dart_state = tonic::DartState::Current();
+ ASSERT_TRUE(dart_state);
+ ASSERT_TRUE(tonic::DartState::Current());
+ weak_persistent_value.Set(dart_state, handle, nullptr, 0, NopFinalizer);
+
+ weak_persistent_value.Clear();
+
+ event.Signal();
+ }));
+
+ ASSERT_TRUE(RunWithEntrypoint("callGiveObjectToNative"));
+
+ event.Wait();
+}
+
+TEST_F(DartWeakPersistentHandle, ClearLaterCc) {
+ auto weak_persistent_value = tonic::DartWeakPersistentValue();
+
+ fml::AutoResetWaitableEvent event;
+
+ AddNativeCallback(
+ "GiveObjectToNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
+ auto handle = Dart_GetNativeArgument(args, 0);
+
+ auto dart_state = tonic::DartState::Current();
+ ASSERT_TRUE(dart_state);
+ ASSERT_TRUE(tonic::DartState::Current());
+ weak_persistent_value.Set(dart_state, handle, nullptr, 0, NopFinalizer);
+
+ // Do not clear handle immediately.
+
+ event.Signal();
+ }));
+
+ ASSERT_TRUE(RunWithEntrypoint("callGiveObjectToNative"));
+
+ event.Wait();
+
+ ASSERT_TRUE(RunInIsolateScope([&weak_persistent_value]() -> bool {
+ // Clear on initiative of native.
+ weak_persistent_value.Clear();
+ return true;
+ }));
+}
+
+TEST_F(DartWeakPersistentHandle, ClearLaterDart) {
+ auto weak_persistent_value = tonic::DartWeakPersistentValue();
+
+ fml::AutoResetWaitableEvent event;
+
+ AddNativeCallback(
+ "GiveObjectToNative", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
+ auto handle = Dart_GetNativeArgument(args, 0);
+
+ auto dart_state = tonic::DartState::Current();
+ ASSERT_TRUE(dart_state);
+ ASSERT_TRUE(tonic::DartState::Current());
+ weak_persistent_value.Set(dart_state, handle, nullptr, 0, NopFinalizer);
+
+ // Do not clear handle immediately.
+ }));
+
+ AddNativeCallback("SignalDone",
+ CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
+ // Clear on initiative of Dart.
+ weak_persistent_value.Clear();
+
+ event.Signal();
+ }));
+
+ ASSERT_TRUE(RunWithEntrypoint("testClearLater"));
+
+ event.Wait();
+}
+
+// Handle outside the test body scope so it survives until isolate shutdown.
+tonic::DartWeakPersistentValue global_weak_persistent_value =
+ tonic::DartWeakPersistentValue();
+
+TEST_F(DartWeakPersistentHandle, ClearOnShutdown) {
+ fml::AutoResetWaitableEvent event;
+
+ AddNativeCallback("GiveObjectToNative",
+ CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
+ auto handle = Dart_GetNativeArgument(args, 0);
+
+ auto dart_state = tonic::DartState::Current();
+ ASSERT_TRUE(dart_state);
+ ASSERT_TRUE(tonic::DartState::Current());
+
+ // The test is repeated, ensure the global var is
+ // cleared before use.
+ global_weak_persistent_value.Clear();
+
+ global_weak_persistent_value.Set(
+ dart_state, handle, nullptr, 0, NopFinalizer);
+
+ // Do not clear handle, so it is cleared on shutdown.
+
+ event.Signal();
+ }));
+
+ ASSERT_TRUE(RunWithEntrypoint("callGiveObjectToNative"));
+
+ event.Wait();
+}
+
+} // namespace testing
+} // namespace flutter
diff --git a/third_party/tonic/tests/fixtures/tonic_test.dart b/third_party/tonic/tests/fixtures/tonic_test.dart
new file mode 100644
index 000000000..83e92d4dc
--- /dev/null
+++ b/third_party/tonic/tests/fixtures/tonic_test.dart
@@ -0,0 +1,25 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+void main() {}
+
+class SomeClass {
+ int i;
+ SomeClass(this.i);
+}
+
+void giveObjectToNative(Object someObject) native 'GiveObjectToNative';
+
+void signalDone() native 'SignalDone';
+
+@pragma('vm:entry-point')
+void callGiveObjectToNative() {
+ giveObjectToNative(SomeClass(123));
+}
+
+@pragma('vm:entry-point')
+void testClearLater() {
+ giveObjectToNative(SomeClass(123));
+ signalDone();
+}
diff --git a/third_party/tonic/typed_data/dart_byte_data.cc b/third_party/tonic/typed_data/dart_byte_data.cc
index dc312de3f..cfb07399f 100644
--- a/third_party/tonic/typed_data/dart_byte_data.cc
+++ b/third_party/tonic/typed_data/dart_byte_data.cc
@@ -16,9 +16,7 @@ namespace {
// with a buffer allocated outside the Dart heap.
const int kExternalSizeThreshold = 1000;
-void FreeFinalizer(void* isolate_callback_data,
- Dart_WeakPersistentHandle handle,
- void* peer) {
+void FreeFinalizer(void* isolate_callback_data, void* peer) {
free(peer);
}