[vm] Native API: Introduce Dart_FinalizableHandle
Introduces Dart_NewFinalizableHandle which does auto delete itself, but does not allow accessing the weak referenced object. Issue: https://github.com/dart-lang/sdk/issues/42312 Change-Id: I24ea732925122c453213c4fa3f629761c352f838 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-dartkb-linux-release-x64-abi-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/+/154695 Reviewed-by: Ryan Macnak <rmacnak@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
+8
-1
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<AsyncDirectoryListing*>(peer);
|
||||
listing->Release();
|
||||
@@ -247,8 +245,8 @@ void FUNCTION_NAME(Directory_SetAsyncDirectoryListerPointer)(
|
||||
DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1));
|
||||
AsyncDirectoryListing* listing =
|
||||
reinterpret_cast<AsyncDirectoryListing*>(listing_pointer);
|
||||
Dart_NewWeakPersistentHandle(dart_this, reinterpret_cast<void*>(listing),
|
||||
sizeof(*listing), ReleaseListing);
|
||||
Dart_NewFinalizableHandle(dart_this, reinterpret_cast<void*>(listing),
|
||||
sizeof(*listing), ReleaseListing);
|
||||
Dart_Handle result = Dart_SetNativeInstanceField(
|
||||
dart_this, kAsyncDirectoryListerFieldIndex, listing_pointer);
|
||||
ThrowIfError(result);
|
||||
|
||||
+4
-6
@@ -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<File*>(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*>(file_pointer);
|
||||
Dart_WeakPersistentHandle handle = Dart_NewWeakPersistentHandle(
|
||||
Dart_FinalizableHandle handle = Dart_NewFinalizableHandle(
|
||||
dart_this, reinterpret_cast<void*>(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(
|
||||
|
||||
+18
-16
@@ -184,21 +184,23 @@ class File : public ReferenceCounted<File> {
|
||||
// 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<File> {
|
||||
|
||||
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<File> {
|
||||
// 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<File>;
|
||||
DISALLOW_COPY_AND_ASSIGN(File);
|
||||
|
||||
@@ -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*>(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<void*>(filter_pointer),
|
||||
size, DeleteFilter);
|
||||
Dart_NewFinalizableHandle(filter, reinterpret_cast<void*>(filter_pointer),
|
||||
size, DeleteFilter);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<Namespace*>(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<void*>(namespc),
|
||||
sizeof(*namespc), ReleaseNamespace);
|
||||
Dart_NewFinalizableHandle(namespc_obj, reinterpret_cast<void*>(namespc),
|
||||
sizeof(*namespc), ReleaseNamespace);
|
||||
Dart_SetReturnValue(args, namespc_obj);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<SSLFilter*>(context_pointer);
|
||||
filter->Release();
|
||||
}
|
||||
@@ -78,8 +76,8 @@ static Dart_Handle SetFilter(Dart_NativeArguments args, SSLFilter* filter) {
|
||||
dart_this, SSLFilter::kSSLFilterNativeFieldIndex,
|
||||
reinterpret_cast<intptr_t>(filter));
|
||||
RETURN_IF_ERROR(err);
|
||||
Dart_NewWeakPersistentHandle(dart_this, reinterpret_cast<void*>(filter),
|
||||
SSLFilter::kApproximateSize, DeleteFilter);
|
||||
Dart_NewFinalizableHandle(dart_this, reinterpret_cast<void*>(filter),
|
||||
SSLFilter::kApproximateSize, DeleteFilter);
|
||||
return Dart_Null();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<SSLCertContext*>(context_pointer);
|
||||
context->Release();
|
||||
}
|
||||
@@ -109,15 +107,13 @@ static Dart_Handle SetSecurityContext(Dart_NativeArguments args,
|
||||
dart_this, SSLCertContext::kSecurityContextNativeFieldIndex,
|
||||
reinterpret_cast<intptr_t>(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<X509*>(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<void*>(certificate),
|
||||
approximate_size_of_certificate,
|
||||
ReleaseCertificate);
|
||||
Dart_NewFinalizableHandle(result, reinterpret_cast<void*>(certificate),
|
||||
approximate_size_of_certificate,
|
||||
ReleaseCertificate);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+7
-15
@@ -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<Socket*>(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<Socket*>(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<Socket*>(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<Socket*>(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<void*>(socket),
|
||||
sizeof(Socket), callback);
|
||||
Dart_NewFinalizableHandle(handle, reinterpret_cast<void*>(socket),
|
||||
sizeof(Socket), callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<SynchronousSocket*>(data);
|
||||
if (socket->fd() >= 0) {
|
||||
SynchronousSocket::Close(socket->fd());
|
||||
@@ -334,9 +332,9 @@ Dart_Handle SynchronousSocket::SetSocketIdNativeField(
|
||||
return error;
|
||||
}
|
||||
|
||||
Dart_NewWeakPersistentHandle(handle, reinterpret_cast<void*>(socket),
|
||||
sizeof(SynchronousSocket),
|
||||
SynchronousSocketFinalizer);
|
||||
Dart_NewFinalizableHandle(handle, reinterpret_cast<void*>(socket),
|
||||
sizeof(SynchronousSocket),
|
||||
SynchronousSocketFinalizer);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
+97
-11
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
#endif /* RUNTIME_INCLUDE_DART_API_DL_H_ */ /* NOLINT */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+121
-11
@@ -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<FinalizablePersistentHandle*>(handle);
|
||||
}
|
||||
FinalizablePersistentHandle* FinalizablePersistentHandle::Cast(
|
||||
Dart_FinalizableHandle handle) {
|
||||
#if defined(DEBUG)
|
||||
ApiState* state = IsolateGroup::Current()->api_state();
|
||||
ASSERT(state->IsValidFinalizableHandle(handle));
|
||||
#endif
|
||||
return reinterpret_cast<FinalizablePersistentHandle*>(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<Dart_WeakPersistentHandle>(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<Dart_WeakPersistentHandle>(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<uint8_t*>(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());
|
||||
}
|
||||
|
||||
@@ -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<intptr_t*>(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<intptr_t*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<int*>(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<int*>(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) \
|
||||
|
||||
+100
-10
@@ -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<uword>(callback_.finalizable);
|
||||
}
|
||||
Dart_WeakPersistentHandle ApiWeakPersistentHandle() {
|
||||
return reinterpret_cast<Dart_WeakPersistentHandle>(this);
|
||||
}
|
||||
Dart_FinalizableHandle ApiFinalizableHandle() {
|
||||
return reinterpret_cast<Dart_FinalizableHandle>(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<uword,
|
||||
intptr_t,
|
||||
@@ -277,7 +320,10 @@ class FinalizablePersistentHandle {
|
||||
friend class FinalizablePersistentHandles;
|
||||
|
||||
FinalizablePersistentHandle()
|
||||
: raw_(nullptr), peer_(NULL), external_data_(0), callback_(NULL) {}
|
||||
: raw_(nullptr),
|
||||
peer_(NULL),
|
||||
external_data_(0),
|
||||
callback_(HandleFinalizer()) {}
|
||||
~FinalizablePersistentHandle() {}
|
||||
|
||||
static void Finalize(IsolateGroup* isolate_group,
|
||||
@@ -293,6 +339,7 @@ class FinalizablePersistentHandle {
|
||||
raw_ = static_cast<ObjectPtr>(reinterpret_cast<uword>(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<uword>(object));
|
||||
}
|
||||
|
||||
bool IsValidHandle(Dart_FinalizableHandle object) const {
|
||||
return IsValidScopedHandle(reinterpret_cast<uword>(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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<TransferableTypedDataPeer*>(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();
|
||||
}
|
||||
|
||||
@@ -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<uword>(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);
|
||||
|
||||
@@ -4269,12 +4269,11 @@ class PersistentHandleVisitor : public HandleVisitor {
|
||||
obj.AddPropertyF(
|
||||
"peer", "0x%" Px "",
|
||||
reinterpret_cast<uintptr_t>(weak_persistent_handle->peer()));
|
||||
obj.AddPropertyF(
|
||||
"callbackAddress", "0x%" Px "",
|
||||
reinterpret_cast<uintptr_t>(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<uword>(weak_persistent_handle->callback()), nullptr);
|
||||
weak_persistent_handle->callback_address(), nullptr);
|
||||
obj.AddProperty("callbackSymbolName", (name == nullptr) ? "" : name);
|
||||
if (name != nullptr) {
|
||||
NativeSymbolResolver::FreeSymbolName(name);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<IntPtr Function(Pointer<Void>),
|
||||
int Function(Pointer<Void>)>("InitDartApiDL");
|
||||
Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);
|
||||
|
||||
@@ -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<IntPtr Function(Pointer<Void>),
|
||||
int Function(Pointer<Void>)>("InitDartApiDL");
|
||||
Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);
|
||||
|
||||
@@ -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<Void>),
|
||||
int Function(Pointer<Void>)>("InitDartApiDL");
|
||||
|
||||
@@ -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<Void>),
|
||||
int Function(Pointer<Void>)>("InitDartApiDL");
|
||||
|
||||
@@ -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<Void>),
|
||||
int Function(Pointer<Void>)>("InitDartApiDL");
|
||||
|
||||
Reference in New Issue
Block a user