File: src\libraries\System.Private.CoreLib\src\System\Threading\Thread.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.ExceptionServices;
using System.Runtime.Versioning;
using System.Security.Principal;
 
namespace System.Threading
{
    public sealed partial class Thread : CriticalFinalizerObject
    {
        private static AsyncLocal<IPrincipal?>? s_asyncLocalPrincipal;
 
        [ThreadStatic]
        private static Thread? t_currentThread;
 
        // State associated with starting new thread
        private sealed class StartHelper
        {
            internal int _maxStackSize;
            internal Delegate _start;
            internal object? _startArg;
            internal CultureInfo? _culture;
            internal CultureInfo? _uiCulture;
            internal ExecutionContext? _executionContext;
 
            internal StartHelper(Delegate start)
            {
                _start = start;
            }
 
            internal static readonly ContextCallback s_threadStartContextCallback = new ContextCallback(Callback);
 
            private static void Callback(object? state)
            {
                Debug.Assert(state != null);
                ((StartHelper)state).RunWorker();
            }
 
            [MethodImpl(MethodImplOptions.AggressiveInlining)] // avoid long-lived stack frame in many threads
            internal void Run()
            {
                if (_executionContext != null && !_executionContext.IsDefault)
                {
                    ExecutionContext.RunInternal(_executionContext, s_threadStartContextCallback, this);
                }
                else
                {
                    RunWorker();
                }
            }
 
            [MethodImpl(MethodImplOptions.AggressiveInlining)] // avoid long-lived stack frame in many threads
            private void RunWorker()
            {
                InitializeCulture();
 
                Delegate start = _start;
                _start = null!;
 
#if FEATURE_OBJCMARSHAL
                if (AutoreleasePool.EnableAutoreleasePool)
                    AutoreleasePool.CreateAutoreleasePool();
#endif
 
                try
                {
                    if (start is ThreadStart threadStart)
                    {
                        threadStart();
                    }
                    else
                    {
                        ParameterizedThreadStart parameterizedThreadStart = (ParameterizedThreadStart)start;
 
                        object? startArg = _startArg;
                        _startArg = null;
 
                        parameterizedThreadStart(startArg);
                    }
                }
                catch (Exception ex) when (ExceptionHandling.IsHandledByGlobalHandler(ex))
                {
                    // the handler returned "true" means the exception is now "handled" and we should gracefully exit.
                }
 
#if FEATURE_OBJCMARSHAL
                // There is no need to wrap this "clean up" code in a finally block since
                // if an exception is thrown above, the process is going to terminate.
                // Optimize for the most common case - no exceptions escape a thread.
                if (AutoreleasePool.EnableAutoreleasePool)
                    AutoreleasePool.DrainAutoreleasePool();
#endif
            }
 
            private void InitializeCulture()
            {
                if (_culture != null)
                {
                    CultureInfo.CurrentCulture = _culture;
                    _culture = null;
                }
 
                if (_uiCulture != null)
                {
                    CultureInfo.CurrentUICulture = _uiCulture;
                    _uiCulture = null;
                }
            }
        }
 
        public Thread(ThreadStart start)
        {
            ArgumentNullException.ThrowIfNull(start);
 
            _startHelper = new StartHelper(start);
 
            Initialize();
        }
 
        public Thread(ThreadStart start, int maxStackSize)
        {
            ArgumentNullException.ThrowIfNull(start);
 
            ArgumentOutOfRangeException.ThrowIfNegative(maxStackSize);
 
            _startHelper = new StartHelper(start) { _maxStackSize = maxStackSize };
 
            Initialize();
        }
 
        public Thread(ParameterizedThreadStart start)
        {
            ArgumentNullException.ThrowIfNull(start);
 
            _startHelper = new StartHelper(start);
 
            Initialize();
        }
 
        public Thread(ParameterizedThreadStart start, int maxStackSize)
        {
            ArgumentNullException.ThrowIfNull(start);
 
            ArgumentOutOfRangeException.ThrowIfNegative(maxStackSize);
 
            _startHelper = new StartHelper(start) { _maxStackSize = maxStackSize };
 
            Initialize();
        }
 
#if (!TARGET_BROWSER && !TARGET_WASI) || FEATURE_WASM_MANAGED_THREADS
        [UnsupportedOSPlatformGuard("browser")]
        [UnsupportedOSPlatformGuard("wasi")]
        internal static bool IsThreadStartSupported => true;
#else
        [UnsupportedOSPlatformGuard("browser")]
        [UnsupportedOSPlatformGuard("wasi")]
        internal static bool IsThreadStartSupported => false;
#endif
 
