@@ -58,6 +58,9 @@ public sealed partial class Thread
5858 // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized.
5959 private bool _mayNeedResetForThreadPool ;
6060
61+ // Set in unmanaged and read in managed code.
62+ private bool _isDead ;
63+
6164 private Thread ( ) { }
6265
6366 public int ManagedThreadId
@@ -74,7 +77,7 @@ internal ThreadHandle GetNativeHandle()
7477 // This should never happen under normal circumstances.
7578 if ( thread == IntPtr . Zero )
7679 {
77- throw new ArgumentException ( null , SR . Argument_InvalidHandle ) ;
80+ throw new ThreadStateException ( SR . Argument_InvalidHandle ) ;
7881 }
7982
8083 return new ThreadHandle ( thread ) ;
@@ -211,26 +214,32 @@ public extern bool IsThreadPoolThread
211214 internal set ;
212215 }
213216
217+ [ LibraryImport ( RuntimeHelpers . QCall , EntryPoint = "ThreadNative_SetPriority" ) ]
218+ [ return : MarshalAs ( UnmanagedType . Bool ) ]
219+ private static partial void SetPriority ( ObjectHandleOnStack thread , int priority ) ;
220+
214221 /// <summary>Returns the priority of the thread.</summary>
215222 public ThreadPriority Priority
216223 {
217- get => ( ThreadPriority ) GetPriorityNative ( ) ;
224+ get
225+ {
226+ if ( _isDead )
227+ {
228+ throw new ThreadStateException ( SR . ThreadState_Dead_Priority ) ;
229+ }
230+ return ( ThreadPriority ) _priority ;
231+ }
218232 set
219233 {
220- SetPriorityNative ( ( int ) value ) ;
234+ Thread _this = this ;
235+ SetPriority ( ObjectHandleOnStack . Create ( ref _this ) , ( int ) value ) ;
221236 if ( value != ThreadPriority . Normal )
222237 {
223238 _mayNeedResetForThreadPool = true ;
224239 }
225240 }
226241 }
227242
228- [ MethodImpl ( MethodImplOptions . InternalCall ) ]
229- private extern int GetPriorityNative ( ) ;
230-
231- [ MethodImpl ( MethodImplOptions . InternalCall ) ]
232- private extern void SetPriorityNative ( int priority ) ;
233-
234243 [ LibraryImport ( RuntimeHelpers . QCall , EntryPoint = "ThreadNative_GetCurrentOSThreadId" ) ]
235244 private static partial ulong GetCurrentOSThreadId ( ) ;
236245
@@ -243,21 +252,31 @@ public ThreadPriority Priority
243252 [ MethodImpl ( MethodImplOptions . InternalCall ) ]
244253 private extern int GetThreadStateNative ( ) ;
245254
246- public ApartmentState GetApartmentState ( ) =>
247- #if FEATURE_COMINTEROP_APARTMENT_SUPPORT
248- ( ApartmentState ) GetApartmentStateNative ( ) ;
249- #else // !FEATURE_COMINTEROP_APARTMENT_SUPPORT
250- ApartmentState . Unknown ;
251- #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
252-
253255 /// <summary>
254256 /// An unstarted thread can be marked to indicate that it will host a
255257 /// single-threaded or multi-threaded apartment.
256258 /// </summary>
257259#if FEATURE_COMINTEROP_APARTMENT_SUPPORT
260+ [ LibraryImport ( RuntimeHelpers . QCall , EntryPoint = "ThreadNative_GetApartmentState" ) ]
261+ private static partial int GetApartmentState ( ObjectHandleOnStack t ) ;
262+
263+ [ LibraryImport ( RuntimeHelpers . QCall , EntryPoint = "ThreadNative_SetApartmentState" ) ]
264+ private static partial int SetApartmentState ( ObjectHandleOnStack t , int state ) ;
265+
266+ public ApartmentState GetApartmentState ( )
267+ {
268+ Thread _this = this ;
269+ return ( ApartmentState ) GetApartmentState ( ObjectHandleOnStack . Create ( ref _this ) ) ;
270+ }
271+
258272 private bool SetApartmentStateUnchecked ( ApartmentState state , bool throwOnError )
259273 {
260- ApartmentState retState = ( ApartmentState ) SetApartmentStateNative ( ( int ) state ) ;
274+ ApartmentState retState ;
275+ lock ( this ) // This lock is only needed when the this is not the current thread.
276+ {
277+ Thread _this = this ;
278+ retState = ( ApartmentState ) SetApartmentState ( ObjectHandleOnStack . Create ( ref _this ) , ( int ) state ) ;
279+ }
261280
262281 // Special case where we pass in Unknown and get back MTA.
263282 // Once we CoUninitialize the thread, the OS will still
@@ -282,12 +301,9 @@ private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
282301 return true ;
283302 }
284303
285- [ MethodImpl ( MethodImplOptions . InternalCall ) ]
286- internal extern int GetApartmentStateNative ( ) ;
287-
288- [ MethodImpl ( MethodImplOptions . InternalCall ) ]
289- internal extern int SetApartmentStateNative ( int state ) ;
290304#else // FEATURE_COMINTEROP_APARTMENT_SUPPORT
305+ public ApartmentState GetApartmentState ( ) => ApartmentState . Unknown ;
306+
291307 private static bool SetApartmentStateUnchecked ( ApartmentState state , bool throwOnError )
292308 {
293309 if ( state != ApartmentState . Unknown )
@@ -331,18 +347,31 @@ public void Interrupt()
331347 [ LibraryImport ( RuntimeHelpers . QCall , EntryPoint = "ThreadNative_Interrupt" ) ]
332348 private static partial void Interrupt ( ThreadHandle t ) ;
333349
350+ [ LibraryImport ( RuntimeHelpers . QCall , EntryPoint = "ThreadNative_Join" ) ]
351+ [ return : MarshalAs ( UnmanagedType . Bool ) ]
352+ private static partial bool Join ( ObjectHandleOnStack thread , int millisecondsTimeout ) ;
353+
334354 /// <summary>
335355 /// Waits for the thread to die or for timeout milliseconds to elapse.
336356 /// </summary>
337357 /// <returns>
338358 /// Returns true if the thread died, or false if the wait timed out. If
339359 /// -1 is given as the parameter, no timeout will occur.
340360 /// </returns>
341- /// <exception cref="ArgumentException ">if timeout < -1 (Timeout.Infinite)</exception>
361+ /// <exception cref="ArgumentOutOfRangeException ">if timeout < -1 (Timeout.Infinite)</exception>
342362 /// <exception cref="ThreadInterruptedException">if the thread is interrupted while waiting</exception>
343363 /// <exception cref="ThreadStateException">if the thread has not been started yet</exception>
344- [ MethodImpl ( MethodImplOptions . InternalCall ) ]
345- public extern bool Join ( int millisecondsTimeout ) ;
364+ public bool Join ( int millisecondsTimeout )
365+ {
366+ // Validate the timeout
367+ if ( millisecondsTimeout < 0 && millisecondsTimeout != Timeout . Infinite )
368+ {
369+ throw new ArgumentOutOfRangeException ( nameof ( millisecondsTimeout ) , SR . ArgumentOutOfRange_NeedNonNegOrNegative1 ) ;
370+ }
371+
372+ Thread _this = this ;
373+ return Join ( ObjectHandleOnStack . Create ( ref _this ) , millisecondsTimeout ) ;
374+ }
346375
347376 /// <summary>
348377 /// Max value to be passed into <see cref="SpinWait(int)"/> for optimal delaying. This value is normalized to be
0 commit comments