[vm/shared] Introduce ScopedThreadLocal to allow keeping state on
thread, outside of isolate, on isolate-group bound callback. Fixes https://github.com/dart-lang/sdk/issues/61523 TEST=scoped_thread_local_test Change-Id: I0c8280d2808d940a5fb0d628db191932400a5edb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/451402 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
committed by
Commit Queue
parent
438da65eb3
commit
de77c6b766
@@ -7,11 +7,12 @@ library from "org-dartlang-test:///main.dart" as main {
|
||||
static method foo() → dynamic {}
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
@@ -14,11 +14,12 @@ library from "org-dartlang-test:///main.dart" as main {
|
||||
static method foo() → dynamic {}
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
@@ -26,10 +26,11 @@ library from "org-dartlang-test:///main.dart" as main {
|
||||
}
|
||||
}
|
||||
|
||||
And 19 platform libraries:
|
||||
And 20 platform libraries:
|
||||
- dart:_builtin
|
||||
- dart:_compact_hash
|
||||
- dart:_internal
|
||||
- dart:_vm
|
||||
- dart:_vmservice
|
||||
- dart:async
|
||||
- dart:cli
|
||||
|
||||
@@ -8,11 +8,12 @@ library from "org-dartlang-test:///main.dart" as main {
|
||||
return 42;
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
@@ -8,11 +8,12 @@ library from "org-dartlang-test:///main.dart" as main {
|
||||
return 42;
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
+2
-1
@@ -16,11 +16,12 @@ library from "org-dartlang-test:///main.dart" as main {
|
||||
}
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
+2
-1
@@ -16,11 +16,12 @@ library from "org-dartlang-test:///main.dart" as main {
|
||||
}
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
@@ -6,11 +6,12 @@ library from "org-dartlang-test:///main.dart" as main {
|
||||
}
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
@@ -14,11 +14,12 @@ library from "org-dartlang-test:///main.dart" as main {
|
||||
}
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
@@ -14,11 +14,12 @@ library from "org-dartlang-test:///main.dart" as main {
|
||||
}
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
@@ -27,11 +27,12 @@ constants {
|
||||
#C1 = 0
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
@@ -29,11 +29,12 @@ constants {
|
||||
#C1 = 0
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
@@ -27,11 +27,12 @@ constants {
|
||||
#C1 = 0
|
||||
}
|
||||
|
||||
And 20 platform libraries:
|
||||
And 21 platform libraries:
|
||||
- dart:_http
|
||||
- dart:_builtin
|
||||
- dart:vmservice_io
|
||||
- dart:_compact_hash
|
||||
- dart:_vm
|
||||
- dart:async
|
||||
- dart:cli
|
||||
- dart:collection
|
||||
|
||||
@@ -30,6 +30,7 @@ class DartRunnerTarget extends VmTarget {
|
||||
'dart:mirrors',
|
||||
|
||||
'dart:typed_data',
|
||||
'dart:_vm',
|
||||
'dart:nativewrappers',
|
||||
'dart:io',
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ class FlutterTarget extends VmTarget {
|
||||
'dart:mirrors',
|
||||
|
||||
'dart:typed_data',
|
||||
'dart:_vm',
|
||||
'dart:nativewrappers',
|
||||
'dart:io',
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ class FlutterRunnerTarget extends VmTarget {
|
||||
'dart:mirrors',
|
||||
|
||||
'dart:typed_data',
|
||||
'dart:_vm',
|
||||
'dart:nativewrappers',
|
||||
'dart:io',
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ class VmTarget extends Target {
|
||||
'dart:mirrors',
|
||||
|
||||
'dart:typed_data',
|
||||
'dart:_vm',
|
||||
'dart:vmservice_io',
|
||||
'dart:_vmservice',
|
||||
'dart:_builtin',
|
||||
|
||||
@@ -139,6 +139,16 @@ class DeeplyImmutableValidator {
|
||||
}
|
||||
}
|
||||
|
||||
if (node.name == 'ScopedThreadLocal') {
|
||||
final uri = node.enclosingLibrary.importUri;
|
||||
if (uri.isScheme('dart') && uri.path == '_vm') {
|
||||
// ScopedThreadLocal has non-deeply-immutable initializer,
|
||||
// but we allow it.
|
||||
// TODO(dartbug.com/61962): remove this once the bug is fixed.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// All instance fields should be non-late final and deeply immutable.
|
||||
for (final field in node.fields) {
|
||||
if (field.isStatic) {
|
||||
|
||||
@@ -10,6 +10,7 @@ LoadingUnit(id=1, parent=0,
|
||||
dart:_builtin
|
||||
dart:_vmservice
|
||||
dart:vmservice_io
|
||||
dart:_vm
|
||||
dart:typed_data
|
||||
dart:mirrors
|
||||
dart:math
|
||||
|
||||
@@ -31,6 +31,7 @@ class Builtin {
|
||||
kIOLibrary,
|
||||
kHttpLibrary,
|
||||
kCLILibrary,
|
||||
kVMLibrary,
|
||||
};
|
||||
|
||||
// Setup native resolver method built in library specified in 'id'.
|
||||
|
||||
@@ -337,6 +337,7 @@ class DartUtils {
|
||||
static constexpr const char* kCLILibPatchURL = "dart:cli-patch";
|
||||
static constexpr const char* kUriLibURL = "dart:uri";
|
||||
static constexpr const char* kHttpScheme = "http:";
|
||||
static constexpr const char* kVMLibURL = "dart:_vm";
|
||||
static constexpr const char* kVMServiceLibURL = "dart:vmservice";
|
||||
|
||||
static void SetEnvironment(dart::SimpleHashMap* environment);
|
||||
|
||||
@@ -1278,6 +1278,84 @@ DEFINE_NATIVE_ENTRY(Isolate_sendOOB, 0, 2) {
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
static void EnsureThreadLocalsTableExistsAndBigEnough(Thread* thread,
|
||||
intptr_t index) {
|
||||
GrowableObjectArray& locals =
|
||||
GrowableObjectArray::Handle(thread->thread_locals());
|
||||
if (locals.IsNull()) {
|
||||
locals = GrowableObjectArray::New();
|
||||
thread->set_thread_locals(locals);
|
||||
}
|
||||
if (index >= locals.Length()) {
|
||||
locals.Grow(index + 1);
|
||||
intptr_t old_length = locals.Length();
|
||||
locals.SetLength(locals.Capacity());
|
||||
for (intptr_t i = old_length; i < locals.Capacity(); i++) {
|
||||
locals.SetAt(i, Object::sentinel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(ScopedThreadLocal_allocateId, 0, 0) {
|
||||
auto isolate_group = thread->isolate_group();
|
||||
isolate_group->increment_scoped_thread_locals_count();
|
||||
intptr_t new_index = isolate_group->scoped_thread_locals_count() - 1;
|
||||
EnsureThreadLocalsTableExistsAndBigEnough(thread, new_index);
|
||||
GrowableObjectArray& locals =
|
||||
GrowableObjectArray::Handle(thread->thread_locals());
|
||||
locals.SetAt(new_index, Object::sentinel());
|
||||
return Integer::New(new_index);
|
||||
}
|
||||
|
||||
static void ValidateScopedThreadLocalId(Thread* thread, intptr_t id) {
|
||||
if (id < 0 || id >= thread->isolate_group()->scoped_thread_locals_count()) {
|
||||
const String& msg = String::Handle(String::New("Invalid local id."));
|
||||
Exceptions::ThrowStateError(msg);
|
||||
UNREACHABLE();
|
||||
}
|
||||
EnsureThreadLocalsTableExistsAndBigEnough(thread, id);
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(ScopedThreadLocal_hasValue, 0, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, id_obj, arguments->NativeArgAt(0));
|
||||
intptr_t id = id_obj.Value();
|
||||
ValidateScopedThreadLocalId(thread, id);
|
||||
GrowableObjectArray& locals =
|
||||
GrowableObjectArray::Handle(thread->thread_locals());
|
||||
return locals.At(id) == Object::sentinel().ptr() ? Bool::False().ptr()
|
||||
: Bool::True().ptr();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(ScopedThreadLocal_getValue, 0, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, id_obj, arguments->NativeArgAt(0));
|
||||
intptr_t id = id_obj.Value();
|
||||
ValidateScopedThreadLocalId(thread, id);
|
||||
GrowableObjectArray& locals =
|
||||
GrowableObjectArray::Handle(thread->thread_locals());
|
||||
return locals.At(id);
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(ScopedThreadLocal_setValue, 0, 2) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, id_obj, arguments->NativeArgAt(0));
|
||||
intptr_t id = id_obj.Value();
|
||||
ValidateScopedThreadLocalId(thread, id);
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(1));
|
||||
GrowableObjectArray& locals =
|
||||
GrowableObjectArray::Handle(thread->thread_locals());
|
||||
locals.SetAt(id, value);
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(ScopedThreadLocal_clearValue, 0, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, id_obj, arguments->NativeArgAt(0));
|
||||
intptr_t id = id_obj.Value();
|
||||
ValidateScopedThreadLocalId(thread, id);
|
||||
GrowableObjectArray& locals =
|
||||
GrowableObjectArray::Handle(thread->thread_locals());
|
||||
locals.SetAt(id, Object::sentinel());
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
static void ExternalTypedDataFinalizer(void* isolate_callback_data,
|
||||
void* peer) {
|
||||
free(peer);
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
// 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 ScopedThreadLocal.
|
||||
//
|
||||
// VMOptions=--experimental-shared-data
|
||||
|
||||
import 'package:dart_internal/isolate_group.dart' show IsolateGroup;
|
||||
import 'dart:_vm' show ScopedThreadLocal;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
/// A collection used to identify cyclic lists during `toString` calls.
|
||||
@pragma('vm:shared')
|
||||
final toStringVisiting = ScopedThreadLocal<List<Object>>(() => <Object>[]);
|
||||
|
||||
/// Check if we are currently visiting [object] in a `toString` call.
|
||||
bool isToStringVisiting(List<Object> toStringVisitingValue, Object object) {
|
||||
for (int i = 0; i < toStringVisitingValue.length; i++) {
|
||||
if (identical(object, toStringVisitingValue[i])) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void _iterablePartsToStrings(Iterable<Object> iterable, List<String> parts) {
|
||||
Iterator<Object> it = iterable.iterator;
|
||||
// Initial run of elements, at least headCount, and then continue until
|
||||
// passing at most lengthLimit characters.
|
||||
while (it.moveNext()) {
|
||||
if (it.current is Iterable<Object>) {
|
||||
parts.add(iterableToShortString(it.current as Iterable<Object>));
|
||||
} else {
|
||||
parts.add("${it.current}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String iterableToShortString(
|
||||
Iterable<Object> iterable, [
|
||||
String leftDelimiter = '(',
|
||||
String rightDelimiter = ')',
|
||||
]) {
|
||||
return toStringVisiting.runInitialized((toStringVisitingValue) {
|
||||
if (isToStringVisiting(toStringVisitingValue, iterable)) {
|
||||
return "$leftDelimiter...$rightDelimiter";
|
||||
}
|
||||
List<String> parts = <String>[];
|
||||
toStringVisitingValue.add(iterable);
|
||||
try {
|
||||
_iterablePartsToStrings(iterable, parts);
|
||||
} finally {
|
||||
assert(identical(toStringVisitingValue.last, iterable));
|
||||
toStringVisitingValue.removeLast();
|
||||
}
|
||||
return (StringBuffer(leftDelimiter)
|
||||
..writeAll(parts, ", ")
|
||||
..write(rightDelimiter))
|
||||
.toString();
|
||||
});
|
||||
}
|
||||
|
||||
@pragma('vm:shared')
|
||||
final threadName = ScopedThreadLocal<String>();
|
||||
|
||||
main() {
|
||||
Expect.isFalse(toStringVisiting.isBound);
|
||||
Expect.throws(
|
||||
() {
|
||||
IsolateGroup.runSync(() {
|
||||
var l = <Object>["foo", "bar"];
|
||||
l.add(l);
|
||||
l.add("baz");
|
||||
final isBoundBefore = toStringVisiting.isBound;
|
||||
final result = iterableToShortString(l);
|
||||
final isBoundAfter = toStringVisiting.isBound;
|
||||
throw "isBoundBefore: $isBoundBefore, $result, isBoundAfter: $isBoundAfter";
|
||||
});
|
||||
},
|
||||
(e) =>
|
||||
e.toString() ==
|
||||
"isBoundBefore: false, (foo, bar, (...), baz), isBoundAfter: false",
|
||||
);
|
||||
{
|
||||
Expect.isFalse(toStringVisiting.isBound);
|
||||
var l = <Object>["foo", "bar"];
|
||||
l.add(l);
|
||||
l.add("baz");
|
||||
Expect.equals("(foo, bar, (...), baz)", iterableToShortString(l));
|
||||
Expect.isFalse(toStringVisiting.isBound);
|
||||
}
|
||||
|
||||
Expect.equals(
|
||||
"123",
|
||||
IsolateGroup.runSync(() {
|
||||
return threadName.runWith("123", (s) {
|
||||
Expect.isTrue(threadName.isBound);
|
||||
return s;
|
||||
});
|
||||
}),
|
||||
);
|
||||
Expect.throws(
|
||||
() => IsolateGroup.runSync(() => threadName.value),
|
||||
(e) => e is StateError,
|
||||
);
|
||||
Expect.throws(() => threadName.value, (e) => e is StateError);
|
||||
Expect.equals(
|
||||
"name:123",
|
||||
threadName.runWith<String>("123", (String name) {
|
||||
Expect.isTrue(threadName.isBound);
|
||||
return "name:$name";
|
||||
}),
|
||||
);
|
||||
Expect.isFalse(threadName.isBound);
|
||||
}
|
||||
@@ -251,6 +251,7 @@ dart/isolates/thread_pool_test: SkipByDesign # https://dartbug.com/37299 Test us
|
||||
dart/reachability_test: SkipByDesign # Test takes too long on the simulator
|
||||
dart/regress_41971_test: SkipByDesign # https://dartbug.com/37299 dart:ffi is not supported on simulator
|
||||
dart/regress_b_216834909_test: SkipByDesign # No FFI on simulators
|
||||
dart/scoped_thread_local_test: SkipByDesign # No FFI on simulators
|
||||
dart/sdk_hash_test: SkipSlow # gen_kernel is slow to run on simarm
|
||||
dart/sendport_api_test: SkipByDesign # No FFI on simulators
|
||||
dart/shared_collect_all_garbage_test: SkipByDesign # No FFI on simulators
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import("//build_overrides/build.gni")
|
||||
import("../../build/executable_suffix.gni")
|
||||
import("../../sdk/lib/_vm/vm_sources.gni")
|
||||
import("../../sdk/lib/async/async_sources.gni")
|
||||
import("../../sdk/lib/collection/collection_sources.gni")
|
||||
import("../../sdk/lib/concurrent/concurrent_sources.gni")
|
||||
|
||||
@@ -141,6 +141,11 @@ void Bootstrap::SetupNativeResolver() {
|
||||
library.set_native_entry_symbol_resolver(symbol_resolver);
|
||||
library.set_ffi_native_resolver(ffi_native_resolver);
|
||||
|
||||
library = Library::VMLibrary();
|
||||
ASSERT(!library.IsNull());
|
||||
library.set_native_entry_resolver(resolver);
|
||||
library.set_native_entry_symbol_resolver(symbol_resolver);
|
||||
|
||||
library = Library::IsolateLibrary();
|
||||
ASSERT(!library.IsNull());
|
||||
library.set_native_entry_resolver(resolver);
|
||||
|
||||
@@ -281,6 +281,11 @@ namespace dart {
|
||||
V(Internal_allocateObjectInstructionsEnd, 0) \
|
||||
V(InvocationMirror_unpackTypeArguments, 2) \
|
||||
V(NoSuchMethodError_existingMethodSignature, 3) \
|
||||
V(ScopedThreadLocal_allocateId, 0) \
|
||||
V(ScopedThreadLocal_clearValue, 1) \
|
||||
V(ScopedThreadLocal_getValue, 1) \
|
||||
V(ScopedThreadLocal_hasValue, 1) \
|
||||
V(ScopedThreadLocal_setValue, 2) \
|
||||
V(Uri_isWindowsPlatform, 0) \
|
||||
V(UserTag_new, 2) \
|
||||
V(UserTag_label, 1) \
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace dart {
|
||||
SuspendState_setErrorCallback, 0xd5e77404) \
|
||||
V(AsyncLibrary, _SuspendState, _clone, SuspendState_clone, 0x751294d7) \
|
||||
V(AsyncLibrary, _SuspendState, _resume, SuspendState_resume, 0x48d39768) \
|
||||
V(ConvertLibrary, _Utf8Decoder, _scan, Utf8DecoderScan, 0x903cbc3e) \
|
||||
V(ConvertLibrary, _Utf8Decoder, _scan, Utf8DecoderScan, 0x3948ffb0) \
|
||||
V(CoreLibrary, ::, identical, ObjectIdentical, 0x03f96b55) \
|
||||
V(CoreLibrary, Object, Object., ObjectConstructor, 0xab6d6cf2) \
|
||||
V(CoreLibrary, _Array, [], ObjectArrayGetIndexed, 0x78d7e092) \
|
||||
@@ -233,18 +233,18 @@ namespace dart {
|
||||
0x56ff3b70) \
|
||||
V(MathLibrary, ::, min, MathMin, 0x63eb7469) \
|
||||
V(MathLibrary, ::, max, MathMax, 0xf9320c82) \
|
||||
V(MathLibrary, ::, _doublePow, MathDoublePow, 0x424e2227) \
|
||||
V(MathLibrary, ::, _intPow, MathIntPow, 0x9a0d648c) \
|
||||
V(MathLibrary, ::, _sin, MathSin, 0x101882d8) \
|
||||
V(MathLibrary, ::, _cos, MathCos, 0xf91585da) \
|
||||
V(MathLibrary, ::, _tan, MathTan, 0xf720c4ea) \
|
||||
V(MathLibrary, ::, _asin, MathAsin, 0xfe7986cb) \
|
||||
V(MathLibrary, ::, _acos, MathAcos, 0x174c6974) \
|
||||
V(MathLibrary, ::, _atan, MathAtan, 0x1ae3f717) \
|
||||
V(MathLibrary, ::, _atan2, MathAtan2, 0x531004a9) \
|
||||
V(MathLibrary, ::, _sqrt, MathSqrt, 0x1f167f7a) \
|
||||
V(MathLibrary, ::, _exp, MathExp, 0x02565a46) \
|
||||
V(MathLibrary, ::, _log, MathLog, 0x106c0978) \
|
||||
V(MathLibrary, ::, _doublePow, MathDoublePow, 0x41e7435b) \
|
||||
V(MathLibrary, ::, _intPow, MathIntPow, 0x831c7f0a) \
|
||||
V(MathLibrary, ::, _sin, MathSin, 0x1e9ad4ea) \
|
||||
V(MathLibrary, ::, _cos, MathCos, 0xf75f2165) \
|
||||
V(MathLibrary, ::, _tan, MathTan, 0xfb847fa8) \
|
||||
V(MathLibrary, ::, _asin, MathAsin, 0xf02d1b9e) \
|
||||
V(MathLibrary, ::, _acos, MathAcos, 0xeda3de2d) \
|
||||
V(MathLibrary, ::, _atan, MathAtan, 0xfc58e253) \
|
||||
V(MathLibrary, ::, _atan2, MathAtan2, 0x751307ef) \
|
||||
V(MathLibrary, ::, _sqrt, MathSqrt, 0x1cadc5cf) \
|
||||
V(MathLibrary, ::, _exp, MathExp, 0x0c99a26a) \
|
||||
V(MathLibrary, ::, _log, MathLog, 0xef163aae) \
|
||||
V(NativeWrappersLibrary, ::, _getNativeField, GetNativeField, 0x8a67a22d) \
|
||||
V(TypedDataLibrary, _Int8List, [], Int8ArrayGetIndexed, 0x23133682) \
|
||||
V(TypedDataLibrary, _ExternalInt8Array, [], ExternalInt8ArrayGetIndexed, \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -853,6 +853,11 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
|
||||
GrowableObjectArrayPtr tag_table() const { return tag_table_; }
|
||||
void set_tag_table(const GrowableObjectArray& value);
|
||||
|
||||
intptr_t scoped_thread_locals_count() { return scoped_thread_locals_count_; }
|
||||
intptr_t increment_scoped_thread_locals_count() {
|
||||
return scoped_thread_locals_count_.fetch_add(1u, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Dart; // For `object_store_ = ` in Dart::Init
|
||||
friend class Heap;
|
||||
@@ -1013,6 +1018,8 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
|
||||
|
||||
SafepointRwLock tag_table_lock_;
|
||||
GrowableObjectArrayPtr tag_table_;
|
||||
|
||||
std::atomic<intptr_t> scoped_thread_locals_count_ = 0;
|
||||
};
|
||||
|
||||
// When an isolate sends-and-exits this class represent things that it passed
|
||||
|
||||
+16
-3
@@ -2532,6 +2532,14 @@ ErrorPtr Object::Init(IsolateGroup* isolate_group,
|
||||
pending_classes.Add(cls);
|
||||
RegisterClass(cls, Symbols::FinalizerEntry(), lib);
|
||||
|
||||
lib = Library::LookupLibrary(thread, Symbols::DartVM());
|
||||
if (lib.IsNull()) {
|
||||
lib = Library::NewLibraryHelper(Symbols::DartVM(), true);
|
||||
lib.SetLoadRequested();
|
||||
lib.Register(thread);
|
||||
}
|
||||
object_store->set_bootstrap_library(ObjectStore::kVM, lib);
|
||||
|
||||
// Finish the initialization by compiling the bootstrap scripts containing
|
||||
// the base interfaces and the implementation of the internal classes.
|
||||
const Error& error = Error::Handle(
|
||||
@@ -15640,6 +15648,10 @@ LibraryPtr Library::InternalLibrary() {
|
||||
return IsolateGroup::Current()->object_store()->_internal_library();
|
||||
}
|
||||
|
||||
LibraryPtr Library::VMLibrary() {
|
||||
return IsolateGroup::Current()->object_store()->_vm_library();
|
||||
}
|
||||
|
||||
LibraryPtr Library::IsolateLibrary() {
|
||||
return IsolateGroup::Current()->object_store()->isolate_library();
|
||||
}
|
||||
@@ -26631,13 +26643,14 @@ const char* ExternalTypedData::ToCString() const {
|
||||
PointerPtr Pointer::New(uword native_address, Heap::Space space) {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
IsolateGroup* isolate_group = thread->isolate_group();
|
||||
|
||||
const auto& type_args = TypeArguments::Handle(
|
||||
zone, IsolateGroup::Current()->object_store()->type_argument_never());
|
||||
zone, isolate_group->object_store()->type_argument_never());
|
||||
|
||||
const Class& cls =
|
||||
Class::Handle(IsolateGroup::Current()->class_table()->At(kPointerCid));
|
||||
cls.EnsureIsAllocateFinalized(Thread::Current());
|
||||
Class::Handle(isolate_group->class_table()->At(kPointerCid));
|
||||
cls.EnsureIsAllocateFinalized(thread);
|
||||
|
||||
const auto& result = Pointer::Handle(zone, Object::Allocate<Pointer>(space));
|
||||
result.SetTypeArguments(type_args);
|
||||
|
||||
@@ -5439,6 +5439,7 @@ class Library : public Object {
|
||||
static LibraryPtr DeveloperLibrary();
|
||||
static LibraryPtr FfiLibrary();
|
||||
static LibraryPtr InternalLibrary();
|
||||
static LibraryPtr VMLibrary();
|
||||
static LibraryPtr IsolateLibrary();
|
||||
static LibraryPtr MathLibrary();
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
@@ -29,6 +29,7 @@ class ObjectPointerVisitor;
|
||||
M(Math, math) \
|
||||
M(Mirrors, mirrors) \
|
||||
M(TypedData, typed_data) \
|
||||
M(VM, _vm) \
|
||||
M(VMService, _vmservice) \
|
||||
M(Concurrent, concurrent)
|
||||
|
||||
@@ -162,6 +163,7 @@ class ObjectPointerVisitor;
|
||||
RW(Library, native_wrappers_library) \
|
||||
RW(Library, root_library) \
|
||||
RW(Library, typed_data_library) \
|
||||
RW(Library, _vm_library) \
|
||||
RW(Library, _vmservice_library) \
|
||||
RW(Library, native_assets_library) \
|
||||
RW(Array, native_assets_map) \
|
||||
|
||||
@@ -74,6 +74,7 @@ class ObjectPointerVisitor;
|
||||
V(DartScheme, "dart:") \
|
||||
V(DartSchemePrivate, "dart:_") \
|
||||
V(DartTypedData, "dart:typed_data") \
|
||||
V(DartVM, "dart:_vm") \
|
||||
V(DartVMProduct, "dart.vm.product") \
|
||||
V(DartVMService, "dart:_vmservice") \
|
||||
V(DebugProcedureName, ":Eval") \
|
||||
|
||||
@@ -96,6 +96,7 @@ Thread::Thread(bool is_vm_isolate)
|
||||
thread_lock_(),
|
||||
reusable_handles_(),
|
||||
sticky_error_(Error::null()),
|
||||
thread_locals_(GrowableObjectArray::null()),
|
||||
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_INITIALIZERS)
|
||||
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_SCOPE_INIT)
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
|
||||
@@ -267,6 +268,10 @@ void Thread::set_default_tag(const UserTag& tag) {
|
||||
default_tag_ = tag.ptr();
|
||||
}
|
||||
|
||||
void Thread::set_thread_locals(const GrowableObjectArray& thread_locals) {
|
||||
thread_locals_ = thread_locals.ptr();
|
||||
}
|
||||
|
||||
ErrorPtr Thread::StealStickyError() {
|
||||
NoSafepointScope no_safepoint;
|
||||
ErrorPtr return_value = sticky_error_;
|
||||
@@ -357,6 +362,7 @@ void Thread::AssertEmptyThreadInvariants() {
|
||||
|
||||
ASSERT(default_tag_ == UserTag::null());
|
||||
ASSERT(current_tag_ == UserTag::null());
|
||||
ASSERT(thread_locals_ == GrowableObjectArray::null());
|
||||
|
||||
// Avoid running these asserts for `vm-isolate`.
|
||||
if (active_stacktrace_.untag() != 0) {
|
||||
@@ -768,6 +774,7 @@ void Thread::FreeActiveThread(Thread* thread,
|
||||
thread->ResetStateLocked();
|
||||
thread->current_tag_ = UserTag::null();
|
||||
thread->default_tag_ = UserTag::null();
|
||||
thread->thread_locals_ = GrowableObjectArray::null();
|
||||
|
||||
thread->AssertEmptyThreadInvariants();
|
||||
thread_registry->ReturnThreadLocked(thread);
|
||||
@@ -1151,6 +1158,7 @@ void Thread::VisitObjectPointers(ObjectPointerVisitor* visitor,
|
||||
|
||||
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(¤t_tag_));
|
||||
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&default_tag_));
|
||||
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&thread_locals_));
|
||||
}
|
||||
|
||||
class RestoreWriteBarrierInvariantVisitor : public ObjectPointerVisitor {
|
||||
|
||||
@@ -1383,6 +1383,9 @@ class Thread : public ThreadState, public IntrusiveDListEntry<Thread> {
|
||||
|
||||
static void VisitMutators(MutatorThreadVisitor* visitor);
|
||||
|
||||
GrowableObjectArrayPtr thread_locals() const { return thread_locals_; }
|
||||
void set_thread_locals(const GrowableObjectArray& thread_locals);
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
T* AllocateReusableHandle();
|
||||
@@ -1576,6 +1579,8 @@ class Thread : public ThreadState, public IntrusiveDListEntry<Thread> {
|
||||
return shared_field_table_values_;
|
||||
}
|
||||
|
||||
GrowableObjectArrayPtr thread_locals_ = nullptr;
|
||||
|
||||
// Reusable handles support.
|
||||
#define REUSABLE_HANDLE_FIELDS(object) object* object##_handle_;
|
||||
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_FIELDS)
|
||||
|
||||
@@ -229,6 +229,7 @@ _full_sdk_libraries = [
|
||||
"mirrors",
|
||||
"svg",
|
||||
"typed_data",
|
||||
"_vm",
|
||||
"_wasm",
|
||||
"web_audio",
|
||||
"web_gl",
|
||||
|
||||
@@ -193,6 +193,11 @@ const Map<String, LibraryInfo> libraries = const {
|
||||
maturity: Maturity.WEB_STABLE,
|
||||
platforms: DART2JS_PLATFORM,
|
||||
),
|
||||
'_vm': const LibraryInfo(
|
||||
'_vm/_vm.dart',
|
||||
categories: 'Server',
|
||||
platforms: VM_PLATFORM,
|
||||
),
|
||||
'vmservice_io': const LibraryInfo(
|
||||
'_internal/vm/bin/vmservice_io.dart',
|
||||
categories: '',
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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.
|
||||
|
||||
library dart._vm;
|
||||
|
||||
import "dart:_internal" show unsafeCast;
|
||||
|
||||
@pragma("vm:deeply-immutable")
|
||||
@pragma('vm:entry-point')
|
||||
final class ScopedThreadLocal<T> {
|
||||
/// Creates scoped thread local value with the given [initializer] function.
|
||||
///
|
||||
/// [initializer] must be trivially shareable.
|
||||
ScopedThreadLocal([this._initializer]) : _id = _allocateId();
|
||||
|
||||
/// Execute [f] binding this [ScopedThreadLocal] to the given
|
||||
/// [value] for the duration of the execution.
|
||||
R runWith<R>(T value, R Function(T) f) {
|
||||
bool hadValue = _hasValue(_id);
|
||||
Object? previous_value = hadValue ? _getValue(_id) : null;
|
||||
_setValue(_id, value);
|
||||
R result = f(value);
|
||||
if (hadValue) {
|
||||
_setValue(_id, previous_value!);
|
||||
} else {
|
||||
_clearValue(_id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Execute [f] initializing this [ScopedThreadLocal] using default initializer if needed.
|
||||
/// Throws [StateError] if this [ScopedThreadLocal] does not have an initializer.
|
||||
R runInitialized<R>(R Function(T) f) {
|
||||
bool hadValue = _hasValue(_id);
|
||||
Object? previous_value = hadValue ? _getValue(_id) : null;
|
||||
late T v;
|
||||
if (!isBound) {
|
||||
if (_initializer == null) {
|
||||
throw StateError(
|
||||
"No initializer was provided for this ScopedThreadLocal.",
|
||||
);
|
||||
}
|
||||
v = _initializer!();
|
||||
_setValue(_id, v);
|
||||
} else {
|
||||
v = unsafeCast<T>(_getValue(_id));
|
||||
}
|
||||
R result = f(v);
|
||||
if (hadValue) {
|
||||
_setValue(_id, previous_value!);
|
||||
} else {
|
||||
_clearValue(_id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns the value specified by the closest enclosing invocation of
|
||||
/// [runWith] or [runInititalized] or throws [StateError] if this
|
||||
/// [ScopedThreadLocal] is not bound to a value.
|
||||
T get value {
|
||||
if (!_hasValue(_id)) {
|
||||
throw StateError(
|
||||
"Attempt to access value that was not bound. "
|
||||
"Use runInititalized or runWith.",
|
||||
);
|
||||
}
|
||||
return unsafeCast<T>(_getValue(_id));
|
||||
}
|
||||
|
||||
/// Returns `true` if this [ScopedThreadLocal] is bound to a value.
|
||||
bool get isBound => _hasValue(_id);
|
||||
|
||||
@pragma("vm:external-name", "ScopedThreadLocal_allocateId")
|
||||
external static int _allocateId();
|
||||
|
||||
@pragma("vm:external-name", "ScopedThreadLocal_hasValue")
|
||||
external static bool _hasValue(int id);
|
||||
|
||||
@pragma("vm:external-name", "ScopedThreadLocal_getValue")
|
||||
external static Object? _getValue(int id);
|
||||
|
||||
@pragma("vm:external-name", "ScopedThreadLocal_setValue")
|
||||
external static void _setValue(int id, Object? value);
|
||||
|
||||
@pragma("vm:external-name", "ScopedThreadLocal_clearValue")
|
||||
external static void _clearValue(int id);
|
||||
|
||||
final int _id;
|
||||
final T Function()? _initializer;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Copyright (c) 2017, 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.
|
||||
|
||||
# This file contains all sources for the dart:_vm library.
|
||||
vm_sdk_sources = [ "_vm.dart" ]
|
||||
@@ -104,6 +104,9 @@
|
||||
"uri": "typed_data/typed_data.dart",
|
||||
"patches": "_internal/vm/lib/typed_data_patch.dart"
|
||||
},
|
||||
"_vm": {
|
||||
"uri": "_vm/_vm.dart"
|
||||
},
|
||||
"_vmservice": {
|
||||
"uri": "vmservice/vmservice.dart"
|
||||
},
|
||||
|
||||
@@ -104,6 +104,9 @@ vm_common:
|
||||
uri: "typed_data/typed_data.dart"
|
||||
patches: "_internal/vm/lib/typed_data_patch.dart"
|
||||
|
||||
_vm:
|
||||
uri: "_vm/_vm.dart"
|
||||
|
||||
_vmservice:
|
||||
uri: "vmservice/vmservice.dart"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user