[test] Make tests more reliably detect races under TSAN.

Bug: https://github.com/dart-lang/sdk/issues/61974
Change-Id: I27ad40af61becc558a8d1650a7f45a1484aecc0b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/464600
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Ryan Macnak
2025-11-25 17:06:20 -08:00
committed by Commit Queue
parent 7faf4969e8
commit 85f97d54ee
4 changed files with 111 additions and 52 deletions
@@ -2,7 +2,7 @@
// 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=--experimental-shared-data
// VMOptions=--experimental-shared-data --no-osr --no-background-compilation
import "dart:ffi";
import "dart:io";
@@ -15,29 +15,31 @@ import '../dylib_utils.dart';
@pragma("vm:shared")
List<dynamic>? box;
@pragma("vm:never-inline")
noopt() {}
@pragma("vm:never-inline")
dataRaceFromMain() {
final localBox = box!;
for (var i = 0; i < 1000000; i++) {
localBox[0] += 1;
noopt();
for (var i = 0; i < 50000; i++) {
usleep(100);
var t = localBox[0];
usleep(100);
localBox[0] = t + 1;
}
}
@pragma("vm:never-inline")
dataRaceFromChild() {
final localBox = box!;
for (var i = 0; i < 1000000; i++) {
localBox[0] += 1;
noopt();
for (var i = 0; i < 50000; i++) {
usleep(100);
var t = localBox[0];
usleep(100);
localBox[0] = t + 1;
}
}
child(_) {
child(replyPort) {
dataRaceFromChild();
replyPort.send(null);
}
final nativeLib = dlopenPlatformSpecific('ffi_test_functions');
@@ -53,6 +55,13 @@ final setFfiNativeResolverForTest = nativeLib
@Native<IntPtr Function(Handle, Handle, Handle)>(symbol: 'UnsafeSetSharedTo')
external int unsafeSetSharedTo(Object library_name, String name, Object value);
// Leaf: we don't want the two threads to synchronize via safepoint.
final usleep = DynamicLibrary.process()
.lookupFunction<Void Function(Long), void Function(int)>(
'usleep',
isLeaf: true,
);
main(List<String> arguments) {
if (arguments.contains("--testee")) {
setFfiNativeResolverForTest(getRootLibraryUrl());
@@ -60,8 +69,13 @@ main(List<String> arguments) {
// Still we want to use it here to test data race detection.
unsafeSetSharedTo(getRootLibraryUrl(), "box", List<dynamic>.filled(1, 0));
print(box); // side effect initialization
Isolate.spawn(child, null);
// Avoid synchronizing via lazy compilation and initialization.
usleep(0);
box![0] += 0;
var port = new RawReceivePort();
port.handler = (_) => port.close();
Isolate.spawn(child, port.sendPort);
dataRaceFromMain();
return;
}
@@ -2,7 +2,7 @@
// 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=--experimental-shared-data
// VMOptions=--experimental-shared-data --no-osr --no-background-compilation
import "dart:ffi";
import "dart:io";
@@ -20,29 +20,31 @@ class Box {
@pragma("vm:shared")
Box? box;
@pragma("vm:never-inline")
noopt() {}
@pragma("vm:never-inline")
dataRaceFromMain() {
final localBox = box!;
for (var i = 0; i < 1000000; i++) {
localBox.foo += 1;
noopt();
for (var i = 0; i < 50000; i++) {
usleep(100);
var t = localBox.foo;
usleep(100);
localBox.foo = t + 1;
}
}
@pragma("vm:never-inline")
dataRaceFromChild() {
final localBox = box!;
for (var i = 0; i < 1000000; i++) {
localBox.foo += 1;
noopt();
for (var i = 0; i < 50000; i++) {
usleep(100);
var t = localBox.foo;
usleep(100);
localBox.foo = t + 1;
}
}
child(_) {
child(replyPort) {
dataRaceFromChild();
replyPort.send(null);
}
final nativeLib = dlopenPlatformSpecific('ffi_test_functions');
@@ -58,12 +60,25 @@ final setFfiNativeResolverForTest = nativeLib
@Native<IntPtr Function(Handle, Handle, Handle)>(symbol: 'UnsafeSetSharedTo')
external int unsafeSetSharedTo(Object library_name, String name, Object value);
// Leaf: we don't want the two threads to synchronize via safepoint.
final usleep = DynamicLibrary.process()
.lookupFunction<Void Function(Long), void Function(int)>(
'usleep',
isLeaf: true,
);
main(List<String> arguments) {
if (arguments.contains("--testee")) {
setFfiNativeResolverForTest(getRootLibraryUrl());
unsafeSetSharedTo(getRootLibraryUrl(), "box", Box());
print(box); // side effect initialization
Isolate.spawn(child, null);
// Avoid synchronizing via lazy compilation.
usleep(0);
box!.foo += 0;
var port = new RawReceivePort();
port.handler = (_) => port.close();
Isolate.spawn(child, port.sendPort);
dataRaceFromMain();
return;
}
@@ -2,7 +2,7 @@
// 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=--experimental-shared-data
// VMOptions=--experimental-shared-data --no-osr --no-background-compilation
import "dart:ffi";
import "dart:io";
@@ -19,24 +19,25 @@ class Box {
@pragma("vm:shared")
Box? box;
@pragma("vm:never-inline")
noopt() {}
@pragma("vm:never-inline")
dataRaceFromMain() {
final localBox = box!;
for (var i = 0; i < 1000000; i++) {
localBox.foo += 1;
noopt();
for (var i = 0; i < 50000; i++) {
usleep(100);
var t = localBox.foo;
usleep(100);
localBox.foo = t + 1;
}
}
@pragma("vm:never-inline")
dataRaceFromChild() {
final localBox = box!;
for (var i = 0; i < 1000000; i++) {
localBox.foo += 1;
noopt();
for (var i = 0; i < 50000; i++) {
usleep(100);
var t = localBox.foo;
usleep(100);
localBox.foo = t + 1;
}
}
@@ -52,8 +53,9 @@ dataRaceFromChildCaller() => dataRaceFromChild();
@pragma("vm:never-inline")
dataRaceFromChildCallerCaller() => dataRaceFromChildCaller();
child(_) {
child(replyPort) {
dataRaceFromChildCallerCaller();
replyPort.send(null);
}
final nativeLib = dlopenPlatformSpecific('ffi_test_functions');
@@ -69,14 +71,27 @@ final setFfiNativeResolverForTest = nativeLib
@Native<IntPtr Function(Handle, Handle, Handle)>(symbol: 'UnsafeSetSharedTo')
external int unsafeSetSharedTo(Object library_name, String name, Object value);
// Leaf: we don't want the two threads to synchronize via safepoint.
final usleep = DynamicLibrary.process()
.lookupFunction<Void Function(Long), void Function(int)>(
'usleep',
isLeaf: true,
);
main(List<String> arguments) {
if (arguments.contains("--testee")) {
setFfiNativeResolverForTest(getRootLibraryUrl());
// At this point List is not allowed to be stored in shaded fields.
// Still we want to use it here to test data race detection.
unsafeSetSharedTo(getRootLibraryUrl(), "box", Box());
print(box); // side effect initialization
Isolate.spawn(child, null);
// Avoid synchronizing via lazy compilation.
usleep(0);
box!.foo += 0;
var port = new RawReceivePort();
port.handler = (_) => port.close();
Isolate.spawn(child, port.sendPort);
dataRaceFromMainCallerCaller();
return;
}
@@ -2,8 +2,9 @@
// 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=--experimental-shared-data
// VMOptions=--experimental-shared-data --no-osr --no-background-compilation
import "dart:ffi";
import "dart:io";
import "dart:isolate";
import "dart:typed_data";
@@ -12,35 +13,49 @@ import "package:expect/expect.dart";
@pragma("vm:shared")
Uint8List box = Uint8List(1);
@pragma("vm:never-inline")
noopt() {}
@pragma("vm:never-inline")
dataRaceFromMain() {
final localBox = box;
for (var i = 0; i < 10000000; i++) {
localBox[0] += 1;
noopt();
for (var i = 0; i < 50000; i++) {
usleep(100);
var t = localBox[0];
usleep(100);
localBox[0] = t + 1;
}
}
@pragma("vm:never-inline")
dataRaceFromChild() {
final localBox = box;
for (var i = 0; i < 10000000; i++) {
localBox[0] += 1;
noopt();
for (var i = 0; i < 50000; i++) {
usleep(100);
var t = localBox[0];
usleep(100);
localBox[0] = t + 1;
}
}
child(_) {
child(replyPort) {
dataRaceFromChild();
replyPort.send(null);
}
// Leaf: we don't want the two threads to synchronize via safepoint.
final usleep = DynamicLibrary.process()
.lookupFunction<Void Function(Long), void Function(int)>(
'usleep',
isLeaf: true,
);
main(List<String> arguments) {
if (arguments.contains("--testee")) {
print(box); // side effect initialization
Isolate.spawn(child, null);
// Avoid synchronizing via lazy compilation and initialization.
usleep(0);
box![0] += 0;
var port = new RawReceivePort();
port.handler = (_) => port.close();
Isolate.spawn(child, port.sendPort);
dataRaceFromMain();
return;
}