Skip to content

Commit

Permalink
[vm] Allow sending closures between isolates of the same isolate group
Browse files Browse the repository at this point in the history
Closures can be shared if they have no state they capture (i.e. a null
context). Otherwise we copy them by also transitively copying the
parent context chain.

Issue #36097

TEST=Added tests to vm/dart{,_2}/isolates/fast_object_copy{,2}_test

Change-Id: Ie641b97806edd0c21f0a8d5c514f8e850823e165
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209110
Commit-Queue: Martin Kustermann <[email protected]>
Reviewed-by: Alexander Aprelev <[email protected]>
Reviewed-by: Ryan Macnak <[email protected]>
  • Loading branch information
mkustermann authored and [email protected] committed Sep 2, 2021
1 parent 714e7e6 commit 26ef8f6
Show file tree
Hide file tree
Showing 11 changed files with 415 additions and 44 deletions.
87 changes: 87 additions & 0 deletions runtime/tests/vm/dart/isolates/fast_object_copy2_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import 'package:expect/expect.dart';
import 'fast_object_copy_test.dart'
show UserObject, SendReceiveTestBase, notAllocatableInTLAB;

topLevelClosure(a, b) {}
topLevelClosureG<T>(T a, T b) {}
Type getType<T>() => T;

class A<T> {
dynamic m<H>(T a, H b) => this;
}

// When running with isolate groups enabled, we can share all of the following
// objects.
final sharableObjects = [
Expand All @@ -32,6 +40,36 @@ final sharableObjects = [
rp.close();
return sp;
})(),
() {
innerClosure(a, b) {}
return innerClosure;
}(),
() {
innerClosureG<T>(T a, T b) {}
return innerClosureG;
}(),
() {
innerClosureG<T>() {
innerClosureG2<H>(T a, H b) {}
return innerClosureG2;
}

return innerClosureG<int>();
}(),
() {
innerClosureG<T>(T a, T b) {}
final Function(int, int) partialInstantiatedInnerClosure = innerClosureG;
return partialInstantiatedInnerClosure;
}(),
() {
return topLevelClosureG;
}(),
() {
final Function(int, int) partialInstantiatedInnerClosure = topLevelClosureG;
return partialInstantiatedInnerClosure;
}(),
getType<void Function(int, double, Object)>(),
getType<T Function<T>(int, double, T)>(),
const [1, 2, 3],
const {1: 1, 2: 2, 3: 2},
const {1, 2, 3},
Expand All @@ -40,13 +78,40 @@ final sharableObjects = [
Int32x4(1, 2, 3, 4),
];

final copyableClosures = <dynamic>[
() {
final a = A<int>();
final Function<T>(int, T) genericMethod = a.m;
return genericMethod;
}(),
() {
final a = A<int>();
final Function(int, double) partialInstantiatedMethod = a.m;
return partialInstantiatedMethod;
}(),
() {
final a = Object();
dynamic inner() => a;
return inner;
}(),
() {
foo(var arg) {
return () => arg;
}

return foo(1);
}(),
];