        internal static void ThrowIfNoThreadStart()
        {
            if (IsThreadStartSupported)
                return;
            throw new PlatformNotSupportedException();
        }
 
        /// <summary>Causes the operating system to change the state of the current instance to <see cref="ThreadState.Running"/>, and optionally supplies an object containing data to be used by the method the thread executes.</summary>
        /// <param name="parameter">An object that contains data to be used by the method the thread executes.</param>
        /// <exception cref="ThreadStateException">The thread has already been started.</exception>
        /// <exception cref="OutOfMemoryException">There is not enough memory available to start this thread.</exception>
        /// <exception cref="InvalidOperationException">This thread was created using a <see cref="ThreadStart"/> delegate instead of a <see cref="ParameterizedThreadStart"/> delegate.</exception>
#if !FEATURE_WASM_MANAGED_THREADS
        [UnsupportedOSPlatform("browser")]
#endif
        public void Start(object? parameter) => Start(parameter, captureContext: true);
 
        /// <summary>Causes the operating system to change the state of the current instance to <see cref="ThreadState.Running"/>, and optionally supplies an object containing data to be used by the method the thread executes.</summary>
        /// <param name="parameter">An object that contains data to be used by the method the thread executes.</param>
        /// <exception cref="ThreadStateException">The thread has already been started.</exception>
        /// <exception cref="OutOfMemoryException">There is not enough memory available to start this thread.</exception>
        /// <exception cref="InvalidOperationException">This thread was created using a <see cref="ThreadStart"/> delegate instead of a <see cref="ParameterizedThreadStart"/> delegate.</exception>
        /// <remarks>
        /// Unlike <see cref="Start"/>, which captures the current <see cref="ExecutionContext"/> and uses that context to invoke the thread's delegate,
        /// <see cref="UnsafeStart"/> explicitly avoids capturing the current context and flowing it to the invocation.
        /// </remarks>
#if !FEATURE_WASM_MANAGED_THREADS
        [UnsupportedOSPlatform("browser")]
#endif
        public void UnsafeStart(object? parameter) => Start(parameter, captureContext: false);
 
        private void Start(object? parameter, bool captureContext)
        {
#if TARGET_WASI
            if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185
#endif
            ThrowIfNoThreadStart();
 
            StartHelper? startHelper = _startHelper;
 
            // In the case of a null startHelper (second call to start on same thread)
            // StartCore method will take care of the error reporting.
            if (startHelper != null)
            {
                if (startHelper._start is ThreadStart)
                {
                    // We expect the thread to be setup with a ParameterizedThreadStart if this Start is called.
                    throw new InvalidOperationException(SR.InvalidOperation_ThreadWrongThreadStart);
                }
 
                startHelper._startArg = parameter;
                startHelper._executionContext = captureContext ? ExecutionContext.Capture() : null;
            }
 
            StartCore();
        }
 
        /// <summary>Causes the operating system to change the state of the current instance to <see cref="ThreadState.Running"/>.</summary>
        /// <exception cref="ThreadStateException">The thread has already been started.</exception>
        /// <exception cref="OutOfMemoryException">There is not enough memory available to start this thread.</exception>
#if !FEATURE_WASM_MANAGED_THREADS
        [UnsupportedOSPlatform("browser")]
#endif
        public void Start() => Start(captureContext: true);
 
        /// <summary>Causes the operating system to change the state of the current instance to <see cref="ThreadState.Running"/>.</summary>
        /// <exception cref="ThreadStateException">The thread has already been started.</exception>
        /// <exception cref="OutOfMemoryException">There is not enough memory available to start this thread.</exception>
        /// <remarks>
        /// Unlike <see cref="Start"/>, which captures the current <see cref="ExecutionContext"/> and uses that context to invoke the thread's delegate,
        /// <see cref="UnsafeStart"/> explicitly avoids capturing the current context and flowing it to the invocation.
        /// </remarks>
#if !FEATURE_WASM_MANAGED_THREADS
        [UnsupportedOSPlatform("browser")]
#endif
        public void UnsafeStart() => Start(captureContext: false);
 
