-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Finalizables don't keep member finalizables alive #49643
Comments
Tentatively passing on to the VM team; it's just my best guess. |
Do you always allocate A and B together? In that case have a single We haven't covered the composition case. class A implements Finalizable {
A() {
_nativeFinalizer.attach(this, Pointer.fromAddress(1), detach: this, externalSize: 1 << 32); // will crash, if it ever runs
}
@pragma('vm:never-inline')
keepAlive() {}
}
class B implements Finalizable {
final A a;
B(this.a);
void someMethod(){
// ...
a.keepAlive();
}
} |
I guess I would wish that any members on a type that implements |
I reworked my code as follows: import 'dart:ffi';
import 'dart:io';
typedef Free = NativeFunction<Void Function(Pointer)>;
final free = DynamicLibrary.process().lookup<Free>('free');
final _nativeFinalizer = NativeFinalizer(free);
class A implements Finalizable {
A() {
_nativeFinalizer.attach(this, Pointer.fromAddress(1), detach: this, externalSize: 1 << 32); // will crash, if it ever runs
}
@pragma('vm:never-inline')
void keepAlive() {}
}
class B implements Finalizable {
final A a;
B(this.a);
@pragma('vm:never-inline')
void keepAlive() {
a.keepAlive();
}
}
Future<void> main() async {
final b = B(A()); // I would expect b.a to live as long as b
final l = <int>[];
Future.doWhile(() {
l.add(1); // put some pressure on GC
return true;
});
await ProcessSignal.sigint.watch().first;
print(b); // b still alive here, but what about b.a?
// b.keepAlive(); // <-- this will save the day, but defeats the purpose I think
} but that still crash, unless I add an explicit call to |
Maybe I should clarify, that "save the day" means we get a OoM first:
as opposed to an immediate crash:
|
It looks like @dcharkes We should probably adjust
to retain write-only fields of finalizable types in tree shaker. |
Classes that implements Finalizable: * Realm, * FlexibleSyncConfiguration, * User, and * App The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643
Classes that implements Finalizable: * Realm, * FlexibleSyncConfiguration, * User, and * App The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643
Classes that implements Finalizable: * Realm, * FlexibleSyncConfiguration, * User, and * App The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643
This calls
That does indeed to the same as overriding
34359738384 = 0x800000010. It looks like MongoDB is adding very many elements to a growable list. 8 GB data memory (and the 16 bytes is maybe the object header). Could you investigate that? If MongoDB is not creating a very large list, could you provide a minimal reproduction that causes this in the VM? Thanks for the pointer @alexmarkov!
We only need to retain the fields if they are in However, that also leads to the question of write elimination optimizations in the VM. The VM can eliminate writes under certain circumstances. We should also double check those. |
@dcharkes The above code don't depend on any MongoDB code. The OoM is expected, since I just keep adding integers to an ordinary list. This is just to provoke a GC run, to expose the finalization call to I understand I can use a real method like I have actually chosen to make |
Classes that implements Finalizable: * App, * FlexibleSyncConfiguration, * HandleBase, * Realm, * RealmEntity, * RealmList, * RealmResults, * Subscription, and * User, The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643
Classes that implements Finalizable: * App, * FlexibleSyncConfiguration, * HandleBase, * Realm, * RealmEntity, * RealmList, * RealmResults, * Subscription, * SubscriptionSet, and * User, The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643
Yes this is fine, extension methods are basically top level methods with the receiver as first argument, and Quick test: https://dart-review.googlesource.com/c/sdk/+/255123
void TriggerGC(uint64_t count) {
Dart_ExecuteInternalCommand("gc-now", nullptr);
} Note that |
Classes that implements Finalizable: * App, * FlexibleSyncConfiguration, * HandleBase, * Realm, * RealmEntity, * RealmList, * RealmResults, * Subscription, * SubscriptionSet, and * User, The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643
Thank you @dcharkes for the confirmation, the nifty |
We should keep it open for: Finalizables don't keep member finalizables alive. |
Classes that implements Finalizable: * App, * FlexibleSyncConfiguration, * HandleBase, * Realm, * RealmEntity, * RealmList, * RealmResults, * Subscription, * SubscriptionSet, and * User, The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643 Resolves: #581, resolves: #582
It looks like the field is already retained: https://dart-review.googlesource.com/c/sdk/+/254904/1/pkg/vm/testcases/transformations/ffi/finalizable_member.dart.aot.expect#20
For cleanliness we might want to add my TFA changes anyway. I'll investigate why |
`Finalizable`s with extension methods work by virtue of being desugared into static methods and the `Finalizable`s being normal arguments. If we ever change the representation of extension methods, `Finalizable`s would need to be treated specially. This test will catch that. TEST=pkg/vm/test/transformations/ffi_test.dart Bug: #49643 (comment) Change-Id: I68cfbc098386a88495d37417c6eb7295b89bfb95 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255123 Reviewed-by: Tess Strickland <sstrickl@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com>
@dcharkes Here is the AOT kernel of the original example:
So |
I'm unsure how I got a different result. I copied the example (https://dart-review.googlesource.com/c/sdk/+/254904/1/pkg/vm/testcases/transformations/ffi/finalizable_member.dart), run the global transformations (https://dart-review.googlesource.com/c/sdk/+/254904/1/pkg/vm/test/transformations/ffi_test.dart#51) and checked that TFA is running with stepping through the debugger. How did you run the transform? |
Classes that implements Finalizable: * App, * FlexibleSyncConfiguration, * HandleBase, * Realm, * RealmEntity, * RealmList, * RealmResults, * Subscription, * SubscriptionSet, and * User, The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643 Resolves: #581, resolves: #582
I just ran the AOT compiler and dumped the intermediate kernel file.
|
Classes that implements Finalizable: * App, * FlexibleSyncConfiguration, * HandleBase, * Realm, * RealmEntity, * RealmList, * RealmResults, * Subscription, * SubscriptionSet, and * User, The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643 Resolves: #581, resolves: #582
Classes that implements Finalizable: * App, * FlexibleSyncConfiguration, * HandleBase, * Realm, * RealmEntity, * RealmList, * RealmResults, * Subscription, * SubscriptionSet, and * User, The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643 Resolves: #581, resolves: #582
* HandleBase implements Finalizable, and uses a common NativeFinalizer * Classes with Finalizable fields implements Finalizable Classes that implements Finalizable: * App, * FlexibleSyncConfiguration, * HandleBase, * Realm, * RealmEntity, * RealmList, * RealmResults, * Subscription, * SubscriptionSet, and * User, The corresponding XInternal extension classes implements a keepAlive function to ensure finalizable fields always live as long as their finalizable parent. This is workaround for: dart-lang/sdk#49643 Resolves: #581, resolves: #582 * Remove redundant code * Implement Finalizable on more classes * RealmObject, * RealmObjectChanges, and * RealmCollectionChanges. Drop from RealmEntity * Implements Finalizable on SyncSession * Don't use `assert(() { ...; return true; }());` trick for finalization trace. Instead have const flag and depend on tree-shaking * Update CHANGELOG
The keepAlive hack introduced as a work-around for dart-lang/sdk#49643 is no longer needed, as of dart 2.19.0 or later.
The keepAlive hack introduced as a work-around for dart-lang/sdk#49643 is no longer needed, as of dart 2.19.0 or later.
The keepAlive hack introduced as a work-around for dart-lang/sdk#49643 is no longer needed, as of dart 2.19.0 or later.
The keepAlive hack introduced as a work-around for dart-lang/sdk#49643 is no longer needed, as of dart 2.19.0 or later.
The keepAlive hack introduced as a work-around for dart-lang/sdk#49643 is no longer needed, as of dart 2.19.0 or later.
The keepAlive hack introduced as a work-around for dart-lang/sdk#49643 is no longer needed, as of dart 2.19.0 or later.
The keepAlive hack introduced as a work-around for dart-lang/sdk#49643 is no longer needed, as of dart 2.19.0 or later.
* Remove keepAlive pseudo functions The keepAlive hack introduced as a work-around for dart-lang/sdk#49643 is no longer needed, as of dart 2.19.0 or later. * Update CHANGELOG
Compiling with
dart compile exe
and running the following program on macOS 12.5 (I believe any posix platform will do) with Dart version 2.17.6, 2.18.0-271.4.beta, or 2.19.0-81.0.dev will cause a crash.When I force
b
to be alive by the end ofmain
s scope, by havingB
implementFinalizable
, then I would also expectb.a
of typeA
(which also implementsFinalizable
) to be kept alive until the end ofmain
s scope. The above example is designed to crash ifb.a
is ever reaped, by freeing0x1
.Is this intensional, and if so, how would I ensure that
b.a
lives as long asb
?The text was updated successfully, but these errors were encountered: