mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:29:48 +00:00
Revert "[vm] Implement Finalizer
"
This reverts commit 7dca34c235
.
Reason for revert: b/226085355 dart_vm_test crashing. Unclear what
the cause is. Reverting so we can triage the issue.
TEST=This is a revert.
Original change's description:
> [vm] Implement `Finalizer`
>
> This CL implements the `Finalizer` in the GC.
>
> (This CL does not yet implement `NativeFinalizer`.)
>
> The GC is specially aware of two types of objects for the purposes of
> running finalizers.
>
> 1) `FinalizerEntry`
> 2) `Finalizer` (`FinalizerBase`, `_FinalizerImpl`)
>
> A `FinalizerEntry` contains the `value`, the optional `detach` key, and
> the `token`, and a reference to the `finalizer`.
> An entry only holds on weakly to the value, detach key, and finalizer.
> (Similar to how `WeakReference` only holds on weakly to target).
>
> A `Finalizer` contains all entries, a list of entries of which the value
> is collected, and a reference to the isolate.
>
> When a the value of an entry is GCed, the enry is added over to the
> collected list.
> If any entry is moved to the collected list, a message is sent that
> invokes the finalizer to call the callback on all entries in that list.
>
> When a finalizer is detached by the user, the entry token is set to the
> entry itself and is removed from the all entries set.
> This ensures that if the entry was already moved to the collected list,
> the finalizer is not executed.
>
> To speed up detaching, we use a weak map from detach keys to list of
> entries. This ensures entries can be GCed.
>
> Both the scavenger and marker tasks process finalizer entries in
> parallel.
> Parallel tasks use an atomic exchange on the head of the collected
> entries list, ensuring no entries get lost.
> The mutator thread is guaranteed to be stopped when processing entries.
> This ensures that we do not need barriers for moving entries into the
> finalizers collected list.
> Dart reads and replaces the collected entries list also with an atomic
> exchange, ensuring the GC doesn't run in between a load/store.
>
> When a finalizer gets posted a message to process finalized objects, it
> is being kept alive by the message.
> An alternative design would be to pre-allocate a `WeakReference` in the
> finalizer pointing to the finalizer, and send that itself.
> This would be at the cost of an extra object.
>
> Send and exit is not supported in this CL, support will be added in a
> follow up CL. Trying to send will throw.
>
> Bug: https://github.com/dart-lang/sdk/issues/47777
>
> TEST=runtime/tests/vm/dart/finalizer/*
> TEST=runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
> TEST=runtime/vm/object_test.cc
>
> Change-Id: I03e6b4a46212316254bf46ba3f2df333abaa686c
> Cq-Include-Trybots: luci.dart.try:vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-ffi-android-debug-arm64c-try,dart-sdk-mac-arm64-try,vm-kernel-mac-release-arm64-try,pkg-mac-release-arm64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try,vm-kernel-win-debug-x64c-try,vm-kernel-win-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,vm-kernel-nnbd-win-release-ia32-try,vm-ffi-android-debug-arm-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-mac-debug-x64-try,vm-kernel-nnbd-mac-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,benchmark-linux-try,flutter-analyze-try,flutter-frontend-try,pkg-linux-debug-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-gcc-linux-try,vm-kernel-optcounter-threshold-linux-release-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64c-try
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/229544
> Reviewed-by: Lasse Nielsen <lrn@google.com>
> Reviewed-by: Slava Egorov <vegorov@google.com>
> Reviewed-by: Martin Kustermann <kustermann@google.com>
> Reviewed-by: Ryan Macnak <rmacnak@google.com>
> Commit-Queue: Daco Harkes <dacoharkes@google.com>
TBR=lrn@google.com,vegorov@google.com,kustermann@google.com,rmacnak@google.com,dacoharkes@google.com
Change-Id: I991f6e49896d18a8d70210cf315d858b462d66c9
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: https://github.com/dart-lang/sdk/issues/47777
Cq-Include-Trybots: luci.dart.try:vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-ffi-android-debug-arm64c-try,dart-sdk-mac-arm64-try,vm-kernel-mac-release-arm64-try,pkg-mac-release-arm64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try,vm-kernel-win-debug-x64c-try,vm-kernel-win-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,vm-kernel-nnbd-win-release-ia32-try,vm-ffi-android-debug-arm-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-mac-debug-x64-try,vm-kernel-nnbd-mac-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,benchmark-linux-try,flutter-analyze-try,flutter-frontend-try,pkg-linux-debug-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-gcc-linux-try,vm-kernel-optcounter-threshold-linux-release-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64c-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/238080
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
parent
7dca34c235
commit
37526fc8d7
|
@ -13,7 +13,7 @@ A tag of 1 has no penalty on heap object access because removing the tag can be
|
|||
Heap objects are always allocated in double-word increments. Objects in old-space are kept at double-word alignment (address % double-word == 0), and objects in new-space are kept offset from double-word alignment (address % double-word == word). This allows checking an object's age without comparing to a boundary address, avoiding restrictions on heap placement and avoiding loading the boundary from thread-local storage. Additionally, the scavenger can quickly skip over both immediates and old objects with a single branch.
|
||||
|
||||
| Pointer | Referent |
|
||||
| ---------- | --------------------------------------- |
|
||||
| --- | --- |
|
||||
| 0x00000002 | Small integer 1 |
|
||||
| 0xFFFFFFFE | Small integer -1 |
|
||||
| 0x00A00001 | Heap object at 0x00A00000, in old-space |
|
||||
|
@ -75,7 +75,7 @@ The Dart VM includes a sliding compactor. The forwarding table is compactly repr
|
|||
|
||||
## Concurrent Marking
|
||||
|
||||
To reduce the time the mutator is paused for old-space GCs, we allow the mutator to continue running during most of the marking work.
|
||||
To reduce the time the mutator is paused for old-space GCs, we allow the mutator to continue running during most of the marking work.
|
||||
|
||||
### Barrier
|
||||
|
||||
|
@ -204,35 +204,3 @@ container <- AllocateObject
|
|||
<instructions that cannot directly call Dart functions>
|
||||
StoreInstanceField(container, value, NoBarrier)
|
||||
```
|
||||
|
||||
## Finalizers
|
||||
|
||||
The GC is aware of two types of objects for the purposes of running finalizers.
|
||||
|
||||
1) `FinalizerEntry`
|
||||
2) `Finalizer` (`FinalizerBase`, `_FinalizerImpl`)
|
||||
|
||||
A `FinalizerEntry` contains the `value`, the optional `detach` key, and the `token`, and a reference to the `finalizer`.
|
||||
An entry only holds on weakly to the value, detach key, and finalizer. (Similar to how `WeakReference` only holds on weakly to target).
|
||||
|
||||
A `Finalizer` contains all entries, a list of entries of which the value is collected, and a reference to the isolate.
|
||||
|
||||
When the value of an entry is GCed, the entry is added over to the collected list.
|
||||
If any entry is moved to the collected list, a message is sent that invokes the finalizer to call the callback on all entries in that list.
|
||||
|
||||
When a finalizer is detached by the user, the entry token is set to the entry itself and is removed from the all entries set.
|
||||
This ensures that if the entry was already moved to the collected list, the finalizer is not executed.
|
||||
|
||||
To speed up detaching, we use a weak map from detach keys to list of entries. This ensures entries can be GCed.
|
||||
|
||||
Both the scavenger and marker can process finalizer entries in parallel.
|
||||
Parallel tasks use an atomic exchange on the head of the collected entries list, ensuring no entries get lost.
|
||||
Mutator threads are guaranteed to be stopped when processing entries.
|
||||
This ensures that we do not need barriers for moving entries into the finalizers collected list.
|
||||
Dart reads and replaces the collected entries list also with an atomic exchange, ensuring the GC doesn't run in between a load/store.
|
||||
|
||||
When a finalizer gets posted a message to process finalized objects, it is being kept alive by the message.
|
||||
An alternative design would be to pre-allocate a `WeakReference` in the finalizer pointing to the finalizer, and send that itself.
|
||||
This would be at the cost of an extra object.
|
||||
|
||||
If the finalizer object itself is GCed, the callback is not run for any of the attachments.
|
||||
|
|
|
@ -242,8 +242,6 @@ static ObjectPtr ValidateMessageObject(Zone* zone,
|
|||
break;
|
||||
|
||||
MESSAGE_SNAPSHOT_ILLEGAL(DynamicLibrary);
|
||||
// TODO(http://dartbug.com/47777): Send and exit support: remove this.
|
||||
MESSAGE_SNAPSHOT_ILLEGAL(Finalizer);
|
||||
MESSAGE_SNAPSHOT_ILLEGAL(MirrorReference);
|
||||
MESSAGE_SNAPSHOT_ILLEGAL(Pointer);
|
||||
MESSAGE_SNAPSHOT_ILLEGAL(ReceivePort);
|
||||
|
@ -286,7 +284,6 @@ static ObjectPtr ValidateMessageObject(Zone* zone,
|
|||
return obj.ptr();
|
||||
}
|
||||
|
||||
// TODO(http://dartbug.com/47777): Add support for Finalizers.
|
||||
DEFINE_NATIVE_ENTRY(Isolate_exit_, 0, 2) {
|
||||
GET_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
|
||||
if (!port.IsNull()) {
|
||||
|
@ -641,10 +638,9 @@ class SpawnIsolateTask : public ThreadPool::Task {
|
|||
// Make a copy of the state's isolate flags and hand it to the callback.
|
||||
Dart_IsolateFlags api_flags = *(state_->isolate_flags());
|
||||
api_flags.is_system_isolate = false;
|
||||
Dart_Isolate isolate =
|
||||
(create_group_callback)(state_->script_url(), name, nullptr,
|
||||
state_->package_config(), &api_flags,
|
||||
parent_isolate_->init_callback_data(), &error);
|
||||
Dart_Isolate isolate = (create_group_callback)(
|
||||
state_->script_url(), name, nullptr, state_->package_config(),
|
||||
&api_flags, parent_isolate_->init_callback_data(), &error);
|
||||
parent_isolate_->DecrementSpawnCount();
|
||||
parent_isolate_ = nullptr;
|
||||
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
int callbackCount = 0;
|
||||
|
||||
void callback(Nonce token) {
|
||||
callbackCount++;
|
||||
print('$name: Running finalizer: token: $token');
|
||||
}
|
||||
|
||||
final finalizer = Finalizer<Nonce>(callback);
|
||||
|
||||
late String name;
|
||||
|
||||
void main(List<String> arguments, SendPort port) async {
|
||||
name = arguments[0];
|
||||
|
||||
final token = Nonce(42);
|
||||
makeObjectWithFinalizer(finalizer, token);
|
||||
|
||||
final awaitBeforeShuttingDown = ReceivePort();
|
||||
port.send(awaitBeforeShuttingDown.sendPort);
|
||||
final message = await awaitBeforeShuttingDown.first;
|
||||
print('$name: $message');
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 1));
|
||||
print('$name: Awaited to see if there were any callbacks.');
|
||||
|
||||
print('$name: Helper isolate exiting. num callbacks: $callbackCount.');
|
||||
port.send(callbackCount);
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// VMOptions=
|
||||
// VMOptions=--use_compactor
|
||||
// VMOptions=--use_compactor --force_evacuation
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() async {
|
||||
await testFinalizerInOtherIsolateGroupGCBeforeExit();
|
||||
await testFinalizerInOtherIsolateGroupGCAfterExit();
|
||||
await testFinalizerInOtherIsolateGroupNoGC();
|
||||
|
||||
print('$name: End of test, shutting down.');
|
||||
}
|
||||
|
||||
const name = 'main';
|
||||
|
||||
late bool hotReloadBot;
|
||||
|
||||
Future<void> testFinalizerInOtherIsolateGroupGCBeforeExit() async {
|
||||
final receivePort = ReceivePort();
|
||||
final messagesQueue = StreamQueue(receivePort);
|
||||
|
||||
await Isolate.spawnUri(
|
||||
Uri.parse('finalizer_isolate_groups_run_gc_helper.dart'),
|
||||
['helper 1'],
|
||||
receivePort.sendPort,
|
||||
);
|
||||
final signalHelperIsolate = await messagesQueue.next as SendPort;
|
||||
|
||||
doGC(name: name);
|
||||
await yieldToMessageLoop(name: name);
|
||||
|
||||
signalHelperIsolate.send('Done GCing.');
|
||||
|
||||
final helperCallbacks = await messagesQueue.next as int;
|
||||
messagesQueue.cancel();
|
||||
print('$name: Helper exited.');
|
||||
// Different isolate group, so we don't expect a GC in this isolate to cause
|
||||
// collected objects in the helper.
|
||||
// Except for in --hot-reload-test-mode, then the GC is triggered.
|
||||
hotReloadBot = helperCallbacks == 1;
|
||||
}
|
||||
|
||||
Future<void> testFinalizerInOtherIsolateGroupGCAfterExit() async {
|
||||
final receivePort = ReceivePort();
|
||||
final messagesQueue = StreamQueue(receivePort);
|
||||
await Isolate.spawnUri(
|
||||
Uri.parse('finalizer_isolate_groups_run_gc_helper.dart'),
|
||||
['helper 2'],
|
||||
receivePort.sendPort,
|
||||
);
|
||||
|
||||
final signalHelperIsolate = await messagesQueue.next as SendPort;
|
||||
|
||||
signalHelperIsolate.send('Before GCing.');
|
||||
|
||||
final helperCallbacks = await messagesQueue.next as int;
|
||||
messagesQueue.cancel();
|
||||
print('$name: Helper exited.');
|
||||
Expect.equals(hotReloadBot ? 1 : 0, helperCallbacks);
|
||||
|
||||
doGC(name: name);
|
||||
await yieldToMessageLoop(name: name);
|
||||
}
|
||||
|
||||
Future<void> testFinalizerInOtherIsolateGroupNoGC() async {
|
||||
final receivePort = ReceivePort();
|
||||
final messagesQueue = StreamQueue(receivePort);
|
||||
|
||||
await Isolate.spawnUri(
|
||||
Uri.parse('finalizer_isolate_groups_run_gc_helper.dart'),
|
||||
['helper 3'],
|
||||
receivePort.sendPort,
|
||||
);
|
||||
final signalHelperIsolate = await messagesQueue.next as SendPort;
|
||||
|
||||
signalHelperIsolate.send('Before quitting main isolate.');
|
||||
|
||||
final helperCallbacks = await messagesQueue.next as int;
|
||||
messagesQueue.cancel();
|
||||
print('$name: Helper exited.');
|
||||
Expect.equals(hotReloadBot ? 1 : 0, helperCallbacks);
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// VMOptions=
|
||||
// VMOptions=--use_compactor
|
||||
// VMOptions=--use_compactor --force_evacuation
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() async {
|
||||
await testNormalExit();
|
||||
await testSendAndExit();
|
||||
await testSendAndExitFinalizer();
|
||||
print('End of test, shutting down.');
|
||||
}
|
||||
|
||||
final finalizerTokens = <Nonce>{};
|
||||
|
||||
void callback(Nonce token) {
|
||||
print('Running finalizer: token: $token');
|
||||
finalizerTokens.add(token);
|
||||
}
|
||||
|
||||
void runIsolateAttachFinalizer(Object? message) {
|
||||
final finalizer = Finalizer<Nonce>(callback);
|
||||
final value = Nonce(1001);
|
||||
final token = Nonce(1002);
|
||||
finalizer.attach(value, token);
|
||||
final token9 = Nonce(9002);
|
||||
makeObjectWithFinalizer(finalizer, token9);
|
||||
if (message == null) {
|
||||
print('Isolate done.');
|
||||
return;
|
||||
}
|
||||
final list = message as List;
|
||||
assert(list.length == 2);
|
||||
final sendPort = list[0] as SendPort;
|
||||
final tryToSendFinalizer = list[1] as bool;
|
||||
if (tryToSendFinalizer) {
|
||||
Expect.throws(() {
|
||||
// TODO(http://dartbug.com/47777): Send and exit support.
|
||||
print('Trying to send and exit finalizer.');
|
||||
Isolate.exit(sendPort, [value, finalizer]);
|
||||
});
|
||||
}
|
||||
print('Isolate sending and exit.');
|
||||
Isolate.exit(sendPort, [value]);
|
||||
}
|
||||
|
||||
Future testNormalExit() async {
|
||||
final portExitMessage = ReceivePort();
|
||||
await Isolate.spawn(
|
||||
runIsolateAttachFinalizer,
|
||||
null,
|
||||
onExit: portExitMessage.sendPort,
|
||||
);
|
||||
await portExitMessage.first;
|
||||
|
||||
doGC();
|
||||
await yieldToMessageLoop();
|
||||
|
||||
Expect.equals(0, finalizerTokens.length);
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
Future<Finalizer?> testSendAndExitHelper(
|
||||
{bool trySendFinalizer = false}) async {
|
||||
final port = ReceivePort();
|
||||
await Isolate.spawn(
|
||||
runIsolateAttachFinalizer,
|
||||
[port.sendPort, trySendFinalizer],
|
||||
);
|
||||
final message = await port.first as List;
|
||||
print('Received message ($message).');
|
||||
final value = message[0] as Nonce;
|
||||
print('Received value ($value), but now forgetting about it.');
|
||||
|
||||
Expect.equals(1, message.length);
|
||||
// TODO(http://dartbug.com/47777): Send and exit support.
|
||||
return null;
|
||||
}
|
||||
|
||||
Future testSendAndExit() async {
|
||||
await testSendAndExitHelper(trySendFinalizer: false);
|
||||
|
||||
doGC();
|
||||
await yieldToMessageLoop();
|
||||
|
||||
Expect.equals(0, finalizerTokens.length);
|
||||
}
|
||||
|
||||
Future testSendAndExitFinalizer() async {
|
||||
final finalizer = await testSendAndExitHelper(trySendFinalizer: true);
|
||||
|
||||
// TODO(http://dartbug.com/47777): Send and exit support.
|
||||
Expect.isNull(finalizer);
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() {
|
||||
testFinalizer();
|
||||
}
|
||||
|
||||
void testFinalizer() async {
|
||||
final finalizerTokens = <Nonce?>{};
|
||||
void callback(Nonce? token) {
|
||||
print('Running finalizer: token: $token');
|
||||
finalizerTokens.add(token);
|
||||
}
|
||||
|
||||
final finalizer = Finalizer<Nonce?>(callback);
|
||||
|
||||
{
|
||||
final detach = Nonce(2022);
|
||||
final token = null;
|
||||
|
||||
makeObjectWithFinalizer(finalizer, token, detach: detach);
|
||||
|
||||
doGC();
|
||||
|
||||
// We haven't stopped running synchronous dart code yet.
|
||||
Expect.isFalse(finalizerTokens.contains(token));
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 1));
|
||||
|
||||
// Now we have.
|
||||
Expect.isTrue(finalizerTokens.contains(token));
|
||||
|
||||
// Try detaching after finalizer ran.
|
||||
finalizer.detach(detach);
|
||||
}
|
||||
|
||||
print('End of test, shutting down.');
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// VMOptions=
|
||||
// VMOptions=--use_compactor
|
||||
// VMOptions=--use_compactor --force_evacuation
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() {
|
||||
testWrongArguments();
|
||||
testFinalizer();
|
||||
}
|
||||
|
||||
void testWrongArguments() {
|
||||
void callback(Object token) {
|
||||
throw 'This should never happen!';
|
||||
}
|
||||
|
||||
final finalizer = Finalizer<Nonce>(callback);
|
||||
final myFinalizable = Nonce(1000);
|
||||
final token = Nonce(2000);
|
||||
final detach = Nonce(3000);
|
||||
|
||||
Expect.throws(() {
|
||||
finalizer.attach(myFinalizable, token, detach: 123);
|
||||
});
|
||||
Expect.throws(() {
|
||||
finalizer.attach(123, token, detach: detach);
|
||||
});
|
||||
}
|
||||
|
||||
void testFinalizer() async {
|
||||
final finalizerTokens = <Object>{};
|
||||
void callback(Object token) {
|
||||
print('Running finalizer: token: $token');
|
||||
finalizerTokens.add(token);
|
||||
}
|
||||
|
||||
final finalizer = Finalizer<Nonce>(callback);
|
||||
|
||||
{
|
||||
final detach = Nonce(2022);
|
||||
final token = Nonce(42);
|
||||
|
||||
makeObjectWithFinalizer(finalizer, token, detach: detach);
|
||||
|
||||
doGC();
|
||||
|
||||
// We haven't stopped running synchronous dart code yet.
|
||||
Expect.isFalse(finalizerTokens.contains(token));
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 1));
|
||||
|
||||
// Now we have.
|
||||
Expect.isTrue(finalizerTokens.contains(token));
|
||||
|
||||
// Try detaching after finalizer ran.
|
||||
finalizer.detach(detach);
|
||||
}
|
||||
|
||||
{
|
||||
final token = Nonce(1337);
|
||||
final token2 = Nonce(1338);
|
||||
final detachkey = Nonce(1984);
|
||||
{
|
||||
final value = Nonce(2);
|
||||
final value2 = Nonce(2000000);
|
||||
finalizer.attach(value, token, detach: detachkey);
|
||||
finalizer.attach(value2, token2, detach: detachkey);
|
||||
// Should detach 2 finalizers.
|
||||
finalizer.detach(detachkey);
|
||||
// Try detaching again, should do nothing.
|
||||
finalizer.detach(detachkey);
|
||||
}
|
||||
doGC();
|
||||
await yieldToMessageLoop();
|
||||
Expect.isFalse(finalizerTokens.contains(token));
|
||||
Expect.isFalse(finalizerTokens.contains(token2));
|
||||
}
|
||||
|
||||
// Not running finalizer on shutdown.
|
||||
final value = Nonce(3);
|
||||
final token = Nonce(1337);
|
||||
finalizer.attach(value, token);
|
||||
print('End of test, shutting down.');
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// VMOptions=
|
||||
// VMOptions=--use_compactor
|
||||
// VMOptions=--use_compactor --force_evacuation
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() async {
|
||||
await testFinalizerZone();
|
||||
await testFinalizerException();
|
||||
}
|
||||
|
||||
Future<void> testFinalizerZone() async {
|
||||
Zone? expectedZone;
|
||||
Zone? actualZone;
|
||||
|
||||
final finalizer = runZoned(() {
|
||||
expectedZone = Zone.current;
|
||||
|
||||
void callback(Object token) {
|
||||
actualZone = Zone.current;
|
||||
}
|
||||
|
||||
return Finalizer<Nonce>(callback);
|
||||
});
|
||||
|
||||
final detach = Nonce(2022);
|
||||
final token = Nonce(42);
|
||||
|
||||
makeObjectWithFinalizer(finalizer, token, detach: detach);
|
||||
|
||||
doGC();
|
||||
|
||||
// We haven't stopped running synchronous dart code yet.
|
||||
Expect.isNull(actualZone);
|
||||
|
||||
await yieldToMessageLoop();
|
||||
|
||||
// Now we have.
|
||||
Expect.equals(expectedZone, actualZone);
|
||||
}
|
||||
|
||||
Future<void> testFinalizerException() async {
|
||||
Object? caughtError;
|
||||
|
||||
final finalizer = runZonedGuarded(() {
|
||||
void callback(Object token) {
|
||||
throw 'uncaught!';
|
||||
}
|
||||
|
||||
return Finalizer<Nonce>(callback);
|
||||
}, (Object error, StackTrace stack) {
|
||||
caughtError = error;
|
||||
})!;
|
||||
|
||||
final detach = Nonce(2022);
|
||||
final token = Nonce(42);
|
||||
|
||||
makeObjectWithFinalizer(finalizer, token, detach: detach);
|
||||
|
||||
doGC();
|
||||
|
||||
Expect.isNull(caughtError);
|
||||
await yieldToMessageLoop();
|
||||
Expect.isNotNull(caughtError);
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// ignore: import_internal_library, unused_import
|
||||
import 'dart:_internal';
|
||||
import 'dart:async';
|
||||
|
||||
/// A user-defined class of which objects can be identified with a field value.
|
||||
class Nonce {
|
||||
final int value;
|
||||
|
||||
Nonce(this.value);
|
||||
|
||||
String toString() => 'Nonce($value)';
|
||||
}
|
||||
|
||||
/// Never inline to ensure `object` becomes unreachable.
|
||||
@pragma('vm:never-inline')
|
||||
void makeObjectWithFinalizer<T>(Finalizer<T> finalizer, T token,
|
||||
{Object? detach}) {
|
||||
final value = Nonce(1);
|
||||
finalizer.attach(value, token, detach: detach);
|
||||
}
|
||||
|
||||
/// Triggers garbage collection.
|
||||
// Defined in `dart:_internal`.
|
||||
// ignore: undefined_identifier
|
||||
void triggerGc() => VMInternalsForTesting.collectAllGarbage();
|
||||
|
||||
void Function(String) _namedPrint(String? name) {
|
||||
if (name != null) {
|
||||
return (String value) => print('$name: $value');
|
||||
}
|
||||
return (String value) => print(value);
|
||||
}
|
||||
|
||||
/// Does a GC and if [doAwait] awaits a future to enable running finalizers.
|
||||
///
|
||||
/// Also prints for debug purposes.
|
||||
///
|
||||
/// If provided, [name] prefixes the debug prints.
|
||||
void doGC({String? name}) {
|
||||
final _print = _namedPrint(name);
|
||||
|
||||
_print('Do GC.');
|
||||
triggerGc();
|
||||
_print('GC done');
|
||||
}
|
||||
|
||||
Future<void> yieldToMessageLoop({String? name}) async {
|
||||
await Future.delayed(Duration(milliseconds: 1));
|
||||
_namedPrint(name)('Await done.');
|
||||
return null;
|
||||
}
|
|
@ -2,9 +2,10 @@
|
|||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
// ignore: import_internal_library, unused_import
|
||||
import 'dart:_internal';
|
||||
|
||||
import 'helpers.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
void main() {
|
||||
testWeakReferenceNonExpandoKey();
|
||||
|
@ -12,6 +13,14 @@ void main() {
|
|||
testWeakReference();
|
||||
}
|
||||
|
||||
class Nonce {
|
||||
final int value;
|
||||
|
||||
Nonce(this.value);
|
||||
|
||||
String toString() => 'Nonce($value)';
|
||||
}
|
||||
|
||||
void testWeakReferenceNonExpandoKey() {
|
||||
Expect.throwsArgumentError(() {
|
||||
WeakReference<String>("Hello world!");
|
||||
|
@ -46,3 +55,7 @@ void testWeakReference() {
|
|||
|
||||
print('End of test, shutting down.');
|
||||
}
|
||||
|
||||
// Defined in `dart:_internal`.
|
||||
// ignore: undefined_identifier
|
||||
void triggerGc() => VMInternalsForTesting.collectAllGarbage();
|
||||
|
|
|
@ -245,7 +245,6 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
|
||||
await testWeakProperty();
|
||||
await testWeakReference();
|
||||
await testFinalizer();
|
||||
|
||||
await testForbiddenClosures();
|
||||
}
|
||||
|
@ -757,14 +756,6 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
}
|
||||
}
|
||||
|
||||
Future testFinalizer() async {
|
||||
print('testFinalizer');
|
||||
|
||||
void callback(Object token) {}
|
||||
final finalizer = Finalizer<Object>(callback);
|
||||
Expect.throwsArgumentError(() => sendPort.send(finalizer));
|
||||
}
|
||||
|
||||
Future testForbiddenClosures() async {
|
||||
print('testForbiddenClosures');
|
||||
for (final closure in nonCopyableClosures) {
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
int callbackCount = 0;
|
||||
|
||||
void callback(Nonce token) {
|
||||
callbackCount++;
|
||||
print('$name: Running finalizer: token: $token');
|
||||
}
|
||||
|
||||
final finalizer = Finalizer<Nonce>(callback);
|
||||
|
||||
String name;
|
||||
|
||||
void main(List<String> arguments, SendPort port) async {
|
||||
name = arguments[0];
|
||||
|
||||
final token = Nonce(42);
|
||||
makeObjectWithFinalizer(finalizer, token);
|
||||
|
||||
final awaitBeforeShuttingDown = ReceivePort();
|
||||
port.send(awaitBeforeShuttingDown.sendPort);
|
||||
final message = await awaitBeforeShuttingDown.first;
|
||||
print('$name: $message');
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 1));
|
||||
print('$name: Awaited to see if there were any callbacks.');
|
||||
|
||||
print('$name: Helper isolate exiting. num callbacks: $callbackCount.');
|
||||
port.send(callbackCount);
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// VMOptions=
|
||||
// VMOptions=--use_compactor
|
||||
// VMOptions=--use_compactor --force_evacuation
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() async {
|
||||
await testFinalizerInOtherIsolateGroupGCBeforeExit();
|
||||
await testFinalizerInOtherIsolateGroupGCAfterExit();
|
||||
await testFinalizerInOtherIsolateGroupNoGC();
|
||||
|
||||
print('$name: End of test, shutting down.');
|
||||
}
|
||||
|
||||
const name = 'main';
|
||||
|
||||
bool hotReloadBot;
|
||||
|
||||
Future<void> testFinalizerInOtherIsolateGroupGCBeforeExit() async {
|
||||
final receivePort = ReceivePort();
|
||||
final messagesQueue = StreamQueue(receivePort);
|
||||
|
||||
await Isolate.spawnUri(
|
||||
Uri.parse('finalizer_isolate_groups_run_gc_helper.dart'),
|
||||
['helper 1'],
|
||||
receivePort.sendPort,
|
||||
);
|
||||
final signalHelperIsolate = await messagesQueue.next as SendPort;
|
||||
|
||||
doGC(name: name);
|
||||
await yieldToMessageLoop(name: name);
|
||||
|
||||
signalHelperIsolate.send('Done GCing.');
|
||||
|
||||
final helperCallbacks = await messagesQueue.next as int;
|
||||
messagesQueue.cancel();
|
||||
print('$name: Helper exited.');
|
||||
// Different isolate group, so we don't expect a GC in this isolate to cause
|
||||
// collected objects in the helper.
|
||||
// Except for in --hot-reload-test-mode, then the GC is triggered.
|
||||
hotReloadBot = helperCallbacks == 1;
|
||||
}
|
||||
|
||||
Future<void> testFinalizerInOtherIsolateGroupGCAfterExit() async {
|
||||
final receivePort = ReceivePort();
|
||||
final messagesQueue = StreamQueue(receivePort);
|
||||
await Isolate.spawnUri(
|
||||
Uri.parse('finalizer_isolate_groups_run_gc_helper.dart'),
|
||||
['helper 2'],
|
||||
receivePort.sendPort,
|
||||
);
|
||||
|
||||
final signalHelperIsolate = await messagesQueue.next as SendPort;
|
||||
|
||||
signalHelperIsolate.send('Before GCing.');
|
||||
|
||||
final helperCallbacks = await messagesQueue.next as int;
|
||||
messagesQueue.cancel();
|
||||
print('$name: Helper exited.');
|
||||
Expect.equals(hotReloadBot ? 1 : 0, helperCallbacks);
|
||||
|
||||
doGC(name: name);
|
||||
await yieldToMessageLoop(name: name);
|
||||
}
|
||||
|
||||
Future<void> testFinalizerInOtherIsolateGroupNoGC() async {
|
||||
final receivePort = ReceivePort();
|
||||
final messagesQueue = StreamQueue(receivePort);
|
||||
|
||||
await Isolate.spawnUri(
|
||||
Uri.parse('finalizer_isolate_groups_run_gc_helper.dart'),
|
||||
['helper 3'],
|
||||
receivePort.sendPort,
|
||||
);
|
||||
final signalHelperIsolate = await messagesQueue.next as SendPort;
|
||||
|
||||
signalHelperIsolate.send('Before quitting main isolate.');
|
||||
|
||||
final helperCallbacks = await messagesQueue.next as int;
|
||||
messagesQueue.cancel();
|
||||
print('$name: Helper exited.');
|
||||
Expect.equals(hotReloadBot ? 1 : 0, helperCallbacks);
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// VMOptions=
|
||||
// VMOptions=--use_compactor
|
||||
// VMOptions=--use_compactor --force_evacuation
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() async {
|
||||
await testNormalExit();
|
||||
await testSendAndExit();
|
||||
await testSendAndExitFinalizer();
|
||||
print('End of test, shutting down.');
|
||||
}
|
||||
|
||||
final finalizerTokens = <Nonce>{};
|
||||
|
||||
void callback(Nonce token) {
|
||||
print('Running finalizer: token: $token');
|
||||
finalizerTokens.add(token);
|
||||
}
|
||||
|
||||
void runIsolateAttachFinalizer(Object message) {
|
||||
final finalizer = Finalizer<Nonce>(callback);
|
||||
final value = Nonce(1001);
|
||||
final token = Nonce(1002);
|
||||
finalizer.attach(value, token);
|
||||
final token9 = Nonce(9002);
|
||||
makeObjectWithFinalizer(finalizer, token9);
|
||||
if (message == null) {
|
||||
print('Isolate done.');
|
||||
return;
|
||||
}
|
||||
final list = message as List;
|
||||
assert(list.length == 2);
|
||||
final sendPort = list[0] as SendPort;
|
||||
final tryToSendFinalizer = list[1] as bool;
|
||||
if (tryToSendFinalizer) {
|
||||
Expect.throws(() {
|
||||
// TODO(http://dartbug.com/47777): Send and exit support.
|
||||
print('Trying to send and exit finalizer.');
|
||||
Isolate.exit(sendPort, [value, finalizer]);
|
||||
});
|
||||
}
|
||||
print('Isolate sending and exit.');
|
||||
Isolate.exit(sendPort, [value]);
|
||||
}
|
||||
|
||||
Future testNormalExit() async {
|
||||
final portExitMessage = ReceivePort();
|
||||
await Isolate.spawn(
|
||||
runIsolateAttachFinalizer,
|
||||
null,
|
||||
onExit: portExitMessage.sendPort,
|
||||
);
|
||||
await portExitMessage.first;
|
||||
|
||||
doGC();
|
||||
await yieldToMessageLoop();
|
||||
|
||||
Expect.equals(0, finalizerTokens.length);
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
Future<Finalizer> testSendAndExitHelper({bool trySendFinalizer = false}) async {
|
||||
final port = ReceivePort();
|
||||
await Isolate.spawn(
|
||||
runIsolateAttachFinalizer,
|
||||
[port.sendPort, trySendFinalizer],
|
||||
);
|
||||
final message = await port.first as List;
|
||||
print('Received message ($message).');
|
||||
final value = message[0] as Nonce;
|
||||
print('Received value ($value), but now forgetting about it.');
|
||||
|
||||
Expect.equals(1, message.length);
|
||||
// TODO(http://dartbug.com/47777): Send and exit support.
|
||||
return null;
|
||||
}
|
||||
|
||||
Future testSendAndExit() async {
|
||||
await testSendAndExitHelper(trySendFinalizer: false);
|
||||
|
||||
doGC();
|
||||
await yieldToMessageLoop();
|
||||
|
||||
Expect.equals(0, finalizerTokens.length);
|
||||
}
|
||||
|
||||
Future testSendAndExitFinalizer() async {
|
||||
final finalizer = await testSendAndExitHelper(trySendFinalizer: true);
|
||||
|
||||
// TODO(http://dartbug.com/47777): Send and exit support.
|
||||
Expect.isNull(finalizer);
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() {
|
||||
testFinalizer();
|
||||
}
|
||||
|
||||
void testFinalizer() async {
|
||||
final finalizerTokens = <Nonce>{};
|
||||
void callback(Nonce token) {
|
||||
print('Running finalizer: token: $token');
|
||||
finalizerTokens.add(token);
|
||||
}
|
||||
|
||||
final finalizer = Finalizer<Nonce>(callback);
|
||||
|
||||
{
|
||||
final detach = Nonce(2022);
|
||||
final token = null;
|
||||
|
||||
makeObjectWithFinalizer(finalizer, token, detach: detach);
|
||||
|
||||
doGC();
|
||||
|
||||
// We haven't stopped running synchronous dart code yet.
|
||||
Expect.isFalse(finalizerTokens.contains(token));
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 1));
|
||||
|
||||
// Now we have.
|
||||
Expect.isTrue(finalizerTokens.contains(token));
|
||||
|
||||
// Try detaching after finalizer ran.
|
||||
finalizer.detach(detach);
|
||||
}
|
||||
|
||||
print('End of test, shutting down.');
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// VMOptions=
|
||||
// VMOptions=--use_compactor
|
||||
// VMOptions=--use_compactor --force_evacuation
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() {
|
||||
testWrongArguments();
|
||||
testFinalizer();
|
||||
}
|
||||
|
||||
void testWrongArguments() {
|
||||
void callback(Object token) {
|
||||
throw 'This should never happen!';
|
||||
}
|
||||
|
||||
final finalizer = Finalizer<Nonce>(callback);
|
||||
final myFinalizable = Nonce(1000);
|
||||
final token = Nonce(2000);
|
||||
final detach = Nonce(3000);
|
||||
|
||||
Expect.throws(() {
|
||||
finalizer.attach(myFinalizable, token, detach: 123);
|
||||
});
|
||||
Expect.throws(() {
|
||||
finalizer.attach(123, token, detach: detach);
|
||||
});
|
||||
}
|
||||
|
||||
void testFinalizer() async {
|
||||
final finalizerTokens = <Object>{};
|
||||
void callback(Object token) {
|
||||
print('Running finalizer: token: $token');
|
||||
finalizerTokens.add(token);
|
||||
}
|
||||
|
||||
final finalizer = Finalizer<Nonce>(callback);
|
||||
|
||||
{
|
||||
final detach = Nonce(2022);
|
||||
final token = Nonce(42);
|
||||
|
||||
makeObjectWithFinalizer(finalizer, token, detach: detach);
|
||||
|
||||
doGC();
|
||||
|
||||
// We haven't stopped running synchronous dart code yet.
|
||||
Expect.isFalse(finalizerTokens.contains(token));
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 1));
|
||||
|
||||
// Now we have.
|
||||
Expect.isTrue(finalizerTokens.contains(token));
|
||||
|
||||
// Try detaching after finalizer ran.
|
||||
finalizer.detach(detach);
|
||||
}
|
||||
|
||||
{
|
||||
final token = Nonce(1337);
|
||||
final token2 = Nonce(1338);
|
||||
final detachkey = Nonce(1984);
|
||||
{
|
||||
final value = Nonce(2);
|
||||
final value2 = Nonce(2000000);
|
||||
finalizer.attach(value, token, detach: detachkey);
|
||||
finalizer.attach(value2, token2, detach: detachkey);
|
||||
// Should detach 2 finalizers.
|
||||
finalizer.detach(detachkey);
|
||||
// Try detaching again, should do nothing.
|
||||
finalizer.detach(detachkey);
|
||||
}
|
||||
doGC();
|
||||
await yieldToMessageLoop();
|
||||
Expect.isFalse(finalizerTokens.contains(token));
|
||||
Expect.isFalse(finalizerTokens.contains(token2));
|
||||
}
|
||||
|
||||
// Not running finalizer on shutdown.
|
||||
final value = Nonce(3);
|
||||
final token = Nonce(1337);
|
||||
finalizer.attach(value, token);
|
||||
print('End of test, shutting down.');
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// VMOptions=
|
||||
// VMOptions=--use_compactor
|
||||
// VMOptions=--use_compactor --force_evacuation
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() async {
|
||||
await testFinalizerZone();
|
||||
await testFinalizerException();
|
||||
}
|
||||
|
||||
Future<void> testFinalizerZone() async {
|
||||
Zone expectedZone;
|
||||
Zone actualZone;
|
||||
|
||||
final finalizer = runZoned(() {
|
||||
expectedZone = Zone.current;
|
||||
|
||||
void callback(Object token) {
|
||||
actualZone = Zone.current;
|
||||
}
|
||||
|
||||
return Finalizer<Nonce>(callback);
|
||||
});
|
||||
|
||||
final detach = Nonce(2022);
|
||||
final token = Nonce(42);
|
||||
|
||||
makeObjectWithFinalizer(finalizer, token, detach: detach);
|
||||
|
||||
doGC();
|
||||
|
||||
// We haven't stopped running synchronous dart code yet.
|
||||
Expect.isNull(actualZone);
|
||||
|
||||
await yieldToMessageLoop();
|
||||
|
||||
// Now we have.
|
||||
Expect.equals(expectedZone, actualZone);
|
||||
}
|
||||
|
||||
Future<void> testFinalizerException() async {
|
||||
Object caughtError;
|
||||
|
||||
final finalizer = runZonedGuarded(() {
|
||||
void callback(Object token) {
|
||||
throw 'uncaught!';
|
||||
}
|
||||
|
||||
return Finalizer<Nonce>(callback);
|
||||
}, (Object error, StackTrace stack) {
|
||||
caughtError = error;
|
||||
});
|
||||
|
||||
final detach = Nonce(2022);
|
||||
final token = Nonce(42);
|
||||
|
||||
makeObjectWithFinalizer(finalizer, token, detach: detach);
|
||||
|
||||
doGC();
|
||||
|
||||
Expect.isNull(caughtError);
|
||||
await yieldToMessageLoop();
|
||||
Expect.isNotNull(caughtError);
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
// ignore: import_internal_library, unused_import
|
||||
import 'dart:_internal';
|
||||
import 'dart:async';
|
||||
|
||||
/// A user-defined class of which objects can be identified with a field value.
|
||||
class Nonce {
|
||||
final int value;
|
||||
|
||||
Nonce(this.value);
|
||||
|
||||
String toString() => 'Nonce($value)';
|
||||
}
|
||||
|
||||
/// Never inline to ensure `value` becomes unreachable.
|
||||
@pragma('vm:never-inline')
|
||||
void makeObjectWithFinalizer<T>(Finalizer<T> finalizer, T token,
|
||||
{Object detach}) {
|
||||
final value = Nonce(1);
|
||||
finalizer.attach(value, token, detach: detach);
|
||||
}
|
||||
|
||||
/// Triggers garbage collection.
|
||||
// Defined in `dart:_internal`.
|
||||
// ignore: undefined_identifier
|
||||
void triggerGc() => VMInternalsForTesting.collectAllGarbage();
|
||||
|
||||
void Function(String) _namedPrint(String name) {
|
||||
if (name != null) {
|
||||
return (String value) => print('$name: $value');
|
||||
}
|
||||
return (String value) => print(value);
|
||||
}
|
||||
|
||||
/// Does a GC and if [doAwait] awaits a future to enable running finalizers.
|
||||
///
|
||||
/// Also prints for debug purposes.
|
||||
///
|
||||
/// If provided, [name] prefixes the debug prints.
|
||||
void doGC({String name}) {
|
||||
final _print = _namedPrint(name);
|
||||
|
||||
_print('Do GC.');
|
||||
triggerGc();
|
||||
_print('GC done');
|
||||
}
|
||||
|
||||
Future<void> yieldToMessageLoop({String name}) async {
|
||||
await Future.delayed(Duration(milliseconds: 1));
|
||||
_namedPrint(name)('Await done.');
|
||||
return null;
|
||||
}
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
// @dart = 2.9
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
// ignore: import_internal_library, unused_import
|
||||
import 'dart:_internal';
|
||||
|
||||
import 'helpers.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
void main() {
|
||||
testWeakReferenceNonExpandoKey();
|
||||
|
@ -14,6 +15,14 @@ void main() {
|
|||
testWeakReference();
|
||||
}
|
||||
|
||||
class Nonce {
|
||||
final int value;
|
||||
|
||||
Nonce(this.value);
|
||||
|
||||
String toString() => 'Nonce($value)';
|
||||
}
|
||||
|
||||
void testWeakReferenceNonExpandoKey() {
|
||||
Expect.throwsArgumentError(() {
|
||||
WeakReference<String>("Hello world!");
|
||||
|
@ -48,3 +57,7 @@ void testWeakReference() {
|
|||
|
||||
print('End of test, shutting down.');
|
||||
}
|
||||
|
||||
// Defined in `dart:_internal`.
|
||||
// ignore: undefined_identifier
|
||||
void triggerGc() => VMInternalsForTesting.collectAllGarbage();
|
||||
|
|
|
@ -247,7 +247,6 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
|
||||
await testWeakProperty();
|
||||
await testWeakReference();
|
||||
await testFinalizer();
|
||||
|
||||
await testForbiddenClosures();
|
||||
}
|
||||
|
@ -759,14 +758,6 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
}
|
||||
}
|
||||
|
||||
Future testFinalizer() async {
|
||||
print('testFinalizer');
|
||||
|
||||
void callback(Object token) {}
|
||||
final finalizer = Finalizer<Object>(callback);
|
||||
Expect.throwsArgumentError(() => sendPort.send(finalizer));
|
||||
}
|
||||
|
||||
Future testForbiddenClosures() async {
|
||||
print('testForbiddenClosures');
|
||||
for (final closure in nonCopyableClosures) {
|
||||
|
|
|
@ -108,7 +108,6 @@ dart_2/isolates/reload_*: SkipByDesign # These tests only run on normal JIT.
|
|||
[ $compiler == dartkp ]
|
||||
dart/causal_stacks/async_throws_stack_no_causal_non_symbolic_test: SkipByDesign # --no-lazy... does nothing on precompiler.
|
||||
dart/causal_stacks/async_throws_stack_no_causal_test: SkipByDesign # --no-lazy... does nothing on precompiler.
|
||||
dart/finalizer/finalizer_isolate_groups_run_gc_test: SkipByDesign # Isolate.spawnUri is not supported in AOT.
|
||||
dart/redirection_type_shuffling_test: SkipByDesign # Uses dart:mirrors.
|
||||
dart/scavenger_abort_test: SkipSlow
|
||||
dart/v8_snapshot_profile_writer_test: Pass, Slow # Can be slow due to re-invoking the precompiler.
|
||||
|
@ -443,11 +442,9 @@ dart_2/*: SkipByDesign # Legacy tests are not supposed to run on NNBD bots.
|
|||
# These Isolate tests that use spawnURI are hence skipped on purpose.
|
||||
[ $runtime == dart_precompiled || $runtime == vm && ($arch == simarm || $arch == simarm64 || $arch == simarm64c || $arch == simriscv32 || $arch == simriscv64) ]
|
||||
dart/data_uri_spawn_test: SkipByDesign # Isolate.spawnUri
|
||||
dart/finalizer/finalizer_isolate_groups_run_gc_test: SkipByDesign # uses spawnUri.
|
||||
dart/isolates/send_object_to_spawn_uri_isolate_test: SkipByDesign # uses spawnUri
|
||||
dart/issue32950_test: SkipByDesign # uses spawnUri.
|
||||
dart_2/data_uri_spawn_test: SkipByDesign # Isolate.spawnUri
|
||||
dart_2/finalizer/finalizer_isolate_groups_run_gc_test: SkipByDesign # uses spawnUri.
|
||||
dart_2/isolates/send_object_to_spawn_uri_isolate_test: SkipByDesign # uses spawnUri
|
||||
dart_2/issue32950_test: SkipByDesign # uses spawnUri.
|
||||
|
||||
|
|
|
@ -5140,7 +5140,7 @@ class WeakPropertyDeserializationCluster : public DeserializationCluster {
|
|||
Deserializer::InitializeHeader(property, kWeakPropertyCid,
|
||||
WeakProperty::InstanceSize());
|
||||
ReadFromTo(property);
|
||||
property->untag()->next_seen_by_gc_ = WeakProperty::null();
|
||||
property->untag()->next_ = WeakProperty::null();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -267,10 +267,6 @@ void ClassFinalizer::VerifyBootstrapClasses() {
|
|||
ASSERT_EQUAL(WeakProperty::InstanceSize(), cls.host_instance_size());
|
||||
cls = object_store->weak_reference_class();
|
||||
ASSERT_EQUAL(WeakReference::InstanceSize(), cls.host_instance_size());
|
||||
cls = object_store->finalizer_class();
|
||||
ASSERT_EQUAL(Finalizer::InstanceSize(), cls.host_instance_size());
|
||||
cls = object_store->finalizer_entry_class();
|
||||
ASSERT_EQUAL(FinalizerEntry::InstanceSize(), cls.host_instance_size());
|
||||
cls = object_store->linked_hash_map_class();
|
||||
ASSERT_EQUAL(LinkedHashMap::InstanceSize(), cls.host_instance_size());
|
||||
cls = object_store->immutable_linked_hash_map_class();
|
||||
|
|
|
@ -68,9 +68,6 @@ typedef uint16_t ClassIdTagType;
|
|||
V(TypeArguments) \
|
||||
V(AbstractType) \
|
||||
V(Type) \
|
||||
V(FinalizerBase) \
|
||||
V(Finalizer) \
|
||||
V(FinalizerEntry) \
|
||||
V(FunctionType) \
|
||||
V(TypeRef) \
|
||||
V(TypeParameter) \
|
||||
|
|
|
@ -2797,16 +2797,6 @@ void LoadFieldInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
|||
case Slot::Kind::kClosure_function:
|
||||
case Slot::Kind::kClosure_function_type_arguments:
|
||||
case Slot::Kind::kClosure_instantiator_type_arguments:
|
||||
case Slot::Kind::kFinalizer_callback:
|
||||
case Slot::Kind::kFinalizer_type_arguments:
|
||||
case Slot::Kind::kFinalizerBase_all_entries:
|
||||
case Slot::Kind::kFinalizerBase_detachments:
|
||||
case Slot::Kind::kFinalizerBase_entries_collected:
|
||||
case Slot::Kind::kFinalizerEntry_detach:
|
||||
case Slot::Kind::kFinalizerEntry_finalizer:
|
||||
case Slot::Kind::kFinalizerEntry_next:
|
||||
case Slot::Kind::kFinalizerEntry_token:
|
||||
case Slot::Kind::kFinalizerEntry_value:
|
||||
case Slot::Kind::kFunction_data:
|
||||
case Slot::Kind::kFunction_signature:
|
||||
case Slot::Kind::kFunctionType_named_parameter_names:
|
||||
|
|
|
@ -227,16 +227,6 @@ bool Slot::IsImmutableLengthSlot() const {
|
|||
case Slot::Kind::kClosure_hash:
|
||||
case Slot::Kind::kCapturedVariable:
|
||||
case Slot::Kind::kDartField:
|
||||
case Slot::Kind::kFinalizer_callback:
|
||||
case Slot::Kind::kFinalizer_type_arguments:
|
||||
case Slot::Kind::kFinalizerBase_all_entries:
|
||||
case Slot::Kind::kFinalizerBase_detachments:
|
||||
case Slot::Kind::kFinalizerBase_entries_collected:
|
||||
case Slot::Kind::kFinalizerEntry_detach:
|
||||
case Slot::Kind::kFinalizerEntry_finalizer:
|
||||
case Slot::Kind::kFinalizerEntry_next:
|
||||
case Slot::Kind::kFinalizerEntry_token:
|
||||
case Slot::Kind::kFinalizerEntry_value:
|
||||
case Slot::Kind::kFunction_data:
|
||||
case Slot::Kind::kFunction_signature:
|
||||
case Slot::Kind::kFunctionType_named_parameter_names:
|
||||
|
|
|
@ -53,15 +53,6 @@ class ParsedFunction;
|
|||
// that) or like a non-final field.
|
||||
#define NULLABLE_BOXED_NATIVE_SLOTS_LIST(V) \
|
||||
V(Array, UntaggedArray, type_arguments, TypeArguments, FINAL) \
|
||||
V(Finalizer, UntaggedFinalizer, type_arguments, TypeArguments, FINAL) \
|
||||
V(FinalizerBase, UntaggedFinalizerBase, all_entries, LinkedHashSet, VAR) \
|
||||
V(FinalizerBase, UntaggedFinalizerBase, detachments, Dynamic, VAR) \
|
||||
V(FinalizerBase, UntaggedFinalizer, entries_collected, FinalizerEntry, VAR) \
|
||||
V(FinalizerEntry, UntaggedFinalizerEntry, value, Dynamic, VAR) \
|
||||
V(FinalizerEntry, UntaggedFinalizerEntry, detach, Dynamic, VAR) \
|
||||
V(FinalizerEntry, UntaggedFinalizerEntry, token, Dynamic, VAR) \
|
||||
V(FinalizerEntry, UntaggedFinalizerEntry, finalizer, FinalizerBase, VAR) \
|
||||
V(FinalizerEntry, UntaggedFinalizerEntry, next, FinalizerEntry, VAR) \
|
||||
V(Function, UntaggedFunction, signature, FunctionType, FINAL) \
|
||||
V(Context, UntaggedContext, parent, Context, FINAL) \
|
||||
V(Closure, UntaggedClosure, instantiator_type_arguments, TypeArguments, \
|
||||
|
@ -100,7 +91,6 @@ class ParsedFunction;
|
|||
V(Closure, UntaggedClosure, function, Function, FINAL) \
|
||||
V(Closure, UntaggedClosure, context, Context, FINAL) \
|
||||
V(Closure, UntaggedClosure, hash, Context, VAR) \
|
||||
V(Finalizer, UntaggedFinalizer, callback, Closure, FINAL) \
|
||||
V(Function, UntaggedFunction, data, Dynamic, FINAL) \
|
||||
V(FunctionType, UntaggedFunctionType, named_parameter_names, Array, FINAL) \
|
||||
V(FunctionType, UntaggedFunctionType, parameter_types, Array, FINAL) \
|
||||
|
@ -169,7 +159,6 @@ NONNULLABLE_BOXED_NATIVE_SLOTS_LIST(FOR_EACH_NATIVE_SLOT)
|
|||
AOT_ONLY_UNBOXED_NATIVE_SLOTS_LIST(V) \
|
||||
V(ClosureData, UntaggedClosureData, default_type_arguments_kind, Uint8, \
|
||||
FINAL) \
|
||||
V(FinalizerBase, UntaggedFinalizerBase, isolate, IntPtr, VAR) \
|
||||
V(Function, UntaggedFunction, entry_point, Uword, FINAL) \
|
||||
V(Function, UntaggedFunction, kind_tag, Uint32, FINAL) \
|
||||
V(Function, UntaggedFunction, packed_fields, Uint32, FINAL) \
|
||||
|
|
|
@ -818,13 +818,6 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
|
|||
V(ByteDataViewLength, TypedDataBase_length) \
|
||||
V(ByteDataViewOffsetInBytes, TypedDataView_offset_in_bytes) \
|
||||
V(ByteDataViewTypedData, TypedDataView_typed_data) \
|
||||
V(Finalizer_getCallback, Finalizer_callback) \
|
||||
V(FinalizerBase_getAllEntries, FinalizerBase_all_entries) \
|
||||
V(FinalizerBase_getDetachments, FinalizerBase_detachments) \
|
||||
V(FinalizerEntry_getDetach, FinalizerEntry_detach) \
|
||||
V(FinalizerEntry_getNext, FinalizerEntry_next) \
|
||||
V(FinalizerEntry_getToken, FinalizerEntry_token) \
|
||||
V(FinalizerEntry_getValue, FinalizerEntry_value) \
|
||||
V(GrowableArrayLength, GrowableObjectArray_length) \
|
||||
V(ImmutableLinkedHashBase_getData, ImmutableLinkedHashBase_data) \
|
||||
V(ImmutableLinkedHashBase_getIndex, ImmutableLinkedHashBase_index) \
|
||||
|
@ -842,14 +835,6 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
|
|||
V(WeakReference_getTarget, WeakReference_target)
|
||||
|
||||
#define STORE_NATIVE_FIELD(V) \
|
||||
V(Finalizer_setCallback, Finalizer_callback) \
|
||||
V(FinalizerBase_setAllEntries, FinalizerBase_all_entries) \
|
||||
V(FinalizerBase_setDetachments, FinalizerBase_detachments) \
|
||||
V(FinalizerEntry_setDetach, FinalizerEntry_detach) \
|
||||
V(FinalizerEntry_setFinalizer, FinalizerEntry_finalizer) \
|
||||
V(FinalizerEntry_setNext, FinalizerEntry_next) \
|
||||
V(FinalizerEntry_setToken, FinalizerEntry_token) \
|
||||
V(FinalizerEntry_setValue, FinalizerEntry_value) \
|
||||
V(LinkedHashBase_setData, LinkedHashBase_data) \
|
||||
V(LinkedHashBase_setIndex, LinkedHashBase_index) \
|
||||
V(WeakProperty_setKey, WeakProperty_key) \
|
||||
|
@ -934,10 +919,6 @@ bool FlowGraphBuilder::IsRecognizedMethodForFlowGraph(
|
|||
case MethodRecognizer::kFfiAsExternalTypedDataFloat:
|
||||
case MethodRecognizer::kFfiAsExternalTypedDataDouble:
|
||||
case MethodRecognizer::kGetNativeField:
|
||||
case MethodRecognizer::kFinalizerBase_exchangeEntriesCollectedWithNull:
|
||||
case MethodRecognizer::kFinalizerBase_getIsolateFinalizers:
|
||||
case MethodRecognizer::kFinalizerBase_setIsolate:
|
||||
case MethodRecognizer::kFinalizerBase_setIsolateFinalizers:
|
||||
case MethodRecognizer::kObjectEquals:
|
||||
case MethodRecognizer::kStringBaseLength:
|
||||
case MethodRecognizer::kStringBaseIsEmpty:
|
||||
|
@ -1586,41 +1567,6 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
|
|||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += MathUnary(MathUnaryInstr::kSqrt);
|
||||
} break;
|
||||
case MethodRecognizer::kFinalizerBase_setIsolate:
|
||||
ASSERT_EQUAL(function.NumParameters(), 1);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadIsolate();
|
||||
body += ConvertUntaggedToUnboxed(kUnboxedIntPtr);
|
||||
body += StoreNativeField(Slot::FinalizerBase_isolate());
|
||||
body += NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kFinalizerBase_getIsolateFinalizers:
|
||||
ASSERT_EQUAL(function.NumParameters(), 0);
|
||||
body += LoadIsolate();
|
||||
body += RawLoadField(compiler::target::Isolate::finalizers_offset());
|
||||
break;
|
||||
case MethodRecognizer::kFinalizerBase_setIsolateFinalizers:
|
||||
ASSERT_EQUAL(function.NumParameters(), 1);
|
||||
body += LoadIsolate();
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += RawStoreField(compiler::target::Isolate::finalizers_offset());
|
||||
body += NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kFinalizerBase_exchangeEntriesCollectedWithNull:
|
||||
ASSERT_EQUAL(function.NumParameters(), 1);
|
||||
ASSERT(this->optimizing_);
|
||||
// This relies on being force-optimized to do an 'atomic' exchange w.r.t.
|
||||
// the GC.
|
||||
// As an alternative design we could introduce an ExchangeNativeFieldInstr
|
||||
// that uses the same machine code as std::atomic::exchange. Or we could
|
||||
// use an FfiNative to do that in C.
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
// No GC from here til StoreNativeField.
|
||||
body += LoadNativeField(Slot::FinalizerBase_entries_collected());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += NullConstant();
|
||||
body += StoreNativeField(Slot::FinalizerBase_entries_collected());
|
||||
break;
|
||||
#define IL_BODY(method, slot) \
|
||||
case MethodRecognizer::k##method: \
|
||||
ASSERT_EQUAL(function.NumParameters(), 1); \
|
||||
|
@ -4028,19 +3974,6 @@ Fragment FlowGraphBuilder::UnboxTruncate(Representation to) {
|
|||
return Fragment(unbox);
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::LoadThread() {
|
||||
LoadThreadInstr* instr = new (Z) LoadThreadInstr();
|
||||
Push(instr);
|
||||
return Fragment(instr);
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::LoadIsolate() {
|
||||
Fragment body;
|
||||
body += LoadThread();
|
||||
body += LoadUntagged(compiler::target::Thread::isolate_offset());
|
||||
return body;
|
||||
}
|
||||
|
||||
// TODO(http://dartbug.com/47487): Support unboxed output value.
|
||||
Fragment FlowGraphBuilder::BoolToInt() {
|
||||
// TODO(http://dartbug.com/36855) Build IfThenElseInstr, instead of letting
|
||||
|
|
|
@ -276,12 +276,6 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
|||
// target representation.
|
||||
Fragment UnboxTruncate(Representation to);
|
||||
|
||||
// Loads the (untagged) thread address.
|
||||
Fragment LoadThread();
|
||||
|
||||
// Loads the (untagged) isolate address.
|
||||
Fragment LoadIsolate();
|
||||
|
||||
// Converts a true to 1 and false to 0.
|
||||
Fragment BoolToInt();
|
||||
|
||||
|
|
|
@ -110,28 +110,6 @@ namespace dart {
|
|||
V(::, _sqrt, MathSqrt, 0x03183390) \
|
||||
V(::, _exp, MathExp, 0x00f4ffd0) \
|
||||
V(::, _log, MathLog, 0x09ae8462) \
|
||||
V(FinalizerBase, get:_allEntries, FinalizerBase_getAllEntries, 0xf03ff26b) \
|
||||
V(FinalizerBase, set:_allEntries, FinalizerBase_setAllEntries, 0x8f0920e8) \
|
||||
V(FinalizerBase, get:_detachments, FinalizerBase_getDetachments, 0x2f650f36) \
|
||||
V(FinalizerBase, set:_detachments, FinalizerBase_setDetachments, 0x788f1df3) \
|
||||
V(FinalizerBase, _exchangeEntriesCollectedWithNull, \
|
||||
FinalizerBase_exchangeEntriesCollectedWithNull, 0x6c9124fb) \
|
||||
V(FinalizerBase, _setIsolate, FinalizerBase_setIsolate, 0xbcf7db91) \
|
||||
V(FinalizerBase, get:_isolateFinalizers, FinalizerBase_getIsolateFinalizers, \
|
||||
0x70f53b2b) \
|
||||
V(FinalizerBase, set:_isolateFinalizers, FinalizerBase_setIsolateFinalizers, \
|
||||
0xb3e66928) \
|
||||
V(_FinalizerImpl, get:_callback, Finalizer_getCallback, 0x6f3d56bc) \
|
||||
V(_FinalizerImpl, set:_callback, Finalizer_setCallback, 0xc6aa96f9) \
|
||||
V(FinalizerEntry, get:value, FinalizerEntry_getValue, 0xf5c9b9d7) \
|
||||
V(FinalizerEntry, set:value, FinalizerEntry_setValue, 0x5501cc54) \
|
||||
V(FinalizerEntry, get:detach, FinalizerEntry_getDetach, 0x171cd968) \
|
||||
V(FinalizerEntry, set:detach, FinalizerEntry_setDetach, 0x7654ebe5) \
|
||||
V(FinalizerEntry, set:finalizer, FinalizerEntry_setFinalizer, 0x15cfefe9) \
|
||||
V(FinalizerEntry, get:token, FinalizerEntry_getToken, 0x04915a72) \
|
||||
V(FinalizerEntry, set:token, FinalizerEntry_setToken, 0x63c96cef) \
|
||||
V(FinalizerEntry, get:next, FinalizerEntry_getNext, 0x7102d7a4) \
|
||||
V(FinalizerEntry, set:next, FinalizerEntry_setNext, 0xd0b2ee61) \
|
||||
V(Float32x4, _Float32x4FromDoubles, Float32x4FromDoubles, 0x1845792b) \
|
||||
V(Float32x4, Float32x4.zero, Float32x4Zero, 0xd3b64002) \
|
||||
V(Float32x4, _Float32x4Splat, Float32x4Splat, 0x13a552c3) \
|
||||
|
|
|
@ -439,10 +439,6 @@ static uword GetInstanceSizeImpl(const dart::Class& handle) {
|
|||
return WeakProperty::InstanceSize();
|
||||
case kWeakReferenceCid:
|
||||
return WeakReference::InstanceSize();
|
||||
case kFinalizerCid:
|
||||
return Finalizer::InstanceSize();
|
||||
case kFinalizerEntryCid:
|
||||
return FinalizerEntry::InstanceSize();
|
||||
case kByteBufferCid:
|
||||
case kByteDataViewCid:
|
||||
case kPointerCid:
|
||||
|
|
|
@ -1045,34 +1045,6 @@ class WeakReference : public AllStatic {
|
|||
FINAL_CLASS();
|
||||
};
|
||||
|
||||
class FinalizerBase : public AllStatic {
|
||||
public:
|
||||
static word all_entries_offset();
|
||||
static word detachments_offset();
|
||||
static word entries_collected_offset();
|
||||
static word isolate_offset();
|
||||
FINAL_CLASS();
|
||||
};
|
||||
|
||||
class Finalizer : public AllStatic {
|
||||
public:
|
||||
static word type_arguments_offset();
|
||||
static word callback_offset();
|
||||
static word InstanceSize();
|
||||
FINAL_CLASS();
|
||||
};
|
||||
|
||||
class FinalizerEntry : public AllStatic {
|
||||
public:
|
||||
static word value_offset();
|
||||
static word detach_offset();
|
||||
static word token_offset();
|
||||
static word next_offset();
|
||||
static word finalizer_offset();
|
||||
static word InstanceSize();
|
||||
FINAL_CLASS();
|
||||
};
|
||||
|
||||
class MirrorReference : public AllStatic {
|
||||
public:
|
||||
static word InstanceSize();
|
||||
|
@ -1270,7 +1242,6 @@ class Isolate : public AllStatic {
|
|||
static word current_tag_offset();
|
||||
static word user_tag_offset();
|
||||
static word ic_miss_code_offset();
|
||||
static word finalizers_offset();
|
||||
#if !defined(PRODUCT)
|
||||
static word single_step_offset();
|
||||
#endif // !defined(PRODUCT)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -149,7 +149,6 @@
|
|||
FIELD(Int32x4, value_offset) \
|
||||
FIELD(Isolate, current_tag_offset) \
|
||||
FIELD(Isolate, default_tag_offset) \
|
||||
FIELD(Isolate, finalizers_offset) \
|
||||
FIELD(Isolate, ic_miss_code_offset) \
|
||||
FIELD(IsolateGroup, object_store_offset) \
|
||||
FIELD(IsolateGroup, shared_class_table_offset) \
|
||||
|
@ -301,17 +300,6 @@
|
|||
FIELD(Type, type_class_id_offset) \
|
||||
FIELD(Type, type_state_offset) \
|
||||
FIELD(Type, nullability_offset) \
|
||||
FIELD(Finalizer, type_arguments_offset) \
|
||||
FIELD(Finalizer, callback_offset) \
|
||||
FIELD(FinalizerBase, all_entries_offset) \
|
||||
FIELD(FinalizerBase, detachments_offset) \
|
||||
FIELD(FinalizerBase, entries_collected_offset) \
|
||||
FIELD(FinalizerBase, isolate_offset) \
|
||||
FIELD(FinalizerEntry, value_offset) \
|
||||
FIELD(FinalizerEntry, detach_offset) \
|
||||
FIELD(FinalizerEntry, token_offset) \
|
||||
FIELD(FinalizerEntry, finalizer_offset) \
|
||||
FIELD(FinalizerEntry, next_offset) \
|
||||
FIELD(FunctionType, hash_offset) \
|
||||
FIELD(FunctionType, named_parameter_names_offset) \
|
||||
FIELD(FunctionType, nullability_offset) \
|
||||
|
@ -374,8 +362,6 @@
|
|||
SIZEOF(ExternalTypedData, InstanceSize, UntaggedExternalTypedData) \
|
||||
SIZEOF(FfiTrampolineData, InstanceSize, UntaggedFfiTrampolineData) \
|
||||
SIZEOF(Field, InstanceSize, UntaggedField) \
|
||||
SIZEOF(Finalizer, InstanceSize, UntaggedFinalizer) \
|
||||
SIZEOF(FinalizerEntry, InstanceSize, UntaggedFinalizerEntry) \
|
||||
SIZEOF(Float32x4, InstanceSize, UntaggedFloat32x4) \
|
||||
SIZEOF(Float64x2, InstanceSize, UntaggedFloat64x2) \
|
||||
SIZEOF(Function, InstanceSize, UntaggedFunction) \
|
||||
|
|
|
@ -370,7 +370,6 @@ char* Dart::DartInit(const Dart_InitializeParams* params) {
|
|||
Object::InitNullAndBool(vm_isolate_->group());
|
||||
vm_isolate_->isolate_group_->set_object_store(new ObjectStore());
|
||||
vm_isolate_->isolate_object_store()->Init();
|
||||
vm_isolate_->finalizers_ = GrowableObjectArray::null();
|
||||
Object::Init(vm_isolate_->group());
|
||||
OffsetsTable::Init();
|
||||
ArgumentsDescriptor::Init();
|
||||
|
|
|
@ -677,14 +677,18 @@ ObjectPtr DartLibraryCalls::Equals(const Instance& left,
|
|||
}
|
||||
|
||||
ObjectPtr DartLibraryCalls::LookupHandler(Dart_Port port_id) {
|
||||
Thread* const thread = Thread::Current();
|
||||
Zone* const zone = thread->zone();
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
const auto& function = Function::Handle(
|
||||
zone, thread->isolate_group()->object_store()->lookup_port_handler());
|
||||
const int kNumArguments = 1;
|
||||
ASSERT(!function.IsNull());
|
||||
Array& args = Array::Handle(
|
||||
zone, thread->isolate()->isolate_object_store()->dart_args_1());
|
||||
ASSERT(!args.IsNull());
|
||||
if (args.IsNull()) {
|
||||
args = Array::New(kNumArguments);
|
||||
thread->isolate()->isolate_object_store()->set_dart_args_1(args);
|
||||
}
|
||||
args.SetAt(0, Integer::Handle(zone, Integer::New(port_id)));
|
||||
const Object& result =
|
||||
Object::Handle(zone, DartEntry::InvokeFunction(function, args));
|
||||
|
@ -702,7 +706,24 @@ ObjectPtr DartLibraryCalls::LookupOpenPorts() {
|
|||
return result.ptr();
|
||||
}
|
||||
|
||||
static void DebuggerSetResumeIfStepping(Isolate* isolate) {
|
||||
ObjectPtr DartLibraryCalls::HandleMessage(Dart_Port port_id,
|
||||
const Instance& message) {
|
||||
auto thread = Thread::Current();
|
||||
auto zone = thread->zone();
|
||||
auto isolate = thread->isolate();
|
||||
auto object_store = thread->isolate_group()->object_store();
|
||||
const auto& function =
|
||||
Function::Handle(zone, object_store->handle_message_function());
|
||||
const int kNumArguments = 2;
|
||||
ASSERT(!function.IsNull());
|
||||
Array& args =
|
||||
Array::Handle(zone, isolate->isolate_object_store()->dart_args_2());
|
||||
if (args.IsNull()) {
|
||||
args = Array::New(kNumArguments);
|
||||
isolate->isolate_object_store()->set_dart_args_2(args);
|
||||
}
|
||||
args.SetAt(0, Integer::Handle(zone, Integer::New(port_id)));
|
||||
args.SetAt(1, message);
|
||||
#if !defined(PRODUCT)
|
||||
if (isolate->debugger()->IsStepping()) {
|
||||
// If the isolate is being debugged and the debugger was stepping
|
||||
|
@ -711,47 +732,6 @@ static void DebuggerSetResumeIfStepping(Isolate* isolate) {
|
|||
isolate->debugger()->SetResumeAction(Debugger::kStepInto);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ObjectPtr DartLibraryCalls::HandleMessage(Dart_Port port_id,
|
||||
const Instance& message) {
|
||||
auto* const thread = Thread::Current();
|
||||
auto* const zone = thread->zone();
|
||||
auto* const isolate = thread->isolate();
|
||||
auto* const object_store = thread->isolate_group()->object_store();
|
||||
const auto& function =
|
||||
Function::Handle(zone, object_store->handle_message_function());
|
||||
ASSERT(!function.IsNull());
|
||||
Array& args =
|
||||
Array::Handle(zone, isolate->isolate_object_store()->dart_args_2());
|
||||
ASSERT(!args.IsNull());
|
||||
args.SetAt(0, Integer::Handle(zone, Integer::New(port_id)));
|
||||
args.SetAt(1, message);
|
||||
DebuggerSetResumeIfStepping(isolate);
|
||||
const Object& handler =
|
||||
Object::Handle(zone, DartEntry::InvokeFunction(function, args));
|
||||
return handler.ptr();
|
||||
}
|
||||
|
||||
ObjectPtr DartLibraryCalls::HandleFinalizerMessage(
|
||||
const FinalizerBase& finalizer) {
|
||||
if (FLAG_trace_finalizers) {
|
||||
THR_Print("Running finalizer %p callback on isolate %p\n",
|
||||
finalizer.ptr()->untag(), finalizer.isolate());
|
||||
}
|
||||
|
||||
auto* const thread = Thread::Current();
|
||||
auto* const zone = thread->zone();
|
||||
auto* const isolate = thread->isolate();
|
||||
auto* const object_store = thread->isolate_group()->object_store();
|
||||
const auto& function =
|
||||
Function::Handle(zone, object_store->handle_finalizer_message_function());
|
||||
ASSERT(!function.IsNull());
|
||||
Array& args =
|
||||
Array::Handle(zone, isolate->isolate_object_store()->dart_args_1());
|
||||
ASSERT(!args.IsNull());
|
||||
args.SetAt(0, finalizer);
|
||||
DebuggerSetResumeIfStepping(isolate);
|
||||
const Object& handler =
|
||||
Object::Handle(zone, DartEntry::InvokeFunction(function, args));
|
||||
return handler.ptr();
|
||||
|
|
|
@ -36,8 +36,8 @@ class ArgumentsDescriptor : public ValueObject {
|
|||
intptr_t TypeArgsLen() const; // 0 if no type argument vector is passed.
|
||||
intptr_t FirstArgIndex() const { return TypeArgsLen() > 0 ? 1 : 0; }
|
||||
intptr_t CountWithTypeArgs() const { return FirstArgIndex() + Count(); }
|
||||
intptr_t Count() const; // Excluding type arguments vector.
|
||||
intptr_t Size() const; // Excluding type arguments vector.
|
||||
intptr_t Count() const; // Excluding type arguments vector.
|
||||
intptr_t Size() const; // Excluding type arguments vector.
|
||||
intptr_t SizeWithTypeArgs() const { return FirstArgIndex() + Size(); }
|
||||
intptr_t PositionalCount() const; // Excluding type arguments vector.
|
||||
intptr_t NamedCount() const { return Count() - PositionalCount(); }
|
||||
|
@ -292,12 +292,10 @@ class DartLibraryCalls : public AllStatic {
|
|||
// handler for this port id.
|
||||
static ObjectPtr HandleMessage(Dart_Port port_id, const Instance& message);
|
||||
|
||||
// Invokes the finalizer to run its callbacks.
|
||||
static ObjectPtr HandleFinalizerMessage(const FinalizerBase& finalizer);
|
||||
|
||||
// Returns a list of open ReceivePorts.
|
||||
static ObjectPtr LookupOpenPorts();
|
||||
|
||||
|
||||
// Returns null on success, an ErrorPtr on failure.
|
||||
static ObjectPtr DrainMicrotaskQueue();
|
||||
|
||||
|
|
|
@ -202,7 +202,6 @@ constexpr bool FLAG_support_il_printer = false;
|
|||
"Generate code for a generic CPU, unknown at compile time") \
|
||||
D(trace_cha, bool, false, "Trace CHA operations") \
|
||||
R(trace_field_guards, false, bool, false, "Trace changes in field's cids.") \
|
||||
D(trace_finalizers, bool, false, "Traces finalizers.") \
|
||||
D(trace_ic, bool, false, "Trace IC handling") \
|
||||
D(trace_ic_miss_in_optimized, bool, false, \
|
||||
"Trace IC miss in optimized code") \
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Logic shared between the Scavenger and Marker.
|
||||
|
||||
#include "vm/heap/gc_shared.h"
|
||||
|
||||
#include "vm/dart_api_state.h"
|
||||
#include "vm/heap/scavenger.h"
|
||||
#include "vm/log.h"
|
||||
#include "vm/message_handler.h"
|
||||
#include "vm/object.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
void GCLinkedLists::Clear() {
|
||||
#define FOREACH(type, var) var.Clear();
|
||||
GC_LINKED_LIST(FOREACH)
|
||||
#undef FOREACH
|
||||
}
|
||||
|
||||
bool GCLinkedLists::IsEmpty() {
|
||||
#define FOREACH(type, var) \
|
||||
if (!var.IsEmpty()) { \
|
||||
return false; \
|
||||
}
|
||||
GC_LINKED_LIST(FOREACH)
|
||||
return true;
|
||||
#undef FOREACH
|
||||
}
|
||||
|
||||
void GCLinkedLists::FlushInto(GCLinkedLists* to) {
|
||||
#define FOREACH(type, var) var.FlushInto(&to->var);
|
||||
GC_LINKED_LIST(FOREACH)
|
||||
#undef FOREACH
|
||||
}
|
||||
|
||||
} // namespace dart
|
|
@ -1,195 +0,0 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Logic shared between the Scavenger and Marker.
|
||||
|
||||
#ifndef RUNTIME_VM_HEAP_GC_SHARED_H_
|
||||
#define RUNTIME_VM_HEAP_GC_SHARED_H_
|
||||
|
||||
#include "vm/compiler/runtime_api.h"
|
||||
#if defined(SHOULD_NOT_INCLUDE_RUNTIME)
|
||||
#error "Should not include runtime"
|
||||
#endif
|
||||
|
||||
#include "vm/dart_api_state.h"
|
||||
#include "vm/heap/scavenger.h"
|
||||
#include "vm/log.h"
|
||||
#include "vm/message_handler.h"
|
||||
#include "vm/object.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
// These object types have a linked list chaining all pending objects when
|
||||
// processing these in the GC.
|
||||
// The field should not be visited by pointer visitors.
|
||||
// The field should only be set during a GC.
|
||||
//
|
||||
// Macro params:
|
||||
// - type
|
||||
// - variable name
|
||||
#define GC_LINKED_LIST(V) \
|
||||
V(WeakProperty, weak_properties) \
|
||||
V(WeakReference, weak_references) \
|
||||
V(FinalizerEntry, finalizer_entries)
|
||||
|
||||
template <typename Type, typename PtrType>
|
||||
struct GCLinkedList {
|
||||
public:
|
||||
void Enqueue(PtrType ptr) {
|
||||
ptr->untag()->next_seen_by_gc_ = head;
|
||||
if (head == Type::null()) {
|
||||
tail = ptr;
|
||||
}
|
||||
head = ptr;
|
||||
}
|
||||
|
||||
void FlushInto(GCLinkedList<Type, PtrType>* to) {
|
||||
if (to->head == Type::null()) {
|
||||
ASSERT(to->tail == Type::null());
|
||||
to->head = head;
|
||||
to->tail = tail;
|
||||
} else {
|
||||
ASSERT(to->tail != Type::null());
|
||||
ASSERT(to->tail->untag()->next_seen_by_gc() == Type::null());
|
||||
to->tail->untag()->next_seen_by_gc_ = head;
|
||||
to->tail = tail;
|
||||
}
|
||||
Clear();
|
||||
}
|
||||
|
||||
PtrType Clear() {
|
||||
PtrType return_value = head;
|
||||
head = Type::null();
|
||||
tail = Type::null();
|
||||
return return_value;
|
||||
}
|
||||
|
||||
bool IsEmpty() { return head == Type::null() && tail == Type::null(); }
|
||||
|
||||
private:
|
||||
PtrType head = Type::null();
|
||||
PtrType tail = Type::null();
|
||||
};
|
||||
|
||||
struct GCLinkedLists {
|
||||
public:
|
||||
void Clear();
|
||||
bool IsEmpty();
|
||||
void FlushInto(GCLinkedLists* to);
|
||||
|
||||
#define FOREACH(type, var) GCLinkedList<type, type##Ptr> var;
|
||||
GC_LINKED_LIST(FOREACH)
|
||||
#undef FOREACH
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
#define TRACE_FINALIZER(format, ...) \
|
||||
if (FLAG_trace_finalizers) { \
|
||||
THR_Print("%s %p " format "\n", visitor->kName, visitor, __VA_ARGS__); \
|
||||
}
|
||||
#else
|
||||
#define TRACE_FINALIZER(format, ...)
|
||||
#endif
|
||||
|
||||
// This function processes all finalizer entries discovered by a scavenger or
|
||||
// marker. If an entry is referencing an object that is going to die, such entry
|
||||
// is cleared and enqueued in the respective finalizer.
|
||||
//
|
||||
// Finalizer entries belonging to unreachable finalizer entries do not get
|
||||
// processed, so the callback will not be called for these finalizers.
|
||||
//
|
||||
// For more documentation see runtime/docs/gc.md.
|
||||
//
|
||||
// |GCVisitorType| is a concrete type implementing either marker or scavenger.
|
||||
// It is expected to provide |SetNullIfCollected| method for clearing fields
|
||||
// referring to dead objects and |kName| field which contains visitor name for
|
||||
// tracing output.
|
||||
template <typename GCVisitorType>
|
||||
void MournFinalized(GCVisitorType* visitor) {
|
||||
FinalizerEntryPtr current_entry = visitor->delayed_.finalizer_entries.Clear();
|
||||
while (current_entry != FinalizerEntry::null()) {
|
||||
TRACE_FINALIZER("Processing Entry %p", current_entry->untag());
|
||||
FinalizerEntryPtr next_entry =
|
||||
current_entry->untag()->next_seen_by_gc_.Decompress(
|
||||
current_entry->heap_base());
|
||||
current_entry->untag()->next_seen_by_gc_ = FinalizerEntry::null();
|
||||
|
||||
uword heap_base = current_entry->heap_base();
|
||||
const bool value_collected_this_gc = GCVisitorType::SetNullIfCollected(
|
||||
heap_base, ¤t_entry->untag()->value_);
|
||||
GCVisitorType::SetNullIfCollected(heap_base,
|
||||
¤t_entry->untag()->detach_);
|
||||
GCVisitorType::SetNullIfCollected(heap_base,
|
||||
¤t_entry->untag()->finalizer_);
|
||||
|
||||
ObjectPtr token_object = current_entry->untag()->token();
|
||||
// See sdk/lib/_internal/vm/lib/internal_patch.dart FinalizerBase.detach.
|
||||
const bool is_detached = token_object == current_entry;
|
||||
|
||||
if (value_collected_this_gc && !is_detached) {
|
||||
FinalizerBasePtr finalizer = current_entry->untag()->finalizer();
|
||||
|
||||
if (finalizer.IsRawNull()) {
|
||||
TRACE_FINALIZER("Value collected entry %p finalizer null",
|
||||
current_entry->untag());
|
||||
|
||||
// Do nothing, the finalizer has been GCed.
|
||||
} else if (finalizer.IsFinalizer()) {
|
||||
TRACE_FINALIZER("Value collected entry %p finalizer %p",
|
||||
current_entry->untag(), finalizer->untag());
|
||||
|
||||
FinalizerPtr finalizer_dart = static_cast<FinalizerPtr>(finalizer);
|
||||
// Move entry to entries collected and current head of that list as
|
||||
// the next element. Using a atomic exchange satisfies concurrency
|
||||
// between the parallel GC tasks.
|
||||
// We rely on the fact that the mutator thread is not running to avoid
|
||||
// races between GC and mutator modifying Finalizer.entries_collected.
|
||||
//
|
||||
// We only run in serial marker or in the finalize step in the marker,
|
||||
// both are in safepoint.
|
||||
// The main scavenger worker is at safepoint, the other scavenger
|
||||
// workers are are not, but they bypass safepoint because the main
|
||||
// worker is at a safepoint already.
|
||||
ASSERT(Thread::Current()->IsAtSafepoint() ||
|
||||
Thread::Current()->BypassSafepoints());
|
||||
|
||||
FinalizerEntryPtr previous_head =
|
||||
finalizer_dart->untag()->exchange_entries_collected(current_entry);
|
||||
current_entry->untag()->set_next(previous_head);
|
||||
const bool first_entry = previous_head.IsRawNull();
|
||||
// Schedule calling Dart finalizer.
|
||||
if (first_entry) {
|
||||
Isolate* isolate = finalizer->untag()->isolate_;
|
||||
if (isolate == nullptr) {
|
||||
TRACE_FINALIZER(
|
||||
"Not scheduling finalizer %p callback on isolate null",
|
||||
finalizer->untag());
|
||||
} else {
|
||||
TRACE_FINALIZER("Scheduling finalizer %p callback on isolate %p",
|
||||
finalizer->untag(), isolate);
|
||||
|
||||
PersistentHandle* handle =
|
||||
isolate->group()->api_state()->AllocatePersistentHandle();
|
||||
handle->set_ptr(finalizer);
|
||||
MessageHandler* message_handler = isolate->message_handler();
|
||||
message_handler->PostMessage(
|
||||
Message::New(handle, Message::kNormalPriority),
|
||||
/*before_events*/ false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO(http://dartbug.com/47777): Implement NativeFinalizer.
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
current_entry = next_entry;
|
||||
}
|
||||
}
|
||||
|
||||
#undef TRACE_FINALIZER
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_HEAP_GC_SHARED_H_
|
|
@ -11,8 +11,6 @@ heap_sources = [
|
|||
"compactor.h",
|
||||
"freelist.cc",
|
||||
"freelist.h",
|
||||
"gc_shared.cc",
|
||||
"gc_shared.h",
|
||||
"heap.cc",
|
||||
"heap.h",
|
||||
"marker.cc",
|
||||
|
|
|
@ -4,12 +4,9 @@
|
|||
|
||||
#include "vm/heap/marker.h"
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "platform/atomic.h"
|
||||
#include "vm/allocation.h"
|
||||
#include "vm/dart_api_state.h"
|
||||
#include "vm/flags.h"
|
||||
#include "vm/heap/gc_shared.h"
|
||||
#include "vm/heap/pages.h"
|
||||
#include "vm/heap/pointer_block.h"
|
||||
#include "vm/isolate.h"
|
||||
|
@ -17,12 +14,10 @@
|
|||
#include "vm/object_id_ring.h"
|
||||
#include "vm/raw_object.h"
|
||||
#include "vm/stack_frame.h"
|
||||
#include "vm/tagged_pointer.h"
|
||||
#include "vm/thread_barrier.h"
|
||||
#include "vm/thread_pool.h"
|
||||
#include "vm/thread_registry.h"
|
||||
#include "vm/timeline.h"
|
||||
#include "vm/token.h"
|
||||
#include "vm/visitor.h"
|
||||
|
||||
namespace dart {
|
||||
|
@ -39,35 +34,34 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
|
|||
page_space_(page_space),
|
||||
work_list_(marking_stack),
|
||||
deferred_work_list_(deferred_marking_stack),
|
||||
delayed_weak_properties_(WeakProperty::null()),
|
||||
delayed_weak_properties_tail_(WeakProperty::null()),
|
||||
delayed_weak_references_(WeakReference::null()),
|
||||
delayed_weak_references_tail_(WeakReference::null()),
|
||||
marked_bytes_(0),
|
||||
marked_micros_(0) {
|
||||
ASSERT(thread_->isolate_group() == isolate_group);
|
||||
}
|
||||
~MarkingVisitorBase() { ASSERT(delayed_.IsEmpty()); }
|
||||
|
||||
#ifdef DEBUG
|
||||
const char* kName = "Marker";
|
||||
#endif
|
||||
~MarkingVisitorBase() {
|
||||
ASSERT(delayed_weak_properties_ == WeakProperty::null());
|
||||
ASSERT(delayed_weak_references_ == WeakReference::null());
|
||||
}
|
||||
|
||||
uintptr_t marked_bytes() const { return marked_bytes_; }
|
||||
int64_t marked_micros() const { return marked_micros_; }
|
||||
void AddMicros(int64_t micros) { marked_micros_ += micros; }
|
||||
|
||||
bool IsMarked(ObjectPtr raw) {
|
||||
ASSERT(raw->IsHeapObject());
|
||||
ASSERT(raw->IsOldObject());
|
||||
return raw->untag()->IsMarked();
|
||||
}
|
||||
|
||||
bool ProcessPendingWeakProperties() {
|
||||
bool more_to_mark = false;
|
||||
WeakPropertyPtr cur_weak = delayed_.weak_properties.Clear();
|
||||
WeakPropertyPtr cur_weak = delayed_weak_properties_;
|
||||
delayed_weak_properties_tail_ = delayed_weak_properties_ =
|
||||
WeakProperty::null();
|
||||
while (cur_weak != WeakProperty::null()) {
|
||||
WeakPropertyPtr next_weak =
|
||||
cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
|
||||
cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
|
||||
ObjectPtr raw_key = cur_weak->untag()->key();
|
||||
// Reset the next pointer in the weak property.
|
||||
cur_weak->untag()->next_seen_by_gc_ = WeakProperty::null();
|
||||
cur_weak->untag()->next_ = WeakProperty::null();
|
||||
if (raw_key->IsSmiOrNewObject() || raw_key->untag()->IsMarked()) {
|
||||
ObjectPtr raw_val = cur_weak->untag()->value();
|
||||
if (!raw_val->IsSmiOrNewObject() && !raw_val->untag()->IsMarked()) {
|
||||
|
@ -79,8 +73,7 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
|
|||
cur_weak->untag()->VisitPointersNonvirtual(this);
|
||||
} else {
|
||||
// Requeue this weak property to be handled later.
|
||||
ASSERT(IsMarked(cur_weak));
|
||||
delayed_.weak_properties.Enqueue(cur_weak);
|
||||
EnqueueWeakProperty(cur_weak);
|
||||
}
|
||||
// Advance to next weak property in the queue.
|
||||
cur_weak = next_weak;
|
||||
|
@ -132,9 +125,6 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
|
|||
} else if (class_id == kWeakReferenceCid) {
|
||||
WeakReferencePtr raw_weak = static_cast<WeakReferencePtr>(raw_obj);
|
||||
size = ProcessWeakReference(raw_weak);
|
||||
} else if (class_id == kFinalizerEntryCid) {
|
||||
FinalizerEntryPtr raw_weak = static_cast<FinalizerEntryPtr>(raw_obj);
|
||||
size = ProcessFinalizerEntry(raw_weak);
|
||||
} else {
|
||||
size = raw_obj->untag()->VisitPointersNonvirtual(this);
|
||||
}
|
||||
|
@ -192,6 +182,34 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
void EnqueueWeakProperty(WeakPropertyPtr raw_weak) {
|
||||
ASSERT(raw_weak->IsHeapObject());
|
||||
ASSERT(raw_weak->IsOldObject());
|
||||
ASSERT(raw_weak->IsWeakProperty());
|
||||
ASSERT(raw_weak->untag()->IsMarked());
|
||||
ASSERT(raw_weak->untag()->next_ ==
|
||||
CompressedWeakPropertyPtr(WeakProperty::null()));
|
||||
raw_weak->untag()->next_ = delayed_weak_properties_;
|
||||
if (delayed_weak_properties_ == WeakProperty::null()) {
|
||||
delayed_weak_properties_tail_ = raw_weak;
|
||||
}
|
||||
delayed_weak_properties_ = raw_weak;
|
||||
}
|
||||
|
||||
void EnqueueWeakReference(WeakReferencePtr raw_weak) {
|
||||
ASSERT(raw_weak->IsHeapObject());
|
||||
ASSERT(raw_weak->IsOldObject());
|
||||
ASSERT(raw_weak->IsWeakReference());
|
||||
ASSERT(raw_weak->untag()->IsMarked());
|
||||
ASSERT(raw_weak->untag()->next_ ==
|
||||
CompressedWeakReferencePtr(WeakReference::null()));
|
||||
raw_weak->untag()->next_ = delayed_weak_references_;
|
||||
if (delayed_weak_references_ == WeakReference::null()) {
|
||||
delayed_weak_references_tail_ = raw_weak;
|
||||
}
|
||||
delayed_weak_references_ = raw_weak;
|
||||
}
|
||||
|
||||
intptr_t ProcessWeakProperty(WeakPropertyPtr raw_weak) {
|
||||
// The fate of the weak property is determined by its key.
|
||||
ObjectPtr raw_key =
|
||||
|
@ -200,8 +218,7 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
|
|||
if (raw_key->IsHeapObject() && raw_key->IsOldObject() &&
|
||||
!raw_key->untag()->IsMarked()) {
|
||||
// Key was white. Enqueue the weak property.
|
||||
ASSERT(IsMarked(raw_weak));
|
||||
delayed_.weak_properties.Enqueue(raw_weak);
|
||||
EnqueueWeakProperty(raw_weak);
|
||||
return raw_weak->untag()->HeapSize();
|
||||
}
|
||||
// Key is gray or black. Make the weak property black.
|
||||
|
@ -218,8 +235,7 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
|
|||
!raw_target->untag()->IsMarked()) {
|
||||
// Target was white. Enqueue the weak reference. It is potentially dead.
|
||||
// It might still be made alive by weak properties in next rounds.
|
||||
ASSERT(IsMarked(raw_weak));
|
||||
delayed_.weak_references.Enqueue(raw_weak);
|
||||
EnqueueWeakReference(raw_weak);
|
||||
}
|
||||
// Always visit the type argument.
|
||||
ObjectPtr raw_type_arguments =
|
||||
|
@ -229,17 +245,6 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
|
|||
return raw_weak->untag()->HeapSize();
|
||||
}
|
||||
|
||||
intptr_t ProcessFinalizerEntry(FinalizerEntryPtr raw_entry) {
|
||||
ASSERT(IsMarked(raw_entry));
|
||||
delayed_.finalizer_entries.Enqueue(raw_entry);
|
||||
// Only visit token and next.
|
||||
MarkObject(LoadCompressedPointerIgnoreRace(&raw_entry->untag()->token_)
|
||||
.Decompress(raw_entry->heap_base()));
|
||||
MarkObject(LoadCompressedPointerIgnoreRace(&raw_entry->untag()->next_)
|
||||
.Decompress(raw_entry->heap_base()));
|
||||
return raw_entry->untag()->HeapSize();
|
||||
}
|
||||
|
||||
void ProcessDeferredMarking() {
|
||||
ObjectPtr raw_obj;
|
||||
while ((raw_obj = deferred_work_list_.Pop()) != nullptr) {
|
||||
|
@ -274,15 +279,15 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
|
|||
void FinalizeMarking() {
|
||||
work_list_.Finalize();
|
||||
deferred_work_list_.Finalize();
|
||||
MournFinalized(this);
|
||||
}
|
||||
|
||||
void MournWeakProperties() {
|
||||
WeakPropertyPtr cur_weak = delayed_.weak_properties.Clear();
|
||||
WeakPropertyPtr cur_weak = delayed_weak_properties_;
|
||||
delayed_weak_properties_ = WeakProperty::null();
|
||||
while (cur_weak != WeakProperty::null()) {
|
||||
WeakPropertyPtr next_weak =
|
||||
cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
|
||||
cur_weak->untag()->next_seen_by_gc_ = WeakProperty::null();
|
||||
cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
|
||||
cur_weak->untag()->next_ = WeakProperty::null();
|
||||
RELEASE_ASSERT(!cur_weak->untag()->key()->untag()->IsMarked());
|
||||
WeakProperty::Clear(cur_weak);
|
||||
cur_weak = next_weak;
|
||||
|
@ -290,58 +295,72 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
|
|||
}
|
||||
|
||||
void MournWeakReferences() {
|
||||
WeakReferencePtr cur_weak = delayed_.weak_references.Clear();
|
||||
WeakReferencePtr cur_weak = delayed_weak_references_;
|
||||
delayed_weak_references_ = WeakReference::null();
|
||||
while (cur_weak != WeakReference::null()) {
|
||||
WeakReferencePtr next_weak =
|
||||
cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
|
||||
cur_weak->untag()->next_seen_by_gc_ = WeakReference::null();
|
||||
|
||||
cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
|
||||
cur_weak->untag()->next_ = WeakReference::null();
|
||||
// If we did not mark the target through a weak property in a later round,
|
||||
// then the target is dead and we should clear it.
|
||||
SetNullIfCollected(cur_weak->heap_base(), &cur_weak->untag()->target_);
|
||||
|
||||
if (!cur_weak->untag()->target()->untag()->IsMarked()) {
|
||||
WeakReference::Clear(cur_weak);
|
||||
}
|
||||
cur_weak = next_weak;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns whether the object referred to in `ptr_address` was GCed this GC.
|
||||
static bool SetNullIfCollected(uword heap_base,
|
||||
CompressedObjectPtr* ptr_address) {
|
||||
ObjectPtr raw = ptr_address->Decompress(heap_base);
|
||||
if (raw.IsRawNull()) {
|
||||
// Object already null before this GC.
|
||||
return false;
|
||||
}
|
||||
if (raw.IsNewObject()) {
|
||||
// Object not touched during this GC.
|
||||
return false;
|
||||
}
|
||||
if (raw->untag()->IsMarked()) {
|
||||
return false;
|
||||
}
|
||||
*ptr_address = Object::null();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaitForWork(RelaxedAtomic<uintptr_t>* num_busy) {
|
||||
return work_list_.WaitForWork(num_busy);
|
||||
}
|
||||
|
||||
void Flush(GCLinkedLists* global_list) {
|
||||
void Flush(WeakPropertyPtr* weak_properties_head,
|
||||
WeakPropertyPtr* weak_properties_tail,
|
||||
WeakReferencePtr* weak_references_head,
|
||||
WeakReferencePtr* weak_references_tail) {
|
||||
work_list_.Flush();
|
||||
deferred_work_list_.Flush();
|
||||
delayed_.FlushInto(global_list);
|
||||
|
||||
if (*weak_properties_head == WeakProperty::null()) {
|
||||
*weak_properties_head = delayed_weak_properties_;
|
||||
*weak_properties_tail = delayed_weak_properties_tail_;
|
||||
} else {
|
||||
(*weak_properties_tail)->untag()->next_ = delayed_weak_properties_;
|
||||
*weak_properties_tail = delayed_weak_properties_tail_;
|
||||
}
|
||||
delayed_weak_properties_tail_ = delayed_weak_properties_ =
|
||||
WeakProperty::null();
|
||||
|
||||
if (*weak_references_head == WeakReference::null()) {
|
||||
*weak_references_head = delayed_weak_references_;
|
||||
*weak_references_tail = delayed_weak_references_tail_;
|
||||
} else {
|
||||
(*weak_references_tail)->untag()->next_ = delayed_weak_references_;
|
||||
*weak_references_tail = delayed_weak_references_tail_;
|
||||
}
|
||||
delayed_weak_references_tail_ = delayed_weak_references_ =
|
||||
WeakReference::null();
|
||||
}
|
||||
|
||||
void Adopt(GCLinkedLists* other) {
|
||||
ASSERT(delayed_.IsEmpty());
|
||||
other->FlushInto(&delayed_);
|
||||
void Adopt(WeakPropertyPtr weak_properties_head,
|
||||
WeakPropertyPtr weak_properties_tail,
|
||||
WeakReferencePtr weak_references_head,
|
||||
WeakReferencePtr weak_references_tail) {
|
||||
ASSERT(delayed_weak_properties_ == WeakProperty::null());
|
||||
ASSERT(delayed_weak_properties_tail_ == WeakProperty::null());
|
||||
ASSERT(delayed_weak_references_ == WeakReference::null());
|
||||
ASSERT(delayed_weak_references_tail_ == WeakReference::null());
|
||||
delayed_weak_properties_ = weak_properties_head;
|
||||
delayed_weak_properties_tail_ = weak_properties_tail;
|
||||
delayed_weak_references_ = weak_references_head;
|
||||
delayed_weak_references_tail_ = weak_references_tail;
|
||||
}
|
||||
|
||||
void AbandonWork() {
|
||||
work_list_.AbandonWork();
|
||||
deferred_work_list_.AbandonWork();
|
||||
delayed_.Clear();
|
||||
delayed_weak_properties_ = WeakProperty::null();
|
||||
delayed_weak_references_ = WeakReference::null();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -411,13 +430,13 @@ class MarkingVisitorBase : public ObjectPointerVisitor {
|
|||
PageSpace* page_space_;
|
||||
MarkerWorkList work_list_;
|
||||
MarkerWorkList deferred_work_list_;
|
||||
GCLinkedLists delayed_;
|
||||
WeakPropertyPtr delayed_weak_properties_;
|
||||
WeakPropertyPtr delayed_weak_properties_tail_;
|
||||
WeakReferencePtr delayed_weak_references_;
|
||||
WeakReferencePtr delayed_weak_references_tail_;
|
||||
uintptr_t marked_bytes_;
|
||||
int64_t marked_micros_;
|
||||
|
||||
template <typename GCVisitorType>
|
||||
friend void MournFinalized(GCVisitorType* visitor);
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(MarkingVisitorBase);
|
||||
};
|
||||
|
||||
|
@ -727,9 +746,6 @@ class ParallelMarkTask : public ThreadPool::Task {
|
|||
// Phase 3: Weak processing and statistics.
|
||||
visitor_->MournWeakProperties();
|
||||
visitor_->MournWeakReferences();
|
||||
// Don't MournFinalized here, do it on main thread, so that we don't have
|
||||
// to coordinate workers.
|
||||
|
||||
marker_->IterateWeakRoots(thread);
|
||||
int64_t stop = OS::GetCurrentMonotonicMicros();
|
||||
visitor_->AddMicros(stop - start);
|
||||
|
@ -968,7 +984,6 @@ void GCMarker::MarkObjects(PageSpace* page_space) {
|
|||
visitor.FinalizeMarking();
|
||||
visitor.MournWeakProperties();
|
||||
visitor.MournWeakReferences();
|
||||
MournFinalized(&visitor);
|
||||
IterateWeakRoots(thread);
|
||||
// All marking done; detach code, etc.
|
||||
int64_t stop = OS::GetCurrentMonotonicMicros();
|
||||
|
@ -983,7 +998,10 @@ void GCMarker::MarkObjects(PageSpace* page_space) {
|
|||
RelaxedAtomic<uintptr_t> num_busy = 0;
|
||||
// Phase 1: Iterate over roots and drain marking stack in tasks.
|
||||
|
||||
GCLinkedLists global_list;
|
||||
WeakPropertyPtr weak_properties_head = WeakProperty::null();
|
||||
WeakPropertyPtr weak_properties_tail = WeakProperty::null();
|
||||
WeakReferencePtr weak_references_head = WeakReference::null();
|
||||
WeakReferencePtr weak_references_tail = WeakReference::null();
|
||||
|
||||
for (intptr_t i = 0; i < num_tasks; ++i) {
|
||||
SyncMarkingVisitor* visitor = visitors_[i];
|
||||
|
@ -995,12 +1013,12 @@ void GCMarker::MarkObjects(PageSpace* page_space) {
|
|||
&marking_stack_, &deferred_marking_stack_);
|
||||
visitors_[i] = visitor;
|
||||
}
|
||||
|
||||
// Move all work from local blocks to the global list. Any given
|
||||
// visitor might not get to run if it fails to reach TryEnter soon
|
||||
// enough, and we must fail to visit objects but they're sitting in
|
||||
// such a visitor's local blocks.
|
||||
visitor->Flush(&global_list);
|
||||
visitor->Flush(&weak_properties_head, &weak_properties_tail,
|
||||
&weak_references_head, &weak_references_tail);
|
||||
// Need to move weak property list too.
|
||||
|
||||
if (i < (num_tasks - 1)) {
|
||||
|
@ -1011,7 +1029,8 @@ void GCMarker::MarkObjects(PageSpace* page_space) {
|
|||
ASSERT(result);
|
||||
} else {
|
||||
// Last worker is the main thread.
|
||||
visitor->Adopt(&global_list);
|
||||
visitor->Adopt(weak_properties_head, weak_properties_tail,
|
||||
weak_references_head, weak_references_tail);
|
||||
ParallelMarkTask task(this, isolate_group_, &marking_stack_, barrier,
|
||||
visitor, &num_busy);
|
||||
task.RunEnteredIsolateGroup();
|
||||
|
|
|
@ -4,14 +4,11 @@
|
|||
|
||||
#include "vm/heap/scavenger.h"
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "platform/leak_sanitizer.h"
|
||||
#include "vm/class_id.h"
|
||||
#include "vm/dart.h"
|
||||
#include "vm/dart_api_state.h"
|
||||
#include "vm/flag_list.h"
|
||||
#include "vm/heap/become.h"
|
||||
#include "vm/heap/gc_shared.h"
|
||||
#include "vm/heap/pages.h"
|
||||
#include "vm/heap/pointer_block.h"
|
||||
#include "vm/heap/safepoint.h"
|
||||
|
@ -23,7 +20,6 @@
|
|||
#include "vm/object.h"
|
||||
#include "vm/object_id_ring.h"
|
||||
#include "vm/object_set.h"
|
||||
#include "vm/port.h"
|
||||
#include "vm/stack_frame.h"
|
||||
#include "vm/thread_barrier.h"
|
||||
#include "vm/thread_registry.h"
|
||||
|
@ -135,12 +131,13 @@ class ScavengerVisitorBase : public ObjectPointerVisitor {
|
|||
freelist_(freelist),
|
||||
bytes_promoted_(0),
|
||||
visiting_old_object_(nullptr),
|
||||
promoted_list_(promotion_stack) {}
|
||||
~ScavengerVisitorBase() { ASSERT(delayed_.IsEmpty()); }
|
||||
|
||||
#ifdef DEBUG
|
||||
const char* kName = "Scavenger";
|
||||
#endif
|
||||
promoted_list_(promotion_stack),
|
||||
delayed_weak_properties_(WeakProperty::null()),
|
||||
delayed_weak_references_(WeakReference::null()) {}
|
||||
~ScavengerVisitorBase() {
|
||||
ASSERT(delayed_weak_properties_ == WeakProperty::null());
|
||||
ASSERT(delayed_weak_references_ == WeakReference::null());
|
||||
}
|
||||
|
||||
virtual void VisitTypedDataViewPointers(TypedDataViewPtr view,
|
||||
CompressedObjectPtr* first,
|
||||
|
@ -302,7 +299,6 @@ class ScavengerVisitorBase : public ObjectPointerVisitor {
|
|||
|
||||
MournWeakProperties();
|
||||
MournOrUpdateWeakReferences();
|
||||
MournFinalized(this);
|
||||
}
|
||||
page_space_->ReleaseLock(freelist_);
|
||||
thread_ = nullptr;
|
||||
|
@ -312,15 +308,13 @@ class ScavengerVisitorBase : public ObjectPointerVisitor {
|
|||
|
||||
void AbandonWork() {
|
||||
promoted_list_.AbandonWork();
|
||||
delayed_.Clear();
|
||||
delayed_weak_properties_ = WeakProperty::null();
|
||||
delayed_weak_references_ = WeakReference::null();
|
||||
}
|
||||
|
||||
NewPage* head() const { return head_; }
|
||||
NewPage* tail() const { return tail_; }
|
||||
|
||||
static bool SetNullIfCollected(uword heap_base,
|
||||
CompressedObjectPtr* ptr_address);
|
||||
|
||||
private:
|
||||
void UpdateStoreBuffer(ObjectPtr obj) {
|
||||
ASSERT(obj->IsHeapObject());
|
||||
|
@ -517,13 +511,8 @@ class ScavengerVisitorBase : public ObjectPointerVisitor {
|
|||
inline void ProcessToSpace();
|
||||
DART_FORCE_INLINE intptr_t ProcessCopied(ObjectPtr raw_obj);
|
||||
inline void ProcessPromotedList();
|
||||
|
||||
bool IsNotForwarding(ObjectPtr raw) {
|
||||
ASSERT(raw->IsHeapObject());
|
||||
ASSERT(raw->IsNewObject());
|
||||
return !IsForwarding(ReadHeaderRelaxed(raw));
|
||||
}
|
||||
|
||||
inline void EnqueueWeakProperty(WeakPropertyPtr raw_weak);
|
||||
inline void EnqueueWeakReference(WeakReferencePtr raw_weak);
|
||||
inline void MournWeakProperties();
|
||||
inline void MournOrUpdateWeakReferences();
|
||||
|
||||
|
@ -534,16 +523,15 @@ class ScavengerVisitorBase : public ObjectPointerVisitor {
|
|||
FreeList* freelist_;
|
||||
intptr_t bytes_promoted_;
|
||||
ObjectPtr visiting_old_object_;
|
||||
|
||||
PromotionWorkList promoted_list_;
|
||||
GCLinkedLists delayed_;
|
||||
WeakPropertyPtr delayed_weak_properties_;
|
||||
WeakReferencePtr delayed_weak_references_;
|
||||
|
||||
NewPage* head_ = nullptr;
|
||||
NewPage* tail_ = nullptr; // Allocating from here.
|
||||
NewPage* scan_ = nullptr; // Resolving from here.
|
||||
|
||||
template <typename GCVisitorType>
|
||||
friend void MournFinalized(GCVisitorType* visitor);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScavengerVisitorBase);
|
||||
};
|
||||
|
||||
|
@ -1335,10 +1323,11 @@ void ScavengerVisitorBase<parallel>::ProcessWeakProperties() {
|
|||
// Finished this round of scavenging. Process the pending weak properties
|
||||
// for which the keys have become reachable. Potentially this adds more
|
||||
// objects to the to space.
|
||||
WeakPropertyPtr cur_weak = delayed_.weak_properties.Clear();
|
||||
WeakPropertyPtr cur_weak = delayed_weak_properties_;
|
||||
delayed_weak_properties_ = WeakProperty::null();
|
||||
while (cur_weak != WeakProperty::null()) {
|
||||
WeakPropertyPtr next_weak =
|
||||
cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
|
||||
cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
|
||||
// Promoted weak properties are not enqueued. So we can guarantee that
|
||||
// we do not need to think about store barriers here.
|
||||
ASSERT(cur_weak->IsNewObject());
|
||||
|
@ -1352,12 +1341,11 @@ void ScavengerVisitorBase<parallel>::ProcessWeakProperties() {
|
|||
ASSERT(from_->Contains(raw_addr));
|
||||
uword header = ReadHeaderRelaxed(raw_key);
|
||||
// Reset the next pointer in the weak property.
|
||||
cur_weak->untag()->next_seen_by_gc_ = WeakProperty::null();
|
||||
cur_weak->untag()->next_ = WeakProperty::null();
|
||||
if (IsForwarding(header)) {
|
||||
cur_weak->untag()->VisitPointersNonvirtual(this);
|
||||
} else {
|
||||
ASSERT(IsNotForwarding(cur_weak));
|
||||
delayed_.weak_properties.Enqueue(cur_weak);
|
||||
EnqueueWeakProperty(cur_weak);
|
||||
}
|
||||
// Advance to next weak property in the queue.
|
||||
cur_weak = next_weak;
|
||||
|
@ -1389,6 +1377,38 @@ void Scavenger::UpdateMaxHeapUsage() {
|
|||
isolate_group->GetHeapNewUsedMaxMetric()->SetValue(UsedInWords() * kWordSize);
|
||||
}
|
||||
|
||||
template <bool parallel>
|
||||
void ScavengerVisitorBase<parallel>::EnqueueWeakProperty(
|
||||
WeakPropertyPtr raw_weak) {
|
||||
ASSERT(raw_weak->IsHeapObject());
|
||||
ASSERT(raw_weak->IsNewObject());
|
||||
ASSERT(raw_weak->IsWeakProperty());
|
||||
#if defined(DEBUG)
|
||||
uword header = ReadHeaderRelaxed(raw_weak);
|
||||
ASSERT(!IsForwarding(header));
|
||||
#endif // defined(DEBUG)
|
||||
ASSERT(raw_weak->untag()->next_ ==
|
||||
CompressedWeakPropertyPtr(WeakProperty::null()));
|
||||
raw_weak->untag()->next_ = delayed_weak_properties_;
|
||||
delayed_weak_properties_ = raw_weak;
|
||||
}
|
||||
|
||||
template <bool parallel>
|
||||
void ScavengerVisitorBase<parallel>::EnqueueWeakReference(
|
||||
WeakReferencePtr raw_weak) {
|
||||
ASSERT(raw_weak->IsHeapObject());
|
||||
ASSERT(raw_weak->IsNewObject());
|
||||
ASSERT(raw_weak->IsWeakReference());
|
||||
#if defined(DEBUG)
|
||||
uword header = ReadHeaderRelaxed(raw_weak);
|
||||
ASSERT(!IsForwarding(header));
|
||||
#endif // defined(DEBUG)
|
||||
ASSERT(raw_weak->untag()->next_ ==
|
||||
CompressedWeakReferencePtr(WeakReference::null()));
|
||||
raw_weak->untag()->next_ = delayed_weak_references_;
|
||||
delayed_weak_references_ = raw_weak;
|
||||
}
|
||||
|
||||
template <bool parallel>
|
||||
intptr_t ScavengerVisitorBase<parallel>::ProcessCopied(ObjectPtr raw_obj) {
|
||||
intptr_t class_id = raw_obj->GetClassId();
|
||||
|
@ -1400,8 +1420,7 @@ intptr_t ScavengerVisitorBase<parallel>::ProcessCopied(ObjectPtr raw_obj) {
|
|||
uword header = ReadHeaderRelaxed(raw_key);
|
||||
if (!IsForwarding(header)) {
|
||||
// Key is white. Enqueue the weak property.
|
||||
ASSERT(IsNotForwarding(raw_weak));
|
||||
delayed_.weak_properties.Enqueue(raw_weak);
|
||||
EnqueueWeakProperty(raw_weak);
|
||||
return raw_weak->untag()->HeapSize();
|
||||
}
|
||||
}
|
||||
|
@ -1415,8 +1434,7 @@ intptr_t ScavengerVisitorBase<parallel>::ProcessCopied(ObjectPtr raw_obj) {
|
|||
if (!IsForwarding(header)) {
|
||||
// Target is white. Enqueue the weak reference. Always visit type
|
||||
// arguments.
|
||||
ASSERT(IsNotForwarding(raw_weak));
|
||||
delayed_.weak_references.Enqueue(raw_weak);
|
||||
EnqueueWeakReference(raw_weak);
|
||||
#if !defined(DART_COMPRESSED_POINTERS)
|
||||
ScavengePointer(&raw_weak->untag()->type_arguments_);
|
||||
#else
|
||||
|
@ -1426,21 +1444,6 @@ intptr_t ScavengerVisitorBase<parallel>::ProcessCopied(ObjectPtr raw_obj) {
|
|||
return raw_weak->untag()->HeapSize();
|
||||
}
|
||||
}
|
||||
} else if (UNLIKELY(class_id == kFinalizerEntryCid)) {
|
||||
FinalizerEntryPtr raw_entry = static_cast<FinalizerEntryPtr>(raw_obj);
|
||||
ASSERT(IsNotForwarding(raw_entry));
|
||||
delayed_.finalizer_entries.Enqueue(raw_entry);
|
||||
// Only visit token and next.
|
||||
#if !defined(DART_COMPRESSED_POINTERS)
|
||||
ScavengePointer(&raw_entry->untag()->token_);
|
||||
ScavengePointer(&raw_entry->untag()->next_);
|
||||
#else
|
||||
ScavengeCompressedPointer(raw_entry->heap_base(),
|
||||
&raw_entry->untag()->token_);
|
||||
ScavengeCompressedPointer(raw_entry->heap_base(),
|
||||
&raw_entry->untag()->next_);
|
||||
#endif
|
||||
return raw_entry->untag()->HeapSize();
|
||||
}
|
||||
return raw_obj->untag()->VisitPointersNonvirtual(this);
|
||||
}
|
||||
|
@ -1504,12 +1507,13 @@ void ScavengerVisitorBase<parallel>::MournWeakProperties() {
|
|||
|
||||
// The queued weak properties at this point do not refer to reachable keys,
|
||||
// so we clear their key and value fields.
|
||||
WeakPropertyPtr cur_weak = delayed_.weak_properties.Clear();
|
||||
WeakPropertyPtr cur_weak = delayed_weak_properties_;
|
||||
delayed_weak_properties_ = WeakProperty::null();
|
||||
while (cur_weak != WeakProperty::null()) {
|
||||
WeakPropertyPtr next_weak =
|
||||
cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
|
||||
cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
|
||||
// Reset the next pointer in the weak property.
|
||||
cur_weak->untag()->next_seen_by_gc_ = WeakProperty::null();
|
||||
cur_weak->untag()->next_ = WeakProperty::null();
|
||||
|
||||
#if defined(DEBUG)
|
||||
ObjectPtr raw_key = cur_weak->untag()->key();
|
||||
|
@ -1533,48 +1537,31 @@ void ScavengerVisitorBase<parallel>::MournOrUpdateWeakReferences() {
|
|||
|
||||
// The queued weak references at this point either should have their target
|
||||
// updated or should be cleared.
|
||||
WeakReferencePtr cur_weak = delayed_.weak_references.Clear();
|
||||
WeakReferencePtr cur_weak = delayed_weak_references_;
|
||||
delayed_weak_references_ = WeakReference::null();
|
||||
while (cur_weak != WeakReference::null()) {
|
||||
WeakReferencePtr next_weak =
|
||||
cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
|
||||
cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
|
||||
// Reset the next pointer in the weak reference.
|
||||
cur_weak->untag()->next_seen_by_gc_ = WeakReference::null();
|
||||
cur_weak->untag()->next_ = WeakReference::null();
|
||||
|
||||
// If we did not mark the target through a weak property in a later round,
|
||||
// then the target is dead and we should clear it.
|
||||
SetNullIfCollected(cur_weak->heap_base(), &cur_weak->untag()->target_);
|
||||
ObjectPtr raw_target = cur_weak->untag()->target();
|
||||
uword raw_addr = UntaggedObject::ToAddr(raw_target);
|
||||
uword header = *reinterpret_cast<uword*>(raw_addr);
|
||||
if (IsForwarding(header)) {
|
||||
// Get the new location of the object.
|
||||
cur_weak->untag()->target_ = ForwardedObj(header);
|
||||
} else {
|
||||
ASSERT(raw_target->IsHeapObject());
|
||||
ASSERT(raw_target->IsNewObject());
|
||||
WeakReference::Clear(cur_weak);
|
||||
}
|
||||
|
||||
// Advance to next weak reference in the queue.
|
||||
cur_weak = next_weak;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns whether the object referred to in `ptr_address` was GCed this GC.
|
||||
template <bool parallel>
|
||||
bool ScavengerVisitorBase<parallel>::SetNullIfCollected(
|
||||
uword heap_base,
|
||||
CompressedObjectPtr* ptr_address) {
|
||||
ObjectPtr raw = ptr_address->Decompress(heap_base);
|
||||
if (raw.IsRawNull()) {
|
||||
// Object already null before this GC.
|
||||
return false;
|
||||
}
|
||||
if (raw.IsOldObject()) {
|
||||
// Object not touched during this GC.
|
||||
return false;
|
||||
}
|
||||
uword header = *reinterpret_cast<uword*>(UntaggedObject::ToAddr(raw));
|
||||
if (IsForwarding(header)) {
|
||||
// Get the new location of the object.
|
||||
*ptr_address = ForwardedObj(header);
|
||||
return false;
|
||||
}
|
||||
ASSERT(raw->IsHeapObject());
|
||||
ASSERT(raw->IsNewObject());
|
||||
*ptr_address = Object::null();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Scavenger::VisitObjectPointers(ObjectPointerVisitor* visitor) const {
|
||||
ASSERT(Thread::Current()->IsAtSafepoint() ||
|
||||
(Thread::Current()->task_kind() == Thread::kMarkerTask) ||
|
||||
|
|
|
@ -1388,15 +1388,6 @@ MessageHandler::MessageStatus IsolateMessageHandler::HandleMessage(
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (message->IsFinalizerInvocationRequest()) {
|
||||
const Object& msg_handler = Object::Handle(
|
||||
zone,
|
||||
DartLibraryCalls::HandleFinalizerMessage(FinalizerBase::Cast(msg)));
|
||||
if (msg_handler.IsError()) {
|
||||
status = ProcessUnhandledException(Error::Cast(msg_handler));
|
||||
} else {
|
||||
// The handler closure which was used to successfully handle the message.
|
||||
}
|
||||
} else if (message->dest_port() == Message::kIllegalPort) {
|
||||
// Check whether this is a delayed OOB message which needed handling as
|
||||
// part of the regular message dispatch. All other messages are dropped on
|
||||
|
@ -1695,7 +1686,6 @@ Isolate::Isolate(IsolateGroup* isolate_group,
|
|||
default_tag_(UserTag::null()),
|
||||
ic_miss_code_(Code::null()),
|
||||
field_table_(new FieldTable(/*isolate=*/this)),
|
||||
finalizers_(GrowableObjectArray::null()),
|
||||
isolate_group_(isolate_group),
|
||||
isolate_object_store_(new IsolateObjectStore()),
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
@ -2475,34 +2465,6 @@ void Isolate::LowLevelShutdown() {
|
|||
}
|
||||
}
|
||||
|
||||
// Set live finalizers isolate to null, before deleting the message handler.
|
||||
// TODO(http://dartbug.com/47777): How to detect if the isolate field was ever
|
||||
// initialized beyond RAW_NULL?
|
||||
const auto& finalizers =
|
||||
GrowableObjectArray::Handle(stack_zone.GetZone(), finalizers_);
|
||||
if (!finalizers.IsNull()) {
|
||||
const intptr_t num_finalizers = finalizers.Length();
|
||||
auto& weak_reference = WeakReference::Handle(stack_zone.GetZone());
|
||||
auto& finalizer = FinalizerBase::Handle(stack_zone.GetZone());
|
||||
for (int i = 0; i < num_finalizers; i++) {
|
||||
weak_reference ^= finalizers.At(i);
|
||||
finalizer ^= weak_reference.target();
|
||||
if (!finalizer.IsNull()) {
|
||||
if (finalizer.isolate() == this) {
|
||||
if (FLAG_trace_finalizers) {
|
||||
THR_Print("Isolate %p Setting finalizer %p isolate to null\n", this,
|
||||
finalizer.ptr()->untag());
|
||||
}
|
||||
// Finalizer was not sent to another isolate with send and exit.
|
||||
finalizer.set_isolate(nullptr);
|
||||
} else {
|
||||
// TODO(http://dartbug.com/47777): Send and exit support.
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close all the ports owned by this isolate.
|
||||
PortMap::ClosePorts(message_handler());
|
||||
|
||||
|
@ -2609,6 +2571,7 @@ void Isolate::Shutdown() {
|
|||
"--check-reloaded is enabled.\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
// Then, proceed with low-level teardown.
|
||||
|
@ -2758,7 +2721,6 @@ void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor,
|
|||
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&ic_miss_code_));
|
||||
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&tag_table_));
|
||||
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&sticky_error_));
|
||||
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&finalizers_));
|
||||
#if !defined(PRODUCT)
|
||||
visitor->VisitPointer(
|
||||
reinterpret_cast<ObjectPtr*>(&pending_service_extension_calls_));
|
||||
|
|
|
@ -1065,10 +1065,6 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
|
|||
void set_init_callback_data(void* value) { init_callback_data_ = value; }
|
||||
void* init_callback_data() const { return init_callback_data_; }
|
||||
|
||||
static intptr_t finalizers_offset() {
|
||||
return OFFSET_OF(Isolate, finalizers_);
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
NativeCallbackTrampolines* native_callback_trampolines() {
|
||||
return &native_callback_trampolines_;
|
||||
|
@ -1544,9 +1540,6 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
|
|||
UserTagPtr default_tag_;
|
||||
CodePtr ic_miss_code_;
|
||||
FieldTable* field_table_ = nullptr;
|
||||
// Used to clear out `UntaggedFinalizerBase::isolate_` pointers on isolate
|
||||
// shutdown to prevent usage of dangling pointers.
|
||||
GrowableObjectArrayPtr finalizers_;
|
||||
bool single_step_ = false;
|
||||
bool is_system_isolate_ = false;
|
||||
// End accessed from generated code.
|
||||
|
@ -1658,7 +1651,7 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
|
|||
Dart_EnvironmentCallback environment_callback_ = nullptr;
|
||||
Random random_;
|
||||
Simulator* simulator_ = nullptr;
|
||||
Mutex mutex_; // Protects compiler stats.
|
||||
Mutex mutex_; // Protects compiler stats.
|
||||
MessageHandler* message_handler_ = nullptr;
|
||||
intptr_t defer_finalization_count_ = 0;
|
||||
DeoptContext* deopt_context_ = nullptr;
|
||||
|
@ -1734,7 +1727,7 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
|
|||
friend class ServiceIsolate;
|
||||
friend class Thread;
|
||||
friend class Timeline;
|
||||
friend class IsolateGroup; // reload_context_
|
||||
friend class IsolateGroup; // reload_context_
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Isolate);
|
||||
};
|
||||
|
|
|
@ -1300,8 +1300,7 @@ void KernelLoader::LoadLibraryImportsAndExports(Library* library,
|
|||
}
|
||||
if (!Api::IsFfiEnabled() &&
|
||||
target_library.url() == Symbols::DartFfi().ptr() &&
|
||||
library->url() != Symbols::DartCore().ptr() &&
|
||||
library->url() != Symbols::DartInternal().ptr()) {
|
||||
library->url() != Symbols::DartCore().ptr()) {
|
||||
H.ReportError(
|
||||
"import of dart:ffi is not supported in the current Dart runtime");
|
||||
}
|
||||
|
|
|
@ -45,20 +45,12 @@ Message::Message(Dart_Port dest_port,
|
|||
ASSERT(IsPersistentHandle());
|
||||
}
|
||||
|
||||
Message::Message(PersistentHandle* handle, Priority priority)
|
||||
: dest_port_(ILLEGAL_PORT),
|
||||
payload_(handle),
|
||||
snapshot_length_(kFinalizerSnapshotLen),
|
||||
priority_(priority) {
|
||||
ASSERT(IsFinalizerInvocationRequest());
|
||||
}
|
||||
|
||||
Message::~Message() {
|
||||
if (IsSnapshot()) {
|
||||
free(payload_.snapshot_);
|
||||
}
|
||||
delete finalizable_data_;
|
||||
if (IsPersistentHandle() || IsFinalizerInvocationRequest()) {
|
||||
if (IsPersistentHandle()) {
|
||||
auto isolate_group = IsolateGroup::Current();
|
||||
isolate_group->api_state()->FreePersistentHandle(
|
||||
payload_.persistent_handle_);
|
||||
|
|
|
@ -62,13 +62,8 @@ class Message {
|
|||
// the VM heap. This is indicated by setting the len_ field to 0.
|
||||
Message(Dart_Port dest_port, ObjectPtr raw_obj, Priority priority);
|
||||
|
||||
// A message sent from SendPort.send or SendPort.sendAndExit where sender and
|
||||
// receiver are in the same isolate group.
|
||||
Message(Dart_Port dest_port, PersistentHandle* handle, Priority priority);
|
||||
|
||||
// A message sent from GC to run a finalizer.
|
||||
Message(PersistentHandle* handle, Priority priority);
|
||||
|
||||
~Message();
|
||||
|
||||
template <typename... Args>
|
||||
|
@ -99,7 +94,7 @@ class Message {
|
|||
return payload_.raw_obj_;
|
||||
}
|
||||
PersistentHandle* persistent_handle() const {
|
||||
ASSERT(IsPersistentHandle() || IsFinalizerInvocationRequest());
|
||||
ASSERT(IsPersistentHandle());
|
||||
return payload_.persistent_handle_;
|
||||
}
|
||||
Priority priority() const { return priority_; }
|
||||
|
@ -108,9 +103,7 @@ class Message {
|
|||
// of at the top of the message loop. Control messages from dart:isolate or
|
||||
// vm-service requests.
|
||||
bool IsOOB() const { return priority_ == Message::kOOBPriority; }
|
||||
bool IsSnapshot() const {
|
||||
return !IsRaw() && !IsPersistentHandle() && !IsFinalizerInvocationRequest();
|
||||
}
|
||||
bool IsSnapshot() const { return !IsRaw() && !IsPersistentHandle(); }
|
||||
// A message whose object is an immortal object from the vm-isolate's heap.
|
||||
bool IsRaw() const { return snapshot_length_ == 0; }
|
||||
// A message sent from SendPort.send or SendPort.sendAndExit where sender and
|
||||
|
@ -118,10 +111,6 @@ class Message {
|
|||
bool IsPersistentHandle() const {
|
||||
return snapshot_length_ == kPersistentHandleSnapshotLen;
|
||||
}
|
||||
// A message sent from GC to run a finalizer.
|
||||
bool IsFinalizerInvocationRequest() const {
|
||||
return snapshot_length_ == kFinalizerSnapshotLen;
|
||||
}
|
||||
|
||||
void DropFinalizers() {
|
||||
if (finalizable_data_ != nullptr) {
|
||||
|
@ -135,7 +124,6 @@ class Message {
|
|||
|
||||
private:
|
||||
static intptr_t const kPersistentHandleSnapshotLen = -1;
|
||||
static intptr_t const kFinalizerSnapshotLen = -2;
|
||||
|
||||
friend class MessageQueue;
|
||||
|
||||
|
|
|
@ -211,8 +211,6 @@ class MessageHandler {
|
|||
Thread* thread() const { return Thread::Current(); }
|
||||
|
||||
private:
|
||||
template <typename GCVisitorType>
|
||||
friend void MournFinalized(GCVisitorType* visitor);
|
||||
friend class PortMap;
|
||||
friend class MessageHandlerTestPeer;
|
||||
friend class MessageHandlerTask;
|
||||
|
|
|
@ -3916,11 +3916,6 @@ ObjectPtr ReadObjectGraphCopyMessage(Thread* thread, PersistentHandle* handle) {
|
|||
ObjectPtr ReadMessage(Thread* thread, Message* message) {
|
||||
if (message->IsRaw()) {
|
||||
return message->raw_obj();
|
||||
} else if (message->IsFinalizerInvocationRequest()) {
|
||||
PersistentHandle* handle = message->persistent_handle();
|
||||
Object& msg_obj = Object::Handle(thread->zone(), handle->ptr());
|
||||
ASSERT(msg_obj.IsFinalizer());
|
||||
return msg_obj.ptr();
|
||||
} else if (message->IsPersistentHandle()) {
|
||||
return ReadObjectGraphCopyMessage(thread, message->persistent_handle());
|
||||
} else {
|
||||
|
|
|
@ -118,7 +118,7 @@ ArrayPtr SubtypeTestCache::cached_array_;
|
|||
|
||||
cpp_vtable Object::builtin_vtables_[kNumPredefinedCids] = {};
|
||||
|
||||
// These are initialized to a value that will force an illegal memory access if
|
||||
// These are initialized to a value that will force a illegal memory access if
|
||||
// they are being used.
|
||||
#if defined(RAW_NULL)
|
||||
#error RAW_NULL should not be defined.
|
||||
|
@ -2341,14 +2341,6 @@ ErrorPtr Object::Init(IsolateGroup* isolate_group,
|
|||
pending_classes.Add(cls);
|
||||
RegisterClass(cls, Symbols::FfiDynamicLibrary(), lib);
|
||||
|
||||
cls = Class::New<Finalizer, RTN::Finalizer>(isolate_group);
|
||||
cls.set_type_arguments_field_offset(
|
||||
Finalizer::type_arguments_offset(),
|
||||
RTN::Finalizer::type_arguments_offset());
|
||||
cls.set_num_type_arguments_unsafe(1);
|
||||
object_store->set_finalizer_class(cls);
|
||||
RegisterPrivateClass(cls, Symbols::_FinalizerImpl(), core_lib);
|
||||
|
||||
// Pre-register the internal library so we can place the vm class
|
||||
// FinalizerEntry there rather than the core library.
|
||||
lib = Library::LookupLibrary(thread, Symbols::DartInternal());
|
||||
|
@ -2361,10 +2353,6 @@ ErrorPtr Object::Init(IsolateGroup* isolate_group,
|
|||
ASSERT(!lib.IsNull());
|
||||
ASSERT(lib.ptr() == Library::InternalLibrary());
|
||||
|
||||
cls = Class::New<FinalizerEntry, RTN::FinalizerEntry>(isolate_group);
|
||||
object_store->set_finalizer_entry_class(cls);
|
||||
RegisterClass(cls, Symbols::FinalizerEntry(), lib);
|
||||
|
||||
// Finish the initialization by compiling the bootstrap scripts containing
|
||||
// the base interfaces and the implementation of the internal classes.
|
||||
const Error& error = Error::Handle(
|
||||
|
@ -2532,10 +2520,6 @@ ErrorPtr Object::Init(IsolateGroup* isolate_group,
|
|||
object_store->set_weak_property_class(cls);
|
||||
cls = Class::New<WeakReference, RTN::WeakReference>(isolate_group);
|
||||
object_store->set_weak_reference_class(cls);
|
||||
cls = Class::New<Finalizer, RTN::Finalizer>(isolate_group);
|
||||
object_store->set_finalizer_class(cls);
|
||||
cls = Class::New<FinalizerEntry, RTN::FinalizerEntry>(isolate_group);
|
||||
object_store->set_finalizer_entry_class(cls);
|
||||
|
||||
cls = Class::New<MirrorReference, RTN::MirrorReference>(isolate_group);
|
||||
cls = Class::New<UserTag, RTN::UserTag>(isolate_group);
|
||||
|
@ -26021,50 +26005,14 @@ WeakReferencePtr WeakReference::New(Heap::Space space) {
|
|||
space, WeakReference::ContainsCompressedPointers());
|
||||
return static_cast<WeakReferencePtr>(raw);
|
||||
}
|
||||
|
||||
const char* WeakReference::ToCString() const {
|
||||
TypeArguments& type_args = TypeArguments::Handle(GetTypeArguments());
|
||||
String& type_args_name = String::Handle(type_args.UserVisibleName());
|
||||
return OS::SCreate(Thread::Current()->zone(), "_WeakReference%s",
|
||||
return OS::SCreate(Thread::Current()->zone(), "WeakReference%s",
|
||||
type_args_name.ToCString());
|
||||
}
|
||||
|
||||
const char* FinalizerBase::ToCString() const {
|
||||
return "FinalizerBase";
|
||||
}
|
||||
|
||||
FinalizerPtr Finalizer::New(Heap::Space space) {
|
||||
ASSERT(IsolateGroup::Current()->object_store()->finalizer_class() !=
|
||||
Class::null());
|
||||
ObjectPtr raw =
|
||||
Object::Allocate(Finalizer::kClassId, Finalizer::InstanceSize(), space,
|
||||
Finalizer::ContainsCompressedPointers());
|
||||
return static_cast<FinalizerPtr>(raw);
|
||||
}
|
||||
|
||||
const char* Finalizer::ToCString() const {
|
||||
TypeArguments& type_args = TypeArguments::Handle(GetTypeArguments());
|
||||
String& type_args_name = String::Handle(type_args.UserVisibleName());
|
||||
return OS::SCreate(Thread::Current()->zone(), "_FinalizerImpl%s",
|
||||
type_args_name.ToCString());
|
||||
}
|
||||
|
||||
FinalizerEntryPtr FinalizerEntry::New(Heap::Space space) {
|
||||
ASSERT(IsolateGroup::Current()->object_store()->finalizer_entry_class() !=
|
||||
Class::null());
|
||||
ObjectPtr raw =
|
||||
Object::Allocate(FinalizerEntry::kClassId, FinalizerEntry::InstanceSize(),
|
||||
space, FinalizerEntry::ContainsCompressedPointers());
|
||||
return static_cast<FinalizerEntryPtr>(raw);
|
||||
}
|
||||
|
||||
void FinalizerEntry::set_finalizer(const FinalizerBase& value) const {
|
||||
untag()->set_finalizer(value.ptr());
|
||||
}
|
||||
|
||||
const char* FinalizerEntry::ToCString() const {
|
||||
return "FinalizerEntry";
|
||||
}
|
||||
|
||||
AbstractTypePtr MirrorReference::GetAbstractTypeReferent() const {
|
||||
ASSERT(Object::Handle(referent()).IsAbstractType());
|
||||
return AbstractType::Cast(Object::Handle(referent())).ptr();
|
||||
|
|
|
@ -3177,29 +3177,7 @@ class Function : public Object {
|
|||
bool ForceOptimize() const {
|
||||
return IsFfiFromAddress() || IsFfiGetAddress() || IsFfiLoad() ||
|
||||
IsFfiStore() || IsFfiTrampoline() || IsFfiAsExternalTypedData() ||
|
||||
IsTypedDataViewFactory() || IsUtf8Scan() || IsGetNativeField() ||
|
||||
IsFinalizerForceOptimized();
|
||||
}
|
||||
|
||||
bool IsFinalizerForceOptimized() const {
|
||||
// Either because of unboxed/untagged data, or because we don't want the GC
|
||||
// to trigger in between.
|
||||
switch (recognized_kind()) {
|
||||
case MethodRecognizer::kFinalizerBase_getIsolateFinalizers:
|
||||
case MethodRecognizer::kFinalizerBase_setIsolate:
|
||||
case MethodRecognizer::kFinalizerBase_setIsolateFinalizers:
|
||||
// Unboxed/untagged representation not supported in unoptimized.
|
||||
return true;
|
||||
case MethodRecognizer::kFinalizerBase_exchangeEntriesCollectedWithNull:
|
||||
// Prevent the GC from running so that the operation is atomic from
|
||||
// a GC point of view. Always double check implementation in
|
||||
// kernel_to_il.cc that no GC can happen in between the relevant IL
|
||||
// instructions.
|
||||
// TODO(https://dartbug.com/48527): Support inlining.
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
IsTypedDataViewFactory() || IsUtf8Scan() || IsGetNativeField();
|
||||
}
|
||||
|
||||
bool CanBeInlined() const;
|
||||
|
@ -5942,7 +5920,9 @@ class CompressedStackMaps : public Object {
|
|||
static intptr_t UnroundedSize(intptr_t length) {
|
||||
return HeaderSize() + length;
|
||||
}
|
||||
static intptr_t InstanceSize() { return 0; }
|
||||
static intptr_t InstanceSize() {
|
||||
return 0;
|
||||
}
|
||||
static intptr_t InstanceSize(intptr_t length) {
|
||||
return RoundedAllocationSize(UnroundedSize(length));
|
||||
}
|
||||
|
@ -12000,7 +11980,7 @@ class WeakProperty : public Instance {
|
|||
}
|
||||
|
||||
static void Clear(WeakPropertyPtr raw_weak) {
|
||||
ASSERT(raw_weak->untag()->next_seen_by_gc_ ==
|
||||
ASSERT(raw_weak->untag()->next_ ==
|
||||
CompressedWeakPropertyPtr(WeakProperty::null()));
|
||||
// This action is performed by the GC. No barrier.
|
||||
raw_weak->untag()->key_ = Object::null();
|
||||
|
@ -12032,112 +12012,18 @@ class WeakReference : public Instance {
|
|||
return RoundedAllocationSize(sizeof(UntaggedWeakReference));
|
||||
}
|
||||
|
||||
static void Clear(WeakReferencePtr raw_weak) {
|
||||
ASSERT(raw_weak->untag()->next_ ==
|
||||
CompressedWeakReferencePtr(WeakReference::null()));
|
||||
// This action is performed by the GC. No barrier.
|
||||
raw_weak->untag()->target_ = Object::null();
|
||||
}
|
||||
|
||||
private:
|
||||
FINAL_HEAP_OBJECT_IMPLEMENTATION(WeakReference, Instance);
|
||||
friend class Class;
|
||||
};
|
||||
|
||||
class FinalizerEntry : public Instance {
|
||||
public:
|
||||
ObjectPtr value() const { return untag()->value(); }
|
||||
void set_value(const Object& value) const { untag()->set_value(value.ptr()); }
|
||||
static intptr_t value_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizerEntry, value_);
|
||||
}
|
||||
|
||||
ObjectPtr detach() const { return untag()->detach(); }
|
||||
void set_detach(const Object& value) const {
|
||||
untag()->set_detach(value.ptr());
|
||||
}
|
||||
static intptr_t detach_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizerEntry, detach_);
|
||||
}
|
||||
|
||||
ObjectPtr token() const { return untag()->token(); }
|
||||
void set_token(const Object& value) const { untag()->set_token(value.ptr()); }
|
||||
static intptr_t token_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizerEntry, token_);
|
||||
}
|
||||
|
||||
FinalizerBasePtr finalizer() const { return untag()->finalizer(); }
|
||||
void set_finalizer(const FinalizerBase& value) const;
|
||||
static intptr_t finalizer_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizerEntry, finalizer_);
|
||||
}
|
||||
|
||||
FinalizerEntryPtr next() const { return untag()->next(); }
|
||||
void set_next(const FinalizerEntry& value) const {
|
||||
untag()->set_next(value.ptr());
|
||||
}
|
||||
static intptr_t next_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizerEntry, next_);
|
||||
}
|
||||
|
||||
static intptr_t InstanceSize() {
|
||||
return RoundedAllocationSize(sizeof(UntaggedFinalizerEntry));
|
||||
}
|
||||
|
||||
static FinalizerEntryPtr New(Heap::Space space = Heap::kNew);
|
||||
|
||||
private:
|
||||
FINAL_HEAP_OBJECT_IMPLEMENTATION(FinalizerEntry, Instance);
|
||||
friend class Class;
|
||||
};
|
||||
|
||||
class FinalizerBase : public Instance {
|
||||
public:
|
||||
static intptr_t isolate_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizerBase, isolate_);
|
||||
}
|
||||
Isolate* isolate() const { return untag()->isolate_; }
|
||||
void set_isolate(Isolate* value) const { untag()->isolate_ = value; }
|
||||
|
||||
static intptr_t detachments_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizerBase, detachments_);
|
||||
}
|
||||
|
||||
LinkedHashSetPtr all_entries() const { return untag()->all_entries(); }
|
||||
static intptr_t all_entries_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizerBase, all_entries_);
|
||||
}
|
||||
|
||||
FinalizerEntryPtr entries_collected() const {
|
||||
return untag()->entries_collected();
|
||||
}
|
||||
void set_entries_collected(const FinalizerEntry& value) const {
|
||||
untag()->set_entries_collected(value.ptr());
|
||||
}
|
||||
static intptr_t entries_collected_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizer, entries_collected_);
|
||||
}
|
||||
|
||||
private:
|
||||
HEAP_OBJECT_IMPLEMENTATION(FinalizerBase, Instance);
|
||||
friend class Class;
|
||||
};
|
||||
|
||||
class Finalizer : public FinalizerBase {
|
||||
public:
|
||||
static intptr_t type_arguments_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizer, type_arguments_);
|
||||
}
|
||||
|
||||
ObjectPtr callback() const { return untag()->callback(); }
|
||||
static intptr_t callback_offset() {
|
||||
return OFFSET_OF(UntaggedFinalizer, callback_);
|
||||
}
|
||||
|
||||
static intptr_t InstanceSize() {
|
||||
return RoundedAllocationSize(sizeof(UntaggedFinalizer));
|
||||
}
|
||||
|
||||
static FinalizerPtr New(Heap::Space space = Heap::kNew);
|
||||
|
||||
private:
|
||||
FINAL_HEAP_OBJECT_IMPLEMENTATION(Finalizer, FinalizerBase);
|
||||
friend class Class;
|
||||
};
|
||||
|
||||
class MirrorReference : public Instance {
|
||||
public:
|
||||
ObjectPtr referent() const { return untag()->referent(); }
|
||||
|
|
|
@ -35,9 +35,6 @@
|
|||
V(ExceptionHandlers) \
|
||||
V(FfiTrampolineData) \
|
||||
V(Field) \
|
||||
V(Finalizer) \
|
||||
V(FinalizerBase) \
|
||||
V(FinalizerEntry) \
|
||||
V(Function) \
|
||||
V(FunctionType) \
|
||||
V(FutureOr) \
|
||||
|
@ -603,7 +600,6 @@ class ObjectCopyBase {
|
|||
// those are the only non-abstract classes (so we avoid checking more cids
|
||||
// here that cannot happen in reality)
|
||||
HANDLE_ILLEGAL_CASE(DynamicLibrary)
|
||||
HANDLE_ILLEGAL_CASE(Finalizer)
|
||||
HANDLE_ILLEGAL_CASE(MirrorReference)
|
||||
HANDLE_ILLEGAL_CASE(Pointer)
|
||||
HANDLE_ILLEGAL_CASE(ReceivePort)
|
||||
|
@ -1366,8 +1362,8 @@ class ObjectCopy : public Base {
|
|||
Object::null());
|
||||
// To satisfy some ASSERT()s in GC we'll use Object:null() explicitly here.
|
||||
Base::StoreCompressedPointerNoBarrier(
|
||||
Types::GetWeakPropertyPtr(to),
|
||||
OFFSET_OF(UntaggedWeakProperty, next_seen_by_gc_), Object::null());
|
||||
Types::GetWeakPropertyPtr(to), OFFSET_OF(UntaggedWeakProperty, next_),
|
||||
Object::null());
|
||||
Base::EnqueueWeakProperty(from);
|
||||
}
|
||||
|
||||
|
@ -1384,8 +1380,8 @@ class ObjectCopy : public Base {
|
|||
from, to, OFFSET_OF(UntaggedWeakReference, type_arguments_));
|
||||
// To satisfy some ASSERT()s in GC we'll use Object:null() explicitly here.
|
||||
Base::StoreCompressedPointerNoBarrier(
|
||||
Types::GetWeakReferencePtr(to),
|
||||
OFFSET_OF(UntaggedWeakReference, next_seen_by_gc_), Object::null());
|
||||
Types::GetWeakReferencePtr(to), OFFSET_OF(UntaggedWeakReference, next_),
|
||||
Object::null());
|
||||
Base::EnqueueWeakReference(from);
|
||||
}
|
||||
|
||||
|
@ -1819,7 +1815,7 @@ class ObjectGraphCopier {
|
|||
// We force the GC to compact, which is more likely to discover
|
||||
// untracked pointers (and other issues, like incorrect class table).
|
||||
thread_->heap()->CollectAllGarbage(GCReason::kDebugging,
|
||||
/*compact=*/true);
|
||||
/*compact=*/ true);
|
||||
}
|
||||
|
||||
// Fast copy failed due to
|
||||
|
|
|
@ -1700,29 +1700,6 @@ void WeakReference::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
|||
jsobj.AddProperty("target", target_handle);
|
||||
}
|
||||
|
||||
void FinalizerBase::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void Finalizer::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
JSONObject jsobj(stream);
|
||||
PrintSharedInstanceJSON(&jsobj, ref);
|
||||
jsobj.AddProperty("kind", "Finalizer");
|
||||
jsobj.AddServiceId(*this);
|
||||
if (ref) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Object& finalizer_callback = Object::Handle(callback());
|
||||
jsobj.AddProperty("callback", finalizer_callback);
|
||||
|
||||
// Not exposing entries.
|
||||
}
|
||||
|
||||
void FinalizerEntry::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void MirrorReference::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
JSONObject jsobj(stream);
|
||||
PrintSharedInstanceJSON(&jsobj, ref);
|
||||
|
|
|
@ -78,8 +78,6 @@ ErrorPtr IsolateObjectStore::PreallocateObjects(const Object& out_of_memory) {
|
|||
resume_capabilities_ = GrowableObjectArray::New();
|
||||
exit_listeners_ = GrowableObjectArray::New();
|
||||
error_listeners_ = GrowableObjectArray::New();
|
||||
dart_args_1_ = Array::New(1);
|
||||
dart_args_2_ = Array::New(2);
|
||||
|
||||
// Allocate pre-allocated unhandled exception object initialized with the
|
||||
// pre-allocated OutOfMemoryError.
|
||||
|
@ -469,24 +467,7 @@ void ObjectStore::LazyInitFfiMembers() {
|
|||
auto* const thread = Thread::Current();
|
||||
SafepointWriteRwLocker locker(thread,
|
||||
thread->isolate_group()->program_lock());
|
||||
if (handle_finalizer_message_function_.load() == Function::null()) {
|
||||
auto* const zone = thread->zone();
|
||||
auto& cls = Class::Handle(zone);
|
||||
auto& function = Function::Handle(zone);
|
||||
auto& error = Error::Handle(zone);
|
||||
|
||||
const auto& ffi_lib = Library::Handle(zone, Library::FfiLibrary());
|
||||
ASSERT(!ffi_lib.IsNull());
|
||||
|
||||
cls = finalizer_class();
|
||||
ASSERT(!cls.IsNull());
|
||||
error = cls.EnsureIsFinalized(thread);
|
||||
ASSERT(error.IsNull());
|
||||
function =
|
||||
cls.LookupFunctionAllowPrivate(Symbols::_handleFinalizerMessage());
|
||||
ASSERT(!function.IsNull());
|
||||
handle_finalizer_message_function_.store(function.ptr());
|
||||
}
|
||||
// TODO(http://dartbug.com/47777): Implement finalizers.
|
||||
}
|
||||
|
||||
void ObjectStore::LazyInitIsolateMembers() {
|
||||
|
@ -531,14 +512,13 @@ void ObjectStore::LazyInitInternalMembers() {
|
|||
auto* const zone = thread->zone();
|
||||
auto& cls = Class::Handle(zone);
|
||||
auto& field = Field::Handle(zone);
|
||||
auto& error = Error::Handle(zone);
|
||||
|
||||
const auto& internal_lib =
|
||||
Library::Handle(zone, Library::InternalLibrary());
|
||||
cls = internal_lib.LookupClass(Symbols::Symbol());
|
||||
ASSERT(!cls.IsNull());
|
||||
error = cls.EnsureIsFinalized(thread);
|
||||
ASSERT(error.IsNull());
|
||||
const auto& error = cls.EnsureIsFinalized(thread);
|
||||
ASSERT(error == Error::null());
|
||||
symbol_class_.store(cls.ptr());
|
||||
|
||||
field = cls.LookupInstanceFieldAllowPrivate(Symbols::_name());
|
||||
|
|
|
@ -55,7 +55,6 @@ class ObjectPointerVisitor;
|
|||
LAZY_CORE(Function, _object_to_string_function) \
|
||||
LAZY_INTERNAL(Class, symbol_class) \
|
||||
LAZY_INTERNAL(Field, symbol_name_field) \
|
||||
LAZY_FFI(Function, handle_finalizer_message_function) \
|
||||
LAZY_ASYNC(Type, non_nullable_future_rare_type) \
|
||||
LAZY_ASYNC(Type, non_nullable_future_never_type) \
|
||||
LAZY_ASYNC(Type, nullable_future_null_type) \
|
||||
|
@ -127,9 +126,6 @@ class ObjectPointerVisitor;
|
|||
RW(Class, expando_class) \
|
||||
RW(Class, weak_property_class) \
|
||||
RW(Class, weak_reference_class) \
|
||||
RW(Class, finalizer_class) \
|
||||
RW(Class, finalizer_entry_class) \
|
||||
RW(Class, finalizer_native_class) \
|
||||
ARW_AR(Array, symbol_table) \
|
||||
RW(Array, canonical_types) \
|
||||
RW(Array, canonical_function_types) \
|
||||
|
@ -319,8 +315,8 @@ class ObjectPointerVisitor;
|
|||
RW(UnhandledException, preallocated_unhandled_exception) \
|
||||
RW(StackTrace, preallocated_stack_trace) \
|
||||
RW(UnwindError, preallocated_unwind_error) \
|
||||
R_(Array, dart_args_1) \
|
||||
R_(Array, dart_args_2) \
|
||||
RW(Array, dart_args_1) \
|
||||
RW(Array, dart_args_2) \
|
||||
R_(GrowableObjectArray, resume_capabilities) \
|
||||
R_(GrowableObjectArray, exit_listeners) \
|
||||
R_(GrowableObjectArray, error_listeners)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -552,8 +552,6 @@ COMPRESSED_VISITOR(StackTrace)
|
|||
COMPRESSED_VISITOR(RegExp)
|
||||
COMPRESSED_VISITOR(WeakProperty)
|
||||
COMPRESSED_VISITOR(WeakReference)
|
||||
COMPRESSED_VISITOR(Finalizer)
|
||||
COMPRESSED_VISITOR(FinalizerEntry)
|
||||
COMPRESSED_VISITOR(MirrorReference)
|
||||
COMPRESSED_VISITOR(UserTag)
|
||||
REGULAR_VISITOR(SubtypeTestCache)
|
||||
|
@ -596,7 +594,6 @@ UNREACHABLE_VISITOR(AbstractType)
|
|||
UNREACHABLE_VISITOR(CallSiteData)
|
||||
UNREACHABLE_VISITOR(TypedDataBase)
|
||||
UNREACHABLE_VISITOR(Error)
|
||||
UNREACHABLE_VISITOR(FinalizerBase)
|
||||
UNREACHABLE_VISITOR(Number)
|
||||
UNREACHABLE_VISITOR(Integer)
|
||||
UNREACHABLE_VISITOR(String)
|
||||
|
|
|
@ -623,20 +623,6 @@ class UntaggedObject {
|
|||
}
|
||||
}
|
||||
|
||||
template <typename type,
|
||||
typename compressed_type,
|
||||
std::memory_order order = std::memory_order_relaxed>
|
||||
type ExchangeCompressedPointer(compressed_type const* addr, type value) {
|
||||
compressed_type previous_value =
|
||||
reinterpret_cast<std::atomic<compressed_type>*>(
|
||||
const_cast<compressed_type*>(addr))
|
||||
->exchange(static_cast<compressed_type>(value), order);
|
||||
if (value.IsHeapObject()) {
|
||||
CheckHeapPointerStore(value, Thread::Current());
|
||||
}
|
||||
return static_cast<type>(previous_value.Decompress(heap_base()));
|
||||
}
|
||||
|
||||
template <std::memory_order order = std::memory_order_relaxed>
|
||||
SmiPtr LoadSmi(SmiPtr const* addr) const {
|
||||
return reinterpret_cast<std::atomic<SmiPtr>*>(const_cast<SmiPtr*>(addr))
|
||||
|
@ -3279,7 +3265,7 @@ class UntaggedRegExp : public UntaggedInstance {
|
|||
class UntaggedWeakProperty : public UntaggedInstance {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(WeakProperty);
|
||||
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, key) // Weak reference.
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, key)
|
||||
VISIT_FROM(key)
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, value)
|
||||
VISIT_TO(value)
|
||||
|
@ -3287,10 +3273,8 @@ class UntaggedWeakProperty : public UntaggedInstance {
|
|||
|
||||
// Linked list is chaining all pending weak properties. Not visited by
|
||||
// pointer visitors.
|
||||
COMPRESSED_POINTER_FIELD(WeakPropertyPtr, next_seen_by_gc)
|
||||
CompressedWeakPropertyPtr next_;
|
||||
|
||||
template <typename Type, typename PtrType>
|
||||
friend struct GCLinkedList;
|
||||
friend class GCMarker;
|
||||
template <bool>
|
||||
friend class MarkingVisitorBase;
|
||||
|
@ -3304,7 +3288,7 @@ class UntaggedWeakProperty : public UntaggedInstance {
|
|||
class UntaggedWeakReference : public UntaggedInstance {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(WeakReference);
|
||||
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, target) // Weak reference.
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, target)
|
||||
VISIT_FROM(target)
|
||||
COMPRESSED_POINTER_FIELD(TypeArgumentsPtr, type_arguments)
|
||||
VISIT_TO(type_arguments)
|
||||
|
@ -3312,10 +3296,8 @@ class UntaggedWeakReference : public UntaggedInstance {
|
|||
|
||||
// Linked list is chaining all pending weak properties. Not visited by
|
||||
// pointer visitors.
|
||||
COMPRESSED_POINTER_FIELD(WeakReferencePtr, next_seen_by_gc)
|
||||
CompressedWeakReferencePtr next_;
|
||||
|
||||
template <typename Type, typename PtrType>
|
||||
friend struct GCLinkedList;
|
||||
friend class GCMarker;
|
||||
template <bool>
|
||||
friend class MarkingVisitorBase;
|
||||
|
@ -3326,92 +3308,6 @@ class UntaggedWeakReference : public UntaggedInstance {
|
|||
friend class SlowObjectCopy; // For OFFSET_OF
|
||||
};
|
||||
|
||||
class UntaggedFinalizerBase : public UntaggedInstance {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(FinalizerBase);
|
||||
|
||||
// The isolate this finalizer belongs to. Updated on sent and exit and set
|
||||
// to null on isolate shutdown. See Isolate::finalizers_.
|
||||
Isolate* isolate_;
|
||||
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, detachments)
|
||||
VISIT_FROM(detachments)
|
||||
COMPRESSED_POINTER_FIELD(LinkedHashSetPtr, all_entries)
|
||||
COMPRESSED_POINTER_FIELD(FinalizerEntryPtr, entries_collected)
|
||||
|
||||
// With compressed pointers, the first field in a subclass is at offset 28.
|
||||
// If the fields would be public, the first field in a subclass is at offset 32.
|
||||
// On Windows, it is always at offset 32, no matter public/private.
|
||||
// This makes it 32 for all OSes.
|
||||
// We can't use ALIGN8 on the first fields of the subclasses because they use
|
||||
// the COMPRESSED_POINTER_FIELD macro to define it.
|
||||
#ifdef DART_COMPRESSED_POINTERS
|
||||
uint32_t align_next_field;
|
||||
#endif
|
||||
|
||||
template <typename GCVisitorType>
|
||||
friend void MournFinalized(GCVisitorType* visitor);
|
||||
friend class GCMarker;
|
||||
template <bool>
|
||||
friend class MarkingVisitorBase;
|
||||
friend class Scavenger;
|
||||
template <bool>
|
||||
friend class ScavengerVisitorBase;
|
||||
};
|
||||
|
||||
class UntaggedFinalizer : public UntaggedFinalizerBase {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(Finalizer);
|
||||
|
||||
COMPRESSED_POINTER_FIELD(ClosurePtr, callback)
|
||||
COMPRESSED_POINTER_FIELD(TypeArgumentsPtr, type_arguments)
|
||||
VISIT_TO(type_arguments)
|
||||
|
||||
template <std::memory_order order = std::memory_order_relaxed>
|
||||
FinalizerEntryPtr exchange_entries_collected(FinalizerEntryPtr value) {
|
||||
return ExchangeCompressedPointer<FinalizerEntryPtr,
|
||||
CompressedFinalizerEntryPtr, order>(
|
||||
&entries_collected_, value);
|
||||
}
|
||||
|
||||
template <typename GCVisitorType>
|
||||
friend void MournFinalized(GCVisitorType* visitor);
|
||||
friend class GCMarker;
|
||||
template <bool>
|
||||
friend class MarkingVisitorBase;
|
||||
friend class Scavenger;
|
||||
template <bool>
|
||||
friend class ScavengerVisitorBase;
|
||||
};
|
||||
|
||||
class UntaggedFinalizerEntry : public UntaggedInstance {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(FinalizerEntry);
|
||||
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, value) // Weak reference.
|
||||
VISIT_FROM(value)
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, detach) // Weak reference.
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, token)
|
||||
COMPRESSED_POINTER_FIELD(FinalizerBasePtr, finalizer) // Weak reference.
|
||||
// Used for the linked list in Finalizer::entries_collected_. That cannot be
|
||||
// an ordinary list because we need to add elements during a GC so we cannot
|
||||
// modify the heap.
|
||||
COMPRESSED_POINTER_FIELD(FinalizerEntryPtr, next)
|
||||
VISIT_TO(next)
|
||||
|
||||
// Linked list is chaining all pending. Not visited by pointer visitors.
|
||||
// Only populated during the GC, otherwise null.
|
||||
COMPRESSED_POINTER_FIELD(FinalizerEntryPtr, next_seen_by_gc)
|
||||
|
||||
template <typename Type, typename PtrType>
|
||||
friend struct GCLinkedList;
|
||||
template <typename GCVisitorType>
|
||||
friend void MournFinalized(GCVisitorType* visitor);
|
||||
friend class GCMarker;
|
||||
template <bool>
|
||||
friend class MarkingVisitorBase;
|
||||
friend class Scavenger;
|
||||
template <bool>
|
||||
friend class ScavengerVisitorBase;
|
||||
};
|
||||
|
||||
// MirrorReferences are used by mirrors to hold reflectees that are VM
|
||||
// internal objects, such as libraries, classes, functions or types.
|
||||
class UntaggedMirrorReference : public UntaggedInstance {
|
||||
|
|
|
@ -137,8 +137,6 @@ class ObjectPointerVisitor;
|
|||
V(FfiVoid, "Void") \
|
||||
V(FfiHandle, "Handle") \
|
||||
V(Field, "Field") \
|
||||
V(FinalizerBase, "FinalizerBase") \
|
||||
V(FinalizerEntry, "FinalizerEntry") \
|
||||
V(FinallyRetVal, ":finally_ret_val") \
|
||||
V(FirstArg, "x") \
|
||||
V(Float32List, "Float32List") \
|
||||
|
@ -310,7 +308,6 @@ class ObjectPointerVisitor;
|
|||
V(_ExternalUint64Array, "_ExternalUint64Array") \
|
||||
V(_ExternalUint8Array, "_ExternalUint8Array") \
|
||||
V(_ExternalUint8ClampedArray, "_ExternalUint8ClampedArray") \
|
||||
V(_FinalizerImpl, "_FinalizerImpl") \
|
||||
V(_Float32ArrayFactory, "Float32List.") \
|
||||
V(_Float32ArrayView, "_Float32ArrayView") \
|
||||
V(_Float32List, "_Float32List") \
|
||||
|
@ -410,7 +407,6 @@ class ObjectPointerVisitor;
|
|||
V(_ensureScheduleImmediate, "_ensureScheduleImmediate") \
|
||||
V(_future, "_future") \
|
||||
V(_handleMessage, "_handleMessage") \
|
||||
V(_handleFinalizerMessage, "_handleFinalizerMessage") \
|
||||
V(_instanceOf, "_instanceOf") \
|
||||
V(_listGetAt, "_listGetAt") \
|
||||
V(_listLength, "_listLength") \
|
||||
|
|
|
@ -413,9 +413,6 @@ DEFINE_TAGGED_POINTER(StackTrace, Instance)
|
|||
DEFINE_TAGGED_POINTER(RegExp, Instance)
|
||||
DEFINE_TAGGED_POINTER(WeakProperty, Instance)
|
||||
DEFINE_TAGGED_POINTER(WeakReference, Instance)
|
||||
DEFINE_TAGGED_POINTER(FinalizerBase, Instance)
|
||||
DEFINE_TAGGED_POINTER(Finalizer, Instance)
|
||||
DEFINE_TAGGED_POINTER(FinalizerEntry, Instance)
|
||||
DEFINE_TAGGED_POINTER(MirrorReference, Instance)
|
||||
DEFINE_TAGGED_POINTER(UserTag, Instance)
|
||||
DEFINE_TAGGED_POINTER(FutureOr, Instance)
|
||||
|
|
|
@ -13,14 +13,10 @@ import "dart:_internal"
|
|||
show
|
||||
allocateOneByteString,
|
||||
allocateTwoByteString,
|
||||
checkValidWeakTarget,
|
||||
ClassID,
|
||||
CodeUnits,
|
||||
copyRangeFromUint8ListToOneByteString,
|
||||
EfficientLengthIterable,
|
||||
FinalizerBase,
|
||||
FinalizerBaseMembers,
|
||||
FinalizerEntry,
|
||||
FixedLengthListBase,
|
||||
IterableElementError,
|
||||
ListIterator,
|
||||
|
@ -32,12 +28,11 @@ import "dart:_internal"
|
|||
makeFixedListUnmodifiable,
|
||||
makeListFixedLength,
|
||||
patch,
|
||||
reachabilityFence,
|
||||
unsafeCast,
|
||||
writeIntoOneByteString,
|
||||
writeIntoTwoByteString;
|
||||
|
||||
import "dart:async" show Completer, DeferredLoadException, Future, Timer, Zone;
|
||||
import "dart:async" show Completer, DeferredLoadException, Future, Timer;
|
||||
|
||||
import "dart:collection"
|
||||
show
|
||||
|
@ -54,9 +49,9 @@ import "dart:collection"
|
|||
|
||||
import "dart:convert" show ascii, Encoding, json, latin1, utf8;
|
||||
|
||||
import "dart:ffi" show Pointer, Struct, Union, NativePort;
|
||||
import "dart:ffi" show Pointer, Struct, Union;
|
||||
|
||||
import "dart:isolate" show Isolate, RawReceivePort;
|
||||
import "dart:isolate" show Isolate;
|
||||
|
||||
import "dart:typed_data"
|
||||
show Endian, Uint8List, Int64List, Uint16List, Uint32List;
|
||||
|
|
|
@ -28,8 +28,7 @@ class Expando<T> {
|
|||
|
||||
@patch
|
||||
T? operator [](Object object) {
|
||||
// TODO(http://dartbug.com/48634): Rename to `key`.
|
||||
checkValidWeakTarget(object, 'object');
|
||||
_checkType(object);
|
||||
|
||||
var mask = _size - 1;
|
||||
var idx = object._identityHashCode & mask;
|
||||
|
@ -51,8 +50,7 @@ class Expando<T> {
|
|||
|
||||
@patch
|
||||
void operator []=(Object object, T? value) {
|
||||
// TODO(http://dartbug.com/48634): Rename to `key`.
|
||||
checkValidWeakTarget(object, 'object');
|
||||
_checkType(object);
|
||||
|
||||
var mask = _size - 1;
|
||||
var idx = object._identityHashCode & mask;
|
||||
|
@ -149,6 +147,19 @@ class Expando<T> {
|
|||
}
|
||||
}
|
||||
|
||||
static _checkType(object) {
|
||||
if ((object == null) ||
|
||||
(object is bool) ||
|
||||
(object is num) ||
|
||||
(object is String) ||
|
||||
(object is Pointer) ||
|
||||
(object is Struct) ||
|
||||
(object is Union)) {
|
||||
throw new ArgumentError.value(object,
|
||||
"Expandos are not allowed on strings, numbers, booleans, null, Pointers, Structs or Unions.");
|
||||
}
|
||||
}
|
||||
|
||||
int get _size => _data.length;
|
||||
int get _limit => (3 * (_size ~/ 4));
|
||||
|
||||
|
@ -159,14 +170,14 @@ class Expando<T> {
|
|||
@patch
|
||||
class WeakReference<T extends Object> {
|
||||
@patch
|
||||
factory WeakReference(T target) = _WeakReferenceImpl<T>;
|
||||
factory WeakReference(T object) = _WeakReferenceImpl<T>;
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
class _WeakReferenceImpl<T extends Object> implements WeakReference<T> {
|
||||
_WeakReferenceImpl(T target) {
|
||||
checkValidWeakTarget(target, 'target');
|
||||
_target = target;
|
||||
_WeakReferenceImpl(T object) {
|
||||
Expando._checkType(object);
|
||||
_target = object;
|
||||
}
|
||||
|
||||
@pragma("vm:recognized", "other")
|
||||
|
@ -179,3 +190,11 @@ class _WeakReferenceImpl<T extends Object> implements WeakReference<T> {
|
|||
@pragma("vm:external-name", "WeakReference_setTarget")
|
||||
external set _target(T? value);
|
||||
}
|
||||
|
||||
@patch
|
||||
class Finalizer<T> {
|
||||
@patch
|
||||
factory Finalizer(void Function(T) object) {
|
||||
throw UnimplementedError("Finalizer");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,83 +4,5 @@
|
|||
|
||||
// part of "core_patch.dart";
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
abstract class Finalizer<T> {
|
||||
@patch
|
||||
factory Finalizer(void Function(T) callback) = _FinalizerImpl<T>;
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
class _FinalizerImpl<T> extends FinalizerBase implements Finalizer<T> {
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external void Function(T) get _callback;
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external set _callback(void Function(T) value);
|
||||
|
||||
/// Constructs a finalizer.
|
||||
///
|
||||
/// This is fine as a non-atomic operation, because the GC only looks at
|
||||
/// finalizer instances when it process their entries. By preventing inlining
|
||||
/// we ensure the the finalizer to have been fully initialized by the time
|
||||
/// any [attach] on it is called.
|
||||
///
|
||||
/// Alternatively, we could make it a recognized method and add a reachability
|
||||
/// fence on the relevant members.
|
||||
@pragma('vm:never-inline')
|
||||
_FinalizerImpl(void Function(T) callback) {
|
||||
allEntries = <FinalizerEntry>{};
|
||||
_callback = Zone.current.bindUnaryCallbackGuarded(callback);
|
||||
setIsolate();
|
||||
isolateRegisterFinalizer();
|
||||
}
|
||||
|
||||
void attach(Object value, T token, {Object? detach}) {
|
||||
checkValidWeakTarget(value, 'value');
|
||||
if (detach != null) {
|
||||
checkValidWeakTarget(detach, 'detach');
|
||||
}
|
||||
|
||||
// Initializing the entry in a non-atomic way should be fine.
|
||||
// The only interesting step in the GC is when value is collected.
|
||||
// If the entry gets processed before initializing value, it will be null,
|
||||
// and this is fine. We will not consider it as being collected that GC.
|
||||
final entry = FinalizerEntry()
|
||||
..value = value
|
||||
..token = token
|
||||
..detach = detach
|
||||
..finalizer = this;
|
||||
allEntries.add(entry);
|
||||
// Ensure value stays reachable until after having initialized the entry.
|
||||
// This ensures the token and finalizer are set.
|
||||
reachabilityFence(value);
|
||||
|
||||
if (detach != null) {
|
||||
(detachments[detach] ??= <FinalizerEntry>{}).add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void _runFinalizers() {
|
||||
FinalizerEntry? entry = exchangeEntriesCollectedWithNull();
|
||||
while (entry != null) {
|
||||
final token = entry.token;
|
||||
// Check token for identical, detach might have been called.
|
||||
if (!identical(token, entry)) {
|
||||
_callback(unsafeCast<T>(token));
|
||||
}
|
||||
allEntries.remove(entry);
|
||||
final detach = entry.detach;
|
||||
if (detach != null) {
|
||||
detachments[detach]?.remove(entry);
|
||||
}
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point", "call")
|
||||
static _handleFinalizerMessage(_FinalizerImpl finalizer) {
|
||||
finalizer._runFinalizers();
|
||||
}
|
||||
}
|
||||
// This is a placeholder file which will shortly contain a Finalizer
|
||||
// implementation.
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import "dart:async" show Timer;
|
||||
import "dart:core" hide Symbol;
|
||||
import "dart:ffi" show Pointer, Struct, Union;
|
||||
|
||||
import "dart:isolate" show SendPort;
|
||||
import "dart:typed_data" show Int32List, Uint8List;
|
||||
|
||||
|
@ -209,225 +209,3 @@ class LateError {
|
|||
throw new LateError.localADI(localName);
|
||||
}
|
||||
}
|
||||
|
||||
void checkValidWeakTarget(object, name) {
|
||||
if ((object == null) ||
|
||||
(object is bool) ||
|
||||
(object is num) ||
|
||||
(object is String) ||
|
||||
(object is Pointer) ||
|
||||
(object is Struct) ||
|
||||
(object is Union)) {
|
||||
throw new ArgumentError.value(object, name,
|
||||
"Cannot be a string, number, boolean, null, Pointer, Struct or Union");
|
||||
}
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
class FinalizerBase {
|
||||
/// The list of finalizers of this isolate.
|
||||
///
|
||||
/// Reuses [WeakReference] so that we don't have to implement yet another
|
||||
/// mechanism to hold on weakly to things.
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external static List<WeakReference<FinalizerBase>>? get _isolateFinalizers;
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external static set _isolateFinalizers(
|
||||
List<WeakReference<FinalizerBase>>? value);
|
||||
|
||||
static int _isolateFinalizersPurgeCollectedAt = 1;
|
||||
|
||||
/// Amortizes the cost for purging nulled out entries.
|
||||
///
|
||||
/// Similar to how Expandos purge their nulled out entries on a rehash when
|
||||
/// resizing.
|
||||
static void _isolateFinalizersEnsureCapacity() {
|
||||
_isolateFinalizers ??= <WeakReference<FinalizerBase>>[];
|
||||
if (_isolateFinalizers!.length < _isolateFinalizersPurgeCollectedAt) {
|
||||
return;
|
||||
}
|
||||
// retainWhere does a single traversal.
|
||||
_isolateFinalizers!.retainWhere((weak) => weak.target != null);
|
||||
// We might have dropped most finalizers, trigger next resize at 2x.
|
||||
_isolateFinalizersPurgeCollectedAt = _isolateFinalizers!.length * 2;
|
||||
}
|
||||
|
||||
/// Registers this [FinalizerBase] to the isolate.
|
||||
///
|
||||
/// This is used to prevent sending messages from the GC to the isolate after
|
||||
/// isolate shutdown.
|
||||
void _isolateRegisterFinalizer() {
|
||||
_isolateFinalizersEnsureCapacity();
|
||||
_isolateFinalizers!.add(WeakReference(this));
|
||||
}
|
||||
|
||||
/// The isolate this [FinalizerBase] belongs to.
|
||||
///
|
||||
/// This is used to send finalizer messages to `_handleFinalizerMessage`
|
||||
/// without a Dart_Port.
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external _setIsolate();
|
||||
|
||||
/// All active attachments.
|
||||
///
|
||||
/// This keeps the [FinalizerEntry]s belonging to this finalizer alive. If an
|
||||
/// entry gets collected, the finalizer is not run when the
|
||||
/// [FinalizerEntry.value] is collected.
|
||||
///
|
||||
/// TODO(http://dartbug.com/47777): For native finalizers, what data structure
|
||||
/// can we use that we can modify in the VM. So that we don't have to send a
|
||||
/// message to Dart to clean up entries for which the GC has run.
|
||||
///
|
||||
/// Requirements for data structure:
|
||||
/// 1. Keeps entries reachable. Entries that are collected will never run
|
||||
/// the GC.
|
||||
/// 2. Atomic insert in Dart on `attach`. GC should not run in between.
|
||||
/// 3. Atomic remove in Dart on `detach`. multiple GC tasks run in parallel.
|
||||
/// 4. Atomic remove in C++ on value being collected. Multiple GC tasks run in
|
||||
/// parallel.
|
||||
///
|
||||
/// For Dart finalizers we execute the remove in Dart, much simpler.
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma('vm:prefer-inline')
|
||||
external Set<FinalizerEntry> get _allEntries;
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma('vm:prefer-inline')
|
||||
external set _allEntries(Set<FinalizerEntry> entries);
|
||||
|
||||
/// Entries of which the value has been collected.
|
||||
///
|
||||
/// This is a linked list, with [FinalizerEntry.next].
|
||||
///
|
||||
/// Atomic exchange: The GC cannot run between reading the value and storing
|
||||
/// `null`. Atomicity guaranteed by force optimizing the function.
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external FinalizerEntry? _exchangeEntriesCollectedWithNull();
|
||||
|
||||
/// A weak map from `detach` keys to [FinalizerEntry]s.
|
||||
///
|
||||
/// Using the [FinalizerEntry.detach] keys as keys in an [Expando] ensures
|
||||
/// they can be GCed.
|
||||
///
|
||||
/// [FinalizerEntry]s do not get GCed themselves when their
|
||||
/// [FinalizerEntry.detach] is unreachable, in contrast to `WeakProperty`s
|
||||
/// which are GCed themselves when their `key` is no longer reachable.
|
||||
/// To prevent [FinalizerEntry]s staying around in [_detachments] forever,
|
||||
/// we reuse `WeakProperty`s.
|
||||
/// To avoid code duplication, we do not inline the code but use an [Expando]
|
||||
/// here instead.
|
||||
///
|
||||
/// We cannot eagerly purge entries from the map (in the Expando) when GCed.
|
||||
/// The map is indexed on detach, and doesn't enable finding the entries
|
||||
/// based on their identity.
|
||||
/// Instead we rely on the WeakProperty being nulled out (assuming the
|
||||
/// `detach` key gets GCed) and then reused.
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma('vm:prefer-inline')
|
||||
external Expando<Set<FinalizerEntry>>? get _detachments;
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma('vm:prefer-inline')
|
||||
external set _detachments(Expando<Set<FinalizerEntry>>? value);
|
||||
|
||||
void detach(Object detach) {
|
||||
final entries = detachments[detach];
|
||||
if (entries != null) {
|
||||
for (final entry in entries) {
|
||||
entry.token = entry;
|
||||
_allEntries.remove(entry);
|
||||
}
|
||||
detachments[detach] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extension so that the members can be accessed from other libs.
|
||||
extension FinalizerBaseMembers on FinalizerBase {
|
||||
/// See documentation on [_allEntries].
|
||||
@pragma('vm:prefer-inline')
|
||||
Set<FinalizerEntry> get allEntries => _allEntries;
|
||||
@pragma('vm:prefer-inline')
|
||||
set allEntries(Set<FinalizerEntry> value) => _allEntries = value;
|
||||
|
||||
/// See documentation on [_exchangeEntriesCollectedWithNull].
|
||||
FinalizerEntry? exchangeEntriesCollectedWithNull() =>
|
||||
_exchangeEntriesCollectedWithNull();
|
||||
|
||||
/// See documentation on [_detachments].
|
||||
@pragma('vm:prefer-inline')
|
||||
Expando<Set<FinalizerEntry>> get detachments {
|
||||
_detachments ??= Expando<Set<FinalizerEntry>>();
|
||||
return unsafeCast<Expando<Set<FinalizerEntry>>>(_detachments);
|
||||
}
|
||||
|
||||
/// See documentation on [_isolateRegisterFinalizer].
|
||||
isolateRegisterFinalizer() => _isolateRegisterFinalizer();
|
||||
|
||||
/// See documentation on [_setIsolate].
|
||||
setIsolate() => _setIsolate();
|
||||
}
|
||||
|
||||
/// Contains the informatation of an active [Finalizer.attach].
|
||||
///
|
||||
/// It holds on to the [value], optional [detach], and [token]. In addition, it
|
||||
/// also keeps a reference the [finalizer] it belings to and a [next] field for
|
||||
/// when being used in a linked list.
|
||||
///
|
||||
/// This is being kept alive by [FinalizerBase._allEntries] until either (1)
|
||||
/// [Finalizer.detach] detaches it, or (2) [value] is collected and the
|
||||
/// `callback` has been invoked.
|
||||
///
|
||||
/// Note that the GC itself uses an extra hidden field `next_seen_by_gc` to keep a
|
||||
/// linked list of pending entries while running the GC.
|
||||
@pragma("vm:entry-point")
|
||||
class FinalizerEntry {
|
||||
/// The [value] the [FinalizerBase] is attached to.
|
||||
///
|
||||
/// Set to `null` by GC when unreachable.
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external Object? get value;
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external set value(Object? value);
|
||||
|
||||
/// The [detach] object can be passed to [FinalizerBase] to detach
|
||||
/// the finalizer.
|
||||
///
|
||||
/// Set to `null` by GC when unreachable.
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external Object? get detach;
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external set detach(Object? value);
|
||||
|
||||
/// The [token] is passed to [FinalizerBase] when the finalizer is run.
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external Object? get token;
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external set token(Object? value);
|
||||
|
||||
/// The [finalizer] this [FinalizerEntry] belongs to.
|
||||
///
|
||||
/// Set to `null` by GC when unreachable.
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external set finalizer(FinalizerBase? finalizer);
|
||||
|
||||
/// The [next] entry in a linked list.
|
||||
///
|
||||
/// Used in for the linked list starting from
|
||||
/// [FinalizerBase._exchangeEntriesCollectedWithNull].
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external FinalizerEntry? get next;
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external set next(FinalizerEntry? value);
|
||||
}
|
||||
|
|
|
@ -207,15 +207,15 @@ abstract class Finalizer<T> {
|
|||
/// with different, or the same, finalization token.
|
||||
void attach(Object value, T finalizationToken, {Object? detach});
|
||||
|
||||
/// Detaches this finalizer from values attached with [detach].
|
||||
/// Detaches the finalizer from values attached with [detachToken].
|
||||
///
|
||||
/// Each attachment between this finalizer and a value,
|
||||
/// which was created by calling [attach] with the [detach] object as
|
||||
/// which was created by calling [attach] with the [detachToken] object as
|
||||
/// `detach` argument, is removed.
|
||||
///
|
||||
/// If the finalizer was attached multiple times to the same value
|
||||
/// with different detachment keys,
|
||||
/// only those attachments which used [detach] are removed.
|
||||
/// only those attachments which used [detachToken] are removed.
|
||||
///
|
||||
/// After detaching, an attachment won't cause any callbacks to happen
|
||||
/// if the object become inaccessible.
|
||||
|
@ -242,5 +242,5 @@ abstract class Finalizer<T> {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
void detach(Object detach);
|
||||
void detach(Object detachToken);
|
||||
}
|
||||
|
|
|
@ -723,7 +723,6 @@ abstract class SendPort implements Capability {
|
|||
/// therefore not be sent.
|
||||
/// - [ReceivePort]
|
||||
/// - [DynamicLibrary]
|
||||
/// - [Finalizer]
|
||||
/// - [Pointer]
|
||||
/// - [UserTag]
|
||||
/// - `MirrorReference`
|
||||
|
|
Loading…
Reference in a new issue