        private void Start(bool captureContext)
        {
            ThrowIfNoThreadStart();
            StartHelper? startHelper = _startHelper;
 
            // In the case of a null startHelper (second call to start on same thread)
            // StartCore method will take care of the error reporting.
            if (startHelper != null)
            {
                startHelper._startArg = null;
                startHelper._executionContext = captureContext ? ExecutionContext.Capture() : null;
            }
 
            StartCore();
        }
 
        private void RequireCurrentThread()
        {
            if (this != CurrentThread)
            {
                throw new InvalidOperationException(SR.Thread_Operation_RequiresCurrentThread);
            }
        }
 
        private void SetCultureOnUnstartedThread(CultureInfo value, bool uiCulture)
        {
            ArgumentNullException.ThrowIfNull(value);
 
            StartHelper? startHelper = _startHelper;
 
            // This check is best effort to catch common user errors only. It won't catch all posssible race
            // conditions between setting culture on unstarted thread and starting the thread.
            if ((ThreadState & ThreadState.Unstarted) == 0)
            {
                throw new InvalidOperationException(SR.Thread_Operation_RequiresCurrentThread);
            }
 
            Debug.Assert(startHelper != null);
 
            if (uiCulture)
            {
                startHelper._uiCulture = value;
            }
            else
            {
                startHelper._culture = value;
            }
        }
 
        public CultureInfo CurrentCulture
        {
            get
            {
                RequireCurrentThread();
                return CultureInfo.CurrentCulture;
            }
            set
            {
                if (this != CurrentThread)
                {
                    SetCultureOnUnstartedThread(value, uiCulture: false);
                    return;
                }
                CultureInfo.CurrentCulture = value;
            }
        }
 
        public CultureInfo CurrentUICulture
        {
            get
            {
                RequireCurrentThread();
                return CultureInfo.CurrentUICulture;
            }
            set
            {
                if (this != CurrentThread)
                {
                    SetCultureOnUnstartedThread(value, uiCulture: true);
                    return;
                }
                CultureInfo.CurrentUICulture = value;
            }
        }
 
        public static IPrincipal? CurrentPrincipal
        {
            get
            {
                IPrincipal? principal = s_asyncLocalPrincipal?.Value;
                if (principal is null)
                {
                    CurrentPrincipal = (principal = AppDomain.CurrentDomain.GetThreadPrincipal());
                }
                return principal;
            }
            set
            {
                if (s_asyncLocalPrincipal is null)
                {
                    if (value is null)
                    {
                        return;
                    }
                    Interlocked.CompareExchange(ref s_asyncLocalPrincipal, new AsyncLocal<IPrincipal?>(), null);
                }
                s_asyncLocalPrincipal.Value = value;
            }
        }
 
        public static Thread CurrentThread
        {
            [Intrinsic]
            get
            {
                return t_currentThread ?? InitializeCurrentThread();
            }
        }
 
