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.Concurrent; 6 using System.Diagnostics; 7 using System.Diagnostics.Tracing; 8 using System.Threading; 9 using System.Threading.Tasks; 10 using Xunit; 11 using Xunit.Abstractions; 12 13 namespace System.Transactions.Tests 14 { 15 public class AsyncTransactionScopeTests 16 { 17 private readonly ITestOutputHelper output; 18 19 // Number of threads to create 20 private const int iterations = 5; 21 22 // The work queue that requests will be placed in to be serviced by the background thread 23 private static BlockingCollection<Tuple<int, TaskCompletionSource<object>, Transaction>> s_workQueue = new BlockingCollection<Tuple<int, TaskCompletionSource<object>, Transaction>>(); 24 25 private static bool s_throwExceptionDefaultOrBeforeAwait; 26 private static bool s_throwExceptionAfterAwait; 27 AsyncTransactionScopeTests(ITestOutputHelper output)28 public AsyncTransactionScopeTests(ITestOutputHelper output) 29 { 30 this.output = output; 31 } 32 33 /// <summary> 34 /// This test case will verify various Async TransactionScope usage with task and async/await and also nested mixed mode(legacy TS and async TS) usage. 35 /// </summary> 36 [Theory] 37 [InlineData(0)] 38 [InlineData(1)] 39 [InlineData(2)] 40 [InlineData(3)] 41 [InlineData(4)] 42 [InlineData(5)] 43 [InlineData(6)] 44 [InlineData(7)] 45 [InlineData(8)] 46 [InlineData(9)] 47 [InlineData(10)] 48 [InlineData(11)] 49 [InlineData(12)] 50 [InlineData(13)] 51 [InlineData(14)] 52 [InlineData(15)] 53 [InlineData(16)] 54 [InlineData(17)] 55 [InlineData(18)] 56 [InlineData(19)] 57 [InlineData(20)] 58 [InlineData(21)] 59 [InlineData(22)] 60 [InlineData(23)] 61 [InlineData(24)] 62 [InlineData(25)] 63 [InlineData(26)] 64 [InlineData(27)] 65 [InlineData(28)] 66 [InlineData(29)] 67 [InlineData(30)] 68 [InlineData(31)] 69 [InlineData(32)] 70 [InlineData(33)] 71 [InlineData(34)] 72 [InlineData(35)] 73 [InlineData(36)] 74 [InlineData(37)] 75 [InlineData(38)] 76 [InlineData(39)] 77 [InlineData(40)] 78 [InlineData(41)] 79 [InlineData(42)] 80 [InlineData(43)] 81 [InlineData(44)] 82 [InlineData(45)] 83 [InlineData(46)] 84 [InlineData(47)] 85 [InlineData(48)] 86 [InlineData(49)] 87 [InlineData(50)] 88 [InlineData(51)] 89 [InlineData(52)] 90 [InlineData(53)] 91 [InlineData(54)] 92 AsyncTSTest(int variation)93 public void AsyncTSTest(int variation) 94 { 95 using (var listener = new TestEventListener(new Guid("8ac2d80a-1f1a-431b-ace4-bff8824aef0b"), System.Diagnostics.Tracing.EventLevel.Verbose)) 96 { 97 var events = new ConcurrentQueue<EventWrittenEventArgs>(); 98 99 bool success = false; 100 try 101 { 102 listener.RunWithCallback(events.Enqueue, () => 103 { 104 switch (variation) 105 { 106 // Running exception test first to make sure any unintentional leak in ambient transaction during exception will be detected when subsequent test are run. 107 case 0: 108 { 109 HandleException(true, false, () => DoSyncTxWork(true, null)); 110 break; 111 } 112 case 1: 113 { 114 HandleException(true, false, () => AssertTransactionNullAndWaitTask(DoAsyncTxWorkAsync(true, null))); 115 break; 116 } 117 case 2: 118 { 119 HandleException(true, false, () => SyncTSL2NestedTxWork(false, false, true, null)); 120 break; 121 } 122 case 3: 123 { 124 HandleException(false, true, () => AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, true, false, null))); 125 break; 126 } 127 case 4: 128 { 129 HandleException(true, false, () => AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, true, false, false, null))); 130 break; 131 } 132 case 5: 133 { 134 HandleException(true, false, () => AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, true, true, false, true, false, null))); 135 break; 136 } 137 case 6: 138 { 139 HandleException(false, true, () => AssertTransactionNullAndWaitTask(DoTaskWorkAsync(false, false, null))); 140 break; 141 } 142 case 7: 143 { 144 HandleException(false, true, () => SyncTSTaskWork(false, null)); 145 break; 146 } 147 148 // The following test has Task under TransactionScope and has few variations. 149 case 8: 150 { 151 DoTaskUnderAsyncTS(false, null); 152 break; 153 } 154 case 9: 155 { 156 Task.Factory.StartNew(() => DoTaskUnderAsyncTS(false, null)).Wait(); 157 break; 158 } 159 case 10: 160 { 161 SyncTSDoTaskUnderAsyncTS(false, false, null); 162 break; 163 } 164 case 11: 165 { 166 SyncTSDoTaskUnderAsyncTS(false, true, null); 167 break; 168 } 169 case 12: 170 { 171 Task.Factory.StartNew(() => SyncTSDoTaskUnderAsyncTS(false, true, null)).Wait(); 172 break; 173 } 174 175 // Simple Sync TS test 176 case 13: 177 { 178 DoSyncTxWork(false, null); 179 break; 180 } 181 case 14: 182 { 183 DoSyncTxWork(true, null); 184 break; 185 } 186 187 // Simple Async TS test. "await" points explicitly switches threads across continuations. 188 case 15: 189 { 190 AssertTransactionNullAndWaitTask(DoAsyncTxWorkAsync(false, null)); 191 break; 192 } 193 case 16: 194 { 195 AssertTransactionNullAndWaitTask(DoAsyncTxWorkAsync(true, null)); 196 break; 197 } 198 199 // Nested TS test. Parent is Sync TS, child TS can be sync or async. 200 case 17: 201 { 202 SyncTSL2NestedTxWork(false, false, false, null); 203 break; 204 } 205 case 18: 206 { 207 SyncTSL2NestedTxWork(false, false, true, null); 208 break; 209 } 210 case 19: 211 { 212 SyncTSL2NestedTxWork(false, true, false, null); 213 break; 214 } 215 case 20: 216 { 217 SyncTSL2NestedTxWork(false, true, true, null); 218 break; 219 } 220 case 21: 221 { 222 SyncTSL2NestedTxWork(true, false, false, null); 223 break; 224 } 225 case 22: 226 { 227 SyncTSL2NestedTxWork(true, false, true, null); 228 break; 229 } 230 case 23: 231 { 232 SyncTSL2NestedTxWork(true, true, false, null); 233 break; 234 } 235 case 24: 236 { 237 SyncTSL2NestedTxWork(true, true, true, null); 238 break; 239 } 240 241 // 2 level deep nested TS test. Parent is Aync TS, child TS can be sync or async. 242 case 25: 243 { 244 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, false, false, false, false, null)); 245 break; 246 } 247 case 26: 248 { 249 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, false, true, false, false, null)); 250 break; 251 } 252 case 27: 253 { 254 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, false, false, null)); 255 break; 256 } 257 case 28: 258 { 259 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, true, false, false, null)); 260 break; 261 } 262 case 29: 263 { 264 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(true, false, false, false, false, null)); 265 break; 266 } 267 case 30: 268 { 269 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(true, false, true, false, false, null)); 270 break; 271 } 272 case 31: 273 { 274 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(true, true, false, false, false, null)); 275 break; 276 } 277 case 32: 278 { 279 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(true, true, true, false, false, null)); 280 break; 281 } 282 283 // Introduce various "await" points to switch threads before/after child TransactionScope. 284 // Introduce some Task variations by running the test under Task. 285 case 33: 286 { 287 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, true, false, null)); 288 break; 289 } 290 case 34: 291 { 292 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, false, true, null)); 293 break; 294 } 295 case 35: 296 { 297 Task.Factory.StartNew(() => AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, false, true, null))).Wait(); 298 break; 299 } 300 case 36: 301 { 302 AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, true, true, null)); 303 break; 304 } 305 306 // 3 level deep nested TS test. 307 case 37: 308 { 309 SyncTSL3AsyncTSL2NestedTxWork(false, false, true, false, false, true, null); 310 break; 311 } 312 case 38: 313 { 314 Task.Factory.StartNew(() => SyncTSL3AsyncTSL2NestedTxWork(false, false, true, false, false, true, null)).Wait(); 315 break; 316 } 317 case 39: 318 { 319 SyncTSL3AsyncTSL2NestedTxWork(false, false, true, false, true, false, null); 320 break; 321 } 322 323 case 40: 324 { 325 AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, false, false, true, false, false, null)); 326 break; 327 } 328 case 41: 329 { 330 AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, false, true, false, false, true, null)); 331 break; 332 } 333 case 42: 334 { 335 AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, false, true, false, true, false, null)); 336 break; 337 } 338 case 43: 339 { 340 AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, false, true, false, true, true, null)); 341 break; 342 } 343 case 44: 344 { 345 Task.Factory.StartNew(() => AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, false, true, false, true, true, null))).Wait(); 346 break; 347 } 348 349 case 45: 350 { 351 AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, true, true, false, false, true, null)); 352 break; 353 } 354 case 46: 355 { 356 AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, true, true, false, true, false, null)); 357 break; 358 } 359 case 47: 360 { 361 AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, true, true, false, true, true, null)); 362 break; 363 } 364 case 48: 365 { 366 Task.Factory.StartNew(() => AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, true, true, false, true, true, null))).Wait(); 367 break; 368 } 369 370 // Have bunch of parallel tasks running various nested TS test cases. There parallel tasks are wrapped by a TransactionScope. 371 case 49: 372 { 373 AssertTransactionNullAndWaitTask(DoTaskWorkAsync(false, false, null)); 374 break; 375 } 376 case 50: 377 { 378 SyncTSTaskWork(false, null); 379 break; 380 } 381 case 51: 382 { 383 SyncTSTaskWork(true, null); 384 break; 385 } 386 case 52: 387 { 388 AssertTransactionNullAndWaitTask(DoAsyncTSTaskWorkAsync(false, false, null)); 389 break; 390 } 391 case 53: 392 { 393 AssertTransactionNullAndWaitTask(DoAsyncTSTaskWorkAsync(true, false, null)); 394 break; 395 } 396 397 // Final test - wrap the DoAsyncTSTaskWorkAsync in syncronous scope 398 case 54: 399 { 400 string txId1 = null; 401 string txId2 = null; 402 403 using (TransactionScope scope = new TransactionScope()) 404 { 405 txId1 = AssertAndGetCurrentTransactionId(); 406 Task task = DoAsyncTSTaskWorkAsync(false, false, txId1); 407 txId2 = AssertAndGetCurrentTransactionId(); 408 task.Wait(); 409 scope.Complete(); 410 } 411 412 VerifyTxId(false, null, txId1, txId2); 413 break; 414 } 415 } 416 }); 417 success = true; 418 } 419 finally 420 { 421 if (!success) 422 { 423 HelperFunctions.DisplaySysTxTracing(output, events); 424 } 425 } 426 } 427 } 428 429 [Theory] 430 [InlineData(true, false, null)] 431 [InlineData(true, true, null)] AsyncTSAndDependantClone(bool requiresNew, bool syncronizeScope, string txId)432 public void AsyncTSAndDependantClone(bool requiresNew, bool syncronizeScope, string txId) 433 { 434 string txId1 = null; 435 string txId2 = null; 436 string txId3 = null; 437 string txId4 = null; 438 string txId5 = null; 439 string txId6 = null; 440 string txId7 = null; 441 Task task2; 442 443 AssertTransaction(txId); 444 445 using (TransactionScope scope1 = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) 446 { 447 txId1 = AssertAndGetCurrentTransactionId(); 448 DependentTransaction dependentTx = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete); 449 Task task1 = Task.Run(delegate 450 { 451 try 452 { 453 // Since we use BlockCommitUntilComplete dependent transaction to syncronize the root TransactionScope, the ambient Tx may not be available and will be be disposed and block on Commit. 454 // The flag will ensure we explicitly syncronize before disposing the root TransactionScope and the ambient transaction will still be available in the Task. 455 if (syncronizeScope) 456 { 457 txId2 = AssertAndGetCurrentTransactionId(); 458 } 459 460 using (TransactionScope scope2 = new TransactionScope(dependentTx)) 461 { 462 txId3 = AssertAndGetCurrentTransactionId(); 463 Task.Delay(10).Wait(); 464 scope2.Complete(); 465 } 466 467 if (syncronizeScope) 468 { 469 txId4 = AssertAndGetCurrentTransactionId(); 470 } 471 } 472 finally 473 { 474 dependentTx.Complete(); 475 dependentTx.Dispose(); 476 } 477 478 if (syncronizeScope) 479 { 480 txId5 = AssertAndGetCurrentTransactionId(); 481 } 482 }); 483 484 task2 = Task.Run(delegate 485 { 486 using (TransactionScope scope3 = new TransactionScope(TransactionScopeOption.RequiresNew)) 487 { 488 txId7 = AssertAndGetCurrentTransactionId(); 489 scope3.Complete(); 490 } 491 }); 492 493 txId6 = AssertAndGetCurrentTransactionId(); 494 495 if (syncronizeScope) 496 { 497 task1.Wait(); 498 } 499 scope1.Complete(); 500 } 501 502 task2.Wait(); 503 504 VerifyTxId(requiresNew, txId, txId1, txId6); 505 506 Assert.Equal(txId1, txId3); 507 Assert.Equal(txId3, txId6); 508 if (syncronizeScope) 509 { 510 Assert.Equal(txId1, txId2); 511 Assert.Equal(txId1, txId4); 512 Assert.Equal(txId4, txId5); 513 } 514 Assert.NotEqual(txId1, txId7); 515 516 AssertTransaction(txId); 517 } 518 519 [Theory] 520 [InlineData(true, false, null)] 521 [InlineData(true, true, null)] NestedAsyncTSAndDependantClone(bool parentrequiresNew, bool childRequiresNew, string txId)522 public void NestedAsyncTSAndDependantClone(bool parentrequiresNew, bool childRequiresNew, string txId) 523 { 524 string txId1; 525 string txId2; 526 527 AssertTransaction(txId); 528 529 using (TransactionScope scope = new TransactionScope(parentrequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required)) 530 { 531 txId1 = AssertAndGetCurrentTransactionId(); 532 AsyncTSAndDependantClone(childRequiresNew, true, txId1); 533 txId2 = AssertAndGetCurrentTransactionId(); 534 scope.Complete(); 535 } 536 537 AssertTransaction(txId); 538 539 VerifyTxId(parentrequiresNew, txId, txId1, txId2); 540 } 541 DoAsyncTSTaskWorkAsync(bool requiresNew, bool parentAsync, string txId)542 private async Task DoAsyncTSTaskWorkAsync(bool requiresNew, bool parentAsync, string txId) 543 { 544 string txId1 = null; 545 string txId2 = null; 546 string txId3 = null; 547 string txId4 = null; 548 string txId5 = null; 549 550 Task task1; 551 int startThreadId, endThreadId; 552 bool parentScopePresent = false; 553 554 if (!string.IsNullOrEmpty(txId)) 555 { 556 parentScopePresent = true; 557 } 558 559 startThreadId = Environment.CurrentManagedThreadId; 560 561 AssertTransaction(txId); 562 563 using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) 564 { 565 txId1 = AssertAndGetCurrentTransactionId(); 566 567 task1 = Task.Run(delegate 568 { 569 txId3 = AssertAndGetCurrentTransactionId(); 570 SyncTSTaskWork(requiresNew, txId1); 571 txId4 = AssertAndGetCurrentTransactionId(); 572 }); 573 574 await DoTaskWorkAsync(requiresNew, true, txId1); 575 txId5 = AssertAndGetCurrentTransactionId(); 576 577 await task1; 578 579 txId2 = AssertAndGetCurrentTransactionId(); 580 581 scope.Complete(); 582 } 583 584 VerifyTxId(requiresNew, txId, txId1, txId2); 585 Assert.Equal(txId1, txId5); 586 587 endThreadId = Environment.CurrentManagedThreadId; 588 589 if (parentScopePresent) 590 { 591 if (parentAsync) 592 { 593 AssertTransaction(txId); 594 } 595 else 596 { 597 if (startThreadId != endThreadId) 598 { 599 AssertTransactionNull(); 600 } 601 else 602 { 603 AssertTransaction(txId); 604 } 605 } 606 } 607 else 608 { 609 AssertTransactionNull(); 610 } 611 } 612 SyncTSTaskWork(bool requiresNew, string txId)613 private void SyncTSTaskWork(bool requiresNew, string txId) 614 { 615 string txId1 = null; 616 string txId2 = null; 617 618 using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required)) 619 { 620 txId1 = AssertAndGetCurrentTransactionId(); 621 Task task = DoTaskWorkAsync(requiresNew, false, txId1); 622 txId2 = AssertAndGetCurrentTransactionId(); 623 task.Wait(); 624 scope.Complete(); 625 } 626 627 VerifyTxId(requiresNew, txId, txId1, txId2); 628 } 629 DoTaskWorkAsync(bool requiresNew, bool parentAsync, string txId)630 private async Task DoTaskWorkAsync(bool requiresNew, bool parentAsync, string txId) 631 { 632 string txId1 = null; 633 string txId2 = null; 634 string txId3 = null; 635 string txId4 = null; 636 string txId5 = null; 637 string txId6 = null; 638 string txId7 = null; 639 string txId8 = null; 640 string txId9 = null; 641 string txId10 = null; 642 string txId11 = null; 643 string txId12 = null; 644 string txId13 = null; 645 string txId14 = null; 646 string txId15 = null; 647 648 Task task1, task2, task3, task4, task5, task6; 649 int startThreadId, endThreadId; 650 bool parentScopePresent = false; 651 652 if (!string.IsNullOrEmpty(txId)) 653 { 654 parentScopePresent = true; 655 } 656 657 startThreadId = Environment.CurrentManagedThreadId; 658 659 AssertTransaction(txId); 660 661 using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) 662 { 663 txId1 = AssertAndGetCurrentTransactionId(); 664 665 task1 = Task.Run(async delegate 666 { 667 txId3 = AssertAndGetCurrentTransactionId(); 668 await DoAsyncTSL2NestedTxWorkAsync(false, false, true, false, false, txId1); 669 txId4 = AssertAndGetCurrentTransactionId(); 670 }); 671 672 task2 = Task.Run(async delegate 673 { 674 txId5 = AssertAndGetCurrentTransactionId(); 675 await DoAsyncTSL2NestedTxWorkAsync(true, false, true, false, false, txId5); 676 677 txId6 = AssertAndGetCurrentTransactionId(); 678 }); 679 680 task3 = Task.Run(delegate 681 { 682 txId7 = AssertAndGetCurrentTransactionId(); 683 NestedAsyncTSAndDependantClone(false, true, txId7); 684 txId8 = AssertAndGetCurrentTransactionId(); 685 }); 686 687 task4 = Task.Run(delegate 688 { 689 txId9 = AssertAndGetCurrentTransactionId(); 690 SyncTSL2NestedTxWork(false, false, true, txId6); 691 txId10 = AssertAndGetCurrentTransactionId(); 692 693 Task.Run(delegate 694 { 695 txId15 = AssertAndGetCurrentTransactionId(); 696 }).Wait(); 697 }); 698 699 task5 = Task.Run(async delegate 700 { 701 txId11 = AssertAndGetCurrentTransactionId(); 702 await DoAsyncTSL2NestedTxWorkAsync(false, true, false, false, true, txId11); 703 txId12 = AssertAndGetCurrentTransactionId(); 704 }); 705 706 task6 = Task.Run(delegate 707 { 708 txId13 = AssertAndGetCurrentTransactionId(); 709 SyncTSL3AsyncTSL2NestedTxWork(false, false, true, false, false, true, txId13); 710 txId14 = AssertAndGetCurrentTransactionId(); 711 }); 712 713 await DoAsyncTSL2NestedTxWorkAsync(false, false, true, false, false, txId1); 714 715 txId2 = AssertAndGetCurrentTransactionId(); 716 717 Task.WaitAll(task1, task2, task3, task4, task5, task6); 718 scope.Complete(); 719 } 720 721 VerifyTxId(requiresNew, txId, txId1, txId2); 722 VerifyTxId(false, txId1, txId3, txId4); 723 VerifyTxId(false, txId1, txId5, txId6); 724 VerifyTxId(false, txId1, txId7, txId8); 725 VerifyTxId(false, txId1, txId9, txId10); 726 VerifyTxId(false, txId1, txId11, txId12); 727 VerifyTxId(false, txId1, txId13, txId14); 728 729 Assert.Equal(txId1, txId15); 730 endThreadId = Environment.CurrentManagedThreadId; 731 732 if (parentScopePresent) 733 { 734 if (parentAsync) 735 { 736 AssertTransaction(txId); 737 } 738 else 739 { 740 if (startThreadId != endThreadId) 741 { 742 AssertTransactionNull(); 743 } 744 else 745 { 746 AssertTransaction(txId); 747 } 748 } 749 } 750 else 751 { 752 AssertTransactionNull(); 753 } 754 } 755 756 [Fact] LegacyNestedTxScope()757 public void LegacyNestedTxScope() 758 { 759 string txId1 = null; 760 string txId2 = null; 761 string txId3 = null; 762 string txId4 = null; 763 string txId5 = null; 764 string txId6 = null; 765 766 Debug.WriteLine("Running NestedScopeTest"); 767 AssertTransactionNull(); 768 769 // Test Sync nested TransactionScope behavior. 770 using (TransactionScope scope = new TransactionScope()) 771 { 772 txId1 = AssertAndGetCurrentTransactionId(); 773 scope.Complete(); 774 } 775 776 AssertTransactionNull(); 777 778 using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Required)) 779 { 780 txId2 = AssertAndGetCurrentTransactionId(); 781 782 using (TransactionScope scope3 = new TransactionScope(TransactionScopeOption.Suppress)) 783 { 784 AssertTransactionNull(); 785 scope3.Complete(); 786 } 787 788 txId6 = AssertAndGetCurrentTransactionId(); 789 790 using (TransactionScope scope4 = new TransactionScope(TransactionScopeOption.RequiresNew)) 791 { 792 txId3 = AssertAndGetCurrentTransactionId(); 793 scope4.Complete(); 794 } 795 796 txId4 = AssertAndGetCurrentTransactionId(); 797 scope2.Complete(); 798 } 799 800 AssertTransactionNull(); 801 802 using (TransactionScope scope = new TransactionScope()) 803 { 804 txId5 = AssertAndGetCurrentTransactionId(); 805 scope.Complete(); 806 } 807 808 AssertTransactionNull(); 809 810 Assert.Equal(txId2, txId4); 811 Assert.Equal(txId2, txId6); 812 Assert.NotEqual(txId1, txId2); 813 Assert.NotEqual(txId2, txId3); 814 Assert.NotEqual(txId1, txId5); 815 } 816 817 [Theory] 818 // Async TS nested inside Sync TS 819 [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)] 820 [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)] 821 [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)] 822 [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)] 823 [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)] 824 [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)] 825 // Sync TS nested inside Async TS. 826 [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)] 827 [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Suppress)] 828 [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress)] 829 [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)] 830 [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Suppress)] 831 [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress)] 832 // Async TS nested inside Async TS 833 [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)] 834 [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)] 835 [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)] 836 [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)] 837 [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)] 838 [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)] VerifyNestedTxScope(TransactionScopeOption parentScopeOption, TransactionScopeAsyncFlowOption parentAsyncFlowOption, TransactionScopeOption childScopeOption, TransactionScopeAsyncFlowOption childAsyncFlowOption)839 public void VerifyNestedTxScope(TransactionScopeOption parentScopeOption, TransactionScopeAsyncFlowOption parentAsyncFlowOption, TransactionScopeOption childScopeOption, TransactionScopeAsyncFlowOption childAsyncFlowOption) 840 { 841 string txId1; 842 string txId2; 843 string txId3; 844 845 AssertTransactionNull(); 846 847 using (TransactionScope parent = new TransactionScope(parentScopeOption, parentAsyncFlowOption)) 848 { 849 txId1 = AssertAndGetCurrentTransactionId(parentScopeOption); 850 851 using (TransactionScope child = new TransactionScope(childScopeOption, childAsyncFlowOption)) 852 { 853 txId2 = AssertAndGetCurrentTransactionId(childScopeOption); 854 child.Complete(); 855 } 856 857 txId3 = AssertAndGetCurrentTransactionId(parentScopeOption); 858 parent.Complete(); 859 } 860 861 AssertTransactionNull(); 862 Assert.Equal(txId1, txId3); 863 switch (childScopeOption) 864 { 865 case TransactionScopeOption.Required: 866 if (parentScopeOption == TransactionScopeOption.Suppress) 867 { 868 Assert.NotEqual(txId1, txId2); 869 } 870 else 871 { 872 Assert.Equal(txId1, txId2); 873 } 874 break; 875 case TransactionScopeOption.RequiresNew: 876 Assert.NotEqual(txId1, txId2); 877 break; 878 case TransactionScopeOption.Suppress: 879 if (parentScopeOption == TransactionScopeOption.Suppress) 880 { 881 Assert.Equal(txId1, txId2); 882 } 883 else 884 { 885 Assert.NotEqual(txId1, txId2); 886 } 887 break; 888 } 889 } 890 891 [Theory] 892 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)] 893 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)] 894 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)] 895 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)] 896 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)] 897 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Suppress)] 898 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)] 899 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Suppress)] 900 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)] 901 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)] 902 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)] 903 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)] 904 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)] 905 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress)] 906 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)] 907 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress)] VerifyParentTwoChildTest(TransactionScopeAsyncFlowOption parentAsyncFlowOption, TransactionScopeOption child1ScopeOption, TransactionScopeAsyncFlowOption child1AsyncFlowOption, TransactionScopeOption child2ScopeOption, TransactionScopeAsyncFlowOption child2AsyncFlowOption)908 public void VerifyParentTwoChildTest(TransactionScopeAsyncFlowOption parentAsyncFlowOption, TransactionScopeOption child1ScopeOption, TransactionScopeAsyncFlowOption child1AsyncFlowOption, TransactionScopeOption child2ScopeOption, TransactionScopeAsyncFlowOption child2AsyncFlowOption) 909 { 910 string txId1; 911 string txId2; 912 string txId3; 913 string txId4; 914 string txId5; 915 916 AssertTransactionNull(); 917 918 using (TransactionScope parent = new TransactionScope(parentAsyncFlowOption)) 919 { 920 txId1 = AssertAndGetCurrentTransactionId(); 921 922 using (TransactionScope child1 = new TransactionScope(child1ScopeOption, child1AsyncFlowOption)) 923 { 924 txId2 = AssertAndGetCurrentTransactionId(child1ScopeOption); 925 child1.Complete(); 926 } 927 928 txId3 = AssertAndGetCurrentTransactionId(); 929 930 using (TransactionScope child2 = new TransactionScope(child2ScopeOption, child2AsyncFlowOption)) 931 { 932 txId4 = AssertAndGetCurrentTransactionId(child2ScopeOption); 933 child2.Complete(); 934 } 935 936 txId5 = AssertAndGetCurrentTransactionId(); 937 parent.Complete(); 938 } 939 940 AssertTransactionNull(); 941 Assert.Equal(txId1, txId3); 942 Assert.Equal(txId3, txId5); 943 944 if (child1ScopeOption == TransactionScopeOption.Required) 945 { 946 Assert.Equal(txId1, txId2); 947 } 948 else 949 { 950 Assert.NotEqual(txId1, txId2); 951 if (child1ScopeOption == TransactionScopeOption.Suppress) 952 { 953 Assert.Equal(null, txId2); 954 } 955 } 956 957 if (child2ScopeOption == TransactionScopeOption.Required) 958 { 959 Assert.Equal(txId1, txId4); 960 } 961 else 962 { 963 Assert.NotEqual(txId1, txId4); 964 if (child2ScopeOption == TransactionScopeOption.Suppress) 965 { 966 Assert.Equal(null, txId4); 967 } 968 } 969 } 970 971 [Theory] 972 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, false)] 973 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, false)] 974 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, true)] 975 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, true)] 976 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, false)] 977 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, true)] 978 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.RequiresNew, true)] 979 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, true)] 980 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, true)] 981 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, false)] 982 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, false)] 983 [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, true)] 984 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, true)] 985 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, false)] 986 [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, true)] VerifyThreeTxScope(TransactionScopeAsyncFlowOption asyncFlowOption1, TransactionScopeAsyncFlowOption asyncFlowOption2, TransactionScopeAsyncFlowOption asyncFlowOption3, TransactionScopeOption scopeOption, bool nested)987 public void VerifyThreeTxScope(TransactionScopeAsyncFlowOption asyncFlowOption1, TransactionScopeAsyncFlowOption asyncFlowOption2, TransactionScopeAsyncFlowOption asyncFlowOption3, TransactionScopeOption scopeOption, bool nested) 988 { 989 string txId1; 990 string txId2; 991 string txId3; 992 string txId4; 993 string txId5; 994 995 AssertTransactionNull(); 996 997 if (nested) 998 { 999 using (TransactionScope scope1 = new TransactionScope(scopeOption, asyncFlowOption1)) 1000 { 1001 txId1 = AssertAndGetCurrentTransactionId(scopeOption); 1002 using (TransactionScope scope2 = new TransactionScope(scopeOption, asyncFlowOption2)) 1003 { 1004 txId2 = AssertAndGetCurrentTransactionId(scopeOption); 1005 using (TransactionScope scope3 = new TransactionScope(scopeOption, asyncFlowOption3)) 1006 { 1007 txId3 = AssertAndGetCurrentTransactionId(scopeOption); 1008 scope3.Complete(); 1009 } 1010 1011 txId4 = AssertAndGetCurrentTransactionId(scopeOption); 1012 scope2.Complete(); 1013 } 1014 1015 txId5 = AssertAndGetCurrentTransactionId(scopeOption); 1016 scope1.Complete(); 1017 } 1018 1019 AssertTransactionNull(); 1020 Assert.Equal(txId1, txId5); 1021 Assert.Equal(txId2, txId4); 1022 if (scopeOption == TransactionScopeOption.RequiresNew) 1023 { 1024 Assert.NotEqual(txId1, txId2); 1025 Assert.NotEqual(txId2, txId3); 1026 } 1027 else 1028 { 1029 Assert.Equal(txId1, txId2); 1030 Assert.Equal(txId2, txId3); 1031 } 1032 } 1033 else 1034 { 1035 using (TransactionScope scope1 = new TransactionScope(scopeOption, asyncFlowOption1)) 1036 { 1037 txId1 = AssertAndGetCurrentTransactionId(scopeOption); 1038 scope1.Complete(); 1039 } 1040 1041 AssertTransactionNull(); 1042 using (TransactionScope scope2 = new TransactionScope(scopeOption, asyncFlowOption2)) 1043 { 1044 txId2 = AssertAndGetCurrentTransactionId(scopeOption); 1045 scope2.Complete(); 1046 } 1047 1048 AssertTransactionNull(); 1049 using (TransactionScope scope3 = new TransactionScope(scopeOption, asyncFlowOption3)) 1050 { 1051 txId3 = AssertAndGetCurrentTransactionId(scopeOption); 1052 scope3.Complete(); 1053 } 1054 1055 if (scopeOption == TransactionScopeOption.Suppress) 1056 { 1057 Assert.Equal(null, txId1); 1058 Assert.Equal(txId1, txId2); 1059 Assert.Equal(txId2, txId3); 1060 } 1061 else 1062 { 1063 Assert.NotEqual(txId1, txId2); 1064 Assert.NotEqual(txId2, txId3); 1065 } 1066 } 1067 1068 AssertTransactionNull(); 1069 } 1070 1071 [Theory] 1072 [InlineData(TransactionScopeAsyncFlowOption.Suppress)] 1073 [InlineData(TransactionScopeAsyncFlowOption.Enabled)] VerifyBYOT(TransactionScopeAsyncFlowOption asyncFlowOption)1074 public void VerifyBYOT(TransactionScopeAsyncFlowOption asyncFlowOption) 1075 { 1076 string txId1; 1077 string txId2; 1078 string txId3; 1079 1080 AssertTransactionNull(); 1081 CommittableTransaction tx = new CommittableTransaction(); 1082 Transaction.Current = tx; 1083 1084 txId1 = AssertAndGetCurrentTransactionId(); 1085 using (TransactionScope scope = new TransactionScope(asyncFlowOption)) 1086 { 1087 txId2 = AssertAndGetCurrentTransactionId(); 1088 scope.Complete(); 1089 } 1090 1091 txId3 = AssertAndGetCurrentTransactionId(); 1092 Transaction.Current = null; 1093 tx.Commit(); 1094 1095 Assert.Equal(txId1, txId2); 1096 Assert.Equal(txId2, txId3); 1097 1098 AssertTransactionNull(); 1099 } 1100 1101 [Fact] VerifyBYOTOpenConnSimulationTest()1102 public void VerifyBYOTOpenConnSimulationTest() 1103 { 1104 // Create threads to do work 1105 Task[] threads = new Task[iterations]; 1106 for (int i = 0; i < iterations; i++) 1107 { 1108 threads[i] = Task.Factory.StartNew((object o) => { SimulateOpenConnTestAsync((int)o).Wait(); }, i, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); 1109 } 1110 1111 for (int i = 0; i < threads.Length; i++) 1112 { 1113 threads[i].Wait(); 1114 } 1115 } 1116 1117 [Fact] VerifyBYOTSyncTSNestedAsync()1118 public void VerifyBYOTSyncTSNestedAsync() 1119 { 1120 string txId1; 1121 string txId2; 1122 1123 AssertTransactionNull(); 1124 1125 CommittableTransaction tx = new CommittableTransaction(); 1126 Transaction.Current = tx; 1127 1128 txId1 = AssertAndGetCurrentTransactionId(); 1129 SyncTSL3AsyncTSL2NestedTxWork(false, false, false, true, false, false, txId1); 1130 txId2 = AssertAndGetCurrentTransactionId(); 1131 1132 Transaction.Current = null; 1133 tx.Commit(); 1134 1135 Assert.Equal(txId1, txId2); 1136 1137 AssertTransactionNull(); 1138 } 1139 1140 [Fact] VerifyBYOTAsyncTSNestedAsync()1141 public void VerifyBYOTAsyncTSNestedAsync() 1142 { 1143 string txId1; 1144 string txId2; 1145 1146 AssertTransactionNull(); 1147 CommittableTransaction tx = new CommittableTransaction(); 1148 Transaction.Current = tx; 1149 1150 txId1 = AssertAndGetCurrentTransactionId(); 1151 Task<string> task = DoAsyncTSL3AsyncTSL2NestedTxWorkAsync(false, false, false, true, false, false, txId1); 1152 txId2 = AssertAndGetCurrentTransactionId(); 1153 1154 string result = task.Result; 1155 1156 Transaction.Current = null; 1157 tx.Commit(); 1158 1159 Assert.Equal(txId1, txId2); 1160 1161 AssertTransactionNull(); 1162 } 1163 1164 [Theory] 1165 [InlineData(TransactionScopeAsyncFlowOption.Suppress)] 1166 [InlineData(TransactionScopeAsyncFlowOption.Enabled)] DoTxQueueWorkItem(TransactionScopeAsyncFlowOption asyncFlowOption)1167 public void DoTxQueueWorkItem(TransactionScopeAsyncFlowOption asyncFlowOption) 1168 { 1169 string txId1; 1170 string txId2; 1171 1172 ManualResetEvent waitCompletion = new ManualResetEvent(false); 1173 AssertTransactionNull(); 1174 1175 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, asyncFlowOption)) 1176 { 1177 txId1 = AssertAndGetCurrentTransactionId(); 1178 1179 ThreadSyncObject context = new ThreadSyncObject() 1180 { 1181 Id = txId1, 1182 Event = waitCompletion, 1183 RootAsyncFlowOption = asyncFlowOption 1184 }; 1185 1186 Task.Factory.StartNew(DoTxWork, context, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); 1187 1188 waitCompletion.WaitOne(); 1189 1190 txId2 = AssertAndGetCurrentTransactionId(); 1191 1192 scope.Complete(); 1193 } 1194 1195 Assert.Equal(txId1, txId2); 1196 1197 AssertTransactionNull(); 1198 } 1199 1200 [Theory] 1201 [InlineData(TransactionScopeAsyncFlowOption.Suppress)] 1202 [InlineData(TransactionScopeAsyncFlowOption.Enabled)] DoTxNewThread(TransactionScopeAsyncFlowOption asyncFlowOption)1203 public void DoTxNewThread(TransactionScopeAsyncFlowOption asyncFlowOption) 1204 { 1205 string txId1; 1206 string txId2; 1207 1208 ManualResetEvent waitCompletion = new ManualResetEvent(false); 1209 AssertTransactionNull(); 1210 1211 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, asyncFlowOption)) 1212 { 1213 txId1 = AssertAndGetCurrentTransactionId(); 1214 1215 ThreadSyncObject context = new ThreadSyncObject() 1216 { 1217 Id = txId1, 1218 RootAsyncFlowOption = asyncFlowOption 1219 }; 1220 1221 Task.Factory.StartNew(DoTxWork, context, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default).Wait(); 1222 1223 txId2 = AssertAndGetCurrentTransactionId(); 1224 1225 scope.Complete(); 1226 } 1227 1228 AssertTransactionNull(); 1229 Assert.Equal(txId1, txId2); 1230 } 1231 SimulateOpenConnTestAsync(int i)1232 private static async Task SimulateOpenConnTestAsync(int i) 1233 { 1234 string txId1; 1235 string txId2; 1236 1237 TaskCompletionSource<object> completionSource = new TaskCompletionSource<object>(); 1238 1239 // Start a transaction - presumably the customer would do this in our code 1240 using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)) 1241 { 1242 txId1 = AssertAndGetCurrentTransactionId(); 1243 // At some point we realize that we can't complete our work, so enqueue it to be completed later 1244 s_workQueue.Add(Tuple.Create(i, completionSource, Transaction.Current)); 1245 1246 // If we are the first thread kicked off, then we are also resposible to kick of the background thread which will do the work for us 1247 if (i == 0) 1248 { 1249 StartWorkProcessingThread(); 1250 } 1251 1252 // Await for our work to be completed by the background thread, then finish the Tx 1253 await completionSource.Task; 1254 1255 txId2 = AssertAndGetCurrentTransactionId(); 1256 ts.Complete(); 1257 } 1258 1259 Assert.Equal(txId1, txId2); 1260 } 1261 StartWorkProcessingThread()1262 private static void StartWorkProcessingThread() 1263 { 1264 Task.Factory.StartNew(() => 1265 { 1266 for (int j = 0; j < iterations; j++) 1267 { 1268 //Get the next item of work 1269 var work = s_workQueue.Take(); 1270 1271 // Set the current transaction, such that anything we call will be aware of it 1272 Transaction.Current = work.Item3; 1273 1274 // Read the current transaction back and check to see if it is what we set 1275 Assert.Equal(Transaction.Current, work.Item3); 1276 1277 Debug.WriteLine("{0}: {1}", work.Item1, Transaction.Current == work.Item3); 1278 1279 // Tell the other thread that we completed its work 1280 work.Item2.SetResult(null); 1281 } 1282 }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); 1283 } 1284 DoTxWork(object obj)1285 private static void DoTxWork(object obj) 1286 { 1287 string txId1 = null; 1288 string txId2 = null; 1289 1290 ThreadSyncObject context = (ThreadSyncObject)obj; 1291 1292 if (context.RootAsyncFlowOption == TransactionScopeAsyncFlowOption.Enabled) 1293 { 1294 txId1 = AssertAndGetCurrentTransactionId(); 1295 Assert.Equal(context.Id, txId1); 1296 } 1297 else 1298 { 1299 AssertTransactionNull(); 1300 } 1301 1302 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) 1303 { 1304 txId2 = AssertAndGetCurrentTransactionId(); 1305 scope.Complete(); 1306 } 1307 1308 if (context.RootAsyncFlowOption == TransactionScopeAsyncFlowOption.Enabled) 1309 { 1310 string txId3 = AssertAndGetCurrentTransactionId(); 1311 Assert.Equal(txId1, txId2); 1312 Assert.Equal(txId1, txId3); 1313 } 1314 else 1315 { 1316 AssertTransactionNull(); 1317 } 1318 1319 if (context.Event != null) 1320 { 1321 context.Event.Set(); 1322 } 1323 } 1324 DoSyncTxWork(bool requiresNew, string txId)1325 private static string DoSyncTxWork(bool requiresNew, string txId) 1326 { 1327 string txId1 = null; 1328 string txId2 = null; 1329 string result; 1330 using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required)) 1331 { 1332 txId1 = AssertAndGetCurrentTransactionId(); 1333 result = DoWork(txId1); 1334 txId2 = AssertAndGetCurrentTransactionId(); 1335 1336 scope.Complete(); 1337 } 1338 1339 VerifyTxId(requiresNew, txId, txId1, txId2); 1340 1341 return result; 1342 } 1343 DoWork(string txId)1344 private static string DoWork(string txId) 1345 { 1346 string txId1 = Transaction.Current != null ? Transaction.Current.TransactionInformation.LocalIdentifier : null; 1347 Assert.Equal(txId, txId1); 1348 1349 if (s_throwExceptionDefaultOrBeforeAwait) 1350 { 1351 throw new Exception("Sync DoWork exception!"); 1352 } 1353 1354 return "Hello" + " World"; 1355 } 1356 DoAsyncTxWorkAsync(bool requiresNew, string txId)1357 private static async Task<string> DoAsyncTxWorkAsync(bool requiresNew, string txId) 1358 { 1359 string txId1 = null; 1360 string txId2 = null; 1361 string result; 1362 using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) 1363 { 1364 txId1 = AssertAndGetCurrentTransactionId(); 1365 result = await DoAsyncWorkAsync(txId1); 1366 txId2 = AssertAndGetCurrentTransactionId(); 1367 1368 scope.Complete(); 1369 } 1370 1371 VerifyTxId(requiresNew, txId, txId1, txId2); 1372 1373 return result; 1374 } 1375 DoAsyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childRequiresNew, bool asyncChild, bool asyncWorkBeforeChild, bool asyncWorkAfterChild, string txId)1376 private static async Task<string> DoAsyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childRequiresNew, bool asyncChild, bool asyncWorkBeforeChild, bool asyncWorkAfterChild, string txId) 1377 { 1378 string txId1 = null; 1379 string txId2 = null; 1380 string txId3 = null; 1381 string result; 1382 using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) 1383 { 1384 txId1 = AssertAndGetCurrentTransactionId(); 1385 1386 if (asyncWorkBeforeChild) 1387 { 1388 result = await DoAsyncWorkAsync(txId1); 1389 } 1390 1391 if (asyncChild) 1392 { 1393 result = await DoAsyncTxWorkAsync(childRequiresNew, txId1); 1394 } 1395 else 1396 { 1397 result = DoSyncTxWork(childRequiresNew, txId1); 1398 } 1399 1400 txId3 = AssertAndGetCurrentTransactionId(); 1401 1402 if (asyncWorkAfterChild) 1403 { 1404 result = await DoAsyncWorkAsync(txId1); 1405 } 1406 1407 txId2 = AssertAndGetCurrentTransactionId(); 1408 1409 scope.Complete(); 1410 } 1411 1412 VerifyTxId(parentRequiresNew, txId, txId1, txId2); 1413 VerifyTxId(parentRequiresNew, txId, txId1, txId3); 1414 1415 return result; 1416 } 1417 SyncTSL2NestedTxWork(bool parentRequiresNew, bool childRequiresNew, bool asyncChild, string txId)1418 private static string SyncTSL2NestedTxWork(bool parentRequiresNew, bool childRequiresNew, bool asyncChild, string txId) 1419 { 1420 string txId1 = null; 1421 string txId2 = null; 1422 Task<string> task = null; 1423 string result = null; 1424 using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required)) 1425 { 1426 txId1 = AssertAndGetCurrentTransactionId(); 1427 1428 if (asyncChild) 1429 { 1430 task = DoAsyncTxWorkAsync(childRequiresNew, txId1); 1431 } 1432 else 1433 { 1434 result = DoSyncTxWork(childRequiresNew, txId1); 1435 } 1436 1437 if (asyncChild) 1438 { 1439 result = task.Result; 1440 } 1441 1442 txId2 = AssertAndGetCurrentTransactionId(); 1443 1444 scope.Complete(); 1445 } 1446 1447 VerifyTxId(parentRequiresNew, txId, txId1, txId2); 1448 1449 return result; 1450 } 1451 SyncTSL3AsyncTSL2NestedTxWork(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL3, bool asyncWorkAfterChildL3, string txId)1452 private static void SyncTSL3AsyncTSL2NestedTxWork(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL3, bool asyncWorkAfterChildL3, string txId) 1453 { 1454 string txId1; 1455 string txId2; 1456 1457 AssertTransaction(txId); 1458 1459 using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required)) 1460 { 1461 txId1 = AssertAndGetCurrentTransactionId(); 1462 Task<string> task = DoAsyncTSL2NestedTxWorkAsync(childL2RequiresNew, childL3RequiresNew, asyncChildL3, asyncWorkBeforeChildL3, asyncWorkAfterChildL3, txId1); 1463 txId2 = AssertAndGetCurrentTransactionId(); 1464 1465 task.Wait(); 1466 scope.Complete(); 1467 } 1468 1469 VerifyTxId(parentRequiresNew, txId, txId1, txId2); 1470 AssertTransaction(txId); 1471 } 1472 SyncTSL3SyncTSL2NestedTxWork(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, string txId)1473 private static void SyncTSL3SyncTSL2NestedTxWork(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, string txId) 1474 { 1475 string txId1; 1476 string txId2; 1477 1478 AssertTransaction(txId); 1479 1480 using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required)) 1481 { 1482 txId1 = AssertAndGetCurrentTransactionId(); 1483 SyncTSL2NestedTxWork(childL2RequiresNew, childL3RequiresNew, asyncChildL3, txId1); 1484 txId2 = AssertAndGetCurrentTransactionId(); 1485 scope.Complete(); 1486 } 1487 1488 VerifyTxId(parentRequiresNew, txId, txId1, txId2); 1489 AssertTransaction(txId); 1490 } 1491 DoAsyncTSL3AsyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL3, bool asyncWorkAfterChildL3, string txId)1492 private static async Task<string> DoAsyncTSL3AsyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL3, bool asyncWorkAfterChildL3, string txId) 1493 { 1494 string txId1; 1495 string txId2; 1496 string result = null; 1497 AssertTransaction(txId); 1498 1499 using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) 1500 { 1501 txId1 = AssertAndGetCurrentTransactionId(); 1502 result = await DoAsyncTSL2NestedTxWorkAsync(childL2RequiresNew, childL3RequiresNew, asyncChildL3, asyncWorkBeforeChildL3, asyncWorkAfterChildL3, txId1); 1503 1504 txId2 = AssertAndGetCurrentTransactionId(); 1505 1506 scope.Complete(); 1507 } 1508 1509 VerifyTxId(parentRequiresNew, txId, txId1, txId2); 1510 1511 return result; 1512 } 1513 DoAsyncTSL3SyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL2, bool asyncWorkAfterChildL2, string txId)1514 private static async Task<string> DoAsyncTSL3SyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL2, bool asyncWorkAfterChildL2, string txId) 1515 { 1516 string txId1; 1517 string txId2; 1518 string txId3; 1519 string result = null; 1520 1521 AssertTransaction(txId); 1522 1523 using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) 1524 { 1525 txId1 = AssertAndGetCurrentTransactionId(); 1526 1527 if (asyncWorkBeforeChildL2) 1528 { 1529 await DoAsyncWorkAsync(txId1); 1530 } 1531 1532 result = SyncTSL2NestedTxWork(childL2RequiresNew, childL3RequiresNew, asyncChildL3, txId1); 1533 1534 txId3 = AssertAndGetCurrentTransactionId(); 1535 1536 if (asyncWorkAfterChildL2) 1537 { 1538 await DoAsyncWorkAsync(txId1); 1539 } 1540 1541 txId2 = AssertAndGetCurrentTransactionId(); 1542 1543 scope.Complete(); 1544 } 1545 1546 VerifyTxId(parentRequiresNew, txId, txId1, txId2); 1547 VerifyTxId(parentRequiresNew, txId, txId1, txId3); 1548 AssertTransaction(txId); 1549 1550 return result; 1551 } 1552 DoAsyncWorkAsync(string txId)1553 private static async Task<string> DoAsyncWorkAsync(string txId) 1554 { 1555 string txId1 = Transaction.Current != null ? Transaction.Current.TransactionInformation.LocalIdentifier : null; 1556 Assert.Equal(txId, txId1); 1557 1558 if (s_throwExceptionDefaultOrBeforeAwait) 1559 { 1560 throw new Exception("DoAsyncWorkAsync exception before await"); 1561 } 1562 1563 await Task.Delay(2); 1564 1565 Task<string> getString = Task.Run(delegate 1566 { 1567 txId1 = Transaction.Current != null ? Transaction.Current.TransactionInformation.LocalIdentifier : null; 1568 Assert.Equal(txId, txId1); 1569 return DoWork(txId); 1570 }); 1571 1572 if (s_throwExceptionAfterAwait) 1573 { 1574 throw new Exception("DoAsyncWorkAsync exception after await"); 1575 } 1576 1577 string result = await getString; 1578 1579 txId1 = Transaction.Current != null ? Transaction.Current.TransactionInformation.LocalIdentifier : null; 1580 Assert.Equal(txId, txId1); 1581 1582 return result; 1583 } 1584 SyncTSDoTaskUnderAsyncTS(bool parentRequiresNew, bool childRequiresNew, string txId)1585 private static void SyncTSDoTaskUnderAsyncTS(bool parentRequiresNew, bool childRequiresNew, string txId) 1586 { 1587 string txId1 = null; 1588 string txId2 = null; 1589 AssertTransaction(txId); 1590 1591 using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required)) 1592 { 1593 txId1 = AssertAndGetCurrentTransactionId(); 1594 DoTaskUnderAsyncTS(childRequiresNew, txId1); 1595 txId2 = AssertAndGetCurrentTransactionId(); 1596 scope.Complete(); 1597 } 1598 1599 VerifyTxId(parentRequiresNew, txId, txId1, txId2); 1600 AssertTransaction(txId); 1601 } 1602 DoTaskUnderAsyncTS(bool requiresNew, string txId)1603 private static void DoTaskUnderAsyncTS(bool requiresNew, string txId) 1604 { 1605 string txId1 = null; 1606 string txId2 = null; 1607 AssertTransaction(txId); 1608 1609 using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) 1610 { 1611 txId1 = AssertAndGetCurrentTransactionId(); 1612 DoALotOfWork(txId1); 1613 txId2 = AssertAndGetCurrentTransactionId(); 1614 scope.Complete(); 1615 } 1616 1617 VerifyTxId(requiresNew, txId, txId1, txId2); 1618 AssertTransaction(txId); 1619 } 1620 DoALotOfWork(string txId)1621 private static void DoALotOfWork(string txId) 1622 { 1623 Task[] a = new Task[32]; 1624 for (int i = 0; i < 32; i++) 1625 { 1626 a[i] = new Task(() => DoWork(txId)); 1627 a[i].Start(); 1628 } 1629 1630 Task.WaitAll(a); 1631 } 1632 VerifyTxId(bool requiresNew, string parentTxId, string beforeTxId, string afterTxId)1633 public static void VerifyTxId(bool requiresNew, string parentTxId, string beforeTxId, string afterTxId) 1634 { 1635 Assert.Equal(beforeTxId, afterTxId); 1636 if (requiresNew) 1637 { 1638 Assert.NotEqual(parentTxId, beforeTxId); 1639 } 1640 else 1641 { 1642 if (!string.IsNullOrEmpty(parentTxId)) 1643 { 1644 Assert.Equal(parentTxId, beforeTxId); 1645 } 1646 } 1647 } 1648 AssertTransaction(string txId)1649 public static void AssertTransaction(string txId) 1650 { 1651 if (string.IsNullOrEmpty(txId)) 1652 { 1653 AssertTransactionNull(); 1654 } 1655 else 1656 { 1657 Assert.Equal(txId, AssertAndGetCurrentTransactionId()); 1658 } 1659 } AssertTransactionNull()1660 public static void AssertTransactionNull() 1661 { 1662 Assert.Equal(null, Transaction.Current); 1663 } 1664 AssertTransactionNotNull()1665 public static void AssertTransactionNotNull() 1666 { 1667 Assert.NotEqual(null, Transaction.Current); 1668 } 1669 AssertAndGetCurrentTransactionId()1670 public static string AssertAndGetCurrentTransactionId() 1671 { 1672 AssertTransactionNotNull(); 1673 return Transaction.Current.TransactionInformation.LocalIdentifier; 1674 } 1675 AssertAndGetCurrentTransactionId(TransactionScopeOption scopeOption)1676 public static string AssertAndGetCurrentTransactionId(TransactionScopeOption scopeOption) 1677 { 1678 if (scopeOption == TransactionScopeOption.Suppress) 1679 { 1680 AssertTransactionNull(); 1681 return null; 1682 } 1683 else 1684 { 1685 AssertTransactionNotNull(); 1686 return Transaction.Current.TransactionInformation.LocalIdentifier; 1687 } 1688 } 1689 AssertTransactionNullAndWaitTask(Task<string> task)1690 public static void AssertTransactionNullAndWaitTask(Task<string> task) 1691 { 1692 AssertTransactionNull(); 1693 task.Wait(); 1694 AssertTransactionNull(); 1695 } 1696 AssertTransactionNullAndWaitTask(Task task)1697 public static void AssertTransactionNullAndWaitTask(Task task) 1698 { 1699 AssertTransactionNull(); 1700 task.Wait(); 1701 AssertTransactionNull(); 1702 } 1703 SetExceptionInjection(bool exceptionDefaultOrBeforeAwait, bool exceptionAfterAwait)1704 public static void SetExceptionInjection(bool exceptionDefaultOrBeforeAwait, bool exceptionAfterAwait) 1705 { 1706 s_throwExceptionDefaultOrBeforeAwait = exceptionDefaultOrBeforeAwait; 1707 s_throwExceptionAfterAwait = exceptionAfterAwait; 1708 } 1709 ResetExceptionInjection()1710 public static void ResetExceptionInjection() 1711 { 1712 s_throwExceptionDefaultOrBeforeAwait = false; 1713 s_throwExceptionAfterAwait = false; 1714 } 1715 HandleException(bool exceptionDefaultOrBeforeAwait, bool exceptionAfterAwait, Action action)1716 public static void HandleException(bool exceptionDefaultOrBeforeAwait, bool exceptionAfterAwait, Action action) 1717 { 1718 bool hasException = false; 1719 1720 SetExceptionInjection(exceptionDefaultOrBeforeAwait, exceptionAfterAwait); 1721 try 1722 { 1723 action.Invoke(); 1724 } 1725 catch (Exception ex) 1726 { 1727 hasException = true; 1728 Debug.WriteLine("Exception: {0}", ex.Message); 1729 } 1730 1731 AssertTransactionNull(); 1732 Assert.Equal<bool>(hasException, true); 1733 ResetExceptionInjection(); 1734 } 1735 } 1736 1737 public class ThreadSyncObject 1738 { 1739 public string Id 1740 { 1741 get; 1742 set; 1743 } 1744 1745 public ManualResetEvent Event 1746 { 1747 get; 1748 set; 1749 } 1750 1751 public TransactionScopeAsyncFlowOption RootAsyncFlowOption 1752 { 1753 get; 1754 set; 1755 } 1756 } 1757 1758 public interface IStatus 1759 { GetStatus(string id, bool fail)1760 string GetStatus(string id, bool fail); 1761 } 1762 } 1763