diff --git a/runtime/tests/vm/dart/thread_local_test.dart b/runtime/tests/vm/dart/thread_local_test.dart new file mode 100644 index 00000000000..d651af15ddc --- /dev/null +++ b/runtime/tests/vm/dart/thread_local_test.dart @@ -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 threadLocal = ThreadLocal(); + +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(); +} diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc index 9727b41e48a..fdc1d793eeb 100644 --- a/runtime/vm/app_snapshot.cc +++ b/runtime/vm/app_snapshot.cc @@ -7546,7 +7546,6 @@ class ProgramDeserializationRoots : public DeserializationRoots { if (Snapshot::IncludesCode(d->kind())) { d->thread()->InitVMConstants(); - d->thread()->set_thread_locals(Array::empty_array()); } auto object_store = isolate_group->object_store(); diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc index f5f6a4d11b2..fad9553493c 100644 --- a/runtime/vm/dart.cc +++ b/runtime/vm/dart.cc @@ -791,7 +791,6 @@ char* Dart::InitializeIsolateGroup(Thread* T, ICData::Init(); StubCode::Init(); T->InitVMConstants(); - T->set_thread_locals(Array::empty_array()); Symbols::Init(IG); Object::FinishInit(IG); Api::InitHandles(); @@ -857,6 +856,8 @@ ErrorPtr Dart::InitializeIsolate(Thread* T, I->field_table()->MarkReadyToUse(); } + T->set_thread_locals(Object::empty_array()); + const auto& error = Error::Handle(Z, I->isolate_object_store()->PreallocateObjects()); if (!error.IsNull()) { diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h index 5a59b3aa33e..716ebee7723 100644 --- a/runtime/vm/object_store.h +++ b/runtime/vm/object_store.h @@ -225,6 +225,7 @@ class ObjectPointerVisitor; #define ISOLATE_OBJECT_STORE_FIELD_LIST(R_, RW) \ R_(Array, dart_args_1) \ R_(Array, dart_args_2) \ + RW(Array, thread_locals) \ R_(GrowableObjectArray, resume_capabilities) \ R_(GrowableObjectArray, exit_listeners) \ R_(GrowableObjectArray, error_listeners) diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc index c93d3c3eb51..0621e147621 100644 --- a/runtime/vm/thread.cc +++ b/runtime/vm/thread.cc @@ -279,6 +279,9 @@ void Thread::set_default_tag(const UserTag& tag) { } void Thread::set_thread_locals(const Array& thread_locals) { + if (isolate_ != nullptr) { + isolate_->isolate_object_store()->set_thread_locals(thread_locals); + } thread_locals_ = thread_locals.ptr(); } @@ -436,10 +439,6 @@ void Thread::EnterIsolate(Isolate* isolate) { /*bypass_safepoint=*/false); thread->SetupMutatorState(); thread->SetupDartMutatorState(isolate); - - if (Array::empty_array().ptr() != nullptr) { - thread->set_thread_locals(Array::empty_array()); - } } isolate->scheduled_mutator_thread_ = thread; @@ -1643,6 +1642,7 @@ void Thread::ResetMutatorState() { void Thread::SetupDartMutatorState(Isolate* isolate) { field_table_values_ = isolate->field_table_->table(); + thread_locals_ = isolate->isolate_object_store()->thread_locals(); SetupDartMutatorStateDependingOnSnapshot(isolate->group()); } @@ -1682,6 +1682,7 @@ void Thread::ResetDartMutatorState() { field_table_values_ = nullptr; shared_field_table_values_ = nullptr; + thread_locals_ = Array::null(); ONLY_IN_PRECOMPILED(global_object_pool_ = ObjectPool::null()); ONLY_IN_PRECOMPILED(dispatch_table_array_ = nullptr); } diff --git a/runtime/vm/thread_registry.cc b/runtime/vm/thread_registry.cc index 9995b2a6c2c..b98474f0554 100644 --- a/runtime/vm/thread_registry.cc +++ b/runtime/vm/thread_registry.cc @@ -178,6 +178,7 @@ void ThreadRegistry::ReturnToFreelistLocked(Thread* thread) { ASSERT(thread->isolate_group_ == nullptr); ASSERT(thread->field_table_values_ == nullptr); ASSERT(thread->shared_field_table_values_ == nullptr); + ASSERT(thread->thread_locals_ == Array::null()); ASSERT(threads_lock()->IsOwnedByCurrentThread()); // Add thread to the free list. thread->next_ = free_list_;