        [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead.
        public static void Sleep(int millisecondsTimeout)
        {
            ArgumentOutOfRangeException.ThrowIfLessThan(millisecondsTimeout, Timeout.Infinite);
            SleepInternal(millisecondsTimeout);
        }
 
#if !NATIVEAOT
        /// <summary>Returns the operating system identifier for the current thread.</summary>
        internal static ulong CurrentOSThreadId => GetCurrentOSThreadId();
#endif
 
        public ExecutionContext? ExecutionContext => ExecutionContext.Capture();
 
        public string? Name
        {
            get => _name;
            set
            {
                lock (this)
                {
                    if (_name != value)
                    {
                        _name = value;
                        ThreadNameChanged(value);
                        _mayNeedResetForThreadPool = true;
                    }
                }
            }
        }
 
        internal void SetThreadPoolWorkerThreadName()
        {
            Debug.Assert(ThreadState.HasFlag(ThreadState.Unstarted) || this == CurrentThread);
            Debug.Assert(IsThreadPoolThread);
 
            lock (this)
            {
                _name = ThreadPool.WorkerThreadName;
                ThreadNameChanged(ThreadPool.WorkerThreadName);
            }
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal void ResetThreadPoolThread()
        {
            Debug.Assert(this == CurrentThread);
            Debug.Assert(!IsThreadStartSupported || IsThreadPoolThread); // there are no dedicated threadpool threads on runtimes where we can't start threads
 
            if (_mayNeedResetForThreadPool)
            {
                ResetThreadPoolThreadSlow();
            }
        }
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        private void ResetThreadPoolThreadSlow()
        {
            Debug.Assert(this == CurrentThread);
            Debug.Assert(!IsThreadStartSupported || IsThreadPoolThread); // there are no dedicated threadpool threads on runtimes where we can't start threads
            Debug.Assert(_mayNeedResetForThreadPool);
 
            _mayNeedResetForThreadPool = false;
 
            if (_name != ThreadPool.WorkerThreadName)
            {
                SetThreadPoolWorkerThreadName();
            }
 
            if (!IsBackground)
            {
                IsBackground = true;
            }
 
            if (Priority != ThreadPriority.Normal)
            {
                Priority = ThreadPriority.Normal;
            }
        }
 
        [Obsolete(Obsoletions.ThreadAbortMessage, DiagnosticId = Obsoletions.ThreadAbortDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public void Abort()
        {
            throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadAbort);
        }
 
        [Obsolete(Obsoletions.ThreadAbortMessage, DiagnosticId = Obsoletions.ThreadAbortDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public void Abort(object? stateInfo)
        {
            throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadAbort);
        }
 
        [Obsolete(Obsoletions.ThreadResetAbortMessage, DiagnosticId = Obsoletions.ThreadAbortDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public static void ResetAbort()
        {
            throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadAbort);
        }
 
        [Obsolete("Thread.Suspend has been deprecated. Use other classes in System.Threading, such as Monitor, Mutex, Event, and Semaphore, to synchronize Threads or protect resources.")]
        public void Suspend()
        {
            throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadSuspend);
        }
 
        [Obsolete("Thread.Resume has been deprecated. Use other classes in System.Threading, such as Monitor, Mutex, Event, and Semaphore, to synchronize Threads or protect resources.")]
        public void Resume()
        {
            throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadSuspend);
        }
 
        // Currently, no special handling is done for critical regions, and no special handling is necessary to ensure thread
        // affinity. If that changes, the relevant functions would instead need to delegate to RuntimeThread.
        public static void BeginCriticalRegion() { }
        public static void EndCriticalRegion() { }
        public static void BeginThreadAffinity() { }
        public static void EndThreadAffinity() { }
 
        public static LocalDataStoreSlot AllocateDataSlot() => LocalDataStore.AllocateSlot();
        public static LocalDataStoreSlot AllocateNamedDataSlot(string name) => LocalDataStore.AllocateNamedSlot(name);
        public static LocalDataStoreSlot GetNamedDataSlot(string name) => LocalDataStore.GetNamedSlot(name);
        public static void FreeNamedDataSlot(string name) => LocalDataStore.FreeNamedSlot(name);
        public static object? GetData(LocalDataStoreSlot slot) => LocalDataStore.GetData(slot);
        public static void SetData(LocalDataStoreSlot slot, object? data) => LocalDataStore.SetData(slot, data);
 
        [Obsolete("The ApartmentState property has been deprecated. Use GetApartmentState, SetApartmentState or TrySetApartmentState instead.")]
        public ApartmentState ApartmentState
        {
            get => GetApartmentState();
            set => TrySetApartmentState(value);
        }
 
        [SupportedOSPlatform("windows")]
        public void SetApartmentState(ApartmentState state)
        {
            SetApartmentState(state, throwOnError: true);
        }
 
        public bool TrySetApartmentState(ApartmentState state)
        {
            return SetApartmentState(state, throwOnError: false);
        }
 
#pragma warning disable CA1822 // SetApartmentStateUnchecked should pass `this`
        private bool SetApartmentState(ApartmentState state, bool throwOnError)
        {
            switch (state)
            {
                case ApartmentState.STA:
                case ApartmentState.MTA:
                case ApartmentState.Unknown:
                    break;
 
                default:
                    throw new ArgumentOutOfRangeException(nameof(state), SR.ArgumentOutOfRange_Enum);
            }
 
            return SetApartmentStateUnchecked(state, throwOnError);
        }
#pragma warning disable CA1822
 
        [Obsolete(Obsoletions.CodeAccessSecurityMessage, DiagnosticId = Obsoletions.CodeAccessSecurityDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public CompressedStack GetCompressedStack()
        {
            throw new InvalidOperationException(SR.Thread_GetSetCompressedStack_NotSupported);
        }
 
        [Obsolete(Obsoletions.CodeAccessSecurityMessage, DiagnosticId = Obsoletions.CodeAccessSecurityDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public void SetCompressedStack(CompressedStack stack)
        {
            throw new InvalidOperationException(SR.Thread_GetSetCompressedStack_NotSupported);
        }
 
        public static AppDomain GetDomain() => AppDomain.CurrentDomain;
        public static int GetDomainID() => 1;
        public override int GetHashCode() => ManagedThreadId;
        public void Join() => Join(-1);
        public bool Join(TimeSpan timeout) => Join(WaitHandle.ToTimeoutMilliseconds(timeout));
        public static void MemoryBarrier() => Interlocked.MemoryBarrier();
        public static void Sleep(TimeSpan timeout) => Sleep(WaitHandle.ToTimeoutMilliseconds(timeout));
 
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static byte VolatileRead(ref byte address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static double VolatileRead(ref double address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static short VolatileRead(ref short address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static int VolatileRead(ref int address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static long VolatileRead(ref long address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static IntPtr VolatileRead(ref IntPtr address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [return: NotNullIfNotNull(nameof(address))]
        public static object? VolatileRead([NotNullIfNotNull(nameof(address))] ref object? address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [CLSCompliant(false)]
        public static sbyte VolatileRead(ref sbyte address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static float VolatileRead(ref float address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [CLSCompliant(false)]
        public static ushort VolatileRead(ref ushort address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [CLSCompliant(false)]
        public static uint VolatileRead(ref uint address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [CLSCompliant(false)]
        public static ulong VolatileRead(ref ulong address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [CLSCompliant(false)]
        public static UIntPtr VolatileRead(ref UIntPtr address) => Volatile.Read(ref address);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void VolatileWrite(ref byte address, byte value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void VolatileWrite(ref double address, double value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void VolatileWrite(ref short address, short value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void VolatileWrite(ref int address, int value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void VolatileWrite(ref long address, long value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void VolatileWrite(ref IntPtr address, IntPtr value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void VolatileWrite([NotNullIfNotNull(nameof(value))] ref object? address, object? value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [CLSCompliant(false)]
        public static void VolatileWrite(ref sbyte address, sbyte value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void VolatileWrite(ref float address, float value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [CLSCompliant(false)]
        public static void VolatileWrite(ref ushort address, ushort value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [CLSCompliant(false)]
        public static void VolatileWrite(ref uint address, uint value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [CLSCompliant(false)]
        public static void VolatileWrite(ref ulong address, ulong value) => Volatile.Write(ref address, value);
        [Obsolete(Obsoletions.ThreadVolatileReadWriteMessage, DiagnosticId = Obsoletions.ThreadVolatileReadWriteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [CLSCompliant(false)]
        public static void VolatileWrite(ref UIntPtr address, UIntPtr value) => Volatile.Write(ref address, value);
 
        /// <summary>
        /// Manages functionality required to support members of <see cref="Thread"/> dealing with thread-local data
        /// </summary>
        private static class LocalDataStore
        {
            private static Dictionary<string, LocalDataStoreSlot>? s_nameToSlotMap;
 
            public static LocalDataStoreSlot AllocateSlot()
            {
                return new LocalDataStoreSlot(new ThreadLocal<object?>());
            }
 
            private static Dictionary<string, LocalDataStoreSlot> EnsureNameToSlotMap()
            {
                Dictionary<string, LocalDataStoreSlot>? nameToSlotMap = s_nameToSlotMap;
                if (nameToSlotMap != null)
                {
                    return nameToSlotMap;
                }
 
                nameToSlotMap = new Dictionary<string, LocalDataStoreSlot>();
                return Interlocked.CompareExchange(ref s_nameToSlotMap, nameToSlotMap, null) ?? nameToSlotMap;
            }
 
            public static LocalDataStoreSlot AllocateNamedSlot(string name)
            {
                LocalDataStoreSlot slot = AllocateSlot();
                Dictionary<string, LocalDataStoreSlot> nameToSlotMap = EnsureNameToSlotMap();
                lock (nameToSlotMap)
                {
                    nameToSlotMap.Add(name, slot);
                }
                return slot;
            }
 
            public static LocalDataStoreSlot GetNamedSlot(string name)
            {
                Dictionary<string, LocalDataStoreSlot> nameToSlotMap = EnsureNameToSlotMap();
                lock (nameToSlotMap)
                {
                    if (!nameToSlotMap.TryGetValue(name, out LocalDataStoreSlot? slot))
                    {
                        slot = AllocateSlot();
                        nameToSlotMap[name] = slot;
                    }
                    return slot;
                }
            }
 
            public static void FreeNamedSlot(string name)
            {
                Dictionary<string, LocalDataStoreSlot> nameToSlotMap = EnsureNameToSlotMap();
                lock (nameToSlotMap)
                {
                    nameToSlotMap.Remove(name);
                }
            }
 
            private static ThreadLocal<object?> GetThreadLocal(LocalDataStoreSlot slot)
            {
                ArgumentNullException.ThrowIfNull(slot);
 
                Debug.Assert(slot.Data != null);
                return slot.Data;
            }
 
            public static object? GetData(LocalDataStoreSlot slot)
            {
                return GetThreadLocal(slot).Value;
            }
 
            public static void SetData(LocalDataStoreSlot slot, object? value)
            {
                GetThreadLocal(slot).Value = value;
            }
        }
 
        // Cached processor id could be used as a hint for which per-core stripe of data to access to avoid sharing.
        // It is periodically refreshed to trail the actual thread core affinity.
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int GetCurrentProcessorId()
        {
            if (s_isProcessorNumberReallyFast)
                return GetCurrentProcessorNumber();
 
            return ProcessorIdCache.GetCurrentProcessorId();
        }
 
        // a speed check will determine refresh rate of the cache and will report if caching is not advisable.
        // we will record that in a readonly static so that it could become a JIT constant and bypass caching entirely.
        private static readonly bool s_isProcessorNumberReallyFast = ProcessorIdCache.ProcessorNumberSpeedCheck();
 
#if FEATURE_WASM_MANAGED_THREADS
        [ThreadStatic]
        public static bool ThrowOnBlockingWaitOnJSInteropThread;
 
        [ThreadStatic]
        public static bool WarnOnBlockingWaitOnJSInteropThread;
 
#pragma warning disable CS3001
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern unsafe void WarnAboutBlockingWait(char* stack, int length);
 
        public static unsafe void AssureBlockingPossible()
        {
            if (ThrowOnBlockingWaitOnJSInteropThread)
            {
                throw new PlatformNotSupportedException(SR.WasmThreads_BlockingWaitNotSupportedOnJSInterop);
            }
            else if (WarnOnBlockingWaitOnJSInteropThread)
            {
                var st = $"Blocking the thread with JS interop is dangerous and could lead to deadlock. ManagedThreadId: {Environment.CurrentManagedThreadId}\n{Environment.StackTrace}";
                fixed (char* stack = st)
                {
                    WarnAboutBlockingWait(stack, st.Length);
                }
            }
        }
 
#pragma warning restore CS3001

        public static void ForceBlockingWait(Action<object?> action, object? state = null)
        {
            var flag = ThrowOnBlockingWaitOnJSInteropThread;
            var wflag = WarnOnBlockingWaitOnJSInteropThread;
            try
            {
                ThrowOnBlockingWaitOnJSInteropThread = false;
                WarnOnBlockingWaitOnJSInteropThread = false;
 
                action(state);
            }
            finally
            {
                ThrowOnBlockingWaitOnJSInteropThread = flag;
                WarnOnBlockingWaitOnJSInteropThread = wflag;
            }
        }
#endif
    }
}