// 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 ThreadLocal { /// Creates dart thread local variable. ThreadLocal() : _id = _allocateId(); /// Returns the value of this thread-local variable or throws [StateError] /// if it has no value. T get value { if (!_hasValue(_id)) { throw StateError( "Attempt to access variable that was not assigned a value.", ); } return unsafeCast(_getValue(_id)); } /// Sets the value of this variable. Overwrites old value if it was previously /// set. set value(T newValue) { _setValue(_id, newValue); } /// Returns `true` if some value was assigned to this variable. bool get hasValue => _hasValue(_id); // Clears this variable of its assigned value. void clearValue() { _clearValue(_id); } @pragma("vm:external-name", "ThreadLocal_allocateId") external static int _allocateId(); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:external-name", "ThreadLocal_hasValue") external static bool _hasValue(int id); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:external-name", "ThreadLocal_getValue") external static Object? _getValue(int id); @pragma("vm:external-name", "ThreadLocal_setValue") external static void _setValue(int id, Object? value); @pragma("vm:external-name", "ThreadLocal_clearValue") external static void _clearValue(int id); final int _id; } @pragma("vm:deeply-immutable") @pragma('vm:entry-point') final class ScopedThreadLocal { /// Creates scoped thread-local variable with given [initializer] function. /// /// [initializer] must be trivially shareable. ScopedThreadLocal([this._initializer]); /// Execute [f] binding this [ScopedThreadLocal] to the given /// [value] for the duration of the execution. R runWith(T newValue, R Function(T) f) { bool hadValue = variable.hasValue; T? previousValue = hadValue ? variable.value : null; variable.value = newValue; R result = f(newValue); if (hadValue) { variable.value = previousValue as T; } else { variable.clearValue(); } 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 Function(T) f) { bool hadValue = variable.hasValue; T? previousValue = hadValue ? variable.value : null; if (!variable.hasValue) { if (_initializer == null) { throw StateError( "No initializer was provided for this ScopedThreadLocal.", ); } variable.value = _initializer!(); } R result = f(variable.value); if (hadValue) { variable.value = previousValue as T; } else { variable.clearValue(); } 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 => variable.value; /// Returns `true` if this [ScopedThreadLocal] is bound to a value. bool get isBound => variable.hasValue; final T Function()? _initializer; final variable = ThreadLocal(); } @pragma("vm:deeply-immutable") @pragma('vm:entry-point') final class FinalThreadLocal { /// Creates thread local value with the given [initializer] function. /// /// The value can be assigned only once, remains assigned for the duration /// of this dart thread lifetime. /// /// [initializer] must be trivially shareable. FinalThreadLocal(this._initializer); /// Returns the value bound to [FinalThreadLocal]. T get value { if (!variable.hasValue) { final v = _initializer(); variable.value = v; return v; } return unsafeCast(ThreadLocal._getValue(variable._id)); } set value(_) { throw StateError("Final value can not be updated"); } final T Function() _initializer; final variable = ThreadLocal(); }