1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Globalization;
8 using System.Reflection;
9 using System.Runtime.InteropServices;
10 using System.Security.Principal;
11 using System.Threading.Tests;
12 using Xunit;
13 
14 namespace System.Threading.Threads.Tests
15 {
16     public class DummyClass : RemoteExecutorTestBase
17     {
18         public static string HostRunnerTest = HostRunner;
19     }
20 
21     public static class ThreadTests
22     {
23         private const int UnexpectedTimeoutMilliseconds = ThreadTestHelpers.UnexpectedTimeoutMilliseconds;
24         private const int ExpectedTimeoutMilliseconds = ThreadTestHelpers.ExpectedTimeoutMilliseconds;
25 
26         [Fact]
ConstructorTest()27         public static void ConstructorTest()
28         {
29             Action<Thread> startThreadAndJoin =
30                 t =>
31                 {
32                     t.IsBackground = true;
33                     t.Start();
34                     Assert.True(t.Join(UnexpectedTimeoutMilliseconds));
35                 };
36             Action<int> verifyStackSize =
37                 stackSizeBytes =>
38                 {
39                     // Try to stack-allocate an array to verify that close to the expected amount of stack space is actually
40                     // available
41                     int bufferSizeBytes = Math.Max(16 << 10, stackSizeBytes - (64 << 10));
42                     unsafe
43                     {
44                         byte* buffer = stackalloc byte[bufferSizeBytes];
45                         Volatile.Write(ref buffer[0], 0xff);
46                         Volatile.Write(ref buffer[bufferSizeBytes - 1], 0xff);
47                     }
48                 };
49             startThreadAndJoin(new Thread(() => verifyStackSize(0)));
50             startThreadAndJoin(new Thread(() => verifyStackSize(0), 0));
51             startThreadAndJoin(new Thread(() => verifyStackSize(64 << 10), 64 << 10)); // 64 KB
52             startThreadAndJoin(new Thread(() => verifyStackSize(16 << 20), 16 << 20)); // 16 MB
53             startThreadAndJoin(new Thread(state => verifyStackSize(0)));
54             startThreadAndJoin(new Thread(state => verifyStackSize(0), 0));
55             startThreadAndJoin(new Thread(state => verifyStackSize(64 << 10), 64 << 10)); // 64 KB
56             startThreadAndJoin(new Thread(state => verifyStackSize(16 << 20), 16 << 20)); // 16 MB
57 
58             Assert.Throws<ArgumentNullException>(() => new Thread((ThreadStart)null));
59             Assert.Throws<ArgumentNullException>(() => new Thread((ThreadStart)null, 0));
60             Assert.Throws<ArgumentNullException>(() => new Thread((ParameterizedThreadStart)null));
61             Assert.Throws<ArgumentNullException>(() => new Thread((ParameterizedThreadStart)null, 0));
62 
63             Assert.Throws<ArgumentOutOfRangeException>(() => new Thread(() => { }, -1));
64             Assert.Throws<ArgumentOutOfRangeException>(() => new Thread(state => { }, -1));
65         }
66 
ApartmentStateTest_MemberData()67         private static IEnumerable<object[]> ApartmentStateTest_MemberData()
68         {
69             yield return
70                 new object[]
71                 {
72 #pragma warning disable 618 // Obsolete members
73                     new Func<Thread, ApartmentState>(t => t.ApartmentState),
74 #pragma warning restore 618 // Obsolete members
75                     new Func<Thread, ApartmentState, int>(
76                         (t, value) =>
77                         {
78                             try
79                             {
80 #pragma warning disable 618 // Obsolete members
81                                 t.ApartmentState = value;
82 #pragma warning restore 618 // Obsolete members
83                                 return 0;
84                             }
85                             catch (ArgumentOutOfRangeException)
86                             {
87                                 return 1;
88                             }
89                             catch (PlatformNotSupportedException)
90                             {
91                                 return 3;
92                             }
93                         }),
94                     0
95                 };
96             yield return
97                 new object[]
98                 {
99                     new Func<Thread, ApartmentState>(t => t.GetApartmentState()),
100                     new Func<Thread, ApartmentState, int>(
101                         (t, value) =>
102                         {
103                             try
104                             {
105                                 t.SetApartmentState(value);
106                                 return 0;
107                             }
108                             catch (ArgumentOutOfRangeException)
109                             {
110                                 return 1;
111                             }
112                             catch (InvalidOperationException)
113                             {
114                                 return 2;
115                             }
116                             catch (PlatformNotSupportedException)
117                             {
118                                 return 3;
119                             }
120                         }),
121                     1
122                 };
123             yield return
124                 new object[]
125                 {
126                     new Func<Thread, ApartmentState>(t => t.GetApartmentState()),
127                     new Func<Thread, ApartmentState, int>(
128                         (t, value) =>
129                         {
130                             try
131                             {
132                                 return t.TrySetApartmentState(value) ? 0 : 2;
133                             }
134                             catch (ArgumentOutOfRangeException)
135                             {
136                                 return 1;
137                             }
138                             catch (PlatformNotSupportedException)
139                             {
140                                 return 3;
141                             }
142                         }),
143                     2
144                 };
145         }
146 
147         [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))]
148         [InlineData("STAMain.exe", "GetApartmentState")]
149         [InlineData("STAMain.exe", "SetApartmentState")]
150         [InlineData("MTAMain.exe", "GetApartmentState")]
151         [InlineData("MTAMain.exe", "SetApartmentState")]
152         [ActiveIssue(20766, TargetFrameworkMonikers.Uap)]
ApartmentState_AttributePresent(string AppName, string mode)153         public static void ApartmentState_AttributePresent(string AppName, string mode)
154         {
155             var psi = new ProcessStartInfo();
156             if (PlatformDetection.IsFullFramework || PlatformDetection.IsNetNative)
157             {
158                 psi.FileName = AppName;
159                 psi.Arguments = $"{mode}";
160             }
161             else
162             {
163                 psi.FileName = DummyClass.HostRunnerTest;
164                 psi.Arguments = $"{AppName} {mode}";
165             }
166             using (Process p = Process.Start(psi))
167             {
168                 p.WaitForExit();
169                 Assert.Equal(PlatformDetection.IsWindows ? 0 : 2, p.ExitCode);
170             }
171         }
172 
173         [Fact]
174         [ActiveIssue(20766,TargetFrameworkMonikers.UapAot)]
175         [PlatformSpecific(TestPlatforms.Windows)]
ApartmentState_NoAttributePresent_DefaultState_Windows()176         public static void ApartmentState_NoAttributePresent_DefaultState_Windows()
177         {
178             DummyClass.RemoteInvoke(() =>
179             {
180                 Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState());
181                 Assert.Throws<InvalidOperationException>(() => Thread.CurrentThread.SetApartmentState(ApartmentState.STA));
182                 Thread.CurrentThread.SetApartmentState(ApartmentState.MTA);
183             }).Dispose();
184         }
185 
186         [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))]
187         [ActiveIssue(20766,TargetFrameworkMonikers.UapAot)]
188         [PlatformSpecific(TestPlatforms.Windows)]
189         [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
ApartmentState_NoAttributePresent_STA_Windows_Core()190         public static void ApartmentState_NoAttributePresent_STA_Windows_Core()
191         {
192             DummyClass.RemoteInvoke(() =>
193             {
194                 Thread.CurrentThread.SetApartmentState(ApartmentState.STA);
195                 Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState());
196                 Assert.Throws<InvalidOperationException>(() => Thread.CurrentThread.SetApartmentState(ApartmentState.MTA));
197             }).Dispose();
198         }
199 
200         // The Thread Apartment State is set to MTA if attribute is not specified on main function
201         [Fact]
202         [PlatformSpecific(TestPlatforms.Windows)]
203         [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
ApartmentState_NoAttributePresent_STA_Windows_Desktop()204         public static void ApartmentState_NoAttributePresent_STA_Windows_Desktop()
205         {
206             DummyClass.RemoteInvoke(() =>
207             {
208                 Assert.Throws<InvalidOperationException>(() => Thread.CurrentThread.SetApartmentState(ApartmentState.STA));
209                 Thread.CurrentThread.SetApartmentState(ApartmentState.MTA);
210                 Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState());
211             }).Dispose();
212         }
213 
214         [Fact]
215         [PlatformSpecific(TestPlatforms.AnyUnix)]
ApartmentState_NoAttributePresent_DefaultState_Unix()216         public static void ApartmentState_NoAttributePresent_DefaultState_Unix()
217         {
218             DummyClass.RemoteInvoke(() =>
219             {
220                 Assert.Equal(ApartmentState.Unknown, Thread.CurrentThread.GetApartmentState());
221                 Assert.Throws<PlatformNotSupportedException>(() => Thread.CurrentThread.SetApartmentState(ApartmentState.MTA));
222             }).Dispose();
223         }
224 
225         // Thread is always initialized as MTA irrespective of the attribute present.
226         [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindowsNanoServer))]
ApartmentState_NoAttributePresent_DefaultState_Nano()227         public static void ApartmentState_NoAttributePresent_DefaultState_Nano()
228         {
229             DummyClass.RemoteInvoke(() =>
230             {
231                 Assert.Throws<InvalidOperationException>(() => Thread.CurrentThread.SetApartmentState(ApartmentState.STA));
232                 Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState());
233             }).Dispose();
234         }
235 
236         [Fact]
237         [PlatformSpecific(TestPlatforms.AnyUnix)]
ApartmentState_NoAttributePresent_STA_Unix()238         public static void ApartmentState_NoAttributePresent_STA_Unix()
239         {
240             DummyClass.RemoteInvoke(() =>
241             {
242                 Assert.Throws<PlatformNotSupportedException>(() => Thread.CurrentThread.SetApartmentState(ApartmentState.STA));
243             }).Dispose();
244         }
245 
246         [Theory]
247         [MemberData(nameof(ApartmentStateTest_MemberData))]
248         [PlatformSpecific(TestPlatforms.Windows)]  // Expected behavior differs on Unix and Windows
249         [ActiveIssue(20766,TargetFrameworkMonikers.UapAot)]
GetSetApartmentStateTest_ChangeAfterThreadStarted_Windows( Func<Thread, ApartmentState> getApartmentState, Func<Thread, ApartmentState, int> setApartmentState, int setType )250         public static void GetSetApartmentStateTest_ChangeAfterThreadStarted_Windows(
251             Func<Thread, ApartmentState> getApartmentState,
252             Func<Thread, ApartmentState, int> setApartmentState,
253             int setType /* 0 = ApartmentState setter, 1 = SetApartmentState, 2 = TrySetApartmentState */)
254         {
255             ThreadTestHelpers.RunTestInBackgroundThread(() =>
256             {
257                 var t = Thread.CurrentThread;
258                 Assert.Equal(1, setApartmentState(t, ApartmentState.STA - 1));
259                 Assert.Equal(1, setApartmentState(t, ApartmentState.Unknown + 1));
260 
261                 Assert.Equal(ApartmentState.MTA, getApartmentState(t));
262                 Assert.Equal(0, setApartmentState(t, ApartmentState.MTA));
263                 Assert.Equal(ApartmentState.MTA, getApartmentState(t));
264                 Assert.Equal(setType == 0 ? 0 : 2, setApartmentState(t, ApartmentState.STA)); // cannot be changed after thread is started
265                 Assert.Equal(ApartmentState.MTA, getApartmentState(t));
266             });
267         }
268 
269         [Theory]
270         [MemberData(nameof(ApartmentStateTest_MemberData))]
271         [PlatformSpecific(TestPlatforms.Windows)]  // Expected behavior differs on Unix and Windows
272         [ActiveIssue(20766,TargetFrameworkMonikers.UapAot)]
ApartmentStateTest_ChangeBeforeThreadStarted_Windows( Func<Thread, ApartmentState> getApartmentState, Func<Thread, ApartmentState, int> setApartmentState, int setType )273         public static void ApartmentStateTest_ChangeBeforeThreadStarted_Windows(
274             Func<Thread, ApartmentState> getApartmentState,
275             Func<Thread, ApartmentState, int> setApartmentState,
276             int setType /* 0 = ApartmentState setter, 1 = SetApartmentState, 2 = TrySetApartmentState */)
277         {
278             ApartmentState apartmentStateInThread = ApartmentState.Unknown;
279             Thread t = null;
280             t = new Thread(() => apartmentStateInThread = getApartmentState(t));
281             t.IsBackground = true;
282             Assert.Equal(0, setApartmentState(t, ApartmentState.STA));
283             Assert.Equal(ApartmentState.STA, getApartmentState(t));
284             Assert.Equal(setType == 0 ? 0 : 2, setApartmentState(t, ApartmentState.MTA)); // cannot be changed more than once
285             Assert.Equal(ApartmentState.STA, getApartmentState(t));
286             t.Start();
287             Assert.True(t.Join(UnexpectedTimeoutMilliseconds));
288 
289             if (PlatformDetection.IsWindowsNanoServer)
290             {
291                 // Nano server threads are always MTA. If you set the thread to STA
292                 // it will read back as STA but when the thread starts it will read back as MTA.
293                 Assert.Equal(ApartmentState.MTA, apartmentStateInThread);
294             }
295             else
296             {
297                 Assert.Equal(ApartmentState.STA, apartmentStateInThread);
298             }
299         }
300 
301         [Theory]
302         [MemberData(nameof(ApartmentStateTest_MemberData))]
303         [PlatformSpecific(TestPlatforms.AnyUnix)]  // Expected behavior differs on Unix and Windows
ApartmentStateTest_Unix( Func<Thread, ApartmentState> getApartmentState, Func<Thread, ApartmentState, int> setApartmentState, int setType )304         public static void ApartmentStateTest_Unix(
305             Func<Thread, ApartmentState> getApartmentState,
306             Func<Thread, ApartmentState, int> setApartmentState,
307             int setType /* 0 = ApartmentState setter, 1 = SetApartmentState, 2 = TrySetApartmentState */)
308         {
309             var t = new Thread(() => { });
310             Assert.Equal(ApartmentState.Unknown, getApartmentState(t));
311             Assert.Equal(0, setApartmentState(t, ApartmentState.Unknown));
312 
313             int expectedFailure;
314             switch (setType)
315             {
316                 case 0:
317                     expectedFailure = 0; // ApartmentState setter - no exception, but value does not change
318                     break;
319                 case 1:
320                     expectedFailure = 3; // SetApartmentState - InvalidOperationException
321                     break;
322                 default:
323                     expectedFailure = 2; // TrySetApartmentState - returns false
324                     break;
325             }
326             Assert.Equal(expectedFailure, setApartmentState(t, ApartmentState.STA));
327             Assert.Equal(expectedFailure, setApartmentState(t, ApartmentState.MTA));
328         }
329 
330         [Fact]
331         [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
332         [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)]
CurrentCultureTest_SkipOnDesktopFramework()333         public static void CurrentCultureTest_SkipOnDesktopFramework()
334         {
335             // Cannot access culture properties on a thread object from a different thread
336             var t = new Thread(() => { });
337             Assert.Throws<InvalidOperationException>(() => t.CurrentCulture);
338             Assert.Throws<InvalidOperationException>(() => t.CurrentUICulture);
339         }
340 
341         [Fact]
CurrentCultureTest()342         public static void CurrentCultureTest()
343         {
344             ThreadTestHelpers.RunTestInBackgroundThread(() =>
345             {
346                 var t = Thread.CurrentThread;
347                 var originalCulture = CultureInfo.CurrentCulture;
348                 var originalUICulture = CultureInfo.CurrentUICulture;
349                 var otherCulture = CultureInfo.InvariantCulture;
350 
351                 // Culture properties return the same value as those on CultureInfo
352                 Assert.Equal(originalCulture, t.CurrentCulture);
353                 Assert.Equal(originalUICulture, t.CurrentUICulture);
354 
355                 try
356                 {
357                     // Changing culture properties on CultureInfo causes the values of properties on the current thread to change
358                     CultureInfo.CurrentCulture = otherCulture;
359                     CultureInfo.CurrentUICulture = otherCulture;
360                     Assert.Equal(otherCulture, t.CurrentCulture);
361                     Assert.Equal(otherCulture, t.CurrentUICulture);
362 
363                     // Changing culture properties on the current thread causes new values to be returned, and causes the values of
364                     // properties on CultureInfo to change
365                     t.CurrentCulture = originalCulture;
366                     t.CurrentUICulture = originalUICulture;
367                     Assert.Equal(originalCulture, t.CurrentCulture);
368                     Assert.Equal(originalUICulture, t.CurrentUICulture);
369                     Assert.Equal(originalCulture, CultureInfo.CurrentCulture);
370                     Assert.Equal(originalUICulture, CultureInfo.CurrentUICulture);
371                 }
372                 finally
373                 {
374                     CultureInfo.CurrentCulture = originalCulture;
375                     CultureInfo.CurrentUICulture = originalUICulture;
376                 }
377 
378                 Assert.Throws<ArgumentNullException>(() => t.CurrentCulture = null);
379                 Assert.Throws<ArgumentNullException>(() => t.CurrentUICulture = null);
380             });
381         }
382 
383         [Fact]
384         [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
385         [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)]
CurrentPrincipalTest_SkipOnDesktopFramework()386         public static void CurrentPrincipalTest_SkipOnDesktopFramework()
387         {
388             ThreadTestHelpers.RunTestInBackgroundThread(() => Assert.Null(Thread.CurrentPrincipal));
389         }
390 
391         [Fact]
CurrentPrincipalTest()392         public static void CurrentPrincipalTest()
393         {
394             ThreadTestHelpers.RunTestInBackgroundThread(() =>
395             {
396                 var originalPrincipal = Thread.CurrentPrincipal;
397                 var otherPrincipal =
398                     new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[] { string.Empty });
399 
400                 Thread.CurrentPrincipal = otherPrincipal;
401                 Assert.Equal(otherPrincipal, Thread.CurrentPrincipal);
402 
403                 Thread.CurrentPrincipal = originalPrincipal;
404                 Assert.Equal(originalPrincipal, Thread.CurrentPrincipal);
405             });
406         }
407 
408         [Fact]
CurrentThreadTest()409         public static void CurrentThreadTest()
410         {
411             Thread otherThread = null;
412             var t = new Thread(() => otherThread = Thread.CurrentThread);
413             t.IsBackground = true;
414             t.Start();
415             Assert.True(t.Join(UnexpectedTimeoutMilliseconds));
416 
417             Assert.Equal(t, otherThread);
418 
419             var mainThread = Thread.CurrentThread;
420             Assert.NotNull(mainThread);
421             Assert.NotEqual(mainThread, otherThread);
422         }
423 
424         [Fact]
425         [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
426         [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)]
ExecutionContextTest()427         public static void ExecutionContextTest()
428         {
429             ThreadTestHelpers.RunTestInBackgroundThread(
430                 () => Assert.Equal(ExecutionContext.Capture(), Thread.CurrentThread.ExecutionContext));
431         }
432 
433         [Fact]
IsAliveTest()434         public static void IsAliveTest()
435         {
436             var isAliveWhenRunning = false;
437             Thread t = null;
438             t = new Thread(() => isAliveWhenRunning = t.IsAlive);
439             t.IsBackground = true;
440 
441             Assert.False(t.IsAlive);
442             t.Start();
443             Assert.True(t.Join(UnexpectedTimeoutMilliseconds));
444             Assert.True(isAliveWhenRunning);
445             Assert.False(t.IsAlive);
446         }
447 
448         [Fact]
IsBackgroundTest()449         public static void IsBackgroundTest()
450         {
451             var t = new Thread(() => { });
452             Assert.False(t.IsBackground);
453             t.IsBackground = true;
454             Assert.True(t.IsBackground);
455             t.Start();
456             Assert.True(t.Join(UnexpectedTimeoutMilliseconds));
457 
458             // Cannot use this property after the thread is dead
459             Assert.Throws<ThreadStateException>(() => t.IsBackground);
460             Assert.Throws<ThreadStateException>(() => t.IsBackground = false);
461             Assert.Throws<ThreadStateException>(() => t.IsBackground = true);
462 
463             // Verify that the test process can shut down gracefully with a hung background thread
464             t = new Thread(() => Thread.Sleep(Timeout.Infinite));
465             t.IsBackground = true;
466             t.Start();
467         }
468 
469         [Fact]
IsThreadPoolThreadTest()470         public static void IsThreadPoolThreadTest()
471         {
472             var isThreadPoolThread = false;
473             Thread t = null;
474             t = new Thread(() => { isThreadPoolThread = t.IsThreadPoolThread; });
475             t.IsBackground = true;
476             Assert.False(t.IsThreadPoolThread);
477 
478             t.Start();
479             Assert.True(t.Join(UnexpectedTimeoutMilliseconds));
480             Assert.False(isThreadPoolThread);
481 
482             var e = new ManualResetEvent(false);
483             ThreadPool.QueueUserWorkItem(
484                 state =>
485                 {
486                     isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread;
487                     e.Set();
488                 });
489             e.CheckedWait();
490             Assert.True(isThreadPoolThread);
491         }
492 
493         [Fact]
ManagedThreadIdTest()494         public static void ManagedThreadIdTest()
495         {
496             var e = new ManualResetEvent(false);
497             Action waitForThread;
498             var t = ThreadTestHelpers.CreateGuardedThread(out waitForThread, e.CheckedWait);
499             t.IsBackground = true;
500             t.Start();
501             Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, t.ManagedThreadId);
502             e.Set();
503             waitForThread();
504         }
505 
506         [Fact]
NameTest()507         public static void NameTest()
508         {
509             var t = new Thread(() => { });
510             Assert.Null(t.Name);
511             t.Name = "a";
512             Assert.Equal("a", t.Name);
513             Assert.Throws<InvalidOperationException>(() => t.Name = "b");
514             Assert.Equal("a", t.Name);
515         }
516 
517         [Fact]
PriorityTest()518         public static void PriorityTest()
519         {
520             var e = new ManualResetEvent(false);
521             Action waitForThread;
522             var t = ThreadTestHelpers.CreateGuardedThread(out waitForThread, e.CheckedWait);
523             t.IsBackground = true;
524             Assert.Equal(ThreadPriority.Normal, t.Priority);
525             t.Priority = ThreadPriority.AboveNormal;
526             Assert.Equal(ThreadPriority.AboveNormal, t.Priority);
527             t.Start();
528             Assert.Equal(ThreadPriority.AboveNormal, t.Priority);
529             t.Priority = ThreadPriority.Normal;
530             Assert.Equal(ThreadPriority.Normal, t.Priority);
531             e.Set();
532             waitForThread();
533         }
534 
535         [Fact]
536         [ActiveIssue(20766, TargetFrameworkMonikers.UapAot)]
ThreadStateTest()537         public static void ThreadStateTest()
538         {
539             var e0 = new ManualResetEvent(false);
540             var e1 = new AutoResetEvent(false);
541             Action waitForThread;
542             var t =
543                 ThreadTestHelpers.CreateGuardedThread(out waitForThread, () =>
544                 {
545                     e0.CheckedWait();
546                     ThreadTestHelpers.WaitForConditionWithoutBlocking(() => e1.WaitOne(0));
547                 });
548             Assert.Equal(ThreadState.Unstarted, t.ThreadState);
549             t.IsBackground = true;
550             Assert.Equal(ThreadState.Unstarted | ThreadState.Background, t.ThreadState);
551 
552             t.Start();
553             ThreadTestHelpers.WaitForCondition(() => t.ThreadState == (ThreadState.WaitSleepJoin | ThreadState.Background));
554 
555             e0.Set();
556             ThreadTestHelpers.WaitForCondition(() => t.ThreadState == (ThreadState.Running | ThreadState.Background));
557 
558             e1.Set();
559             waitForThread();
560             Assert.Equal(ThreadState.Stopped, t.ThreadState);
561 
562             t = ThreadTestHelpers.CreateGuardedThread(
563                     out waitForThread,
564                     () => ThreadTestHelpers.WaitForConditionWithoutBlocking(() => e1.WaitOne(0)));
565             t.Start();
566             ThreadTestHelpers.WaitForCondition(() => t.ThreadState == ThreadState.Running);
567 
568             e1.Set();
569             waitForThread();
570             Assert.Equal(ThreadState.Stopped, t.ThreadState);
571         }
572 
573         [Fact]
574         [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
575         [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)]
AbortSuspendTest()576         public static void AbortSuspendTest()
577         {
578             var e = new ManualResetEvent(false);
579             Action waitForThread;
580             var t = ThreadTestHelpers.CreateGuardedThread(out waitForThread, e.CheckedWait);
581             t.IsBackground = true;
582 
583             Action verify = () =>
584             {
585                 Assert.Throws<PlatformNotSupportedException>(() => t.Abort());
586                 Assert.Throws<PlatformNotSupportedException>(() => t.Abort(t));
587 #pragma warning disable 618 // Obsolete members
588                 Assert.Throws<PlatformNotSupportedException>(() => t.Suspend());
589                 Assert.Throws<PlatformNotSupportedException>(() => t.Resume());
590 #pragma warning restore 618 // Obsolete members
591             };
592             verify();
593 
594             t.Start();
595             verify();
596 
597             e.Set();
598             waitForThread();
599 
600             Assert.Throws<PlatformNotSupportedException>(() => Thread.ResetAbort());
601         }
602 
VerifyLocalDataSlot(LocalDataStoreSlot slot)603         private static void VerifyLocalDataSlot(LocalDataStoreSlot slot)
604         {
605             Assert.NotNull(slot);
606 
607             var waitForThreadArray = new Action[2];
608             var threadArray = new Thread[2];
609             var barrier = new Barrier(threadArray.Length);
610             var cancellationTokenSource = new CancellationTokenSource();
611             var cancellationToken = cancellationTokenSource.Token;
612 
613             Func<bool> barrierSignalAndWait =
614                 () =>
615                 {
616                     try
617                     {
618                         Assert.True(barrier.SignalAndWait(UnexpectedTimeoutMilliseconds, cancellationToken));
619                     }
620                     catch (OperationCanceledException)
621                     {
622                         return false;
623                     }
624                     return true;
625                 };
626 
627             Action<int> threadMain =
628                 threadIndex =>
629                 {
630                     try
631                     {
632                         Assert.Null(Thread.GetData(slot));
633                         if (!barrierSignalAndWait())
634                         {
635                             return;
636                         }
637 
638                         if (threadIndex == 0)
639                         {
640                             Thread.SetData(slot, threadIndex);
641                         }
642                         if (!barrierSignalAndWait())
643                         {
644                             return;
645                         }
646 
647                         if (threadIndex == 0)
648                         {
649                             Assert.Equal(threadIndex, Thread.GetData(slot));
650                         }
651                         else
652                         {
653                             Assert.Null(Thread.GetData(slot));
654                         }
655                         if (!barrierSignalAndWait())
656                         {
657                             return;
658                         }
659 
660                         if (threadIndex != 0)
661                         {
662                             Thread.SetData(slot, threadIndex);
663                         }
664                         if (!barrierSignalAndWait())
665                         {
666                             return;
667                         }
668 
669                         Assert.Equal(threadIndex, Thread.GetData(slot));
670                         if (!barrierSignalAndWait())
671                         {
672                             return;
673                         }
674                     }
675                     catch (Exception ex)
676                     {
677                         cancellationTokenSource.Cancel();
678                         throw new TargetInvocationException(ex);
679                     }
680                 };
681 
682             for (int i = 0; i < threadArray.Length; ++i)
683             {
684                 threadArray[i] = ThreadTestHelpers.CreateGuardedThread(out waitForThreadArray[i], () => threadMain(i));
685                 threadArray[i].IsBackground = true;
686                 threadArray[i].Start();
687             }
688 
689             foreach (var waitForThread in waitForThreadArray)
690             {
691                 waitForThread();
692             }
693         }
694 
695         [Fact]
LocalDataSlotTest()696         public static void LocalDataSlotTest()
697         {
698             var slot = Thread.AllocateDataSlot();
699             var slot2 = Thread.AllocateDataSlot();
700             Assert.NotEqual(slot, slot2);
701             VerifyLocalDataSlot(slot);
702             VerifyLocalDataSlot(slot2);
703 
704             var slotName = "System.Threading.Threads.Tests.LocalDataSlotTest";
705             var slotName2 = slotName + ".2";
706             var invalidSlotName = slotName + ".Invalid";
707 
708             try
709             {
710                 // AllocateNamedDataSlot allocates
711                 slot = Thread.AllocateNamedDataSlot(slotName);
712                 AssertExtensions.Throws<ArgumentException>(null, () => Thread.AllocateNamedDataSlot(slotName));
713                 slot2 = Thread.AllocateNamedDataSlot(slotName2);
714                 Assert.NotEqual(slot, slot2);
715                 VerifyLocalDataSlot(slot);
716                 VerifyLocalDataSlot(slot2);
717 
718                 // Free the same slot twice, should be fine
719                 Thread.FreeNamedDataSlot(slotName2);
720                 Thread.FreeNamedDataSlot(slotName2);
721             }
722             catch (Exception ex)
723             {
724                 Thread.FreeNamedDataSlot(slotName);
725                 Thread.FreeNamedDataSlot(slotName2);
726                 throw new TargetInvocationException(ex);
727             }
728 
729             try
730             {
731                 // GetNamedDataSlot gets or allocates
732                 var tempSlot = Thread.GetNamedDataSlot(slotName);
733                 Assert.Equal(slot, tempSlot);
734                 tempSlot = Thread.GetNamedDataSlot(slotName2);
735                 Assert.NotEqual(slot2, tempSlot);
736                 slot2 = tempSlot;
737                 Assert.NotEqual(slot, slot2);
738                 VerifyLocalDataSlot(slot);
739                 VerifyLocalDataSlot(slot2);
740             }
741             finally
742             {
743                 Thread.FreeNamedDataSlot(slotName);
744                 Thread.FreeNamedDataSlot(slotName2);
745             }
746 
747             try
748             {
749                 // A named slot can be used after the name is freed, since only the name is freed, not the slot
750                 slot = Thread.AllocateNamedDataSlot(slotName);
751                 Thread.FreeNamedDataSlot(slotName);
752                 slot2 = Thread.AllocateNamedDataSlot(slotName); // same name
753                 Thread.FreeNamedDataSlot(slotName);
754                 Assert.NotEqual(slot, slot2);
755                 VerifyLocalDataSlot(slot);
756                 VerifyLocalDataSlot(slot2);
757             }
758             finally
759             {
760                 Thread.FreeNamedDataSlot(slotName);
761                 Thread.FreeNamedDataSlot(slotName2);
762             }
763         }
764 
765         [Fact]
766         [ActiveIssue(20766, TargetFrameworkMonikers.UapAot)]
InterruptTest()767         public static void InterruptTest()
768         {
769             // Interrupting a thread that is not blocked does not do anything, but once the thread starts blocking, it gets
770             // interrupted and does not auto-reset the signaled event
771             var threadReady = new AutoResetEvent(false);
772             var continueThread = new AutoResetEvent(false);
773             bool continueThreadBool = false;
774             Action waitForThread;
775             var t =
776                 ThreadTestHelpers.CreateGuardedThread(out waitForThread, () =>
777                 {
778                     threadReady.Set();
779                     ThreadTestHelpers.WaitForConditionWithoutBlocking(() => Volatile.Read(ref continueThreadBool));
780                     threadReady.Set();
781                     Assert.Throws<ThreadInterruptedException>(() => continueThread.CheckedWait());
782                 });
783             t.IsBackground = true;
784             t.Start();
785             threadReady.CheckedWait();
786             continueThread.Set();
787             t.Interrupt();
788             Assert.False(threadReady.WaitOne(ExpectedTimeoutMilliseconds));
789             Volatile.Write(ref continueThreadBool, true);
790             waitForThread();
791             Assert.True(continueThread.WaitOne(0));
792 
793             // Interrupting a dead thread does nothing
794             t.Interrupt();
795 
796             // Interrupting an unstarted thread causes the thread to be interrupted after it is started and starts blocking
797             t = ThreadTestHelpers.CreateGuardedThread(out waitForThread, () =>
798                     Assert.Throws<ThreadInterruptedException>(() => continueThread.CheckedWait()));
799             t.IsBackground = true;
800             t.Interrupt();
801             t.Start();
802             waitForThread();
803 
804             // A thread that is already blocked on a synchronization primitive unblocks immediately
805             continueThread.Reset();
806             t = ThreadTestHelpers.CreateGuardedThread(out waitForThread, () =>
807                     Assert.Throws<ThreadInterruptedException>(() => continueThread.CheckedWait()));
808             t.IsBackground = true;
809             t.Start();
810             ThreadTestHelpers.WaitForCondition(() => (t.ThreadState & ThreadState.WaitSleepJoin) != 0);
811             t.Interrupt();
812             waitForThread();
813         }
814 
815         [Fact]
816         [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
817         [ActiveIssue(20766,TargetFrameworkMonikers.UapAot)]
InterruptInFinallyBlockTest_SkipOnDesktopFramework()818         public static void InterruptInFinallyBlockTest_SkipOnDesktopFramework()
819         {
820             // A wait in a finally block can be interrupted. The desktop framework applies the same rules as thread abort, and
821             // does not allow thread interrupt in a finally block. There is nothing special about thread interrupt that requires
822             // not allowing it in finally blocks, so this behavior has changed in .NET Core.
823             var continueThread = new AutoResetEvent(false);
824             Action waitForThread;
825             Thread t =
826                 ThreadTestHelpers.CreateGuardedThread(out waitForThread, () =>
827                 {
828                     try
829                     {
830                     }
831                     finally
832                     {
833                         Assert.Throws<ThreadInterruptedException>(() => continueThread.CheckedWait());
834                     }
835                 });
836             t.IsBackground = true;
837             t.Start();
838             t.Interrupt();
839             waitForThread();
840         }
841 
842         [Fact]
JoinTest()843         public static void JoinTest()
844         {
845             var threadReady = new ManualResetEvent(false);
846             var continueThread = new ManualResetEvent(false);
847             Action waitForThread;
848             var t =
849                 ThreadTestHelpers.CreateGuardedThread(out waitForThread, () =>
850                 {
851                     threadReady.Set();
852                     continueThread.CheckedWait();
853                     Thread.Sleep(ExpectedTimeoutMilliseconds);
854                 });
855             t.IsBackground = true;
856 
857             Assert.Throws<ArgumentOutOfRangeException>(() => t.Join(-2));
858             Assert.Throws<ArgumentOutOfRangeException>(() => t.Join(TimeSpan.FromMilliseconds(-2)));
859             Assert.Throws<ArgumentOutOfRangeException>(() => t.Join(TimeSpan.FromMilliseconds((double)int.MaxValue + 1)));
860 
861             Assert.Throws<ThreadStateException>(() => t.Join());
862             Assert.Throws<ThreadStateException>(() => t.Join(UnexpectedTimeoutMilliseconds));
863             Assert.Throws<ThreadStateException>(() => t.Join(TimeSpan.FromMilliseconds(UnexpectedTimeoutMilliseconds)));
864 
865             t.Start();
866             threadReady.CheckedWait();
867             Assert.False(t.Join(ExpectedTimeoutMilliseconds));
868             Assert.False(t.Join(TimeSpan.FromMilliseconds(ExpectedTimeoutMilliseconds)));
869             continueThread.Set();
870             waitForThread();
871 
872             Assert.True(t.Join(0));
873             Assert.True(t.Join(TimeSpan.Zero));
874         }
875 
876         [Fact]
SleepTest()877         public static void SleepTest()
878         {
879             Assert.Throws<ArgumentOutOfRangeException>(() => Thread.Sleep(-2));
880             Assert.Throws<ArgumentOutOfRangeException>(() => Thread.Sleep(TimeSpan.FromMilliseconds(-2)));
881             Assert.Throws<ArgumentOutOfRangeException>(() => Thread.Sleep(TimeSpan.FromMilliseconds((double)int.MaxValue + 1)));
882 
883             Thread.Sleep(0);
884 
885             var stopwatch = Stopwatch.StartNew();
886             Thread.Sleep(500);
887             stopwatch.Stop();
888             Assert.InRange((int)stopwatch.ElapsedMilliseconds, 100, int.MaxValue);
889         }
890 
891         [Fact]
StartTest()892         public static void StartTest()
893         {
894             var e = new AutoResetEvent(false);
895             Action waitForThread;
896             var t = ThreadTestHelpers.CreateGuardedThread(out waitForThread, e.CheckedWait);
897             t.IsBackground = true;
898             Assert.Throws<InvalidOperationException>(() => t.Start(null));
899             Assert.Throws<InvalidOperationException>(() => t.Start(t));
900             t.Start();
901             Assert.Throws<ThreadStateException>(() => t.Start());
902             e.Set();
903             waitForThread();
904             Assert.Throws<ThreadStateException>(() => t.Start());
905 
906             t = ThreadTestHelpers.CreateGuardedThread(out waitForThread, parameter => e.CheckedWait());
907             t.IsBackground = true;
908             t.Start();
909             Assert.Throws<ThreadStateException>(() => t.Start());
910             Assert.Throws<ThreadStateException>(() => t.Start(null));
911             Assert.Throws<ThreadStateException>(() => t.Start(t));
912             e.Set();
913             waitForThread();
914             Assert.Throws<ThreadStateException>(() => t.Start());
915             Assert.Throws<ThreadStateException>(() => t.Start(null));
916             Assert.Throws<ThreadStateException>(() => t.Start(t));
917 
918             t = ThreadTestHelpers.CreateGuardedThread(out waitForThread, parameter => Assert.Null(parameter));
919             t.IsBackground = true;
920             t.Start();
921             waitForThread();
922 
923             t = ThreadTestHelpers.CreateGuardedThread(out waitForThread, parameter => Assert.Null(parameter));
924             t.IsBackground = true;
925             t.Start(null);
926             waitForThread();
927 
928             t = ThreadTestHelpers.CreateGuardedThread(out waitForThread, parameter => Assert.Equal(t, parameter));
929             t.IsBackground = true;
930             t.Start(t);
931             waitForThread();
932         }
933 
934         [Fact]
935         [ActiveIssue(20766,TargetFrameworkMonikers.UapAot)]
936         [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)]
MiscellaneousTest()937         public static void MiscellaneousTest()
938         {
939             Thread.BeginCriticalRegion();
940             Thread.EndCriticalRegion();
941             Thread.BeginThreadAffinity();
942             Thread.EndThreadAffinity();
943 
944             ThreadTestHelpers.RunTestInBackgroundThread(() =>
945             {
946                 // TODO: Port tests for these once all of the necessary interop APIs are available
947                 Thread.CurrentThread.DisableComObjectEagerCleanup();
948                 Marshal.CleanupUnusedObjectsInCurrentContext();
949             });
950 
951 #pragma warning disable 618 // obsolete members
952             Assert.Throws<InvalidOperationException>(() => Thread.CurrentThread.GetCompressedStack());
953             Assert.Throws<InvalidOperationException>(() => Thread.CurrentThread.SetCompressedStack(CompressedStack.Capture()));
954 #pragma warning restore 618 // obsolete members
955 
956             Thread.MemoryBarrier();
957 
958             var ad = Thread.GetDomain();
959             Assert.NotNull(ad);
960             Assert.Equal(AppDomain.CurrentDomain, ad);
961             Assert.Equal(ad.Id, Thread.GetDomainID());
962 
963             Thread.SpinWait(int.MinValue);
964             Thread.SpinWait(-1);
965             Thread.SpinWait(0);
966             Thread.SpinWait(1);
967             Thread.Yield();
968         }
969     }
970 }
971