blob: a9db8536c6a0259d699e053e1bc0840becb97d83 [file] [log] [blame]
// 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.
// 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();
}
}