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