diff --git a/CHANGELOG.md b/CHANGELOG.md index 0688b0d4035..40e9a81439e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 2.10.0 + +### Dart VM + +* Introduces `Dart_FinalizableHandle`s. They do auto-delete, and the weakly + referred object cannot be accessed through them. + ## 2.9.0 ### Language @@ -134,7 +141,7 @@ Updated the Linter to `0.1.117`, which includes: * Preserve Windows line endings in `pubspec.lock` if they are already there ([#2489](https://github.com/dart-lang/pub/pull/2489)). * Better terminal color-detection. Use colors in terminals on Windows. -* Fix git folder names in cache, allowing for ssh-style git +* Fix git folder names in cache, allowing for ssh-style git dependencies. * Fix: Avoid precompilation of dependencies of global packages. diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart index c3bc86a1c4a..7f843c31a76 100644 --- a/pkg/vm_service/lib/src/vm_service.dart +++ b/pkg/vm_service/lib/src/vm_service.dart @@ -5430,10 +5430,11 @@ class MemoryUsage extends Response { /// The amount of non-Dart memory that is retained by Dart objects. For /// example, memory associated with Dart objects through APIs such as - /// Dart_NewWeakPersistentHandle and Dart_NewExternalTypedData. This usage is - /// only as accurate as the values supplied to these APIs from the VM embedder - /// or native extensions. This external memory applies GC pressure, but is - /// separate from heapUsage and heapCapacity. + /// Dart_NewFinalizableHandle, Dart_NewWeakPersistentHandle and + /// Dart_NewExternalTypedData. This usage is only as accurate as the values + /// supplied to these APIs from the VM embedder or native extensions. This + /// external memory applies GC pressure, but is separate from heapUsage and + /// heapCapacity. int externalUsage; /// The total capacity of the heap in bytes. This is the amount of memory used diff --git a/runtime/bin/directory.cc b/runtime/bin/directory.cc index 4a5629e83d8..9920d0f08b4 100644 --- a/runtime/bin/directory.cc +++ b/runtime/bin/directory.cc @@ -232,9 +232,7 @@ void FUNCTION_NAME(Directory_GetAsyncDirectoryListerPointer)( } } -static void ReleaseListing(void* isolate_callback_data, - Dart_WeakPersistentHandle handle, - void* peer) { +static void ReleaseListing(void* isolate_callback_data, void* peer) { AsyncDirectoryListing* listing = reinterpret_cast(peer); listing->Release(); @@ -247,8 +245,8 @@ void FUNCTION_NAME(Directory_SetAsyncDirectoryListerPointer)( DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1)); AsyncDirectoryListing* listing = reinterpret_cast(listing_pointer); - Dart_NewWeakPersistentHandle(dart_this, reinterpret_cast(listing), - sizeof(*listing), ReleaseListing); + Dart_NewFinalizableHandle(dart_this, reinterpret_cast(listing), + sizeof(*listing), ReleaseListing); Dart_Handle result = Dart_SetNativeInstanceField( dart_this, kAsyncDirectoryListerFieldIndex, listing_pointer); ThrowIfError(result); diff --git a/runtime/bin/file.cc b/runtime/bin/file.cc index 886970e2f5b..29a7c118732 100644 --- a/runtime/bin/file.cc +++ b/runtime/bin/file.cc @@ -68,9 +68,7 @@ void FUNCTION_NAME(File_GetPointer)(Dart_NativeArguments args) { Dart_SetIntegerReturnValue(args, file_pointer); } -static void ReleaseFile(void* isolate_callback_data, - Dart_WeakPersistentHandle handle, - void* peer) { +static void ReleaseFile(void* isolate_callback_data, void* peer) { File* file = reinterpret_cast(peer); file->Release(); } @@ -79,9 +77,9 @@ void FUNCTION_NAME(File_SetPointer)(Dart_NativeArguments args) { Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); intptr_t file_pointer = DartUtils::GetNativeIntptrArgument(args, 1); File* file = reinterpret_cast(file_pointer); - Dart_WeakPersistentHandle handle = Dart_NewWeakPersistentHandle( + Dart_FinalizableHandle handle = Dart_NewFinalizableHandle( dart_this, reinterpret_cast(file), sizeof(*file), ReleaseFile); - file->SetWeakHandle(handle); + file->SetFinalizableHandle(handle); SetFile(dart_this, file_pointer); } @@ -148,7 +146,7 @@ void FUNCTION_NAME(File_Close)(Dart_NativeArguments args) { return; } file->Close(); - file->DeleteWeakHandle(Dart_CurrentIsolate()); + file->DeleteFinalizableHandle(Dart_CurrentIsolate(), dart_this); file->Release(); ThrowIfError( diff --git a/runtime/bin/file.h b/runtime/bin/file.h index 9e683461855..98d374840a2 100644 --- a/runtime/bin/file.h +++ b/runtime/bin/file.h @@ -184,21 +184,23 @@ class File : public ReferenceCounted { // Calls the platform-specific functions to close the file. void Close(); - // Returns the weak persistent handle for the File's Dart wrapper. - Dart_WeakPersistentHandle WeakHandle() const { return weak_handle_; } - - // Set the weak persistent handle for the File's Dart wrapper. - void SetWeakHandle(Dart_WeakPersistentHandle handle) { - ASSERT(weak_handle_ == NULL); - weak_handle_ = handle; + // Returns the finalizable handle for the File's Dart wrapper. + Dart_FinalizableHandle FinalizableHandle() const { + return finalizable_handle_; } - // Deletes the weak persistent handle for the File's Dart wrapper. Call + // Set the finalizable handle for the File's Dart wrapper. + void SetFinalizableHandle(Dart_FinalizableHandle handle) { + ASSERT(finalizable_handle_ == NULL); + finalizable_handle_ = handle; + } + + // Deletes the finalizable handle for the File's Dart wrapper. Call // when the file is explicitly closed and the finalizer is no longer // needed. - void DeleteWeakHandle(Dart_Isolate isolate) { - Dart_DeleteWeakPersistentHandle(weak_handle_); - weak_handle_ = NULL; + void DeleteFinalizableHandle(Dart_Isolate isolate, Dart_Handle strong_ref) { + Dart_DeleteFinalizableHandle(finalizable_handle_, strong_ref); + finalizable_handle_ = NULL; } // Open the file with the given path. The file is always opened for @@ -319,7 +321,7 @@ class File : public ReferenceCounted { private: explicit File(FileHandle* handle) - : ReferenceCounted(), handle_(handle), weak_handle_(NULL) {} + : ReferenceCounted(), handle_(handle), finalizable_handle_(NULL) {} ~File(); @@ -330,10 +332,10 @@ class File : public ReferenceCounted { // FileHandle is an OS specific class which stores data about the file. FileHandle* handle_; // OS specific handle for the file. - // We retain the weak handle because we can do cleanup eagerly when Dart code - // calls closeSync(). In that case, we delete the weak handle so that the - // finalizer doesn't run. - Dart_WeakPersistentHandle weak_handle_; + // We retain the finalizable handle because we can do cleanup eagerly when + // Dart code calls closeSync(). In that case, we delete the finalizable + // handle so that the finalizer doesn't run. + Dart_FinalizableHandle finalizable_handle_; friend class ReferenceCounted; DISALLOW_COPY_AND_ASSIGN(File); diff --git a/runtime/bin/filter.cc b/runtime/bin/filter.cc index c7eb4dbdc7e..53f69671e71 100644 --- a/runtime/bin/filter.cc +++ b/runtime/bin/filter.cc @@ -256,9 +256,7 @@ void FUNCTION_NAME(Filter_Processed)(Dart_NativeArguments args) { } } -static void DeleteFilter(void* isolate_data, - Dart_WeakPersistentHandle handle, - void* filter_pointer) { +static void DeleteFilter(void* isolate_data, void* filter_pointer) { Filter* filter = reinterpret_cast(filter_pointer); delete filter; } @@ -272,8 +270,8 @@ Dart_Handle Filter::SetFilterAndCreateFinalizer(Dart_Handle filter, if (Dart_IsError(err)) { return err; } - Dart_NewWeakPersistentHandle(filter, reinterpret_cast(filter_pointer), - size, DeleteFilter); + Dart_NewFinalizableHandle(filter, reinterpret_cast(filter_pointer), + size, DeleteFilter); return err; } diff --git a/runtime/bin/loader.cc b/runtime/bin/loader.cc index e5c869ade1b..a058ebb9149 100644 --- a/runtime/bin/loader.cc +++ b/runtime/bin/loader.cc @@ -145,9 +145,7 @@ Dart_Handle Loader::LibraryTagHandler(Dart_LibraryTag tag, return Dart_Null(); } #else -static void MallocFinalizer(void* isolate_callback_data, - Dart_WeakPersistentHandle handle, - void* peer) { +static void MallocFinalizer(void* isolate_callback_data, void* peer) { free(peer); } @@ -185,8 +183,8 @@ Dart_Handle Loader::LibraryTagHandler(Dart_LibraryTag tag, } result = Dart_NewExternalTypedData(Dart_TypedData_kUint8, kernel_buffer, kernel_buffer_size); - Dart_NewWeakPersistentHandle(result, kernel_buffer, kernel_buffer_size, - MallocFinalizer); + Dart_NewFinalizableHandle(result, kernel_buffer, kernel_buffer_size, + MallocFinalizer); return result; } if (tag == Dart_kImportExtensionTag) { diff --git a/runtime/bin/namespace.cc b/runtime/bin/namespace.cc index c322c52b84d..fd5a091546e 100644 --- a/runtime/bin/namespace.cc +++ b/runtime/bin/namespace.cc @@ -16,9 +16,7 @@ namespace bin { static const int kNamespaceNativeFieldIndex = 0; -static void ReleaseNamespace(void* isolate_callback_data, - Dart_WeakPersistentHandle handle, - void* peer) { +static void ReleaseNamespace(void* isolate_callback_data, void* peer) { Namespace* namespc = reinterpret_cast(peer); ASSERT(namespc != NULL); namespc->Release(); @@ -84,8 +82,8 @@ void FUNCTION_NAME(Namespace_Create)(Dart_NativeArguments args) { // Set up a finalizer for the Dart object so that we can do any necessary // platform-specific cleanup for the namespc. - Dart_NewWeakPersistentHandle(namespc_obj, reinterpret_cast(namespc), - sizeof(*namespc), ReleaseNamespace); + Dart_NewFinalizableHandle(namespc_obj, reinterpret_cast(namespc), + sizeof(*namespc), ReleaseNamespace); Dart_SetReturnValue(args, namespc_obj); } diff --git a/runtime/bin/secure_socket_filter.cc b/runtime/bin/secure_socket_filter.cc index 56060bab565..a21bc8ef510 100644 --- a/runtime/bin/secure_socket_filter.cc +++ b/runtime/bin/secure_socket_filter.cc @@ -62,9 +62,7 @@ static SSLFilter* GetFilter(Dart_NativeArguments args) { return filter; } -static void DeleteFilter(void* isolate_data, - Dart_WeakPersistentHandle handle, - void* context_pointer) { +static void DeleteFilter(void* isolate_data, void* context_pointer) { SSLFilter* filter = reinterpret_cast(context_pointer); filter->Release(); } @@ -78,8 +76,8 @@ static Dart_Handle SetFilter(Dart_NativeArguments args, SSLFilter* filter) { dart_this, SSLFilter::kSSLFilterNativeFieldIndex, reinterpret_cast(filter)); RETURN_IF_ERROR(err); - Dart_NewWeakPersistentHandle(dart_this, reinterpret_cast(filter), - SSLFilter::kApproximateSize, DeleteFilter); + Dart_NewFinalizableHandle(dart_this, reinterpret_cast(filter), + SSLFilter::kApproximateSize, DeleteFilter); return Dart_Null(); } diff --git a/runtime/bin/security_context.cc b/runtime/bin/security_context.cc index 5d649df619c..3b98b302659 100644 --- a/runtime/bin/security_context.cc +++ b/runtime/bin/security_context.cc @@ -93,9 +93,7 @@ SSLCertContext* SSLCertContext::GetSecurityContext(Dart_NativeArguments args) { return context; } -static void DeleteSecurityContext(void* isolate_data, - Dart_WeakPersistentHandle handle, - void* context_pointer) { +static void DeleteSecurityContext(void* isolate_data, void* context_pointer) { SSLCertContext* context = static_cast(context_pointer); context->Release(); } @@ -109,15 +107,13 @@ static Dart_Handle SetSecurityContext(Dart_NativeArguments args, dart_this, SSLCertContext::kSecurityContextNativeFieldIndex, reinterpret_cast(context)); RETURN_IF_ERROR(err); - Dart_NewWeakPersistentHandle(dart_this, context, - SSLCertContext::kApproximateSize, - DeleteSecurityContext); + Dart_NewFinalizableHandle(dart_this, context, + SSLCertContext::kApproximateSize, + DeleteSecurityContext); return Dart_Null(); } -static void ReleaseCertificate(void* isolate_data, - Dart_WeakPersistentHandle handle, - void* context_pointer) { +static void ReleaseCertificate(void* isolate_data, void* context_pointer) { X509* cert = reinterpret_cast(context_pointer); X509_free(cert); } @@ -158,9 +154,9 @@ Dart_Handle X509Helper::WrappedX509Certificate(X509* certificate) { const intptr_t approximate_size_of_certificate = sizeof(*certificate) + EstimateX509Size(certificate); ASSERT(approximate_size_of_certificate > 0); - Dart_NewWeakPersistentHandle(result, reinterpret_cast(certificate), - approximate_size_of_certificate, - ReleaseCertificate); + Dart_NewFinalizableHandle(result, reinterpret_cast(certificate), + approximate_size_of_certificate, + ReleaseCertificate); return result; } diff --git a/runtime/bin/socket.cc b/runtime/bin/socket.cc index ac3814f190e..ec9020675d6 100644 --- a/runtime/bin/socket.cc +++ b/runtime/bin/socket.cc @@ -1253,9 +1253,7 @@ void FUNCTION_NAME(Socket_AvailableDatagram)(Dart_NativeArguments args) { Dart_SetBooleanReturnValue(args, available); } -static void NormalSocketFinalizer(void* isolate_data, - Dart_WeakPersistentHandle handle, - void* data) { +static void NormalSocketFinalizer(void* isolate_data, void* data) { Socket* socket = reinterpret_cast(data); if (socket->fd() >= 0) { const int64_t flags = 1 << kCloseCommand; @@ -1266,9 +1264,7 @@ static void NormalSocketFinalizer(void* isolate_data, socket->Release(); } -static void ListeningSocketFinalizer(void* isolate_data, - Dart_WeakPersistentHandle handle, - void* data) { +static void ListeningSocketFinalizer(void* isolate_data, void* data) { Socket* socket = reinterpret_cast(data); if (socket->fd() >= 0) { const int64_t flags = (1 << kListeningSocket) | (1 << kCloseCommand); @@ -1279,9 +1275,7 @@ static void ListeningSocketFinalizer(void* isolate_data, socket->Release(); } -static void StdioSocketFinalizer(void* isolate_data, - Dart_WeakPersistentHandle handle, - void* data) { +static void StdioSocketFinalizer(void* isolate_data, void* data) { Socket* socket = reinterpret_cast(data); if (socket->fd() >= 0) { socket->CloseFd(); @@ -1289,9 +1283,7 @@ static void StdioSocketFinalizer(void* isolate_data, socket->Release(); } -static void SignalSocketFinalizer(void* isolate_data, - Dart_WeakPersistentHandle handle, - void* data) { +static void SignalSocketFinalizer(void* isolate_data, void* data) { Socket* socket = reinterpret_cast(data); if (socket->fd() >= 0) { Process::ClearSignalHandlerByFd(socket->fd(), socket->isolate_port()); @@ -1312,7 +1304,7 @@ void Socket::ReuseSocketIdNativeField(Dart_Handle handle, if (Dart_IsError(err)) { Dart_PropagateError(err); } - Dart_WeakPersistentHandleFinalizer callback; + Dart_HandleFinalizer callback; switch (finalizer) { case kFinalizerNormal: callback = NormalSocketFinalizer; @@ -1332,8 +1324,8 @@ void Socket::ReuseSocketIdNativeField(Dart_Handle handle, break; } if (callback != NULL) { - Dart_NewWeakPersistentHandle(handle, reinterpret_cast(socket), - sizeof(Socket), callback); + Dart_NewFinalizableHandle(handle, reinterpret_cast(socket), + sizeof(Socket), callback); } } diff --git a/runtime/bin/sync_socket.cc b/runtime/bin/sync_socket.cc index 99cf7928237..c8e7714bcd8 100644 --- a/runtime/bin/sync_socket.cc +++ b/runtime/bin/sync_socket.cc @@ -313,9 +313,7 @@ void FUNCTION_NAME(SynchronousSocket_GetRemotePeer)(Dart_NativeArguments args) { delete addr; } -static void SynchronousSocketFinalizer(void* isolate_data, - Dart_WeakPersistentHandle handle, - void* data) { +static void SynchronousSocketFinalizer(void* isolate_data, void* data) { SynchronousSocket* socket = reinterpret_cast(data); if (socket->fd() >= 0) { SynchronousSocket::Close(socket->fd()); @@ -334,9 +332,9 @@ Dart_Handle SynchronousSocket::SetSocketIdNativeField( return error; } - Dart_NewWeakPersistentHandle(handle, reinterpret_cast(socket), - sizeof(SynchronousSocket), - SynchronousSocketFinalizer); + Dart_NewFinalizableHandle(handle, reinterpret_cast(socket), + sizeof(SynchronousSocket), + SynchronousSocketFinalizer); return error; } diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index 8c6afd44a72..978d67b50ad 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -239,17 +239,26 @@ typedef struct _Dart_IsolateGroup* Dart_IsolateGroup; * The type Dart_PersistentHandle is a Dart_Handle and it is used to * document that a persistent handle is expected as a parameter to a call * or the return value from a call is a persistent handle. + * + * FinalizableHandles are persistent handles which are auto deleted when + * 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. */ typedef struct _Dart_Handle* Dart_Handle; typedef Dart_Handle Dart_PersistentHandle; typedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle; -// These three structs are versioned by DART_API_DL_MAJOR_VERSION, bump the +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); /** * Is this an error handle? @@ -444,6 +453,8 @@ DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1, /** * Deallocates a persistent handle. + * + * Requires there to be a current isolate group. */ DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object); @@ -457,16 +468,17 @@ DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object); * 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 not have a current isolate, and 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 from the callback. If the handle is deleted before the object becomes - * unreachable, the callback is never invoked. + * 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. * * Requires there to be a current isolate. * @@ -488,12 +500,86 @@ Dart_NewWeakPersistentHandle(Dart_Handle object, intptr_t external_allocation_size, Dart_WeakPersistentHandleFinalizer callback); +/** + * Deletes the given weak persistent [object] handle. + * + * Requires there to be a current isolate group. + */ DART_EXPORT void Dart_DeleteWeakPersistentHandle( Dart_WeakPersistentHandle object); +/** + * Updates the external memory size for the given weak persistent handle. + * + * May trigger garbage collection. + */ DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object, intptr_t external_allocation_size); +/** + * Allocates a finalizable handle for an object. + * + * This handle has the lifetime of the current isolate group 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_DeleteFinalizableHandle. + * + * If the object becomes unreachable the callback is invoked with the + * the peer as argument. 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. + * 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. + * + * Requires there to be a current isolate. + * + * \param object An object. + * \param peer A pointer to a native object or NULL. This value is + * provided to callback when it is invoked. + * \param external_allocation_size The number of externally allocated + * bytes for peer. Used to inform the garbage collector. + * \param callback A function pointer that will be invoked sometime + * after the object is garbage collected, unless the handle has been deleted. + * A valid callback needs to be specified it cannot be NULL. + * + * \return The finalizable handle or NULL. NULL is returned in case of bad + * parameters. + */ +DART_EXPORT Dart_FinalizableHandle +Dart_NewFinalizableHandle(Dart_Handle object, + void* peer, + intptr_t external_allocation_size, + Dart_HandleFinalizer callback); + +/** + * Deletes the given finalizable [object] handle. + * + * The caller has to provide the actual Dart object the handle was created from + * to prove the object (and therefore the finalizable handle) is still alive. + * + * Requires there to be a current isolate. + */ +DART_EXPORT void Dart_DeleteFinalizableHandle(Dart_FinalizableHandle object, + Dart_Handle strong_ref_to_object); + +/** + * Updates the external memory size for the given finalizable handle. + * + * The caller has to provide the actual Dart object the handle was created from + * to prove the object (and therefore the finalizable handle) is still alive. + * + * May trigger garbage collection. + */ +DART_EXPORT void Dart_UpdateFinalizableExternalSize( + Dart_FinalizableHandle object, + Dart_Handle strong_ref_to_object, + intptr_t external_allocation_size); + /* * ========================== * Initialization and Globals diff --git a/runtime/include/dart_api_dl.h b/runtime/include/dart_api_dl.h index ba8424dd1ec..19fd0f3b0cb 100644 --- a/runtime/include/dart_api_dl.h +++ b/runtime/include/dart_api_dl.h @@ -108,6 +108,25 @@ DART_EXTERN_C Dart_WeakPersistentHandle (*Dart_NewWeakPersistentHandle_DL)( DART_EXTERN_C void (*Dart_DeleteWeakPersistentHandle_DL)( Dart_WeakPersistentHandle object); +DART_EXTERN_C void (*Dart_UpdateExternalSize_DL)( + Dart_WeakPersistentHandle object, + intptr_t external_allocation_size); + +DART_EXTERN_C Dart_FinalizableHandle (*Dart_NewFinalizableHandle_DL)( + Dart_Handle object, + void* peer, + intptr_t external_allocation_size, + Dart_HandleFinalizer callback); + +DART_EXTERN_C void (*Dart_DeleteFinalizableHandle_DL)( + Dart_FinalizableHandle object, + Dart_Handle strong_ref_to_object); + +DART_EXTERN_C void (*Dart_UpdateFinalizableExternalSize_DL)( + Dart_FinalizableHandle object, + Dart_Handle strong_ref_to_object, + intptr_t external_allocation_size); + DART_EXTERN_C bool (*Dart_Post_DL)(Dart_Port_DL port_id, Dart_Handle object); DART_EXTERN_C Dart_Handle (*Dart_NewSendPort_DL)(Dart_Port_DL port_id); @@ -123,4 +142,4 @@ DART_EXTERN_C void (*Dart_ExitScope_DL)(); // // End of verbatim copy. -#endif /* RUNTIME_INCLUDE_DART_API_DL_H_ */ /* NOLINT */ \ No newline at end of file +#endif /* RUNTIME_INCLUDE_DART_API_DL_H_ */ /* NOLINT */ diff --git a/runtime/include/dart_version.h b/runtime/include/dart_version.h index 777620be946..d2d904dd988 100644 --- a/runtime/include/dart_version.h +++ b/runtime/include/dart_version.h @@ -11,6 +11,6 @@ // 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 0 +#define DART_API_DL_MINOR_VERSION 1 #endif /* RUNTIME_INCLUDE_DART_VERSION_H_ */ /* NOLINT */ diff --git a/runtime/include/internal/dart_api_dl_impl.h b/runtime/include/internal/dart_api_dl_impl.h index f77063d99bf..a306e2d3a18 100644 --- a/runtime/include/internal/dart_api_dl_impl.h +++ b/runtime/include/internal/dart_api_dl_impl.h @@ -41,6 +41,10 @@ F(Dart_NewWeakPersistentHandle) \ F(Dart_HandleFromWeakPersistent) \ F(Dart_DeleteWeakPersistentHandle) \ + F(Dart_UpdateExternalSize) \ + F(Dart_NewFinalizableHandle) \ + F(Dart_DeleteFinalizableHandle) \ + F(Dart_UpdateFinalizableExternalSize) \ /* Dart_Port */ \ F(Dart_Post) \ F(Dart_NewSendPort) \ diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc index fe943ae91ec..4ed6bfe3174 100644 --- a/runtime/lib/isolate.cc +++ b/runtime/lib/isolate.cc @@ -615,7 +615,6 @@ DEFINE_NATIVE_ENTRY(Isolate_sendOOB, 0, 2) { } static void ExternalTypedDataFinalizer(void* isolate_callback_data, - Dart_WeakPersistentHandle handle, void* peer) { free(peer); } @@ -725,7 +724,8 @@ DEFINE_NATIVE_ENTRY(TransferableTypedData_materialize, 0, 1) { thread->heap()->SpaceForExternal(length))); FinalizablePersistentHandle::New(thread->isolate(), typed_data, /* peer= */ data, - &ExternalTypedDataFinalizer, length); + &ExternalTypedDataFinalizer, length, + /*auto_delete=*/true); return typed_data.raw(); } diff --git a/runtime/observatory/lib/object_graph.dart b/runtime/observatory/lib/object_graph.dart index 6e448723905..0e891ce7b1d 100644 --- a/runtime/observatory/lib/object_graph.dart +++ b/runtime/observatory/lib/object_graph.dart @@ -218,7 +218,7 @@ abstract class SnapshotObject { int get internalSize; /// The sum of all external allocations associated with this object. - /// See Dart_NewWeakPersistentHandle. + /// See Dart_NewFinalizableHandle and Dart_NewWeakPersistentHandle. int get externalSize; /// The [shallowSize] of this object, plus the retainedSize of all its diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index a6c66f32a56..d77f84cd777 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -10,6 +10,7 @@ #include "lib/stacktrace.h" #include "platform/assert.h" +#include "platform/unicode.h" #include "vm/class_finalizer.h" #include "vm/clustered_snapshot.h" #include "vm/compilation_trace.h" @@ -22,7 +23,6 @@ #include "vm/debugger.h" #include "vm/dwarf.h" #include "vm/elf.h" -#include "platform/unicode.h" #include "vm/exceptions.h" #include "vm/flags.h" #include "vm/growable_array.h" @@ -690,6 +690,14 @@ FinalizablePersistentHandle* FinalizablePersistentHandle::Cast( #endif return reinterpret_cast(handle); } +FinalizablePersistentHandle* FinalizablePersistentHandle::Cast( + Dart_FinalizableHandle handle) { +#if defined(DEBUG) + ApiState* state = IsolateGroup::Current()->api_state(); + ASSERT(state->IsValidFinalizableHandle(handle)); +#endif + return reinterpret_cast(handle); +} void FinalizablePersistentHandle::Finalize( IsolateGroup* isolate_group, @@ -697,13 +705,24 @@ void FinalizablePersistentHandle::Finalize( if (!handle->raw()->IsHeapObject()) { return; // Free handle. } - Dart_WeakPersistentHandleFinalizer callback = handle->callback(); - ASSERT(callback != NULL); void* peer = handle->peer(); - Dart_WeakPersistentHandle object = handle->apiHandle(); - (*callback)(isolate_group->embedder_data(), object, 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); + } + state->FreeWeakPersistentHandle(handle); } @@ -920,6 +939,19 @@ Dart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object) { return Api::NewHandle(thread, weak_ref->raw()); } +static Dart_Handle HandleFromFinalizable(Dart_FinalizableHandle object) { + Thread* thread = Thread::Current(); + Isolate* isolate = thread->isolate(); + CHECK_ISOLATE(isolate); + ApiState* state = isolate->group()->api_state(); + ASSERT(state != NULL); + TransitionNativeToVM transition(thread); + NoSafepointScope no_safepoint_scope; + FinalizablePersistentHandle* weak_ref = + FinalizablePersistentHandle::Cast(object); + return Api::NewHandle(thread, weak_ref->raw()); +} + DART_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object) { DARTSCOPE(Thread::Current()); Isolate* I = T->isolate(); @@ -943,7 +975,7 @@ DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1, obj1_ref->set_raw(obj2_ref); } -static Dart_WeakPersistentHandle AllocateFinalizableHandle( +static Dart_WeakPersistentHandle AllocateWeakPersistentHandle( Thread* thread, const Object& ref, void* peer, @@ -954,11 +986,12 @@ static Dart_WeakPersistentHandle AllocateFinalizableHandle( } FinalizablePersistentHandle* finalizable_ref = FinalizablePersistentHandle::New(thread->isolate(), ref, peer, callback, - external_allocation_size); - return finalizable_ref->apiHandle(); + external_allocation_size, + /*auto_delete=*/true); + return finalizable_ref->ApiWeakPersistentHandle(); } -static Dart_WeakPersistentHandle AllocateFinalizableHandle( +static Dart_WeakPersistentHandle AllocateWeakPersistentHandle( Thread* thread, Dart_Handle object, void* peer, @@ -967,6 +1000,36 @@ static Dart_WeakPersistentHandle AllocateFinalizableHandle( REUSABLE_OBJECT_HANDLESCOPE(thread); Object& ref = thread->ObjectHandle(); ref = Api::UnwrapHandle(object); + return AllocateWeakPersistentHandle(thread, ref, peer, + external_allocation_size, callback); +} + +static Dart_FinalizableHandle AllocateFinalizableHandle( + Thread* thread, + const Object& ref, + void* peer, + intptr_t external_allocation_size, + 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); + return finalizable_ref->ApiFinalizableHandle(); +} + +static Dart_FinalizableHandle AllocateFinalizableHandle( + Thread* thread, + Dart_Handle object, + void* peer, + intptr_t external_allocation_size, + Dart_HandleFinalizer callback) { + REUSABLE_OBJECT_HANDLESCOPE(thread); + Object& ref = thread->ObjectHandle(); + ref = Api::UnwrapHandle(object); return AllocateFinalizableHandle(thread, ref, peer, external_allocation_size, callback); } @@ -982,6 +1045,22 @@ Dart_NewWeakPersistentHandle(Dart_Handle object, return NULL; } TransitionNativeToVM transition(thread); + + return AllocateWeakPersistentHandle(thread, object, peer, + external_allocation_size, callback); +} + +DART_EXPORT Dart_FinalizableHandle +Dart_NewFinalizableHandle(Dart_Handle object, + void* peer, + intptr_t external_allocation_size, + Dart_HandleFinalizer callback) { + Thread* thread = Thread::Current(); + CHECK_ISOLATE(thread->isolate()); + if (callback == nullptr) { + return nullptr; + } + TransitionNativeToVM transition(thread); return AllocateFinalizableHandle(thread, object, peer, external_allocation_size, callback); } @@ -998,6 +1077,21 @@ DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object, weak_ref->UpdateExternalSize(external_size, isolate_group); } +DART_EXPORT void Dart_UpdateFinalizableExternalSize( + Dart_FinalizableHandle object, + Dart_Handle strong_ref_to_object, + intptr_t external_allocation_size) { + if (!Dart_IdentityEquals(strong_ref_to_object, + HandleFromFinalizable(object))) { + FATAL1( + "%s expects arguments 'object' and 'strong_ref_to_object' to point to " + "the same object.", + CURRENT_FUNC); + } + auto wph_object = reinterpret_cast(object); + Dart_UpdateExternalSize(wph_object, external_allocation_size); +} + DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object) { IsolateGroup* isolate_group = IsolateGroup::Current(); CHECK_ISOLATE_GROUP(isolate_group); @@ -1025,6 +1119,22 @@ DART_EXPORT void Dart_DeleteWeakPersistentHandle( state->FreeWeakPersistentHandle(weak_ref); } +DART_EXPORT void Dart_DeleteFinalizableHandle( + Dart_FinalizableHandle object, + Dart_Handle strong_ref_to_object) { + if (!Dart_IdentityEquals(strong_ref_to_object, + HandleFromFinalizable(object))) { + FATAL1( + "%s expects arguments 'object' and 'strong_ref_to_object' to point to " + "the same object.", + CURRENT_FUNC); + } + + auto wph_object = reinterpret_cast(object); + + return Dart_DeleteWeakPersistentHandle(wph_object); +} + // --- Initialization and Globals --- DART_EXPORT const char* Dart_VersionString() { @@ -3814,8 +3924,8 @@ static Dart_Handle NewExternalTypedData( result = ExternalTypedData::New(cid, reinterpret_cast(data), length, thread->heap()->SpaceForExternal(bytes)); if (callback != nullptr) { - AllocateFinalizableHandle(thread, result, peer, external_allocation_size, - callback); + AllocateWeakPersistentHandle(thread, result, peer, external_allocation_size, + callback); } return Api::NewHandle(thread, result.raw()); } diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc index 70633535630..84681a8772e 100644 --- a/runtime/vm/dart_api_impl_test.cc +++ b/runtime/vm/dart_api_impl_test.cc @@ -2792,12 +2792,16 @@ TEST_CASE(DartAPI_ExternalUint8ClampedArrayAccess) { 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, @@ -2826,9 +2830,7 @@ TEST_CASE(DartAPI_ExternalTypedDataCallback) { } } -static void SlowFinalizer(void* isolate_callback_data, - Dart_WeakPersistentHandle handle, - void* peer) { +static void SlowFinalizer(void* isolate_callback_data, void* peer) { OS::Sleep(10); intptr_t* count = reinterpret_cast(peer); (*count)++; @@ -2839,9 +2841,36 @@ TEST_CASE(DartAPI_SlowFinalizer) { for (intptr_t i = 0; i < 10; i++) { Dart_EnterScope(); Dart_Handle str1 = Dart_NewStringFromCString("Live fast"); - Dart_NewWeakPersistentHandle(str1, &count, 0, SlowFinalizer); + Dart_NewFinalizableHandle(str1, &count, 0, SlowFinalizer); Dart_Handle str2 = Dart_NewStringFromCString("Die young"); - Dart_NewWeakPersistentHandle(str2, &count, 0, SlowFinalizer); + Dart_NewFinalizableHandle(str2, &count, 0, SlowFinalizer); + Dart_ExitScope(); + + { + TransitionNativeToVM transition(thread); + GCTestHelper::CollectAllGarbage(); + } + } + + EXPECT_EQ(20, count); +} + +static void SlowWeakPersistentHandle(void* isolate_callback_data, + Dart_WeakPersistentHandle handle, + void* peer) { + OS::Sleep(10); + intptr_t* count = reinterpret_cast(peer); + (*count)++; +} + +TEST_CASE(DartAPI_SlowWeakPersistenhandle) { + 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); + Dart_Handle str2 = Dart_NewStringFromCString("Die young"); + Dart_NewWeakPersistentHandle(str2, &count, 0, SlowWeakPersistentHandle); Dart_ExitScope(); { @@ -3219,6 +3248,122 @@ TEST_CASE(DartAPI_WeakPersistentHandle) { } } +static Dart_FinalizableHandle finalizable_new_ref = nullptr; +static void* finalizable_new_ref_peer = 0; +static Dart_FinalizableHandle finalizable_old_ref = nullptr; +static void* finalizable_old_ref_peer = 0; + +static void FinalizableHandleCallback(void* isolate_callback_data, void* peer) { + if (peer == finalizable_new_ref_peer) { + finalizable_new_ref_peer = 0; + finalizable_new_ref = nullptr; + } else if (peer == finalizable_old_ref_peer) { + finalizable_old_ref_peer = 0; + finalizable_old_ref = nullptr; + } +} + +TEST_CASE(DartAPI_FinalizableHandle) { + // GCs due to allocations or weak handle creation can cause early promotion + // and interfer with the scenario this test is verifying. + NoHeapGrowthControlScope force_growth; + + void* peer = reinterpret_cast(0); + Dart_Handle local_new_ref = Dart_Null(); + finalizable_new_ref = Dart_NewFinalizableHandle(local_new_ref, peer, 0, + FinalizableHandleCallback); + finalizable_new_ref_peer = peer; + + peer = reinterpret_cast(1); + Dart_Handle local_old_ref = Dart_Null(); + finalizable_old_ref = Dart_NewFinalizableHandle(local_old_ref, peer, 0, + FinalizableHandleCallback); + finalizable_old_ref_peer = peer; + + { + Dart_EnterScope(); + + // Create an object in new space. + Dart_Handle new_ref = AllocateNewString("new string"); + EXPECT_VALID(new_ref); + + // Create an object in old space. + Dart_Handle old_ref = AllocateOldString("old string"); + EXPECT_VALID(old_ref); + + // Create a weak ref to the new space object. + peer = reinterpret_cast(2); + finalizable_new_ref = + Dart_NewFinalizableHandle(new_ref, peer, 0, FinalizableHandleCallback); + finalizable_new_ref_peer = peer; + + // Create a weak ref to the old space object. + peer = reinterpret_cast(3); + finalizable_old_ref = + Dart_NewFinalizableHandle(old_ref, peer, 0, FinalizableHandleCallback); + finalizable_old_ref_peer = peer; + + { + TransitionNativeToVM transition(thread); + // Garbage collect new space. + GCTestHelper::CollectNewSpace(); + } + + // Nothing should be invalidated or cleared. + EXPECT_VALID(new_ref); + EXPECT(!Dart_IsNull(new_ref)); + EXPECT_VALID(old_ref); + EXPECT(!Dart_IsNull(old_ref)); + + { + TransitionNativeToVM transition(thread); + // Garbage collect old space. + GCTestHelper::CollectOldSpace(); + } + + // Nothing should be invalidated or cleared. + EXPECT_VALID(new_ref); + EXPECT(!Dart_IsNull(new_ref)); + EXPECT_VALID(old_ref); + EXPECT(!Dart_IsNull(old_ref)); + + // Delete local (strong) references. + Dart_ExitScope(); + } + + { + TransitionNativeToVM transition(thread); + // Garbage collect new space again. + GCTestHelper::CollectNewSpace(); + } + + { + Dart_EnterScope(); + // Weak ref to new space object should now be cleared. + EXPECT(finalizable_new_ref == nullptr); + Dart_ExitScope(); + } + + { + TransitionNativeToVM transition(thread); + // Garbage collect old space again. + GCTestHelper::CollectOldSpace(); + } + + { + Dart_EnterScope(); + // Weak ref to old space object should now be cleared. + EXPECT(finalizable_new_ref == nullptr); + EXPECT(finalizable_old_ref == nullptr); + Dart_ExitScope(); + } + + { + TransitionNativeToVM transition(thread); + GCTestHelper::CollectAllGarbage(); + } +} + TEST_CASE(DartAPI_WeakPersistentHandleErrors) { Dart_EnterScope(); @@ -3239,6 +3384,26 @@ TEST_CASE(DartAPI_WeakPersistentHandleErrors) { Dart_ExitScope(); } +TEST_CASE(DartAPI_FinalizableHandleErrors) { + Dart_EnterScope(); + + // NULL callback. + Dart_Handle obj1 = NewString("new string"); + EXPECT_VALID(obj1); + Dart_FinalizableHandle ref1 = + Dart_NewFinalizableHandle(obj1, nullptr, 0, nullptr); + EXPECT_EQ(ref1, static_cast(nullptr)); + + // Immediate object. + Dart_Handle obj2 = Dart_NewInteger(0); + EXPECT_VALID(obj2); + Dart_FinalizableHandle ref2 = + Dart_NewFinalizableHandle(obj2, nullptr, 0, FinalizableHandleCallback); + EXPECT_EQ(ref2, static_cast(nullptr)); + + Dart_ExitScope(); +} + static Dart_PersistentHandle persistent_handle1; static Dart_WeakPersistentHandle weak_persistent_handle2; static Dart_WeakPersistentHandle weak_persistent_handle3; @@ -3285,6 +3450,44 @@ TEST_CASE(DartAPI_WeakPersistentHandleCleanupFinalizer) { Dart_ExitScope(); } +static Dart_FinalizableHandle finalizable_handle3; + +static void FinalizableHandlePeerCleanupFinalizer(void* isolate_callback_data, + void* peer) { + Dart_DeletePersistentHandle(persistent_handle1); + Dart_DeleteWeakPersistentHandle(weak_persistent_handle2); + *static_cast(peer) = 42; +} + +TEST_CASE(DartAPI_FinalizableHandleCleanupFinalizer) { + Heap* heap = Isolate::Current()->heap(); + + const char* kTestString1 = "Test String1"; + Dart_EnterScope(); + CHECK_API_SCOPE(thread); + Dart_Handle ref1 = Dart_NewStringFromCString(kTestString1); + persistent_handle1 = Dart_NewPersistentHandle(ref1); + Dart_Handle ref2 = Dart_NewStringFromCString(kTestString1); + int peer2 = 0; + weak_persistent_handle2 = + Dart_NewWeakPersistentHandle(ref2, &peer2, 0, NopCallback); + int peer3 = 0; + { + Dart_EnterScope(); + Dart_Handle ref3 = Dart_NewStringFromCString(kTestString1); + finalizable_handle3 = Dart_NewFinalizableHandle( + ref3, &peer3, 0, FinalizableHandlePeerCleanupFinalizer); + Dart_ExitScope(); + } + { + TransitionNativeToVM transition(thread); + GCTestHelper::CollectAllGarbage(); + EXPECT(heap->ExternalInWords(Heap::kOld) == 0); + EXPECT(peer3 == 42); + } + Dart_ExitScope(); +} + static void WeakPersistentHandlePeerFinalizer(void* isolate_callback_data, Dart_WeakPersistentHandle handle, void* peer) { @@ -3313,6 +3516,32 @@ TEST_CASE(DartAPI_WeakPersistentHandleCallback) { } } +static void FinalizableHandlePeerFinalizer(void* isolate_callback_data, + void* peer) { + *static_cast(peer) = 42; +} + +TEST_CASE(DartAPI_FinalizableHandleCallback) { + Dart_FinalizableHandle weak_ref = nullptr; + int peer = 0; + { + Dart_EnterScope(); + Dart_Handle obj = NewString("new string"); + EXPECT_VALID(obj); + weak_ref = Dart_NewFinalizableHandle(obj, &peer, 0, + FinalizableHandlePeerFinalizer); + EXPECT(peer == 0); + Dart_ExitScope(); + } + { + TransitionNativeToVM transition(thread); + GCTestHelper::CollectOldSpace(); + EXPECT(peer == 0); + GCTestHelper::CollectNewSpace(); + EXPECT(peer == 42); + } +} + TEST_CASE(DartAPI_WeakPersistentHandleNoCallback) { Dart_WeakPersistentHandle weak_ref = NULL; int peer = 0; @@ -3337,6 +3566,35 @@ TEST_CASE(DartAPI_WeakPersistentHandleNoCallback) { } } +TEST_CASE(DartAPI_FinalizableHandleNoCallback) { + Dart_FinalizableHandle weak_ref = nullptr; + Dart_PersistentHandle strong_ref = nullptr; + int peer = 0; + { + Dart_EnterScope(); + Dart_Handle obj = NewString("new string"); + EXPECT_VALID(obj); + weak_ref = Dart_NewFinalizableHandle(obj, &peer, 0, + FinalizableHandlePeerFinalizer); + strong_ref = Dart_NewPersistentHandle(obj); + Dart_ExitScope(); + } + // A finalizer is not invoked on a deleted handle. Therefore, the + // peer value should not change after the referent is collected. + Dart_DeleteFinalizableHandle(weak_ref, strong_ref); + Dart_DeletePersistentHandle(strong_ref); + EXPECT(peer == 0); + { + TransitionNativeToVM transition(thread); + GCTestHelper::CollectOldSpace(); + EXPECT(peer == 0); + GCTestHelper::CollectNewSpace(); + EXPECT(peer == 0); + } +} + +Dart_WeakPersistentHandle delete_on_finalization; + VM_UNIT_TEST_CASE(DartAPI_WeakPersistentHandlesCallbackShutdown) { TestCase::CreateTestIsolate(); Dart_EnterScope(); @@ -3349,6 +3607,17 @@ VM_UNIT_TEST_CASE(DartAPI_WeakPersistentHandlesCallbackShutdown) { EXPECT(peer == 42); } +VM_UNIT_TEST_CASE(DartAPI_FinalizableHandlesCallbackShutdown) { + TestCase::CreateTestIsolate(); + Dart_EnterScope(); + Dart_Handle ref = Dart_True(); + int peer = 1234; + Dart_NewFinalizableHandle(ref, &peer, 0, FinalizableHandlePeerFinalizer); + Dart_ExitScope(); + Dart_ShutdownIsolate(); + EXPECT(peer == 42); +} + TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSize) { Heap* heap = Isolate::Current()->heap(); EXPECT(heap->ExternalInWords(Heap::kNew) == 0); @@ -3397,6 +3666,52 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSize) { } } +TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSize) { + Heap* heap = Isolate::Current()->heap(); + EXPECT(heap->ExternalInWords(Heap::kNew) == 0); + EXPECT(heap->ExternalInWords(Heap::kOld) == 0); + Dart_FinalizableHandle weak1 = nullptr; + static const intptr_t kWeak1ExternalSize = 1 * KB; + { + Dart_EnterScope(); + Dart_Handle obj = NewString("weakly referenced string"); + EXPECT_VALID(obj); + weak1 = Dart_NewFinalizableHandle(obj, nullptr, kWeak1ExternalSize, + NopCallback); + Dart_ExitScope(); + } + Dart_PersistentHandle strong_ref = nullptr; + Dart_FinalizableHandle weak2 = nullptr; + static const intptr_t kWeak2ExternalSize = 2 * KB; + { + Dart_EnterScope(); + Dart_Handle obj = NewString("strongly referenced string"); + EXPECT_VALID(obj); + strong_ref = Dart_NewPersistentHandle(obj); + weak2 = Dart_NewFinalizableHandle(obj, nullptr, kWeak2ExternalSize, + NopCallback); + EXPECT_VALID(AsHandle(strong_ref)); + Dart_ExitScope(); + } + { + TransitionNativeToVM transition(thread); + GCTestHelper::CollectOldSpace(); + EXPECT(heap->ExternalInWords(Heap::kNew) == + (kWeak1ExternalSize + kWeak2ExternalSize) / kWordSize); + // Collect weakly referenced string, and promote strongly referenced string. + GCTestHelper::CollectNewSpace(); + GCTestHelper::CollectNewSpace(); + EXPECT(heap->ExternalInWords(Heap::kNew) == 0); + EXPECT(heap->ExternalInWords(Heap::kOld) == kWeak2ExternalSize / kWordSize); + } + Dart_DeletePersistentHandle(strong_ref); + { + TransitionNativeToVM transition(thread); + GCTestHelper::CollectOldSpace(); + EXPECT(heap->ExternalInWords(Heap::kOld) == 0); + } +} + TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeNewspaceGC) { Heap* heap = Isolate::Current()->heap(); Dart_WeakPersistentHandle weak1 = NULL; @@ -3439,6 +3754,49 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeNewspaceGC) { } } +TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeNewspaceGC) { + Heap* heap = Isolate::Current()->heap(); + Dart_FinalizableHandle weak1 = nullptr; + Dart_PersistentHandle strong1 = nullptr; + // Large enough to exceed any new space limit. Not actually allocated. + const intptr_t kWeak1ExternalSize = 500 * MB; + { + Dart_EnterScope(); + Dart_Handle obj = NewString("weakly referenced string"); + EXPECT_VALID(obj); + // Triggers a scavenge immediately, since kWeak1ExternalSize is above limit. + weak1 = Dart_NewFinalizableHandle(obj, nullptr, kWeak1ExternalSize, + NopCallback); + strong1 = Dart_NewPersistentHandle(obj); + // ... but the object is still alive and not yet promoted, so external size + // in new space is still above the limit. Thus, even the following tiny + // external allocation will trigger another scavenge. + Dart_FinalizableHandle trigger = + Dart_NewFinalizableHandle(obj, nullptr, 1, NopCallback); + Dart_DeleteFinalizableHandle(trigger, obj); + // After the two scavenges above, 'obj' should now be promoted, hence its + // external size charged to old space. + { + CHECK_API_SCOPE(thread); + TransitionNativeToVM transition(thread); + HANDLESCOPE(thread); + String& handle = String::Handle(thread->zone()); + handle ^= Api::UnwrapHandle(obj); + EXPECT(handle.IsOld()); + } + EXPECT(heap->ExternalInWords(Heap::kNew) == 0); + EXPECT(heap->ExternalInWords(Heap::kOld) == kWeak1ExternalSize / kWordSize); + Dart_ExitScope(); + } + Dart_DeleteFinalizableHandle(weak1, strong1); + Dart_DeletePersistentHandle(strong1); + { + TransitionNativeToVM transition(thread); + GCTestHelper::CollectOldSpace(); + EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld)); + } +} + TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOldspaceGC) { // Check that external allocation in old space can trigger GC. Isolate* isolate = Isolate::Current(); @@ -3446,6 +3804,7 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOldspaceGC) { Dart_Handle live = AllocateOldString("live"); EXPECT_VALID(live); Dart_WeakPersistentHandle weak = NULL; + Dart_WeakPersistentHandle weak2 = NULL; { TransitionNativeToVM transition(thread); GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size. @@ -3469,7 +3828,48 @@ 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); + weak2 = + Dart_NewWeakPersistentHandle(live, NULL, kHugeExternalSize, NopCallback); + { + TransitionNativeToVM transition(thread); + GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size. + // Expect small garbage to be collected. + EXPECT_EQ(kHugeExternalSize, + isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize); + } + Dart_ExitScope(); +} + +TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeOldspaceGC) { + // Check that external allocation in old space can trigger GC. + Isolate* isolate = Isolate::Current(); + Dart_EnterScope(); + Dart_Handle live = AllocateOldString("live"); + EXPECT_VALID(live); + Dart_FinalizableHandle weak = NULL; + { + TransitionNativeToVM transition(thread); + GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size. + EXPECT_EQ(0, isolate->heap()->ExternalInWords(Heap::kOld)); + } + const intptr_t kSmallExternalSize = 1 * KB; + { + Dart_EnterScope(); + Dart_Handle dead = AllocateOldString("dead"); + EXPECT_VALID(dead); + weak = Dart_NewFinalizableHandle(dead, nullptr, kSmallExternalSize, + NopCallback); + Dart_ExitScope(); + } + { + TransitionNativeToVM transition(thread); + GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size. + EXPECT_EQ(kSmallExternalSize, + isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize); + } + // Large enough to trigger GC in old space. Not actually allocated. + const intptr_t kHugeExternalSize = (kWordSize == 4) ? 513 * MB : 1025 * MB; + Dart_NewFinalizableHandle(live, nullptr, kHugeExternalSize, NopCallback); { TransitionNativeToVM transition(thread); GCTestHelper::WaitForGCTasks(); // Finalize GC for accurate live size. @@ -3514,6 +3914,44 @@ TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOddReferents) { } } +TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeOddReferents) { + Heap* heap = Isolate::Current()->heap(); + Dart_FinalizableHandle weak1 = nullptr; + Dart_PersistentHandle strong1 = nullptr; + static const intptr_t kWeak1ExternalSize = 1 * KB; + Dart_FinalizableHandle weak2 = nullptr; + Dart_PersistentHandle strong2 = nullptr; + static const intptr_t kWeak2ExternalSize = 2 * KB; + EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld)); + { + Dart_EnterScope(); + Dart_Handle dart_true = Dart_True(); // VM heap object. + EXPECT_VALID(dart_true); + weak1 = Dart_NewFinalizableHandle(dart_true, nullptr, kWeak1ExternalSize, + UnreachedCallback); + strong1 = Dart_NewPersistentHandle(dart_true); + Dart_Handle zero = Dart_False(); // VM heap object. + EXPECT_VALID(zero); + weak2 = Dart_NewFinalizableHandle(zero, nullptr, kWeak2ExternalSize, + UnreachedCallback); + strong2 = Dart_NewPersistentHandle(zero); + // Both should be charged to old space. + EXPECT(heap->ExternalInWords(Heap::kOld) == + (kWeak1ExternalSize + kWeak2ExternalSize) / kWordSize); + Dart_ExitScope(); + } + Dart_DeleteFinalizableHandle(weak1, strong1); + Dart_DeletePersistentHandle(strong1); + Dart_DeleteFinalizableHandle(weak2, strong2); + Dart_DeletePersistentHandle(strong2); + EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld)); + { + TransitionNativeToVM transition(thread); + GCTestHelper::CollectOldSpace(); + EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld)); + } +} + #define EXAMPLE_RESOURCE_NATIVE_LIST(V) \ V(ExampleResource_Allocate, 1) \ V(ExampleResource_Use, 1) \ diff --git a/runtime/vm/dart_api_state.h b/runtime/vm/dart_api_state.h index 5b22c935cf9..7b52e98b981 100644 --- a/runtime/vm/dart_api_state.h +++ b/runtime/vm/dart_api_state.h @@ -190,12 +190,29 @@ 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); + intptr_t external_size, + bool auto_delete); + + static FinalizablePersistentHandle* New(Isolate* isolate, + const Object& object, + void* peer, + Dart_HandleFinalizer callback, + intptr_t external_size, + bool auto_delete); // Accessors. ObjectPtr raw() const { return raw_; } @@ -204,10 +221,26 @@ class FinalizablePersistentHandle { return OFFSET_OF(FinalizablePersistentHandle, raw_); } void* peer() const { return peer_; } - Dart_WeakPersistentHandleFinalizer callback() const { return callback_; } - Dart_WeakPersistentHandle apiHandle() { + 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(callback_.finalizable); + } + Dart_WeakPersistentHandle ApiWeakPersistentHandle() { return reinterpret_cast(this); } + Dart_FinalizableHandle ApiFinalizableHandle() { + return reinterpret_cast(this); + } + + bool auto_delete() const { return auto_delete_; } intptr_t external_size() const { return ExternalSizeInWordsBits::decode(external_data_) * kWordSize; @@ -256,6 +289,7 @@ class FinalizablePersistentHandle { } static FinalizablePersistentHandle* Cast(Dart_WeakPersistentHandle handle); + static FinalizablePersistentHandle* Cast(Dart_FinalizableHandle handle); private: enum { @@ -264,6 +298,15 @@ 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(reinterpret_cast(free_list)); ASSERT(!raw_->IsHeapObject()); } + void FreeHandle(FinalizablePersistentHandle* free_list) { Clear(); SetNext(free_list); @@ -302,7 +349,9 @@ class FinalizablePersistentHandle { raw_ = Object::null(); peer_ = NULL; external_data_ = 0; - callback_ = NULL; + callback_ = HandleFinalizer(); + auto_delete_ = false; + callback_signature_ = CallbackSignature::kWeakPersistentHandleFinalizer; } void set_raw(ObjectPtr raw) { raw_ = raw; } @@ -311,10 +360,14 @@ class FinalizablePersistentHandle { void set_peer(void* peer) { peer_ = peer; } - void set_callback(Dart_WeakPersistentHandleFinalizer callback) { - callback_ = callback; + void set_callback_signature(CallbackSignature callback_signature) { + callback_signature_ = callback_signature; } + void set_callback(HandleFinalizer callback) { callback_ = callback; } + + void set_auto_delete(bool auto_delete) { auto_delete_ = auto_delete; } + void set_external_size(intptr_t size) { intptr_t size_in_words = Utils::RoundUp(size, kObjectAlignment) / kWordSize; ASSERT(ExternalSizeInWordsBits::is_valid(size_in_words)); @@ -343,7 +396,9 @@ class FinalizablePersistentHandle { ObjectPtr raw_; void* peer_; uword external_data_; - Dart_WeakPersistentHandleFinalizer callback_; + HandleFinalizer callback_; + bool auto_delete_; + CallbackSignature callback_signature_; DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods. DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandle); @@ -563,6 +618,10 @@ class FinalizablePersistentHandles return IsValidScopedHandle(reinterpret_cast(object)); } + bool IsValidHandle(Dart_FinalizableHandle object) const { + return IsValidScopedHandle(reinterpret_cast(object)); + } + bool IsFreeHandle(Dart_WeakPersistentHandle object) const { FinalizablePersistentHandle* handle = free_list_; while (handle != NULL) { @@ -746,6 +805,7 @@ class ApiState { MutexLocker ml(&mutex_); return weak_persistent_handles_.AllocateHandle(); } + void FreeWeakPersistentHandle(FinalizablePersistentHandle* weak_ref) { MutexLocker ml(&mutex_); weak_persistent_handles_.FreeHandle(weak_ref); @@ -767,6 +827,11 @@ class ApiState { return weak_persistent_handles_.IsValidHandle(object); } + bool IsValidFinalizableHandle(Dart_FinalizableHandle object) { + MutexLocker ml(&mutex_); + return weak_persistent_handles_.IsValidHandle(object); + } + bool IsActiveWeakPersistentHandle(Dart_WeakPersistentHandle object) { MutexLocker ml(&mutex_); return weak_persistent_handles_.IsValidHandle(object) && @@ -832,13 +897,38 @@ inline FinalizablePersistentHandle* FinalizablePersistentHandle::New( const Object& object, void* peer, Dart_WeakPersistentHandleFinalizer callback, - intptr_t external_size) { + 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, + void* peer, + Dart_HandleFinalizer callback, + intptr_t external_size, + bool auto_delete) { ApiState* state = isolate->group()->api_state(); ASSERT(state != NULL); FinalizablePersistentHandle* ref = state->AllocateWeakPersistentHandle(); ref->set_raw(object); ref->set_peer(peer); - ref->set_callback(callback); + ref->set_callback_signature(CallbackSignature::kHandleFinalizer); + 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; diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc index 1eb27bf8102..83ff8a82630 100644 --- a/runtime/vm/heap/heap.cc +++ b/runtime/vm/heap/heap.cc @@ -595,7 +595,7 @@ void Heap::CollectAllGarbage(GCReason reason) { EvacuateNewSpace(thread, reason); if (thread->is_marking()) { // If incremental marking is happening, we need to finish the GC cycle - // and perform a follow-up GC to pruge any "floating garbage" that may be + // and perform a follow-up GC to purge any "floating garbage" that may be // retained by the incremental barrier. CollectOldSpaceGarbage(thread, kMarkSweep, reason); } diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h index 841edb133a7..f780ea199f0 100644 --- a/runtime/vm/heap/heap.h +++ b/runtime/vm/heap/heap.h @@ -61,7 +61,7 @@ class Heap { kOldSpace, // Old space limit crossed. kFinalize, // Concurrent marking finished. kFull, // Heap::CollectAllGarbage - kExternal, // Dart_NewWeakPersistentHandle + kExternal, // Dart_NewFinalizableHandle Dart_NewWeakPersistentHandle kIdle, // Dart_NotifyIdle kLowMemory, // Dart_NotifyLowMemory kDebugging, // service request, etc. diff --git a/runtime/vm/heap/heap_test.cc b/runtime/vm/heap/heap_test.cc index cd2c0e86433..f7201f195dc 100644 --- a/runtime/vm/heap/heap_test.cc +++ b/runtime/vm/heap/heap_test.cc @@ -571,9 +571,7 @@ ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveOldToNewChain) { EXPECT(size_before < size_after); } -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(ExternalPromotion) { Isolate* isolate = Isolate::Current(); @@ -587,7 +585,8 @@ ISOLATE_UNIT_TEST_CASE(ExternalPromotion) { Array& neu = Array::Handle(); for (intptr_t i = 0; i < 100; i++) { neu = Array::New(1, Heap::kNew); - FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB); + FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB, + /*auto_delete=*/true); old.SetAt(i, neu); } @@ -753,7 +752,8 @@ ISOLATE_UNIT_TEST_CASE(ExternalAllocationStats) { Array& neu = Array::Handle(); for (intptr_t i = 0; i < 100; i++) { neu = Array::New(1, Heap::kNew); - FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB); + FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB, + /*auto_delete=*/true); old.SetAt(i, neu); if ((i % 4) == 0) { diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 64a51b793df..9616065cebe 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -22421,7 +22421,8 @@ static FinalizablePersistentHandle* AddFinalizer( intptr_t external_size) { ASSERT(callback != NULL); return FinalizablePersistentHandle::New(Isolate::Current(), referent, peer, - callback, external_size); + callback, external_size, + /*auto_delete=*/true); } StringPtr String::Transform(int32_t (*mapping)(int32_t ch), @@ -24052,7 +24053,6 @@ const char* SendPort::ToCString() const { } static void TransferableTypedDataFinalizer(void* isolate_callback_data, - Dart_WeakPersistentHandle handle, void* peer) { delete (reinterpret_cast(peer)); } @@ -24075,8 +24075,8 @@ TransferableTypedDataPtr TransferableTypedData::New(uint8_t* data, // Set up finalizer so it frees allocated memory if handle is // garbage-collected. peer->set_handle(FinalizablePersistentHandle::New( - thread->isolate(), result, peer, &TransferableTypedDataFinalizer, - length)); + thread->isolate(), result, peer, &TransferableTypedDataFinalizer, length, + /*auto_delete=*/true)); return result.raw(); } diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc index 7e0ab00d755..901efbac309 100644 --- a/runtime/vm/object_graph.cc +++ b/runtime/vm/object_graph.cc @@ -982,7 +982,7 @@ class Pass2Visitor : public ObjectVisitor, writer_->WriteUnsigned(weak_persistent_handle->external_size()); // Attempt to include a native symbol name. auto const name = NativeSymbolResolver::LookupSymbolName( - reinterpret_cast(weak_persistent_handle->callback()), nullptr); + weak_persistent_handle->callback_address(), nullptr); writer_->WriteUtf8((name == nullptr) ? "Unknown native function" : name); if (name != nullptr) { NativeSymbolResolver::FreeSymbolName(name); diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc index a2a61285237..9d6b227fbf8 100644 --- a/runtime/vm/service.cc +++ b/runtime/vm/service.cc @@ -4269,12 +4269,11 @@ class PersistentHandleVisitor : public HandleVisitor { obj.AddPropertyF( "peer", "0x%" Px "", reinterpret_cast(weak_persistent_handle->peer())); - obj.AddPropertyF( - "callbackAddress", "0x%" Px "", - reinterpret_cast(weak_persistent_handle->callback())); + obj.AddPropertyF("callbackAddress", "0x%" Px "", + weak_persistent_handle->callback_address()); // Attempt to include a native symbol name. char* name = NativeSymbolResolver::LookupSymbolName( - reinterpret_cast(weak_persistent_handle->callback()), nullptr); + weak_persistent_handle->callback_address(), nullptr); obj.AddProperty("callbackSymbolName", (name == nullptr) ? "" : name); if (name != nullptr) { NativeSymbolResolver::FreeSymbolName(name); diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md index b46ceac660c..39dfd7e3ce3 100644 --- a/runtime/vm/service/service.md +++ b/runtime/vm/service/service.md @@ -3098,10 +3098,11 @@ class MapAssociation { class MemoryUsage extends Response { // The amount of non-Dart memory that is retained by Dart objects. For // example, memory associated with Dart objects through APIs such as - // Dart_NewWeakPersistentHandle and Dart_NewExternalTypedData. This usage is - // only as accurate as the values supplied to these APIs from the VM embedder or - // native extensions. This external memory applies GC pressure, but is separate - // from heapUsage and heapCapacity. + // Dart_NewFinalizableHandle, Dart_NewWeakPersistentHandle and + // Dart_NewExternalTypedData. This usage is only as accurate as the values + // supplied to these APIs from the VM embedder or native extensions. This + // external memory applies GC pressure, but is separate from heapUsage and + // heapCapacity. int externalUsage; // The total capacity of the heap in bytes. This is the amount of memory used diff --git a/samples/ffi/async/sample_async_callback.dart b/samples/ffi/async/sample_async_callback.dart index 432a2ebfdbe..06cbef3fc13 100644 --- a/samples/ffi/async/sample_async_callback.dart +++ b/samples/ffi/async/sample_async_callback.dart @@ -27,7 +27,7 @@ main() async { print("C = C T1 or C T2."); print("Dart: Setup."); Expect.isTrue(NativeApi.majorVersion == 1); - Expect.isTrue(NativeApi.minorVersion >= 0); + Expect.isTrue(NativeApi.minorVersion >= 1); final initializeApi = dl.lookupFunction), int Function(Pointer)>("InitDartApiDL"); Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0); diff --git a/samples/ffi/async/sample_native_port_call.dart b/samples/ffi/async/sample_native_port_call.dart index aa4b48e96d5..43891e2751c 100644 --- a/samples/ffi/async/sample_native_port_call.dart +++ b/samples/ffi/async/sample_native_port_call.dart @@ -36,7 +36,7 @@ main() async { print("C = C T1 or C T2."); print("Dart: Setup."); Expect.isTrue(NativeApi.majorVersion == 1); - Expect.isTrue(NativeApi.minorVersion >= 0); + Expect.isTrue(NativeApi.minorVersion >= 1); final initializeApi = dl.lookupFunction), int Function(Pointer)>("InitDartApiDL"); Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0); diff --git a/samples/ffi/sample_ffi_functions_callbacks_closures.dart b/samples/ffi/sample_ffi_functions_callbacks_closures.dart index ba876da7d44..e5bb1577df2 100644 --- a/samples/ffi/sample_ffi_functions_callbacks_closures.dart +++ b/samples/ffi/sample_ffi_functions_callbacks_closures.dart @@ -54,7 +54,7 @@ final closureCallbackPointer = void doDynamicLinking() { Expect.isTrue(NativeApi.majorVersion == 1); - Expect.isTrue(NativeApi.minorVersion >= 0); + Expect.isTrue(NativeApi.minorVersion >= 1); final initializeApi = testLibrary.lookupFunction< IntPtr Function(Pointer), int Function(Pointer)>("InitDartApiDL"); diff --git a/tests/ffi/vmspecific_handle_dynamically_linked_test.dart b/tests/ffi/vmspecific_handle_dynamically_linked_test.dart index e2237e86399..64229582850 100644 --- a/tests/ffi/vmspecific_handle_dynamically_linked_test.dart +++ b/tests/ffi/vmspecific_handle_dynamically_linked_test.dart @@ -17,7 +17,7 @@ void main() { void doDynamicLinking() { Expect.isTrue(NativeApi.majorVersion == 1); - Expect.isTrue(NativeApi.minorVersion >= 0); + Expect.isTrue(NativeApi.minorVersion >= 1); final initializeApi = testLibrary.lookupFunction< IntPtr Function(Pointer), int Function(Pointer)>("InitDartApiDL"); diff --git a/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart b/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart index e2237e86399..64229582850 100644 --- a/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart +++ b/tests/ffi_2/vmspecific_handle_dynamically_linked_test.dart @@ -17,7 +17,7 @@ void main() { void doDynamicLinking() { Expect.isTrue(NativeApi.majorVersion == 1); - Expect.isTrue(NativeApi.minorVersion >= 0); + Expect.isTrue(NativeApi.minorVersion >= 1); final initializeApi = testLibrary.lookupFunction< IntPtr Function(Pointer), int Function(Pointer)>("InitDartApiDL");