// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.ConstrainedExecution; using System.Security.Principal; namespace System.Threading { #if PROJECTN [Internal.Runtime.CompilerServices.RelocatedType("System.Threading.Thread")] #endif public sealed partial class Thread : CriticalFinalizerObject { private static AsyncLocal? s_asyncLocalPrincipal; [ThreadStatic] private static Thread? t_currentThread; public Thread(ThreadStart start) : this() { if (start == null) { throw new ArgumentNullException(nameof(start)); } Create(start); } public Thread(ThreadStart start, int maxStackSize) : this() { if (start == null) { throw new ArgumentNullException(nameof(start)); } if (maxStackSize < 0) { throw new ArgumentOutOfRangeException(nameof(maxStackSize), SR.ArgumentOutOfRange_NeedNonNegNum); } Create(start, maxStackSize); } public Thread(ParameterizedThreadStart start) : this() { if (start == null) { throw new ArgumentNullException(nameof(start)); } Create(start); } public Thread(ParameterizedThreadStart start, int maxStackSize) : this() { if (start == null) { throw new ArgumentNullException(nameof(start)); } if (maxStackSize < 0) { throw new ArgumentOutOfRangeException(nameof(maxStackSize), SR.ArgumentOutOfRange_NeedNonNegNum); } Create(start, maxStackSize); } private void RequireCurrentThread() { if (this != CurrentThread) { throw new InvalidOperationException(SR.Thread_Operation_RequiresCurrentThread); } } private void SetCultureOnUnstartedThread(CultureInfo value, bool uiCulture) { if (value == null) { throw new ArgumentNullException(nameof(value)); } if ((ThreadState & ThreadState.Unstarted) == 0) { throw new InvalidOperationException(SR.Thread_Operation_RequiresCurrentThread); } SetCultureOnUnstartedThreadNoCheck(value, uiCulture); } partial void ThreadNameChanged(string? 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(), null); } s_asyncLocalPrincipal.Value = value; } } public static Thread CurrentThread => t_currentThread ?? InitializeCurrentThread(); public ExecutionContext? ExecutionContext => ExecutionContext.Capture(); public string? Name { get => _name; set { lock (this) { if (_name != null) { throw new InvalidOperationException(SR.InvalidOperation_WriteOnce); } _name = value; ThreadNameChanged(value); } } } public void Abort() { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadAbort); } public void Abort(object? stateInfo) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadAbort); } public static void ResetAbort() { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadAbort); } [ObsoleteAttribute("Thread.Suspend has been deprecated. Please use other classes in System.Threading, such as Monitor, Mutex, Event, and Semaphore, to synchronize Threads or protect resources. https://go.microsoft.com/fwlink/?linkid=14202", false)] public void Suspend() { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadSuspend); } [ObsoleteAttribute("Thread.Resume has been deprecated. Please use other classes in System.Threading, such as Monitor, Mutex, Event, and Semaphore, to synchronize Threads or protect resources. https://go.microsoft.com/fwlink/?linkid=14202", false)] 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.", false)] public ApartmentState ApartmentState { get { return GetApartmentState(); } set { TrySetApartmentState(value); } } public void SetApartmentState(ApartmentState state) { if (!TrySetApartmentState(state)) { throw GetApartmentStateChangeFailedException(); } } public bool TrySetApartmentState(ApartmentState state) { switch (state) { case ApartmentState.STA: case ApartmentState.MTA: case ApartmentState.Unknown: break; default: throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_Enum, nameof(state)); } return TrySetApartmentStateUnchecked(state); } [Obsolete("Thread.GetCompressedStack is no longer supported. Please use the System.Threading.CompressedStack class")] public CompressedStack GetCompressedStack() { throw new InvalidOperationException(SR.Thread_GetSetCompressedStack_NotSupported); } [Obsolete("Thread.SetCompressedStack is no longer supported. Please use the System.Threading.CompressedStack class")] 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)); public static byte VolatileRead(ref byte address) => Volatile.Read(ref address); public static double VolatileRead(ref double address) => Volatile.Read(ref address); public static short VolatileRead(ref short address) => Volatile.Read(ref address); public static int VolatileRead(ref int address) => Volatile.Read(ref address); public static long VolatileRead(ref long address) => Volatile.Read(ref address); public static IntPtr VolatileRead(ref IntPtr address) => Volatile.Read(ref address); [return: NotNullIfNotNull("address")] public static object? VolatileRead(ref object? address) => Volatile.Read(ref address); [CLSCompliant(false)] public static sbyte VolatileRead(ref sbyte address) => Volatile.Read(ref address); public static float VolatileRead(ref float address) => Volatile.Read(ref address); [CLSCompliant(false)] public static ushort VolatileRead(ref ushort address) => Volatile.Read(ref address); [CLSCompliant(false)] public static uint VolatileRead(ref uint address) => Volatile.Read(ref address); [CLSCompliant(false)] public static ulong VolatileRead(ref ulong address) => Volatile.Read(ref address); [CLSCompliant(false)] public static UIntPtr VolatileRead(ref UIntPtr address) => Volatile.Read(ref address); public static void VolatileWrite(ref byte address, byte value) => Volatile.Write(ref address, value); public static void VolatileWrite(ref double address, double value) => Volatile.Write(ref address, value); public static void VolatileWrite(ref short address, short value) => Volatile.Write(ref address, value); public static void VolatileWrite(ref int address, int value) => Volatile.Write(ref address, value); public static void VolatileWrite(ref long address, long value) => Volatile.Write(ref address, value); public static void VolatileWrite(ref IntPtr address, IntPtr value) => Volatile.Write(ref address, value); public static void VolatileWrite([NotNullIfNotNull("value")] ref object? address, object? value) => Volatile.Write(ref address, value); [CLSCompliant(false)] public static void VolatileWrite(ref sbyte address, sbyte value) => Volatile.Write(ref address, value); public static void VolatileWrite(ref float address, float value) => Volatile.Write(ref address, value); [CLSCompliant(false)] public static void VolatileWrite(ref ushort address, ushort value) => Volatile.Write(ref address, value); [CLSCompliant(false)] public static void VolatileWrite(ref uint address, uint value) => Volatile.Write(ref address, value); [CLSCompliant(false)] public static void VolatileWrite(ref ulong address, ulong value) => Volatile.Write(ref address, value); [CLSCompliant(false)] public static void VolatileWrite(ref UIntPtr address, UIntPtr value) => Volatile.Write(ref address, value); /// /// Manages functionality required to support members of dealing with thread-local data /// private static class LocalDataStore { private static Dictionary? s_nameToSlotMap; public static LocalDataStoreSlot AllocateSlot() { return new LocalDataStoreSlot(new ThreadLocal()); } private static Dictionary EnsureNameToSlotMap() { Dictionary? nameToSlotMap = s_nameToSlotMap; if (nameToSlotMap != null) { return nameToSlotMap; } nameToSlotMap = new Dictionary(); return Interlocked.CompareExchange(ref s_nameToSlotMap, nameToSlotMap, null) ?? nameToSlotMap; } public static LocalDataStoreSlot AllocateNamedSlot(string name) { LocalDataStoreSlot slot = AllocateSlot(); Dictionary nameToSlotMap = EnsureNameToSlotMap(); lock (nameToSlotMap) { nameToSlotMap.Add(name, slot); } return slot; } public static LocalDataStoreSlot GetNamedSlot(string name) { Dictionary nameToSlotMap = EnsureNameToSlotMap(); lock (nameToSlotMap) { LocalDataStoreSlot? slot; if (!nameToSlotMap.TryGetValue(name, out slot)) { slot = AllocateSlot(); nameToSlotMap[name] = slot; } return slot; } } public static void FreeNamedSlot(string name) { Dictionary nameToSlotMap = EnsureNameToSlotMap(); lock (nameToSlotMap) { nameToSlotMap.Remove(name); } } private static ThreadLocal GetThreadLocal(LocalDataStoreSlot slot) { if (slot == null) { throw new ArgumentNullException(nameof(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; } } } }