class SendReceiveTest extends SendReceiveTestBase {
Future runTests() async {
await testSharable();
await testSharable2();
await testCopyableClosures();
}

Future testSharable() async {
print('testSharable');
final sharableObjectsCopy = await sendReceive([
...sharableObjects,
]);
Expand All @@ -57,6 +122,7 @@ class SendReceiveTest extends SendReceiveTestBase {
}

Future testSharable2() async {
print('testSharable2');
final sharableObjectsCopy = await sendReceive([
notAllocatableInTLAB,
...sharableObjects,
Expand All @@ -68,6 +134,27 @@ class SendReceiveTest extends SendReceiveTestBase {
Expect.identical(sharableObjects[i], sharableObjectsCopy[i + 1]);
}
}

Future testCopyableClosures() async {
print('testCopyableClosures');
final copy = await sendReceive([
notAllocatableInTLAB,
...copyableClosures,
]);
for (int i = 0; i < copyableClosures.length; ++i) {
Expect.notIdentical(copyableClosures[i], copy[1 + i]);
Expect.equals(copyableClosures[i].runtimeType, copy[1 + i].runtimeType);
}

final copy2 = await sendReceive([
...copyableClosures,
notAllocatableInTLAB,
]);
for (int i = 0; i < copyableClosures.length; ++i) {
Expect.notIdentical(copyableClosures[i], copy2[i]);
Expect.equals(copyableClosures[i].runtimeType, copy2[i].runtimeType);
}
}
}

main() async {
Expand Down
38 changes: 37 additions & 1 deletion runtime/tests/vm/dart/isolates/fast_object_copy_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import 'dart:typed_data';

import 'package:expect/expect.dart';

class ClassWithNativeFields extends NativeFieldWrapperClass1 {}
class ClassWithNativeFields extends NativeFieldWrapperClass1 {
void m() {}
}

final Uint8List largeExternalTypedData =
File(Platform.resolvedExecutable).readAsBytesSync()..[0] = 42;
Expand Down Expand Up @@ -221,6 +223,8 @@ class SendReceiveTest extends SendReceiveTestBase {
await testSlowOnly();

await testWeakProperty();

await testForbiddenClosures();
}

Future testTransferrable() async {
Expand Down Expand Up @@ -644,6 +648,38 @@ class SendReceiveTest extends SendReceiveTestBase {
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
}
}

Future testForbiddenClosures() async {
print('testForbiddenClosures');
final nonCopyableClosures = <dynamic>[
(() {
final a = ClassWithNativeFields();
return a.m;
})(),
(() {
final a = ClassWithNativeFields();
dynamic inner() => a;
return inner;
})(),
(() {
foo(var arg) {
return () => arg;
}

return foo(ClassWithNativeFields());
})(),
];
for (final closure in nonCopyableClosures) {
Expect.throwsArgumentError(() => sendPort.send(closure));
}
for (final closure in nonCopyableClosures) {
Expect.throwsArgumentError(() => sendPort.send([closure]));
}
for (final closure in nonCopyableClosures) {
Expect.throwsArgumentError(
() => sendPort.send([notAllocatableInTLAB, closure]));
}
}
}

main() async {
Expand Down
86 changes: 86 additions & 0 deletions runtime/tests/vm/dart_2/isolates/fast_object_copy2_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ import 'package:expect/expect.dart';
import 'fast_object_copy_test.dart'
show UserObject, SendReceiveTestBase, notAllocatableInTLAB;

topLevelClosure(a, b) {}
topLevelClosureG<T>(T a, T b) {}
Type getType<T>() => T;

class A<T> {
dynamic m<H>(T a, H b) => this;
}

// When running with isolate groups enabled, we can share all of the following
// objects.
final sharableObjects = [
Expand All @@ -34,6 +42,35 @@ final sharableObjects = [
rp.close();
return sp;
})(),
() {
innerClosure(a, b) {}
return innerClosure;
}(),
() {
innerClosureG<T>(T a, T b) {}
return innerClosureG;
}(),
() {
innerClosureG<T>() {
innerClosureG2<H>(T a, H b) {}
return innerClosureG2;
}

return innerClosureG<int>();
}(),
() {
innerClosureG<T>(T a, T b) {}
final Function(int, int) partialInstantiatedInnerClosure = innerClosureG;
return partialInstantiatedInnerClosure;
}(),
() {
return topLevelClosureG;
}(),
() {
final Function(int, int) partialInstantiatedInnerClosure = topLevelClosureG;
return partialInstantiatedInnerClosure;
}(),
getType<void Function(int, double, Object)>(),
const [1, 2, 3],
const {1: 1, 2: 2, 3: 2},
const {1, 2, 3},
Expand All @@ -42,13 +79,40 @@ final sharableObjects = [
Int32x4(1, 2, 3, 4),
];

final copyableClosures = <dynamic>[
() {
final a = A<int>();
final Function<T>(int, T) genericMethod = a.m;
return genericMethod;
}(),
() {
final a = A<int>();
final Function(int, double) partialInstantiatedMethod = a.m;
return partialInstantiatedMethod;
}(),
() {
final a = Object();
dynamic inner() => a;
return inner;
}(),
() {
foo(var arg) {
return () => arg;
}

return foo(1);
}(),
];

class SendReceiveTest extends SendReceiveTestBase {
Future runTests() async {
await testSharable();
await testSharable2();
await testCopyableClosures();
}

Future testSharable() async {
print('testSharable');
final sharableObjectsCopy = await sendReceive([
...sharableObjects,
]);
Expand All @@ -59,6 +123,7 @@ class SendReceiveTest extends SendReceiveTestBase {
}

Future testSharable2() async {
print('testSharable2');
final sharableObjectsCopy = await sendReceive([
notAllocatableInTLAB,
...sharableObjects,
Expand All @@ -70,6 +135,27 @@ class SendReceiveTest extends SendReceiveTestBase {
Expect.identical(sharableObjects[i], sharableObjectsCopy[i + 1]);
}
}

Future testCopyableClosures() async {
print('testCopyableClosures');
final copy = await sendReceive([
notAllocatableInTLAB,
...copyableClosures,
]);
for (int i = 0; i < copyableClosures.length; ++i) {
Expect.notIdentical(copyableClosures[i], copy[1 + i]);
Expect.equals(copyableClosures[i].runtimeType, copy[1 + i].runtimeType);
}

final copy2 = await sendReceive([
...copyableClosures,
notAllocatableInTLAB,
]);
for (int i = 0; i < copyableClosures.length; ++i) {
Expect.notIdentical(copyableClosures[i], copy2[i]);
Expect.equals(copyableClosures[i].runtimeType, copy2[i].runtimeType);
}
}
}

main() async {
Expand Down
38 changes: 37 additions & 1 deletion runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import 'dart:typed_data';

import 'package:expect/expect.dart';

class ClassWithNativeFields extends NativeFieldWrapperClass1 {}
class ClassWithNativeFields extends NativeFieldWrapperClass1 {
void m() {}
}

final Uint8List largeExternalTypedData =
File(Platform.resolvedExecutable).readAsBytesSync()..[0] = 42;
Expand Down Expand Up @@ -223,6 +225,8 @@ class SendReceiveTest extends SendReceiveTestBase {
await testSlowOnly();

await testWeakProperty();

await testForbiddenClosures();
}

Future testTransferrable() async {
Expand Down Expand Up @@ -646,6 +650,38 @@ class SendReceiveTest extends SendReceiveTestBase {
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
}
}

Future testForbiddenClosures() async {
print('testForbiddenClosures');
final nonCopyableClosures = <dynamic>[
(() {
final a = ClassWithNativeFields();
return a.m;
})(),
(() {
final a = ClassWithNativeFields();
dynamic inner() => a;
return inner;
})(),
(() {
foo(var arg) {
return () => arg;
}

return foo(ClassWithNativeFields());
})(),
];
for (final closure in nonCopyableClosures) {
Expect.throwsArgumentError(() => sendPort.send(closure));
}
for (final closure in nonCopyableClosures) {
Expect.throwsArgumentError(() => sendPort.send([closure]));
}
for (final closure in nonCopyableClosures) {
Expect.throwsArgumentError(
() => sendPort.send([notAllocatableInTLAB, closure]));
}
}
}

main() async {
Expand Down
Loading

0 comments on commit 26ef8f6

Please sign in to comment.