[vm] Preserve ThreadLocal value if dart::Thread is reclaimed

dart::Thread object representing Isolate's mutator can be reclaimed when
thread is suspended, so we need to preserve thread_locals on Isolate
itself.

TEST=vm/dart/thread_local_test

Fixes https://github.com/dart-lang/sdk/issues/63408

Change-Id: I7502b9bc67a07fb2e82479d3052740516a6a6964
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/504921
Commit-Queue: Slava Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Slava Egorov
2026-05-20 06:21:29 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent cbb94bc140
commit bc16ca4d51
6 changed files with 41 additions and 6 deletions
@@ -0,0 +1,32 @@
// Copyright (c) 2025, 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.
// Tests ThreadLocal.
//
// VMOptions=--experimental-shared-data
import 'dart:isolate';
import 'dart:_vm' show ThreadLocal;
import 'package:expect/expect.dart';
import 'package:expect/async_helper.dart';
@pragma('vm:shared')
final ThreadLocal<String> threadLocal = ThreadLocal<String>();
void main() async {
asyncStart();
// Make sure that threadLocal retains its value even if underlying
// dart::Thread gets reclaimed and recreated.
final results = await List.generate(
64,
(_) => Isolate.run(() async {
threadLocal.value = "ok";
await Future.delayed(const Duration(milliseconds: 10));
return threadLocal.hasValue ? threadLocal.value : "fail";
}),
).wait;
Expect.listEquals(['ok'], results.toSet().toList());
asyncEnd();
}