Best Xunit code snippet using Xunit.Sdk.AsyncManualResetEvent
AsyncReaderWriterLockTests.cs
Source:AsyncReaderWriterLockTests.cs
...1109 }1110 [Fact]1111 public async Task OnExclusiveLockReleasedAsyncAcquiresProjectLock()1112 {1113 var innerLockReleased = new AsyncManualResetEvent();1114 var onExclusiveLockReleasedBegun = new AsyncManualResetEvent();1115 var asyncLock = new LockDerived();1116 asyncLock.OnBeforeExclusiveLockReleasedAsyncDelegate = async delegate1117 {1118 using (AsyncReaderWriterLock.Releaser innerReleaser = await asyncLock.WriteLockAsync())1119 {1120 await Task.WhenAny(onExclusiveLockReleasedBegun.WaitAsync(), Task.Delay(AsyncDelay));1121 await innerReleaser.ReleaseAsync();1122 innerLockReleased.Set();1123 }1124 };1125 asyncLock.OnExclusiveLockReleasedAsyncDelegate = async delegate1126 {1127 onExclusiveLockReleasedBegun.Set();1128 await innerLockReleased;1129 };1130 await using (await asyncLock.WriteLockAsync())1131 {1132 }1133 }1134 [Fact]1135 public async Task MitigationAgainstAccidentalUpgradeableReadLockConcurrency()1136 {1137 using (await this.asyncLock.UpgradeableReadLockAsync())1138 {1139 await this.CheckContinuationsConcurrencyHelper();1140 }1141 }1142 [Fact]1143 public async Task MitigationAgainstAccidentalUpgradeableReadLockConcurrencyBeforeFirstYieldSTA()1144 {1145 using (await this.asyncLock.UpgradeableReadLockAsync())1146 {1147 await this.CheckContinuationsConcurrencyBeforeYieldHelper();1148 }1149 }1150 [Fact]1151 public void MitigationAgainstAccidentalUpgradeableReadLockConcurrencyBeforeFirstYieldMTA()1152 {1153 Task.Run(async delegate1154 {1155 using (await this.asyncLock.UpgradeableReadLockAsync())1156 {1157 await this.CheckContinuationsConcurrencyBeforeYieldHelper();1158 }1159 }).GetAwaiter().GetResult();1160 }1161 [Fact]1162 public async Task UpgradeableReadLockAsyncYieldsIfSyncContextSet()1163 {1164 await Task.Run(async delegate1165 {1166 SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());1167 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.UpgradeableReadLockAsync().GetAwaiter();1168 try1169 {1170 Assert.False(awaiter.IsCompleted);1171 }1172 catch1173 {1174 awaiter.GetResult().Dispose(); // avoid test hangs on test failure1175 throw;1176 }1177 var lockAcquired = new TaskCompletionSource<object?>();1178 awaiter.OnCompleted(delegate1179 {1180 using (awaiter.GetResult())1181 {1182 }1183 lockAcquired.SetAsync();1184 });1185 await lockAcquired.Task;1186 });1187 }1188 /// <summary>1189 /// Tests that a common way to accidentally fork an exclusive lock for1190 /// concurrent access gets called out as an error.1191 /// </summary>1192 /// <remarks>1193 /// Test ignored because the tested behavior is incompatible with the1194 /// <see cref="UpgradeableReadLockTraversesAcrossSta"/> and <see cref="WriteLockTraversesAcrossSta"/> tests,1195 /// which are deemed more important.1196 /// </remarks>1197 [Fact(Skip = "Ignored")]1198 public async Task MitigationAgainstAccidentalUpgradeableReadLockForking()1199 {1200 await this.MitigationAgainstAccidentalLockForkingHelper(1201 () => this.asyncLock.UpgradeableReadLockAsync());1202 }1203 [Fact]1204 public async Task UpgradeableReadLockAsyncSimple()1205 {1206 // Get onto an MTA thread so that a lock may be synchronously granted.1207 await Task.Run(async delegate1208 {1209 using (await this.asyncLock.UpgradeableReadLockAsync())1210 {1211 Assert.True(this.asyncLock.IsAnyLockHeld);1212 Assert.True(this.asyncLock.IsUpgradeableReadLockHeld);1213 await Task.Yield();1214 Assert.True(this.asyncLock.IsAnyLockHeld);1215 Assert.True(this.asyncLock.IsUpgradeableReadLockHeld);1216 }1217 Assert.False(this.asyncLock.IsUpgradeableReadLockHeld);1218 using (await this.asyncLock.UpgradeableReadLockAsync(AsyncReaderWriterLock.LockFlags.None))1219 {1220 Assert.True(this.asyncLock.IsUpgradeableReadLockHeld);1221 await Task.Yield();1222 Assert.True(this.asyncLock.IsUpgradeableReadLockHeld);1223 }1224 Assert.False(this.asyncLock.IsUpgradeableReadLockHeld);1225 });1226 }1227 [Fact]1228 public async Task UpgradeableReadLockAsyncContention()1229 {1230 var firstLockObtained = new TaskCompletionSource<object?>();1231 await Task.WhenAll(1232 Task.Run(async delegate1233 {1234 using (await this.asyncLock.WriteLockAsync())1235 {1236 Assert.True(this.asyncLock.IsWriteLockHeld);1237 Task? nowait = firstLockObtained.SetAsync();1238 await Task.Delay(AsyncDelay); // hold it long enough to ensure our other thread blocks waiting for the read lock.1239 Assert.True(this.asyncLock.IsWriteLockHeld);1240 }1241 Assert.False(this.asyncLock.IsWriteLockHeld);1242 }),1243 Task.Run(async delegate1244 {1245 await firstLockObtained.Task;1246 using (await this.asyncLock.UpgradeableReadLockAsync())1247 {1248 Assert.True(this.asyncLock.IsUpgradeableReadLockHeld);1249 await Task.Yield();1250 Assert.True(this.asyncLock.IsUpgradeableReadLockHeld);1251 }1252 Assert.False(this.asyncLock.IsUpgradeableReadLockHeld);1253 }));1254 }1255 [Fact]1256 public void ReleasingUpgradeableReadLockAsyncSynchronouslyClearsSyncContext()1257 {1258 Task.Run(async delegate1259 {1260 Assert.Null(SynchronizationContext.Current);1261 using (await this.asyncLock.UpgradeableReadLockAsync())1262 {1263 Assert.NotNull(SynchronizationContext.Current);1264 }1265 Assert.Null(SynchronizationContext.Current);1266 }).GetAwaiter().GetResult();1267 }1268 [Fact, Trait("TestCategory", "FailsInCloudTest")]1269 public void UpgradeableReadLockAsyncSynchronousReleaseAllowsOtherUpgradeableReaders()1270 {1271 var testComplete = new ManualResetEventSlim(); // deliberately synchronous1272 var firstLockReleased = new AsyncManualResetEvent();1273 var firstLockTask = Task.Run(async delegate1274 {1275 using (await this.asyncLock.UpgradeableReadLockAsync())1276 {1277 }1278 // Synchronously block until the test is complete.1279 firstLockReleased.Set();1280 Assert.True(testComplete.Wait(AsyncDelay));1281 });1282 var secondLockTask = Task.Run(async delegate1283 {1284 await firstLockReleased;1285 using (await this.asyncLock.UpgradeableReadLockAsync())1286 {1287 }1288 });1289 Assert.True(secondLockTask.Wait(TestTimeout));1290 testComplete.Set();1291 Assert.True(firstLockTask.Wait(TestTimeout)); // rethrow any exceptions1292 }1293 [Fact]1294 public async Task WriteLockAsync()1295 {1296 Assert.False(this.asyncLock.IsWriteLockHeld);1297 using (await this.asyncLock.WriteLockAsync())1298 {1299 Assert.False(this.asyncLock.IsReadLockHeld);1300 Assert.False(this.asyncLock.IsUpgradeableReadLockHeld);1301 Assert.True(this.asyncLock.IsWriteLockHeld);1302 await Task.Yield();1303 Assert.False(this.asyncLock.IsReadLockHeld);1304 Assert.False(this.asyncLock.IsUpgradeableReadLockHeld);1305 Assert.True(this.asyncLock.IsWriteLockHeld);1306 }1307 Assert.False(this.asyncLock.IsReadLockHeld);1308 Assert.False(this.asyncLock.IsUpgradeableReadLockHeld);1309 Assert.False(this.asyncLock.IsWriteLockHeld);1310 }1311 [StaFact]1312 public void WriteLockAsyncReleaseOnSta()1313 {1314 this.LockReleaseTestHelper(this.asyncLock.WriteLockAsync());1315 }1316 [Fact]1317 public async Task WriteLockAsyncWhileHoldingUpgradeableReadLockContestedByActiveReader()1318 {1319 var upgradeableLockAcquired = new TaskCompletionSource<object?>();1320 var readLockAcquired = new TaskCompletionSource<object?>();1321 var writeLockRequested = new TaskCompletionSource<object?>();1322 var writeLockAcquired = new TaskCompletionSource<object?>();1323 await Task.WhenAll(1324 Task.Run(async delegate1325 {1326 using (await this.asyncLock.UpgradeableReadLockAsync())1327 {1328 Task? nowait = upgradeableLockAcquired.SetAsync();1329 await readLockAcquired.Task;1330 AsyncReaderWriterLock.Awaiter? upgradeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();1331 Assert.False(upgradeAwaiter.IsCompleted); // contested lock should not be immediately available.1332 upgradeAwaiter.OnCompleted(delegate1333 {1334 using (upgradeAwaiter.GetResult())1335 {1336 writeLockAcquired.SetAsync();1337 }1338 });1339 nowait = writeLockRequested.SetAsync();1340 await writeLockAcquired.Task;1341 }1342 }),1343 Task.Run(async delegate1344 {1345 await upgradeableLockAcquired.Task;1346 using (await this.asyncLock.ReadLockAsync())1347 {1348 Task? nowait = readLockAcquired.SetAsync();1349 await writeLockRequested.Task;1350 }1351 }));1352 }1353 [Fact]1354 public async Task WriteLockAsyncWhileHoldingUpgradeableReadLockContestedByWaitingWriter()1355 {1356 var upgradeableLockAcquired = new TaskCompletionSource<object?>();1357 var contendingWriteLockRequested = new TaskCompletionSource<object?>();1358 await Task.WhenAll(1359 Task.Run(async delegate1360 {1361 using (await this.asyncLock.UpgradeableReadLockAsync())1362 {1363 Task? nowait = upgradeableLockAcquired.SetAsync();1364 await contendingWriteLockRequested.Task;1365 AsyncReaderWriterLock.Awaiter? upgradeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();1366 Assert.True(upgradeAwaiter.IsCompleted); // the waiting writer should not have priority of this one.1367 upgradeAwaiter.GetResult().Dispose(); // accept and release the lock.1368 }1369 }),1370 Task.Run(async delegate1371 {1372 await upgradeableLockAcquired.Task;1373 AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();1374 Assert.False(writeAwaiter.IsCompleted);1375 var contestingWriteLockAcquired = new TaskCompletionSource<object?>();1376 writeAwaiter.OnCompleted(delegate1377 {1378 using (writeAwaiter.GetResult())1379 {1380 contestingWriteLockAcquired.SetAsync();1381 }1382 });1383 Task? nowait = contendingWriteLockRequested.SetAsync();1384 await contestingWriteLockAcquired.Task;1385 }));1386 }1387 [Fact]1388 public async Task WriteLockAsyncWhileHoldingUpgradeableReadLockContestedByActiveReaderAndWaitingWriter()1389 {1390 var upgradeableLockAcquired = new TaskCompletionSource<object?>();1391 var readLockAcquired = new TaskCompletionSource<object?>();1392 var contendingWriteLockRequested = new TaskCompletionSource<object?>();1393 var writeLockRequested = new TaskCompletionSource<object?>();1394 var writeLockAcquired = new TaskCompletionSource<object?>();1395 await Task.WhenAll(1396 Task.Run(async delegate1397 {1398 this.Logger.WriteLine("Task 1: Requesting an upgradeable read lock.");1399 using (await this.asyncLock.UpgradeableReadLockAsync())1400 {1401 this.Logger.WriteLine("Task 1: Acquired an upgradeable read lock.");1402 Task? nowait = upgradeableLockAcquired.SetAsync();1403 await Task.WhenAll(readLockAcquired.Task, contendingWriteLockRequested.Task);1404 this.Logger.WriteLine("Task 1: Requesting a write lock.");1405 AsyncReaderWriterLock.Awaiter? upgradeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();1406 this.Logger.WriteLine("Task 1: Write lock requested.");1407 Assert.False(upgradeAwaiter.IsCompleted); // contested lock should not be immediately available.1408 upgradeAwaiter.OnCompleted(delegate1409 {1410 using (upgradeAwaiter.GetResult())1411 {1412 this.Logger.WriteLine("Task 1: Write lock acquired.");1413 writeLockAcquired.SetAsync();1414 }1415 });1416 nowait = writeLockRequested.SetAsync();1417 this.Logger.WriteLine("Task 1: Waiting for write lock acquisition.");1418 await writeLockAcquired.Task;1419 this.Logger.WriteLine("Task 1: Write lock acquisition complete. Exiting task 1.");1420 }1421 }),1422 Task.Run(async delegate1423 {1424 this.Logger.WriteLine("Task 2: Waiting for upgradeable read lock acquisition in task 1.");1425 await upgradeableLockAcquired.Task;1426 this.Logger.WriteLine("Task 2: Requesting read lock.");1427 using (await this.asyncLock.ReadLockAsync())1428 {1429 this.Logger.WriteLine("Task 2: Acquired read lock.");1430 Task? nowait = readLockAcquired.SetAsync();1431 this.Logger.WriteLine("Task 2: Awaiting write lock request by task 1.");1432 await writeLockRequested.Task;1433 this.Logger.WriteLine("Task 2: Releasing read lock.");1434 }1435 this.Logger.WriteLine("Task 2: Released read lock.");1436 }),1437 Task.Run(async delegate1438 {1439 this.Logger.WriteLine("Task 3: Waiting for upgradeable read lock acquisition in task 1 and read lock acquisition in task 2.");1440 await Task.WhenAll(upgradeableLockAcquired.Task, readLockAcquired.Task);1441 this.Logger.WriteLine("Task 3: Requesting write lock.");1442 AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();1443 this.Logger.WriteLine("Task 3: Write lock requested.");1444 Assert.False(writeAwaiter.IsCompleted);1445 var contestingWriteLockAcquired = new TaskCompletionSource<object?>();1446 writeAwaiter.OnCompleted(delegate1447 {1448 using (writeAwaiter.GetResult())1449 {1450 this.Logger.WriteLine("Task 3: Write lock acquired.");1451 contestingWriteLockAcquired.SetAsync();1452 this.Logger.WriteLine("Task 3: Releasing write lock.");1453 }1454 this.Logger.WriteLine("Task 3: Write lock released.");1455 });1456 Task? nowait = contendingWriteLockRequested.SetAsync();1457 this.Logger.WriteLine("Task 3: Awaiting write lock acquisition.");1458 await contestingWriteLockAcquired.Task;1459 this.Logger.WriteLine("Task 3: Write lock acquisition complete.");1460 }));1461 }1462#if ISOLATED_TEST_SUPPORT1463 [Fact, Trait("GC", "true")]1464 public async Task UncontestedTopLevelWriteLockAsyncGarbageCheck()1465 {1466 if (await this.ExecuteInIsolationAsync())1467 {1468 var cts = new CancellationTokenSource();1469 await this.UncontestedTopLevelLocksAllocFreeHelperAsync(() => this.asyncLock.WriteLockAsync(cts.Token), true);1470 }1471 }1472 [Fact, Trait("GC", "true")]1473 public async Task NestedWriteLockAsyncGarbageCheck()1474 {1475 if (await this.ExecuteInIsolationAsync())1476 {1477 await this.NestedLocksAllocFreeHelperAsync(() => this.asyncLock.WriteLockAsync(), true);1478 }1479 }1480#endif1481 [Fact]1482 public async Task MitigationAgainstAccidentalWriteLockConcurrency()1483 {1484 using (await this.asyncLock.WriteLockAsync())1485 {1486 await this.CheckContinuationsConcurrencyHelper();1487 }1488 }1489 [Fact]1490 public async Task MitigationAgainstAccidentalWriteLockConcurrencyBeforeFirstYieldSTA()1491 {1492 using (await this.asyncLock.WriteLockAsync())1493 {1494 await this.CheckContinuationsConcurrencyBeforeYieldHelper();1495 }1496 }1497 [Fact]1498 public void MitigationAgainstAccidentalWriteLockConcurrencyBeforeFirstYieldMTA()1499 {1500 Task.Run(async delegate1501 {1502 using (await this.asyncLock.WriteLockAsync())1503 {1504 await this.CheckContinuationsConcurrencyBeforeYieldHelper();1505 }1506 }).GetAwaiter().GetResult();1507 }1508 /// <summary>1509 /// Tests that a common way to accidentally fork an exclusive lock for1510 /// concurrent access gets called out as an error.1511 /// </summary>1512 /// <remarks>1513 /// Test ignored because the tested behavior is incompatible with the1514 /// <see cref="UpgradeableReadLockTraversesAcrossSta"/> and <see cref="WriteLockTraversesAcrossSta"/> tests,1515 /// which are deemed more important.1516 /// </remarks>1517 [Fact(Skip = "Ignored")]1518 public async Task MitigationAgainstAccidentalWriteLockForking()1519 {1520 await this.MitigationAgainstAccidentalLockForkingHelper(1521 () => this.asyncLock.WriteLockAsync());1522 }1523 [Fact]1524 public async Task WriteLockAsyncYieldsIfSyncContextSet()1525 {1526 await Task.Run(async delegate1527 {1528 SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());1529 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter();1530 try1531 {1532 Assert.False(awaiter.IsCompleted);1533 }1534 catch1535 {1536 awaiter.GetResult().Dispose(); // avoid test hangs on test failure1537 throw;1538 }1539 var lockAcquired = new TaskCompletionSource<object?>();1540 awaiter.OnCompleted(delegate1541 {1542 using (awaiter.GetResult())1543 {1544 }1545 lockAcquired.SetAsync();1546 });1547 await lockAcquired.Task;1548 });1549 }1550 [Fact]1551 public void ReleasingWriteLockAsyncSynchronouslyClearsSyncContext()1552 {1553 Task.Run(async delegate1554 {1555 Assert.Null(SynchronizationContext.Current);1556 using (await this.asyncLock.WriteLockAsync())1557 {1558 Assert.NotNull(SynchronizationContext.Current);1559 }1560 Assert.Null(SynchronizationContext.Current);1561 }).GetAwaiter().GetResult();1562 }1563 [Fact]1564 public void WriteLockAsyncSynchronousReleaseAllowsOtherWriters()1565 {1566 var testComplete = new ManualResetEventSlim(); // deliberately synchronous1567 var firstLockReleased = new AsyncManualResetEvent();1568 var firstLockTask = Task.Run(async delegate1569 {1570 using (await this.asyncLock.WriteLockAsync())1571 {1572 }1573 // Synchronously block until the test is complete.1574 firstLockReleased.Set();1575 Assert.True(testComplete.Wait(UnexpectedTimeout));1576 });1577 var secondLockTask = Task.Run(async delegate1578 {1579 await firstLockReleased;1580 using (await this.asyncLock.WriteLockAsync())1581 {1582 }1583 });1584 Assert.True(secondLockTask.Wait(TestTimeout));1585 testComplete.Set();1586 Assert.True(firstLockTask.Wait(TestTimeout)); // rethrow any exceptions1587 }1588 /// <summary>1589 /// Test to verify that we don't block the code to dispose a write lock, when it has been released, and a new write lock was issued right between Release and Dispose.1590 /// That happens in the original implementation, because it shares a same NonConcurrentSynchronizationContext, so a new write lock can take over it, and block the original lock task1591 /// to resume back to the context.1592 /// </summary>1593 [Fact]1594 public void WriteLockDisposingShouldNotBlockByOtherWriters()1595 {1596 var firstLockToRelease = new AsyncManualResetEvent();1597 var firstLockAccquired = new AsyncManualResetEvent();1598 var firstLockToDispose = new AsyncManualResetEvent();1599 var firstLockTask = Task.Run(async delegate1600 {1601 using (AsyncReaderWriterLock.Releaser firstLock = await this.asyncLock.WriteLockAsync())1602 {1603 firstLockAccquired.Set();1604 await firstLockToRelease.WaitAsync();1605 await firstLock.ReleaseAsync();1606 // Wait for the second lock to be issued1607 await firstLockToDispose.WaitAsync();1608 }1609 });1610 var secondLockReleased = new TaskCompletionSource<int>();1611 var secondLockTask = Task.Run(async delegate1612 {1613 await firstLockAccquired.WaitAsync();1614 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter();1615 Assert.False(awaiter.IsCompleted);1616 awaiter.OnCompleted(() =>1617 {1618 try1619 {1620 using (AsyncReaderWriterLock.Releaser access = awaiter.GetResult())1621 {1622 firstLockToDispose.Set();1623 // We must block the thread synchronously, so it won't release the NonConcurrentSynchronizationContext until the first lock is completely disposed.1624 firstLockTask.Wait(TestTimeout * 2);1625 }1626 }1627 catch (Exception ex)1628 {1629 secondLockReleased.TrySetException(ex);1630 }1631 secondLockReleased.TrySetResult(0);1632 });1633 firstLockToRelease.Set();1634 // clean up logic1635 await firstLockTask;1636 await secondLockReleased.Task;1637 });1638 Assert.True(secondLockTask.Wait(TestTimeout)); // rethrow any exceptions1639 Assert.False(secondLockReleased.Task.IsFaulted);1640 }1641 [Fact]1642 public async Task WriteLockAsyncSimple()1643 {1644 // Get onto an MTA thread so that a lock may be synchronously granted.1645 await Task.Run(async delegate1646 {1647 using (await this.asyncLock.WriteLockAsync())1648 {1649 Assert.True(this.asyncLock.IsAnyLockHeld);1650 Assert.True(this.asyncLock.IsWriteLockHeld);1651 await Task.Yield();1652 Assert.True(this.asyncLock.IsAnyLockHeld);1653 Assert.True(this.asyncLock.IsWriteLockHeld);1654 }1655 Assert.False(this.asyncLock.IsWriteLockHeld);1656 using (await this.asyncLock.WriteLockAsync(AsyncReaderWriterLock.LockFlags.None))1657 {1658 Assert.True(this.asyncLock.IsWriteLockHeld);1659 await Task.Yield();1660 Assert.True(this.asyncLock.IsWriteLockHeld);1661 }1662 Assert.False(this.asyncLock.IsWriteLockHeld);1663 });1664 }1665 [Fact]1666 public async Task WriteLockAsyncContention()1667 {1668 var firstLockObtained = new TaskCompletionSource<object?>();1669 await Task.WhenAll(1670 Task.Run(async delegate1671 {1672 using (await this.asyncLock.WriteLockAsync())1673 {1674 Assert.True(this.asyncLock.IsWriteLockHeld);1675 Task? nowait = firstLockObtained.SetAsync();1676 await Task.Delay(AsyncDelay); // hold it long enough to ensure our other thread blocks waiting for the read lock.1677 Assert.True(this.asyncLock.IsWriteLockHeld);1678 }1679 Assert.False(this.asyncLock.IsWriteLockHeld);1680 }),1681 Task.Run(async delegate1682 {1683 await firstLockObtained.Task;1684 using (await this.asyncLock.WriteLockAsync())1685 {1686 Assert.True(this.asyncLock.IsWriteLockHeld);1687 await Task.Yield();1688 Assert.True(this.asyncLock.IsWriteLockHeld);1689 }1690 Assert.False(this.asyncLock.IsWriteLockHeld);1691 }));1692 }1693 /// <summary>Verifies that reads and upgradeable reads can run concurrently.</summary>1694 [Fact]1695 public async Task UpgradeableReadAvailableWithExistingReaders()1696 {1697 var readerHasLock = new TaskCompletionSource<object?>();1698 var upgradeableReaderHasLock = new TaskCompletionSource<object?>();1699 await Task.WhenAll(1700 Task.Run(async delegate1701 {1702 using (await this.asyncLock.ReadLockAsync())1703 {1704 await readerHasLock.SetAsync();1705 await upgradeableReaderHasLock.Task;1706 }1707 }),1708 Task.Run(async delegate1709 {1710 await readerHasLock.Task;1711 using (await this.asyncLock.UpgradeableReadLockAsync())1712 {1713 await upgradeableReaderHasLock.SetAsync();1714 }1715 }));1716 }1717 /// <summary>Verifies that reads and upgradeable reads can run concurrently.</summary>1718 [Fact]1719 public async Task ReadAvailableWithExistingUpgradeableReader()1720 {1721 var readerHasLock = new TaskCompletionSource<object?>();1722 var upgradeableReaderHasLock = new TaskCompletionSource<object?>();1723 await Task.WhenAll(1724 Task.Run(async delegate1725 {1726 await upgradeableReaderHasLock.Task;1727 using (await this.asyncLock.ReadLockAsync())1728 {1729 await readerHasLock.SetAsync();1730 }1731 }),1732 Task.Run(async delegate1733 {1734 using (await this.asyncLock.UpgradeableReadLockAsync())1735 {1736 await upgradeableReaderHasLock.SetAsync();1737 await readerHasLock.Task;1738 }1739 }));1740 }1741 /// <summary>Verifies that an upgradeable reader can obtain write access even while a writer is waiting for a lock.</summary>1742 [Fact]1743 public async Task UpgradeableReaderCanUpgradeWhileWriteRequestWaiting()1744 {1745 var upgradeableReadHeld = new TaskCompletionSource<object?>();1746 var upgradeableReadUpgraded = new TaskCompletionSource<object?>();1747 var writeRequestPending = new TaskCompletionSource<object?>();1748 var writeLockObtained = new TaskCompletionSource<object?>();1749 await Task.WhenAll(1750 Task.Run(async delegate1751 {1752 using (await this.asyncLock.UpgradeableReadLockAsync())1753 {1754 await upgradeableReadHeld.SetAsync();1755 await writeRequestPending.Task;1756 using (await this.asyncLock.WriteLockAsync())1757 {1758 Assert.False(writeLockObtained.Task.IsCompleted, "The upgradeable read should have received its write lock first.");1759 this.PrintHangReport();1760 await upgradeableReadUpgraded.SetAsync();1761 }1762 }1763 }),1764 Task.Run(async delegate1765 {1766 await upgradeableReadHeld.Task;1767 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter();1768 Assert.False(awaiter.IsCompleted, "We shouldn't get a write lock when an upgradeable read is held.");1769 awaiter.OnCompleted(delegate1770 {1771 using (AsyncReaderWriterLock.Releaser releaser = awaiter.GetResult())1772 {1773 writeLockObtained.SetAsync();1774 }1775 });1776 await writeRequestPending.SetAsync();1777 await writeLockObtained.Task;1778 }));1779 }1780 /// <summary>Verifies that an upgradeable reader blocks for upgrade while other readers release their locks.</summary>1781 [Fact]1782 public async Task UpgradeableReaderWaitsForExistingReadersToExit()1783 {1784 var readerHasLock = new TaskCompletionSource<object?>();1785 var upgradeableReaderWaitingForUpgrade = new TaskCompletionSource<object?>();1786 var upgradeableReaderHasUpgraded = new TaskCompletionSource<object?>();1787 var writeLockHasReleased = new TaskCompletionSource<object?>(TaskCreationOptions.RunContinuationsAsynchronously);1788 await Task.WhenAll(1789 Task.Run(async delegate1790 {1791 using (await this.asyncLock.UpgradeableReadLockAsync())1792 {1793 await readerHasLock.Task;1794 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter();1795 Assert.False(awaiter.IsCompleted, "The upgradeable read lock should not be upgraded while readers still have locks.");1796 awaiter.OnCompleted(delegate1797 {1798 using (awaiter.GetResult())1799 {1800 upgradeableReaderHasUpgraded.SetAsync();1801 }1802 writeLockHasReleased.SetResult(null);1803 });1804 Assert.False(upgradeableReaderHasUpgraded.Task.IsCompleted);1805 await upgradeableReaderWaitingForUpgrade.SetAsync();1806 await writeLockHasReleased.Task;1807 }1808 }),1809 Task.Run(async delegate1810 {1811 using (await this.asyncLock.ReadLockAsync())1812 {1813 await readerHasLock.SetAsync();1814 await upgradeableReaderWaitingForUpgrade.Task;1815 }1816 }),1817 upgradeableReaderHasUpgraded.Task);1818 }1819 /// <summary>Verifies that read lock requests are not serviced until any writers have released their locks.</summary>1820 [Fact]1821 public async Task ReadersWaitForWriter()1822 {1823 var readerHasLock = new TaskCompletionSource<object?>();1824 var writerHasLock = new TaskCompletionSource<object?>();1825 await Task.WhenAll(1826 Task.Run(async delegate1827 {1828 await writerHasLock.Task;1829 using (await this.asyncLock.ReadLockAsync())1830 {1831 await readerHasLock.SetAsync();1832 }1833 }),1834 Task.Run(async delegate1835 {1836 using (await this.asyncLock.WriteLockAsync())1837 {1838 await writerHasLock.SetAsync();1839 await Task.Delay(AsyncDelay);1840 Assert.False(readerHasLock.Task.IsCompleted, "Reader was issued lock while writer still had lock.");1841 }1842 }));1843 }1844 /// <summary>Verifies that write lock requests are not serviced until all existing readers have released their locks.</summary>1845 [Fact]1846 public async Task WriterWaitsForReaders()1847 {1848 var readerHasLock = new TaskCompletionSource<object?>();1849 var writerHasLock = new TaskCompletionSource<object?>();1850 await Task.WhenAll(1851 Task.Run(async delegate1852 {1853 using (await this.asyncLock.ReadLockAsync())1854 {1855 await readerHasLock.SetAsync();1856 await Task.Delay(AsyncDelay);1857 Assert.False(writerHasLock.Task.IsCompleted, "Writer was issued lock while reader still had lock.");1858 }1859 }),1860 Task.Run(async delegate1861 {1862 await readerHasLock.Task;1863 using (await this.asyncLock.WriteLockAsync())1864 {1865 await writerHasLock.SetAsync();1866 Assert.True(this.asyncLock.IsWriteLockHeld);1867 }1868 }));1869 }1870 /// <summary>Verifies that if a read lock is open, and a writer is waiting for a lock, that no new top-level read locks will be issued.</summary>1871 [Fact]1872 public async Task NewReadersWaitForWaitingWriters()1873 {1874 var readLockHeld = new TaskCompletionSource<object?>();1875 var writerWaitingForLock = new TaskCompletionSource<object?>();1876 var newReaderWaiting = new TaskCompletionSource<object?>();1877 var writerLockHeld = new TaskCompletionSource<object?>();1878 var newReaderLockHeld = new TaskCompletionSource<object?>();1879 await Task.WhenAll(1880 Task.Run(async delegate1881 {1882 this.Logger.WriteLine("About to wait for first read lock.");1883 using (await this.asyncLock.ReadLockAsync())1884 {1885 this.Logger.WriteLine("First read lock now held, and waiting for second reader to get blocked.");1886 await readLockHeld.SetAsync();1887 await newReaderWaiting.Task;1888 this.Logger.WriteLine("Releasing first read lock.");1889 }1890 this.Logger.WriteLine("First read lock released.");1891 }),1892 Task.Run(async delegate1893 {1894 await readLockHeld.Task;1895 AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();1896 Assert.False(writeAwaiter.IsCompleted, "The writer should not be issued a lock while a read lock is held.");1897 this.Logger.WriteLine("Write lock in queue.");1898 writeAwaiter.OnCompleted(delegate1899 {1900 using (writeAwaiter.GetResult())1901 {1902 try1903 {1904 this.Logger.WriteLine("Write lock issued.");1905 Assert.False(newReaderLockHeld.Task.IsCompleted, "Read lock should not be issued till after the write lock is released.");1906 writerLockHeld.SetResult(null); // must not be the asynchronous Set() extension method since we use it as a flag to check ordering later.1907 }1908 catch (Exception ex)1909 {1910 writerLockHeld.SetException(ex);1911 }1912 }1913 });1914 await writerWaitingForLock.SetAsync();1915 }),1916 Task.Run(async delegate1917 {1918 await writerWaitingForLock.Task;1919 AsyncReaderWriterLock.Awaiter? readAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter();1920 Assert.False(readAwaiter.IsCompleted, "The new reader should not be issued a lock while a write lock is pending.");1921 this.Logger.WriteLine("Second reader in queue.");1922 readAwaiter.OnCompleted(delegate1923 {1924 try1925 {1926 this.Logger.WriteLine("Second read lock issued.");1927 using (readAwaiter.GetResult())1928 {1929 Assert.True(writerLockHeld.Task.IsCompleted);1930 newReaderLockHeld.SetAsync();1931 }1932 }1933 catch (Exception ex)1934 {1935 newReaderLockHeld.SetException(ex);1936 }1937 });1938 await newReaderWaiting.SetAsync();1939 }),1940 readLockHeld.Task,1941 writerWaitingForLock.Task,1942 newReaderWaiting.Task,1943 writerLockHeld.Task,1944 newReaderLockHeld.Task);1945 }1946 [Fact]1947 public async Task NoDeadlockByBlockingReaderLocks()1948 {1949 var taskContext = new JoinableTaskContext();1950 var taskCollection = new JoinableTaskCollection(taskContext);1951 JoinableTaskFactory taskFactory = taskContext.CreateFactory(taskCollection);1952 var lockService = new ReaderWriterLockWithFastDeadlockCheck(taskContext);1953 var firstReadLockHeld = new TaskCompletionSource<object?>();1954 var writerWaitingForLock = new TaskCompletionSource<object?>();1955 var writeLockReleased = new TaskCompletionSource<object?>();1956 JoinableTask computationTask = taskFactory.RunAsync(async delegate1957 {1958 await writerWaitingForLock.Task;1959 this.Logger.WriteLine("About to wait for second read lock.");1960 using (await lockService.ReadLockAsync())1961 {1962 this.Logger.WriteLine("Second read lock now held.");1963 await Task.Yield();1964 this.Logger.WriteLine("Releasing second read lock.");1965 }1966 this.Logger.WriteLine("Second read lock released.");1967 });1968 await Task.WhenAll(1969 Task.Run(delegate1970 {1971 return taskFactory.RunAsync(async delegate1972 {1973 this.Logger.WriteLine("About to wait for first read lock.");1974 using (await lockService.ReadLockAsync())1975 {1976 this.Logger.WriteLine("First read lock now held, and waiting a task requiring second read lock.");1977 await firstReadLockHeld.SetAsync();1978 await computationTask;1979 this.Logger.WriteLine("Releasing first read lock.");1980 }1981 });1982 }),1983 Task.Run(async delegate1984 {1985 await firstReadLockHeld.Task;1986 AsyncReaderWriterLock.Awaiter? writeAwaiter = lockService.WriteLockAsync().GetAwaiter();1987 Assert.False(writeAwaiter.IsCompleted, "The writer should not be issued a lock while a read lock is held.");1988 this.Logger.WriteLine("Write lock in queue.");1989 writeAwaiter.OnCompleted(delegate1990 {1991 using (writeAwaiter.GetResult())1992 {1993 this.Logger.WriteLine("Write lock issued.");1994 }1995 writeLockReleased.SetResult(null);1996 });1997 await writerWaitingForLock.SetAsync();1998 }),1999 computationTask.Task,2000 firstReadLockHeld.Task,2001 writerWaitingForLock.Task,2002 writeLockReleased.Task);2003 }2004 [Fact]2005 public async Task NoDeadlockByBlockingUpgradeableReaderLocks()2006 {2007 var taskContext = new JoinableTaskContext();2008 var taskCollection = new JoinableTaskCollection(taskContext);2009 JoinableTaskFactory taskFactory = taskContext.CreateFactory(taskCollection);2010 var lockService = new ReaderWriterLockWithFastDeadlockCheck(taskContext);2011 var firstReadLockHeld = new TaskCompletionSource<object?>();2012 var writerWaitingForLock = new TaskCompletionSource<object?>();2013 var writeLockReleased = new TaskCompletionSource<object?>();2014 JoinableTask computationTask = taskFactory.RunAsync(async delegate2015 {2016 await writerWaitingForLock.Task;2017 this.Logger.WriteLine("About to wait for second read lock.");2018 using (await lockService.ReadLockAsync())2019 {2020 this.Logger.WriteLine("Second read lock now held.");2021 await Task.Yield();2022 this.Logger.WriteLine("Releasing second read lock.");2023 }2024 this.Logger.WriteLine("Second read lock released.");2025 });2026 await Task.WhenAll(2027 Task.Run(delegate2028 {2029 return taskFactory.RunAsync(async delegate2030 {2031 this.Logger.WriteLine("About to wait for first read lock.");2032 using (await lockService.UpgradeableReadLockAsync())2033 {2034 this.Logger.WriteLine("First upgradable read lock now held, and waiting a task requiring second read lock.");2035 await firstReadLockHeld.SetAsync();2036 await computationTask;2037 this.Logger.WriteLine("Releasing upgradable read lock.");2038 }2039 });2040 }),2041 Task.Run(async delegate2042 {2043 await firstReadLockHeld.Task;2044 AsyncReaderWriterLock.Awaiter? writeAwaiter = lockService.WriteLockAsync().GetAwaiter();2045 Assert.False(writeAwaiter.IsCompleted, "The writer should not be issued a lock while a read lock is held.");2046 this.Logger.WriteLine("Write lock in queue.");2047 writeAwaiter.OnCompleted(delegate2048 {2049 using (writeAwaiter.GetResult())2050 {2051 this.Logger.WriteLine("Write lock issued.");2052 }2053 writeLockReleased.SetResult(null);2054 });2055 await writerWaitingForLock.SetAsync();2056 }),2057 firstReadLockHeld.Task,2058 writerWaitingForLock.Task,2059 writeLockReleased.Task);2060 await computationTask.Task;2061 }2062 [Fact]2063 public async Task NoDeadlockByBlockingSequenceReaderLocks()2064 {2065 var taskContext = new JoinableTaskContext();2066 var taskCollection = new JoinableTaskCollection(taskContext);2067 JoinableTaskFactory? taskFactory = taskContext.CreateFactory(taskCollection);2068 var lockService = new ReaderWriterLockWithFastDeadlockCheck(taskContext);2069 var firstLockHeld = new TaskCompletionSource<object?>();2070 var writerWaitingForLock = new TaskCompletionSource<object?>();2071 var writeLockReleased = new TaskCompletionSource<object?>();2072 var computationTasks = new JoinableTask[4];2073 for (int i = 0; i < computationTasks.Length; i++)2074 {2075 computationTasks[i] = CreateReaderLockTask(taskFactory, lockService, writerWaitingForLock.Task, i, i > 0 ? computationTasks[i - 1] : null);2076 }2077 JoinableTask unrelatedTask = taskFactory.RunAsync(async delegate2078 {2079 await writerWaitingForLock.Task;2080 this.Logger.WriteLine("About to wait for unrelated read lock.");2081 using (await lockService.ReadLockAsync())2082 {2083 this.Logger.WriteLine("unrelated read lock now held.");2084 await Task.Yield();2085 this.Logger.WriteLine("Releasing unrelated read lock.");2086 }2087 this.Logger.WriteLine("unrelated read lock released.");2088 });2089 await Task.WhenAll(2090 Task.Run(delegate2091 {2092 return taskFactory.RunAsync(async delegate2093 {2094 this.Logger.WriteLine("About to wait for first read lock.");2095 using (await lockService.ReadLockAsync())2096 {2097 this.Logger.WriteLine("first read lock now held, and waiting a task requiring related read lock.");2098 await firstLockHeld.SetAsync();2099 await computationTasks[computationTasks.Length - 1];2100 this.Logger.WriteLine("Releasing first read lock.");2101 }2102 });2103 }),2104 Task.Run(async delegate2105 {2106 await firstLockHeld.Task;2107 AsyncReaderWriterLock.Awaiter? writeAwaiter = lockService.WriteLockAsync().GetAwaiter();2108 Assert.False(writeAwaiter.IsCompleted, "The writer should not be issued a lock while a read lock is held.");2109 this.Logger.WriteLine("Write lock in queue.");2110 writeAwaiter.OnCompleted(delegate2111 {2112 using (writeAwaiter.GetResult())2113 {2114 this.Logger.WriteLine("Write lock issued.");2115 Assert.False(unrelatedTask.IsCompleted, "Unrelated reader lock should not be issued.");2116 }2117 writeLockReleased.SetResult(null);2118 });2119 await writerWaitingForLock.SetAsync();2120 }),2121 firstLockHeld.Task,2122 writerWaitingForLock.Task,2123 unrelatedTask.Task,2124 writeLockReleased.Task);2125 await Task.WhenAll(computationTasks.Select(t => t.Task));2126 JoinableTask CreateReaderLockTask(JoinableTaskFactory taskFactory, AsyncReaderWriterLock lockService, Task initTask, int sequence, JoinableTask? previousTask)2127 {2128 return taskFactory.RunAsync(async delegate2129 {2130 await initTask;2131 this.Logger.WriteLine($"About to wait for read lock {sequence}.");2132 using (await lockService.ReadLockAsync())2133 {2134 this.Logger.WriteLine($"Related read lock {sequence} now held.");2135 await Task.Yield();2136 this.Logger.WriteLine($"Releasing related read lock {sequence}.");2137 }2138 if (previousTask is not null)2139 {2140 await previousTask;2141 }2142 this.Logger.WriteLine($"Read lock {sequence} released.");2143 });2144 }2145 }2146 /// <summary>Verifies proper behavior when multiple read locks are held, and both read and write locks are in the queue, and a read lock is released.</summary>2147 [Fact]2148 public async Task ManyReadersBlockWriteAndSubsequentReadRequest()2149 {2150 var firstReaderAcquired = new TaskCompletionSource<object?>();2151 var secondReaderAcquired = new TaskCompletionSource<object?>();2152 var writerWaiting = new TaskCompletionSource<object?>();2153 var thirdReaderWaiting = new TaskCompletionSource<object?>();2154 var releaseFirstReader = new TaskCompletionSource<object?>();2155 var releaseSecondReader = new TaskCompletionSource<object?>();2156 var writeAcquired = new TaskCompletionSource<object?>();2157 var thirdReadAcquired = new TaskCompletionSource<object?>();2158 await Task.WhenAll(2159 Task.Run(async delegate2160 { // FIRST READER2161 using (await this.asyncLock.ReadLockAsync())2162 {2163 Task? nowait = firstReaderAcquired.SetAsync();2164 await releaseFirstReader.Task;2165 }2166 }),2167 Task.Run(async delegate2168 { // SECOND READER2169 using (await this.asyncLock.ReadLockAsync())2170 {2171 Task? nowait = secondReaderAcquired.SetAsync();2172 await releaseSecondReader.Task;2173 }2174 }),2175 Task.Run(async delegate2176 { // WRITER2177 await Task.WhenAll(firstReaderAcquired.Task, secondReaderAcquired.Task);2178 AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();2179 Assert.False(writeAwaiter.IsCompleted);2180 writeAwaiter.OnCompleted(delegate2181 {2182 using (writeAwaiter.GetResult())2183 {2184 writeAcquired.SetAsync();2185 Assert.False(thirdReadAcquired.Task.IsCompleted);2186 }2187 });2188 Task? nowait = writerWaiting.SetAsync();2189 await writeAcquired.Task;2190 }),2191 Task.Run(async delegate2192 { // THIRD READER2193 await writerWaiting.Task;2194 AsyncReaderWriterLock.Awaiter? readAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter();2195 Assert.False(readAwaiter.IsCompleted, "Third reader should not have been issued a new top-level lock while writer is in the queue.");2196 readAwaiter.OnCompleted(delegate2197 {2198 using (readAwaiter.GetResult())2199 {2200 thirdReadAcquired.SetAsync();2201 Assert.True(writeAcquired.Task.IsCompleted);2202 }2203 });2204 Task? nowait = thirdReaderWaiting.SetAsync();2205 await thirdReadAcquired.Task;2206 }),2207 Task.Run(async delegate2208 { // Coordinator2209 await thirdReaderWaiting.Task;2210 Task? nowait = releaseFirstReader.SetAsync();2211 nowait = releaseSecondReader.SetAsync();2212 }));2213 }2214 /// <summary>Verifies that if a read lock is open, and a writer is waiting for a lock, that nested read locks will still be issued.</summary>2215 [Fact]2216 public async Task NestedReadersStillIssuedLocksWhileWaitingWriters()2217 {2218 var readerLockHeld = new TaskCompletionSource<object?>();2219 var writerQueued = new TaskCompletionSource<object?>();2220 var readerNestedLockHeld = new TaskCompletionSource<object?>();2221 var writerLockHeld = new TaskCompletionSource<object?>();2222 await Task.WhenAll(2223 Task.Run(async delegate2224 {2225 using (await this.asyncLock.ReadLockAsync())2226 {2227 await readerLockHeld.SetAsync();2228 await writerQueued.Task;2229 using (await this.asyncLock.ReadLockAsync())2230 {2231 Assert.False(writerLockHeld.Task.IsCompleted);2232 await readerNestedLockHeld.SetAsync();2233 }2234 }2235 }),2236 Task.Run(async delegate2237 {2238 await readerLockHeld.Task;2239 AsyncReaderWriterLock.Awaiter? writerAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();2240 Assert.False(writerAwaiter.IsCompleted);2241 writerAwaiter.OnCompleted(delegate2242 {2243 using (writerAwaiter.GetResult())2244 {2245 writerLockHeld.SetAsync();2246 }2247 });2248 await writerQueued.SetAsync();2249 }),2250 readerNestedLockHeld.Task,2251 writerLockHeld.Task);2252 }2253 /// <summary>Verifies that an upgradeable reader can 'downgrade' to a standard read lock without releasing the overall lock.</summary>2254 [Fact]2255 public async Task DowngradeUpgradeableReadToNormalRead()2256 {2257 var firstUpgradeableReadHeld = new TaskCompletionSource<object?>();2258 var secondUpgradeableReadHeld = new TaskCompletionSource<object?>();2259 await Task.WhenAll(2260 Task.Run(async delegate2261 {2262 using (AsyncReaderWriterLock.Releaser upgradeableReader = await this.asyncLock.UpgradeableReadLockAsync())2263 {2264 Assert.True(this.asyncLock.IsUpgradeableReadLockHeld);2265 await firstUpgradeableReadHeld.SetAsync();2266 using (AsyncReaderWriterLock.Releaser standardReader = await this.asyncLock.ReadLockAsync())2267 {2268 Assert.True(this.asyncLock.IsReadLockHeld);2269 Assert.True(this.asyncLock.IsUpgradeableReadLockHeld);2270 // Give up the upgradeable reader lock right away.2271 // This allows another upgradeable reader to obtain that kind of lock.2272 // Since we're *also* holding a (non-upgradeable) read lock, we're not letting writers in.2273 upgradeableReader.Dispose();2274 Assert.True(this.asyncLock.IsReadLockHeld);2275 Assert.False(this.asyncLock.IsUpgradeableReadLockHeld);2276 // Ensure that the second upgradeable read lock is now obtainable.2277 await secondUpgradeableReadHeld.Task;2278 }2279 }2280 }),2281 Task.Run(async delegate2282 {2283 await firstUpgradeableReadHeld.Task;2284 using (await this.asyncLock.UpgradeableReadLockAsync())2285 {2286 await secondUpgradeableReadHeld.SetAsync();2287 }2288 }));2289 }2290 [Fact]2291 public async Task MitigationAgainstAccidentalExclusiveLockConcurrency()2292 {2293 using (await this.asyncLock.UpgradeableReadLockAsync())2294 {2295 using (await this.asyncLock.WriteLockAsync())2296 {2297 await this.CheckContinuationsConcurrencyHelper();2298 using (await this.asyncLock.WriteLockAsync())2299 {2300 await this.CheckContinuationsConcurrencyHelper();2301 }2302 await this.CheckContinuationsConcurrencyHelper();2303 }2304 await this.CheckContinuationsConcurrencyHelper();2305 }2306 using (await this.asyncLock.WriteLockAsync())2307 {2308 await this.CheckContinuationsConcurrencyHelper();2309 using (await this.asyncLock.WriteLockAsync())2310 {2311 await this.CheckContinuationsConcurrencyHelper();2312 }2313 await this.CheckContinuationsConcurrencyHelper();2314 }2315 }2316 [Fact]2317 public async Task UpgradedReadWithSyncContext()2318 {2319 var contestingReadLockAcquired = new TaskCompletionSource<object?>();2320 var writeLockWaiting = new TaskCompletionSource<object?>();2321 await Task.WhenAll(2322 Task.Run(async delegate2323 {2324 using (await this.asyncLock.UpgradeableReadLockAsync())2325 {2326 await contestingReadLockAcquired.Task;2327 AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();2328 Assert.False(writeAwaiter.IsCompleted);2329 var nestedLockAcquired = new TaskCompletionSource<object?>();2330 writeAwaiter.OnCompleted(async delegate2331 {2332 using (writeAwaiter.GetResult())2333 {2334 using (await this.asyncLock.UpgradeableReadLockAsync())2335 {2336 Task? nowait2 = nestedLockAcquired.SetAsync();2337 }2338 }2339 });2340 Task? nowait = writeLockWaiting.SetAsync();2341 await nestedLockAcquired.Task;2342 }2343 }),2344 Task.Run(async delegate2345 {2346 using (await this.asyncLock.ReadLockAsync())2347 {2348 Task? nowait = contestingReadLockAcquired.SetAsync();2349 await writeLockWaiting.Task;2350 }2351 }));2352 }2353 [Fact]2354 public async Task PrecancelledReadLockAsyncRequest()2355 {2356 await Task.Run(delegate2357 { // get onto an MTA2358 var cts = new CancellationTokenSource();2359 cts.Cancel();2360 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync(cts.Token).GetAwaiter();2361 Assert.True(awaiter.IsCompleted);2362 try2363 {2364 awaiter.GetResult();2365 Assert.True(false, "Expected OperationCanceledException not thrown.");2366 }2367 catch (OperationCanceledException)2368 {2369 }2370 });2371 }2372 [StaFact]2373 public void PrecancelledWriteLockAsyncRequestOnSTA()2374 {2375 var cts = new CancellationTokenSource();2376 cts.Cancel();2377 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync(cts.Token).GetAwaiter();2378 Assert.True(awaiter.IsCompleted);2379 try2380 {2381 awaiter.GetResult();2382 Assert.True(false, "Expected OperationCanceledException not thrown.");2383 }2384 catch (OperationCanceledException)2385 {2386 }2387 }2388 [Fact]2389 public async Task CancelPendingLock()2390 {2391 var firstWriteHeld = new TaskCompletionSource<object?>();2392 var cancellationTestConcluded = new TaskCompletionSource<object?>();2393 await Task.WhenAll(2394 Task.Run(async delegate2395 {2396 using (await this.asyncLock.WriteLockAsync())2397 {2398 await firstWriteHeld.SetAsync();2399 await cancellationTestConcluded.Task;2400 }2401 }),2402 Task.Run(async delegate2403 {2404 await firstWriteHeld.Task;2405 var cts = new CancellationTokenSource();2406 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync(cts.Token).GetAwaiter();2407 Assert.False(awaiter.IsCompleted);2408 awaiter.OnCompleted(delegate2409 {2410 try2411 {2412 awaiter.GetResult();2413 cancellationTestConcluded.SetException(new ThrowsException(typeof(OperationCanceledException)));2414 }2415 catch (OperationCanceledException)2416 {2417 cancellationTestConcluded.SetAsync();2418 }2419 });2420 cts.Cancel();2421 }));2422 }2423 [Fact]2424 public async Task CancelPendingLockFollowedByAnotherLock()2425 {2426 var firstWriteHeld = new TaskCompletionSource<object?>();2427 var releaseWriteLock = new TaskCompletionSource<object?>();2428 var cancellationTestConcluded = new TaskCompletionSource<object?>();2429 var readerConcluded = new TaskCompletionSource<object?>();2430 await Task.WhenAll(2431 Task.Run(async delegate2432 {2433 using (await this.asyncLock.WriteLockAsync())2434 {2435 await firstWriteHeld.SetAsync();2436 await releaseWriteLock.Task;2437 }2438 }),2439 Task.Run(async delegate2440 {2441 await firstWriteHeld.Task;2442 var cts = new CancellationTokenSource();2443 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync(cts.Token).GetAwaiter();2444 Assert.False(awaiter.IsCompleted);2445 awaiter.OnCompleted(delegate2446 {2447 try2448 {2449 awaiter.GetResult();2450 cancellationTestConcluded.SetException(new ThrowsException(typeof(OperationCanceledException)));2451 }2452 catch (OperationCanceledException)2453 {2454 cancellationTestConcluded.SetAsync();2455 }2456 });2457 cts.Cancel();2458 // Pend another lock request. Make it a read lock this time.2459 // The point of this test is to ensure that the canceled (Write) awaiter doesn't get2460 // reused as a read awaiter while it is still in the writer queue.2461 await cancellationTestConcluded.Task;2462 Assert.False(this.asyncLock.IsWriteLockHeld);2463 Assert.False(this.asyncLock.IsReadLockHeld);2464 AsyncReaderWriterLock.Awaiter? readLockAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter();2465 readLockAwaiter.OnCompleted(delegate2466 {2467 using (readLockAwaiter.GetResult())2468 {2469 Assert.True(this.asyncLock.IsReadLockHeld);2470 Assert.False(this.asyncLock.IsWriteLockHeld);2471 }2472 Assert.False(this.asyncLock.IsReadLockHeld);2473 Assert.False(this.asyncLock.IsWriteLockHeld);2474 readerConcluded.SetAsync();2475 });2476 Task? nowait = releaseWriteLock.SetAsync();2477 await readerConcluded.Task;2478 }));2479 }2480 [Fact]2481 public async Task CancelNonImpactfulToIssuedLocks()2482 {2483 var cts = new CancellationTokenSource();2484 using (await this.asyncLock.WriteLockAsync(cts.Token))2485 {2486 Assert.True(this.asyncLock.IsWriteLockHeld);2487 cts.Cancel();2488 Assert.True(this.asyncLock.IsWriteLockHeld);2489 }2490 Assert.False(this.asyncLock.IsWriteLockHeld);2491 }2492 [StaFact]2493 public async Task CancelJustBeforeIsCompletedNoLeak()2494 {2495 var lockAwaitFinished = new TaskCompletionSource<object?>();2496 var cts = new CancellationTokenSource();2497 AsyncReaderWriterLock.Awaitable awaitable = this.asyncLock.UpgradeableReadLockAsync(cts.Token);2498 AsyncReaderWriterLock.Awaiter? awaiter = awaitable.GetAwaiter();2499 cts.Cancel();2500 if (awaiter.IsCompleted)2501 {2502 // The lock should not be issued on an STA thread2503 Assert.ThrowsAny<OperationCanceledException>(() => awaiter.GetResult().Dispose());2504 await lockAwaitFinished.SetAsync();2505 }2506 else2507 {2508 awaiter.OnCompleted(delegate2509 {2510 try2511 {2512 awaiter.GetResult().Dispose();2513 Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState());2514 }2515 catch (OperationCanceledException)2516 {2517 }2518 lockAwaitFinished.SetAsync();2519 });2520 }2521 await lockAwaitFinished.Task.WithTimeout(UnexpectedTimeout);2522 // No lock is leaked2523 using (await this.asyncLock.UpgradeableReadLockAsync())2524 {2525 Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState());2526 }2527 }2528 [Fact]2529 public async Task CancelJustAfterIsCompleted()2530 {2531 var lockAwaitFinished = new TaskCompletionSource<object?>();2532 var testCompleted = new TaskCompletionSource<object?>();2533 var readlockTask = Task.Run(async delegate2534 {2535 using (await this.asyncLock.ReadLockAsync())2536 {2537 await lockAwaitFinished.SetAsync();2538 await testCompleted.Task;2539 }2540 });2541 await lockAwaitFinished.Task;2542 var cts = new CancellationTokenSource();2543 AsyncReaderWriterLock.Awaitable awaitable = this.asyncLock.WriteLockAsync(cts.Token);2544 AsyncReaderWriterLock.Awaiter? awaiter = awaitable.GetAwaiter();2545 Assert.False(awaiter.IsCompleted, "The lock should not be issued until read lock is released.");2546 cts.Cancel();2547 awaiter.OnCompleted(delegate2548 {2549 Assert.ThrowsAny<OperationCanceledException>(() => awaiter.GetResult().Dispose());2550 testCompleted.SetAsync();2551 });2552 await readlockTask.WithTimeout(UnexpectedTimeout);2553 }2554 [Fact]2555 public async Task CancelWriteLockUnblocksReadLocks()2556 {2557 var firstReadLockAcquired = new AsyncManualResetEvent();2558 var firstReadLockToRelease = new AsyncManualResetEvent();2559 var firstReadLockTask = Task.Run(async () =>2560 {2561 using (await this.asyncLock.ReadLockAsync())2562 {2563 firstReadLockAcquired.Set();2564 await firstReadLockToRelease.WaitAsync();2565 }2566 });2567 await firstReadLockAcquired.WaitAsync();2568 var cancellationSource = new CancellationTokenSource();2569 AsyncReaderWriterLock.Awaiter? writeLockAwaiter = this.asyncLock.WriteLockAsync(cancellationSource.Token).GetAwaiter();2570 Assert.False(writeLockAwaiter.IsCompleted);2571 writeLockAwaiter.OnCompleted(delegate2572 {2573 try2574 {2575 writeLockAwaiter.GetResult();2576 }2577 catch (OperationCanceledException)2578 {2579 }2580 });2581 AsyncReaderWriterLock.Awaiter? readLockAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter();2582 var secondReadLockAcquired = new AsyncManualResetEvent();2583 Assert.False(readLockAwaiter.IsCompleted);2584 readLockAwaiter.OnCompleted(delegate2585 {2586 using (readLockAwaiter.GetResult())2587 {2588 secondReadLockAcquired.Set();2589 }2590 });2591 cancellationSource.Cancel();2592 await secondReadLockAcquired.WaitAsync();2593 firstReadLockToRelease.Set();2594 await firstReadLockTask;2595 }2596 [Fact]2597 public async Task CancelWriteLockUnblocksUpgradeableReadLocks()2598 {2599 var firstReadLockAcquired = new AsyncManualResetEvent();2600 var firstReadLockToRelease = new AsyncManualResetEvent();2601 var firstReadLockTask = Task.Run(async () =>2602 {2603 using (await this.asyncLock.ReadLockAsync())2604 {2605 firstReadLockAcquired.Set();2606 await firstReadLockToRelease.WaitAsync();2607 }2608 });2609 await firstReadLockAcquired.WaitAsync();2610 var cancellationSource = new CancellationTokenSource();2611 AsyncReaderWriterLock.Awaiter? writeLockAwaiter = this.asyncLock.WriteLockAsync(cancellationSource.Token).GetAwaiter();2612 Assert.False(writeLockAwaiter.IsCompleted);2613 writeLockAwaiter.OnCompleted(delegate2614 {2615 try2616 {2617 writeLockAwaiter.GetResult();2618 }2619 catch (OperationCanceledException)2620 {2621 }2622 });2623 AsyncReaderWriterLock.Awaiter? upgradeableReadLockAwaiter = this.asyncLock.UpgradeableReadLockAsync().GetAwaiter();2624 var upgradeableReadLockAcquired = new AsyncManualResetEvent();2625 Assert.False(upgradeableReadLockAwaiter.IsCompleted);2626 upgradeableReadLockAwaiter.OnCompleted(delegate2627 {2628 using (upgradeableReadLockAwaiter.GetResult())2629 {2630 upgradeableReadLockAcquired.Set();2631 }2632 });2633 cancellationSource.Cancel();2634 await upgradeableReadLockAcquired.WaitAsync();2635 firstReadLockToRelease.Set();2636 await firstReadLockTask;2637 }2638 [Fact]2639 public async Task CancelWriteLockDoesNotUnblocksReadLocksIncorrectly()2640 {2641 var firstWriteLockAcquired = new AsyncManualResetEvent();2642 var firstWriteLockToRelease = new AsyncManualResetEvent();2643 var firstCancellationSource = new CancellationTokenSource();2644 var firstWriteLockTask = Task.Run(async () =>2645 {2646 using (await this.asyncLock.WriteLockAsync(firstCancellationSource.Token))2647 {2648 firstWriteLockAcquired.Set();2649 await firstWriteLockToRelease.WaitAsync();2650 }2651 });2652 await firstWriteLockAcquired.WaitAsync();2653 var cancellationSource = new CancellationTokenSource();2654 AsyncReaderWriterLock.Awaiter? writeLockAwaiter = this.asyncLock.WriteLockAsync(cancellationSource.Token).GetAwaiter();2655 Assert.False(writeLockAwaiter.IsCompleted);2656 writeLockAwaiter.OnCompleted(delegate2657 {2658 try2659 {2660 writeLockAwaiter.GetResult();2661 }2662 catch (OperationCanceledException)2663 {2664 }2665 });2666 AsyncReaderWriterLock.Awaiter? readLockAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter();2667 var readLockAcquired = new AsyncManualResetEvent();2668 Assert.False(readLockAwaiter.IsCompleted);2669 readLockAwaiter.OnCompleted(delegate2670 {2671 using (readLockAwaiter.GetResult())2672 {2673 readLockAcquired.Set();2674 }2675 });2676 cancellationSource.Cancel();2677 firstCancellationSource.Cancel();2678 Assert.False(readLockAcquired.WaitAsync().Wait(AsyncDelay));2679 firstWriteLockToRelease.Set();2680 await firstWriteLockAcquired;2681 await readLockAcquired.WaitAsync();2682 }2683 [StaFact]2684 public void CompleteBlocksNewTopLevelLocksSTA()2685 {2686 Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); // test requires STA2687 this.asyncLock.Complete();2688 // Exceptions should always be thrown via the awaitable result rather than synchronously thrown2689 // so that we meet expectations of C# async methods.2690 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter();2691 Assert.True(awaiter.IsCompleted);2692 try2693 {2694 awaiter.GetResult();2695 Assert.True(false, "Expected exception not thrown.");2696 }2697 catch (InvalidOperationException)2698 {2699 }2700 }2701 [Fact]2702 public async Task CompleteBlocksNewTopLevelLocksMTA()2703 {2704 this.asyncLock.Complete();2705 await Task.Run(delegate2706 {2707 // Exceptions should always be thrown via the awaitable result rather than synchronously thrown2708 // so that we meet expectations of C# async methods.2709 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter();2710 Assert.True(awaiter.IsCompleted);2711 try2712 {2713 awaiter.GetResult();2714 Assert.True(false, "Expected exception not thrown.");2715 }2716 catch (InvalidOperationException)2717 {2718 }2719 });2720 }2721 [Fact]2722 public async Task CompleteDoesNotBlockNestedLockRequests()2723 {2724 using (await this.asyncLock.ReadLockAsync())2725 {2726 this.asyncLock.Complete();2727 Assert.False(this.asyncLock.Completion.IsCompleted, "Lock shouldn't be completed while there are open locks.");2728 using (await this.asyncLock.ReadLockAsync())2729 {2730 }2731 Assert.False(this.asyncLock.Completion.IsCompleted, "Lock shouldn't be completed while there are open locks.");2732 }2733 await this.asyncLock.Completion; // ensure that Completion transitions to completed as a result of releasing all locks.2734 }2735 [Fact]2736 public async Task CompleteAllowsPreviouslyQueuedLockRequests()2737 {2738 var firstLockAcquired = new TaskCompletionSource<object?>();2739 var secondLockQueued = new TaskCompletionSource<object?>();2740 var completeSignaled = new TaskCompletionSource<object?>();2741 var secondLockAcquired = new TaskCompletionSource<object?>();2742 await Task.WhenAll(2743 Task.Run(async delegate2744 {2745 using (await this.asyncLock.WriteLockAsync())2746 {2747 this.Logger.WriteLine("First write lock acquired.");2748 await firstLockAcquired.SetAsync();2749 await completeSignaled.Task;2750 Assert.False(this.asyncLock.Completion.IsCompleted);2751 }2752 }),2753 Task.Run(async delegate2754 {2755 try2756 {2757 await firstLockAcquired.Task;2758 AsyncReaderWriterLock.Awaiter? secondWriteAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();2759 Assert.False(secondWriteAwaiter.IsCompleted);2760 this.Logger.WriteLine("Second write lock request pended.");2761 secondWriteAwaiter.OnCompleted(delegate2762 {2763 using (secondWriteAwaiter.GetResult())2764 {2765 this.Logger.WriteLine("Second write lock acquired.");2766 secondLockAcquired.SetAsync();2767 Assert.False(this.asyncLock.Completion.IsCompleted);2768 }2769 });2770 await secondLockQueued.SetAsync();2771 }2772 catch (Exception ex)2773 {2774 secondLockAcquired.TrySetException(ex);2775 }2776 }),2777 Task.Run(async delegate2778 {2779 await secondLockQueued.Task;2780 this.Logger.WriteLine("Calling Complete() method.");2781 this.asyncLock.Complete();2782 await completeSignaled.SetAsync();2783 }),2784 secondLockAcquired.Task);2785 await this.asyncLock.Completion;2786 }2787 [Fact]2788 public async Task OnBeforeExclusiveLockReleasedAsyncSimpleSyncHandler()2789 {2790 var asyncLock = new LockDerived();2791 int callbackInvoked = 0;2792 asyncLock.OnBeforeExclusiveLockReleasedAsyncDelegate = delegate2793 {2794 callbackInvoked++;2795 Assert.True(asyncLock.IsWriteLockHeld);2796 return Task.CompletedTask;2797 };2798 using (await asyncLock.WriteLockAsync())2799 {2800 }2801 Assert.Equal(1, callbackInvoked);2802 }2803 [Fact]2804 public async Task OnBeforeExclusiveLockReleasedAsyncNestedLockSyncHandler()2805 {2806 var asyncLock = new LockDerived();2807 int callbackInvoked = 0;2808 asyncLock.OnBeforeExclusiveLockReleasedAsyncDelegate = async delegate2809 {2810 Assert.True(asyncLock.IsWriteLockHeld);2811 using (await asyncLock.ReadLockAsync())2812 {2813 Assert.True(asyncLock.IsWriteLockHeld);2814 using (await asyncLock.WriteLockAsync())2815 { // this should succeed because our caller has a write lock.2816 Assert.True(asyncLock.IsWriteLockHeld);2817 }2818 }2819 callbackInvoked++;2820 };2821 using (await asyncLock.WriteLockAsync())2822 {2823 }2824 Assert.Equal(1, callbackInvoked);2825 }2826 [Fact]2827 public async Task OnBeforeExclusiveLockReleasedAsyncSimpleAsyncHandler()2828 {2829 var asyncLock = new LockDerived();2830 var callbackCompleted = new TaskCompletionSource<object?>();2831 asyncLock.OnBeforeExclusiveLockReleasedAsyncDelegate = async delegate2832 {2833 try2834 {2835 Assert.True(asyncLock.IsWriteLockHeld);2836 await Task.Yield();2837 Assert.True(asyncLock.IsWriteLockHeld);2838 callbackCompleted.SetResult(null);2839 }2840 catch (Exception ex)2841 {2842 callbackCompleted.SetException(ex);2843 }2844 };2845 using (await asyncLock.WriteLockAsync())2846 {2847 }2848 await callbackCompleted.Task;2849 }2850 [Fact]2851 public async Task OnBeforeExclusiveLockReleasedAsyncReadLockAcquiringAsyncHandler()2852 {2853 var asyncLock = new LockDerived();2854 var callbackCompleted = new TaskCompletionSource<object?>();2855 asyncLock.OnBeforeExclusiveLockReleasedAsyncDelegate = async delegate2856 {2857 try2858 {2859 Assert.True(asyncLock.IsWriteLockHeld);2860 using (await asyncLock.ReadLockAsync())2861 {2862 Assert.True(asyncLock.IsWriteLockHeld);2863 Assert.True(asyncLock.IsReadLockHeld);2864 await Task.Yield();2865 Assert.True(asyncLock.IsWriteLockHeld);2866 Assert.True(asyncLock.IsReadLockHeld);2867 }2868 callbackCompleted.SetResult(null);2869 }2870 catch (Exception ex)2871 {2872 callbackCompleted.SetException(ex);2873 }2874 };2875 using (await asyncLock.WriteLockAsync())2876 {2877 }2878 await callbackCompleted.Task;2879 }2880 [Fact]2881 public async Task OnBeforeExclusiveLockReleasedAsyncNestedWriteLockAsyncHandler()2882 {2883 var asyncLock = new LockDerived();2884 var callbackCompleted = new TaskCompletionSource<object?>();2885 bool outermostExecuted = false;2886 asyncLock.OnBeforeExclusiveLockReleasedAsyncDelegate = async delegate2887 {2888 try2889 {2890 // Only do our deed on the outermost lock release -- not the one we take.2891 if (!outermostExecuted)2892 {2893 outermostExecuted = true;2894 Assert.True(asyncLock.IsWriteLockHeld);2895 using (await asyncLock.WriteLockAsync())2896 {2897 await Task.Yield();2898 using (await asyncLock.ReadLockAsync())2899 {2900 Assert.True(asyncLock.IsWriteLockHeld);2901 using (await asyncLock.WriteLockAsync())2902 {2903 Assert.True(asyncLock.IsWriteLockHeld);2904 await Task.Yield();2905 Assert.True(asyncLock.IsWriteLockHeld);2906 }2907 }2908 }2909 callbackCompleted.SetResult(null);2910 }2911 }2912 catch (Exception ex)2913 {2914 callbackCompleted.SetException(ex);2915 }2916 };2917 using (await asyncLock.WriteLockAsync())2918 {2919 }2920 await callbackCompleted.Task;2921 }2922 [Fact]2923 public async Task OnBeforeExclusiveLockReleasedAsyncWriteLockWrapsBaseMethod()2924 {2925 var callbackCompleted = new AsyncManualResetEvent();2926 var asyncLock = new LockDerivedWriteLockAroundOnBeforeExclusiveLockReleased();2927 using (await asyncLock.WriteLockAsync())2928 {2929 asyncLock.OnBeforeWriteLockReleased(async delegate2930 {2931 await Task.Yield();2932 });2933 }2934 await asyncLock.OnBeforeExclusiveLockReleasedAsyncInvoked.WaitAsync();2935 }2936 [Fact]2937 public async Task OnBeforeExclusiveLockReleasedAsyncWriteLockReleaseAsync()2938 {2939 var asyncLock = new LockDerivedWriteLockAroundOnBeforeExclusiveLockReleased();2940 await using (await asyncLock.WriteLockAsync())2941 {2942 }2943 }2944 [Fact]2945 public async Task OnBeforeExclusiveLockReleasedAsyncReadLockReleaseAsync()2946 {2947 var asyncLock = new LockDerivedReadLockAroundOnBeforeExclusiveLockReleased();2948 await using (await asyncLock.WriteLockAsync())2949 {2950 }2951 }2952 [Fact]2953 public async Task OnBeforeWriteLockReleasedNullArgument()2954 {2955 using (await this.asyncLock.WriteLockAsync())2956 {2957 Assert.Throws<ArgumentNullException>(() => this.asyncLock.OnBeforeWriteLockReleased(null!));2958 }2959 }2960 [Fact]2961 public async Task OnBeforeWriteLockReleasedSingle()2962 {2963 var afterWriteLock = new TaskCompletionSource<object?>();2964 using (await this.asyncLock.WriteLockAsync())2965 {2966 this.asyncLock.OnBeforeWriteLockReleased(async delegate2967 {2968 try2969 {2970 Assert.True(this.asyncLock.IsWriteLockHeld);2971 afterWriteLock.SetResult(null);2972 await Task.Yield();2973 Assert.True(this.asyncLock.IsWriteLockHeld);2974 }2975 catch (Exception ex)2976 {2977 afterWriteLock.SetException(ex);2978 }2979 });2980 Assert.False(afterWriteLock.Task.IsCompleted);2981 // Set Complete() this early to verify that callbacks can fire even after Complete() is called.2982 this.asyncLock.Complete();2983 }2984 await afterWriteLock.Task;2985 await this.asyncLock.Completion;2986 }2987 [Fact]2988 public async Task OnBeforeWriteLockReleasedMultiple()2989 {2990 var afterWriteLock1 = new TaskCompletionSource<object?>();2991 var afterWriteLock2 = new TaskCompletionSource<object?>();2992 using (await this.asyncLock.WriteLockAsync())2993 {2994 this.asyncLock.OnBeforeWriteLockReleased(async delegate2995 {2996 try2997 {2998 Assert.True(this.asyncLock.IsWriteLockHeld);2999 afterWriteLock1.SetResult(null);3000 await Task.Yield();3001 Assert.True(this.asyncLock.IsWriteLockHeld);3002 }3003 catch (Exception ex)3004 {3005 afterWriteLock1.SetException(ex);3006 }3007 });3008 this.asyncLock.OnBeforeWriteLockReleased(async delegate3009 {3010 try3011 {3012 Assert.True(this.asyncLock.IsWriteLockHeld);3013 afterWriteLock2.SetResult(null);3014 await Task.Yield();3015 Assert.True(this.asyncLock.IsWriteLockHeld);3016 }3017 catch (Exception ex)3018 {3019 afterWriteLock2.SetException(ex);3020 }3021 });3022 Assert.False(afterWriteLock1.Task.IsCompleted);3023 Assert.False(afterWriteLock2.Task.IsCompleted);3024 }3025 this.asyncLock.Complete();3026 await afterWriteLock1.Task;3027 await afterWriteLock2.Task;3028 await this.asyncLock.Completion;3029 }3030 [Fact]3031 public async Task OnBeforeWriteLockReleasedNestedCallbacks()3032 {3033 var callback1 = new TaskCompletionSource<object?>();3034 var callback2 = new TaskCompletionSource<object?>();3035 using (await this.asyncLock.WriteLockAsync())3036 {3037 this.asyncLock.OnBeforeWriteLockReleased(async delegate3038 {3039 Assert.True(this.asyncLock.IsWriteLockHeld);3040 await Task.Yield();3041 Assert.True(this.asyncLock.IsWriteLockHeld);3042 await callback1.SetAsync();3043 // Now within a callback, let's pretend we made some change that caused another callback to register.3044 this.asyncLock.OnBeforeWriteLockReleased(async delegate3045 {3046 Assert.True(this.asyncLock.IsWriteLockHeld);3047 await Task.Yield();3048 Assert.True(this.asyncLock.IsWriteLockHeld);3049 await callback2.SetAsync();3050 });3051 });3052 // Set Complete() this early to verify that callbacks can fire even after Complete() is called.3053 this.asyncLock.Complete();3054 }3055 await callback2.Task;3056 await this.asyncLock.Completion;3057 }3058 [Fact]3059 public async Task OnBeforeWriteLockReleasedDelegateThrows()3060 {3061 var afterWriteLock = new TaskCompletionSource<object?>();3062 var exceptionToThrow = new ApplicationException();3063 try3064 {3065 using (await this.asyncLock.WriteLockAsync())3066 {3067 this.asyncLock.OnBeforeWriteLockReleased(delegate3068 {3069 afterWriteLock.SetResult(null);3070 throw exceptionToThrow;3071 });3072 Assert.False(afterWriteLock.Task.IsCompleted);3073 this.asyncLock.Complete();3074 }3075 Assert.True(false, "Expected exception not thrown.");3076 }3077 catch (AggregateException ex)3078 {3079 Assert.Same(exceptionToThrow, ex.Flatten().InnerException);3080 }3081 Assert.False(this.asyncLock.IsWriteLockHeld);3082 await afterWriteLock.Task;3083 await this.asyncLock.Completion;3084 }3085 [Fact]3086 public async Task OnBeforeWriteLockReleasedWithUpgradedWrite()3087 {3088 var callbackFired = new TaskCompletionSource<object?>();3089 using (await this.asyncLock.UpgradeableReadLockAsync())3090 {3091 using (await this.asyncLock.WriteLockAsync())3092 {3093 this.asyncLock.OnBeforeWriteLockReleased(async delegate3094 {3095 Assert.True(this.asyncLock.IsWriteLockHeld);3096 await Task.Yield();3097 Assert.True(this.asyncLock.IsWriteLockHeld);3098 await callbackFired.SetAsync();3099 });3100 }3101 Assert.True(callbackFired.Task.IsCompleted, "This should have completed synchronously with releasing the write lock.");3102 }3103 }3104 [Fact]3105 public async Task OnBeforeWriteLockReleasedWithNestedStickyUpgradedWrite()3106 {3107 var callbackFired = new TaskCompletionSource<object?>();3108 using (await this.asyncLock.UpgradeableReadLockAsync())3109 {3110 using (await this.asyncLock.UpgradeableReadLockAsync(AsyncReaderWriterLock.LockFlags.StickyWrite))3111 {3112 using (await this.asyncLock.WriteLockAsync())3113 {3114 this.asyncLock.OnBeforeWriteLockReleased(async delegate3115 {3116 Assert.True(this.asyncLock.IsWriteLockHeld);3117 await callbackFired.SetAsync();3118 await Task.Yield();3119 Assert.True(this.asyncLock.IsWriteLockHeld);3120 });3121 }3122 Assert.False(callbackFired.Task.IsCompleted, "This shouldn't have run yet because the upgradeable read lock bounding the write lock is a sticky one.");3123 }3124 Assert.True(callbackFired.Task.IsCompleted, "This should have completed synchronously with releasing the upgraded sticky upgradeable read lock.");3125 }3126 }3127 [Fact]3128 public async Task OnBeforeWriteLockReleasedWithStickyUpgradedWrite()3129 {3130 var callbackBegin = new TaskCompletionSource<object?>();3131 var callbackEnding = new TaskCompletionSource<object?>();3132 var releaseCallback = new TaskCompletionSource<object?>();3133 using (await this.asyncLock.UpgradeableReadLockAsync(AsyncReaderWriterLock.LockFlags.StickyWrite))3134 {3135 using (await this.asyncLock.WriteLockAsync())3136 {3137 this.asyncLock.OnBeforeWriteLockReleased(async delegate3138 {3139 await callbackBegin.SetAsync();3140 Assert.True(this.asyncLock.IsWriteLockHeld);3141 await Task.Delay(AsyncDelay);3142 Assert.True(this.asyncLock.IsWriteLockHeld);3143 // Technically this callback should be able to complete asynchronously3144 // with respect to the thread that released the lock, but for now that3145 // feature is disabled to keep things a bit sane (it also gives us a3146 // listener if one of the exclusive lock callbacks throw an exception).3147 ////await releaseCallback.Task;3148 callbackEnding.SetResult(null); // don't use Set() extension method because that's asynchronous, and we measure this to verify ordered behavior.3149 });3150 }3151 Assert.False(callbackBegin.Task.IsCompleted, "This shouldn't have run yet because the upgradeable read lock bounding the write lock is a sticky one.");3152 }3153 // This next assert is commented out because (again), the lock's current behavior is to3154 // synchronously block when releasing locks, even if it's arguably not necessary.3155 ////Assert.False(callbackEnding.Task.IsCompleted, "This should have completed asynchronously because no read lock remained after the sticky upgraded read lock was released.");3156 Assert.True(callbackEnding.Task.IsCompleted, "Once this fails, uncomment the previous assert and the await earlier in the test if it's intended behavior.");3157 await releaseCallback.SetAsync();3158 // Because the callbacks are fired asynchronously, we must wait for it to settle before allowing the test to finish3159 // to avoid a false failure from the Cleanup method.3160 this.asyncLock.Complete();3161 await this.asyncLock.Completion;3162 Assert.True(callbackEnding.Task.IsCompleted, "The completion task should not have completed until the callbacks had completed.");3163 }3164 [Fact]3165 public async Task OnBeforeWriteLockReleasedWithStickyUpgradedWriteWithNestedLocks()3166 {3167 var asyncLock = new LockDerived3168 {3169 OnExclusiveLockReleasedAsyncDelegate = async delegate3170 {3171 await Task.Yield();3172 },3173 };3174 var releaseCallback = new TaskCompletionSource<object?>();3175 using (await asyncLock.UpgradeableReadLockAsync(AsyncReaderWriterLock.LockFlags.StickyWrite))3176 {3177 using (await asyncLock.WriteLockAsync())3178 {3179 asyncLock.OnBeforeWriteLockReleased(async delegate3180 {3181 // For this test, we deliberately do not yield before3182 // requesting first lock because we're racing to request a lock3183 // while the reenterConcurrencyPrepRunning field is "true".3184 Assert.True(asyncLock.IsWriteLockHeld);3185 using (await asyncLock.ReadLockAsync())3186 {3187 using (await asyncLock.WriteLockAsync())3188 {3189 }3190 }3191 using (await asyncLock.UpgradeableReadLockAsync())3192 {3193 }3194 using (await asyncLock.WriteLockAsync())3195 {3196 }3197 await Task.Yield();3198 Assert.True(asyncLock.IsWriteLockHeld);3199 // Technically this callback should be able to complete asynchronously3200 // with respect to the thread that released the lock, but for now that3201 // feature is disabled to keep things a bit sane (it also gives us a3202 // listener if one of the exclusive lock callbacks throw an exception).3203 ////await releaseCallback.Task;3204 ////Assert.True(asyncLock.IsWriteLockHeld);3205 });3206 }3207 }3208 await releaseCallback.SetAsync();3209 // Because the callbacks are fired asynchronously, we must wait for it to settle before allowing the test to finish3210 // to avoid a false failure from the Cleanup method.3211 asyncLock.Complete();3212 await asyncLock.Completion;3213 }3214 [Fact]3215 public void OnBeforeWriteLockReleasedWithoutAnyLock()3216 {3217 Assert.Throws<InvalidOperationException>(() =>3218 {3219 this.asyncLock.OnBeforeWriteLockReleased(delegate3220 {3221 return Task.FromResult<object?>(null);3222 });3223 });3224 }3225 [Fact]3226 public async Task OnBeforeWriteLockReleasedInReadlock()3227 {3228 using (await this.asyncLock.ReadLockAsync())3229 {3230 Assert.Throws<InvalidOperationException>(() =>3231 {3232 this.asyncLock.OnBeforeWriteLockReleased(delegate3233 {3234 return Task.FromResult<object?>(null);3235 });3236 });3237 }3238 }3239 [Fact]3240 public async Task OnBeforeWriteLockReleasedCallbackFiresSynchronouslyWithoutPrivateLockHeld()3241 {3242 var callbackFired = new TaskCompletionSource<object?>();3243 var writeLockRequested = new TaskCompletionSource<object?>();3244 await Task.WhenAll(3245 Task.Run(async delegate3246 {3247 using (await this.asyncLock.UpgradeableReadLockAsync())3248 {3249 using (await this.asyncLock.WriteLockAsync())3250 {3251 // Set up a callback that will deadlock if a private lock is held (so the test will fail3252 // to identify the misuse of the lock).3253 this.asyncLock.OnBeforeWriteLockReleased(async delegate3254 {3255 Assert.True(this.asyncLock.IsWriteLockHeld);3256 await Task.Yield();3257 // If a private lock were held, now that we're on a different thread this should deadlock.3258 Assert.True(this.asyncLock.IsWriteLockHeld);3259 // And if that weren't enough, we can hold this while another thread tries to get a lock.3260 // They should immediately get a "not available" flag, but if they block due to a private3261 // lock behind held while this callback executes, then we'll deadlock.3262 await callbackFired.SetAsync();3263 await writeLockRequested.Task;3264 });3265 }3266 Assert.True(callbackFired.Task.IsCompleted, "This should have completed synchronously with releasing the write lock.");3267 }3268 }),3269 Task.Run(async delegate3270 {3271 await callbackFired.Task;3272 try3273 {3274 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter();3275 Assert.False(awaiter.IsCompleted);3276 await writeLockRequested.SetAsync();3277 }3278 catch (Exception ex)3279 {3280 writeLockRequested.SetException(ex);3281 }3282 }));3283 }3284 [StaFact]3285 public void OnBeforeWriteLockReleasedCallbackNeverInvokedOnSTA()3286 {3287 TestUtilities.Run(async delegate3288 {3289 var callbackCompleted = new TaskCompletionSource<object?>();3290 AsyncReaderWriterLock.Releaser releaser = default(AsyncReaderWriterLock.Releaser);3291 var staScheduler = TaskScheduler.FromCurrentSynchronizationContext();3292 var nowait = Task.Run(async delegate3293 {3294 using (await this.asyncLock.UpgradeableReadLockAsync())3295 {3296 using (releaser = await this.asyncLock.WriteLockAsync())3297 {3298 this.asyncLock.OnBeforeWriteLockReleased(async delegate3299 {3300 try3301 {3302 Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState());3303 await Task.Yield();3304 Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState());3305 await callbackCompleted.SetAsync();3306 }3307 catch (Exception ex)3308 {3309 callbackCompleted.SetException(ex);3310 }3311 });3312 // Transition to an STA thread prior to calling Release (the point of this test).3313 await staScheduler;3314 }3315 }3316 });3317 await callbackCompleted.Task;3318 });3319 }3320 /// <summary>3321 /// Test for when the write queue is NOT empty when a write lock is released on an STA to a (non-sticky)3322 /// upgradeable read lock and a synchronous callback is to be invoked.3323 /// </summary>3324 [StaFact]3325 public async Task OnBeforeWriteLockReleasedToUpgradeableReadOnStaWithCallbacksAndWaitingWriter()3326 {3327 TestUtilities.Run(async delegate3328 {3329 var firstWriteHeld = new TaskCompletionSource<object?>();3330 var callbackCompleted = new TaskCompletionSource<object?>();3331 var secondWriteLockQueued = new TaskCompletionSource<object?>();3332 var secondWriteLockHeld = new TaskCompletionSource<object?>();3333 AsyncReaderWriterLock.Releaser releaser = default(AsyncReaderWriterLock.Releaser);3334 var staScheduler = TaskScheduler.FromCurrentSynchronizationContext();3335 await Task.WhenAll(3336 Task.Run(async delegate3337 {3338 using (await this.asyncLock.UpgradeableReadLockAsync())3339 {3340 using (releaser = await this.asyncLock.WriteLockAsync())3341 {3342 await firstWriteHeld.SetAsync();3343 this.asyncLock.OnBeforeWriteLockReleased(async delegate3344 {3345 await callbackCompleted.SetAsync();3346 });3347 await secondWriteLockQueued.Task;3348 // Transition to an STA thread prior to calling Release (the point of this test).3349 await staScheduler;3350 }3351 }3352 }),3353 Task.Run(async delegate3354 {3355 await firstWriteHeld.Task;3356 AsyncReaderWriterLock.Awaiter? writerAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter();3357 Assert.False(writerAwaiter.IsCompleted);3358 writerAwaiter.OnCompleted(delegate3359 {3360 using (writerAwaiter.GetResult())3361 {3362 try3363 {3364 Assert.True(callbackCompleted.Task.IsCompleted);3365 secondWriteLockHeld.SetAsync();3366 }3367 catch (Exception ex)3368 {3369 secondWriteLockHeld.SetException(ex);3370 }3371 }3372 });3373 await secondWriteLockQueued.SetAsync();3374 }),3375 callbackCompleted.Task,3376 secondWriteLockHeld.Task);3377 });3378 this.asyncLock.Complete();3379 await this.asyncLock.Completion;3380 }3381 [Fact]3382 public async Task OnBeforeWriteLockReleasedAndReenterConcurrency()3383 {3384 var stub = new LockDerived();3385 var beforeWriteLockReleasedTaskSource = new TaskCompletionSource<object?>();3386 var exclusiveLockReleasedTaskSource = new TaskCompletionSource<object?>();3387 stub.OnExclusiveLockReleasedAsyncDelegate = () => exclusiveLockReleasedTaskSource.Task;3388 using (AsyncReaderWriterLock.Releaser releaser = await stub.WriteLockAsync())3389 {3390 stub.OnBeforeWriteLockReleased(() => beforeWriteLockReleasedTaskSource.Task);3391 Task? releaseTask = releaser.ReleaseAsync();3392 beforeWriteLockReleasedTaskSource.SetResult(null);3393 exclusiveLockReleasedTaskSource.SetResult(null);3394 await releaseTask;3395 }3396 }3397 /// <summary>Verifies that locks requested on STA threads will marshal to an MTA.</summary>3398 [StaFact]3399 public async Task StaLockRequestsMarshalToMTA()3400 {3401 var testComplete = new TaskCompletionSource<object?>();3402 Thread staThread = new Thread((ThreadStart)delegate3403 {3404 try3405 {3406 AsyncReaderWriterLock.Awaitable awaitable = this.asyncLock.ReadLockAsync();3407 AsyncReaderWriterLock.Awaiter? awaiter = awaitable.GetAwaiter();3408 Assert.False(awaiter.IsCompleted, "The lock should not be issued on an STA thread.");3409 awaiter.OnCompleted(delegate3410 {3411 Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState());3412 awaiter.GetResult().Dispose();3413 testComplete.SetAsync();3414 });3415 testComplete.Task.Wait();3416 }3417 catch (Exception ex)3418 {3419 testComplete.TrySetException(ex);3420 }3421 });3422 staThread.SetApartmentState(ApartmentState.STA);3423 staThread.Start();3424 await testComplete.Task;3425 }3426 /// <summary>Verifies that when an MTA holding a lock traverses (via CallContext) to an MTA that the MTA DOES appear to hold a lock.</summary>3427 [Fact]3428 public async Task MtaLockSharedWithMta()3429 {3430 using (await this.asyncLock.ReadLockAsync())3431 {3432 await Task.Run(() => Assert.True(this.asyncLock.IsReadLockHeld, "MTA should be told it holds a read lock."));3433 }3434 }3435 /// <summary>Verifies that when an MTA holding a lock traverses (via CallContext) to an STA that the STA does not appear to hold a lock.</summary>3436 [SkippableFact]3437 public async Task MtaLockNotSharedWithSta()3438 {3439 Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));3440 using (await this.asyncLock.ReadLockAsync())3441 {3442 var testComplete = new TaskCompletionSource<object?>();3443 Thread staThread = new Thread((ThreadStart)delegate3444 {3445 try3446 {3447 Assert.False(this.asyncLock.IsReadLockHeld, "STA should not be told it holds a read lock.");3448 testComplete.SetAsync();3449 }3450 catch (Exception ex)3451 {3452 testComplete.TrySetException(ex);3453 }3454 });3455 staThread.SetApartmentState(ApartmentState.STA);3456 staThread.Start();3457 await testComplete.Task;3458 }3459 }3460 /// <summary>Verifies that when an MTA holding a lock traverses (via CallContext) to an STA that the STA will be able to access the same lock by marshaling back to an MTA.</summary>3461 [SkippableFact]3462 public async Task ReadLockTraversesAcrossSta()3463 {3464 Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));3465 using (await this.asyncLock.ReadLockAsync())3466 {3467 var testComplete = new TaskCompletionSource<object?>();3468 Thread staThread = new Thread((ThreadStart)delegate3469 {3470 try3471 {3472 Assert.False(this.asyncLock.IsReadLockHeld, "STA should not be told it holds a read lock.");3473 Thread mtaThread = new Thread((ThreadStart)delegate3474 {3475 try3476 {3477 Assert.True(this.asyncLock.IsReadLockHeld, "MTA thread couldn't access lock across STA.");3478 testComplete.SetAsync();3479 }3480 catch (Exception ex)3481 {3482 testComplete.TrySetException(ex);3483 }3484 });3485 mtaThread.SetApartmentState(ApartmentState.MTA);3486 mtaThread.Start();3487 }3488 catch (Exception ex)3489 {3490 testComplete.TrySetException(ex);3491 }3492 });3493 staThread.SetApartmentState(ApartmentState.STA);3494 staThread.Start();3495 await testComplete.Task;3496 }3497 }3498 /// <summary>Verifies that when an MTA holding a lock traverses (via CallContext) to an STA that the STA will be able to access the same lock by requesting it and moving back to an MTA.</summary>3499 [SkippableFact]3500 public async Task UpgradeableReadLockTraversesAcrossSta()3501 {3502 Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));3503 using (await this.asyncLock.UpgradeableReadLockAsync())3504 {3505 var testComplete = new TaskCompletionSource<object?>();3506 Thread staThread = new Thread((ThreadStart)delegate3507 {3508 try3509 {3510 Assert.False(this.asyncLock.IsUpgradeableReadLockHeld, "STA should not be told it holds an upgradeable read lock.");3511 Task.Run(async delegate3512 {3513 try3514 {3515 using (await this.asyncLock.UpgradeableReadLockAsync())3516 {3517 Assert.True(this.asyncLock.IsUpgradeableReadLockHeld, "MTA thread couldn't access lock across STA.");3518 }3519 testComplete.SetAsync().Forget();3520 }3521 catch (Exception ex)3522 {3523 testComplete.TrySetException(ex);3524 }3525 });3526 }3527 catch (Exception ex)3528 {3529 testComplete.TrySetException(ex);3530 }3531 });3532 staThread.SetApartmentState(ApartmentState.STA);3533 staThread.Start();3534 await testComplete.Task;3535 }3536 }3537 /// <summary>Verifies that when an MTA holding a lock traverses (via CallContext) to an STA that the STA will be able to access the same lock by requesting it and moving back to an MTA.</summary>3538 [SkippableFact]3539 public async Task WriteLockTraversesAcrossSta()3540 {3541 Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));3542 using (await this.asyncLock.WriteLockAsync())3543 {3544 var testComplete = new TaskCompletionSource<object?>();3545 Thread staThread = new Thread((ThreadStart)delegate3546 {3547 try3548 {3549 Assert.False(this.asyncLock.IsWriteLockHeld, "STA should not be told it holds an upgradeable read lock.");3550 Task.Run(async delegate3551 {3552 try3553 {3554 using (await this.asyncLock.WriteLockAsync())3555 {3556 Assert.True(this.asyncLock.IsWriteLockHeld, "MTA thread couldn't access lock across STA.");3557 }3558 testComplete.SetAsync().Forget();3559 }3560 catch (Exception ex)3561 {3562 testComplete.TrySetException(ex);3563 }3564 });3565 }3566 catch (Exception ex)3567 {3568 testComplete.TrySetException(ex);3569 }3570 });3571 staThread.SetApartmentState(ApartmentState.STA);3572 staThread.Start();3573 await testComplete.Task;3574 }3575 }3576 [Fact]3577 public async Task NestedLocksScenarios()3578 {3579 // R = Reader, U = non-sticky Upgradeable reader, S = Sticky upgradeable reader, W = Writer3580 var scenarios = new Dictionary<string, bool>3581 {3582 { "RU", false }, // false means this lock sequence should throw at the last step.3583 { "RS", false },3584 { "RW", false },3585 // Legal simple nested locks3586 { "RRR", true },3587 { "UUU", true },3588 { "SSS", true },3589 { "WWW", true },3590 // Legal interleaved nested locks3591 { "WRW", true },3592 { "UW", true },3593 { "UWW", true },3594 { "URW", true },3595 { "UWR", true },3596 { "UURRWW", true },3597 { "WUW", true },3598 { "WRRUW", true },3599 { "SW", true },3600 { "USW", true },3601 { "WSWRU", true },3602 { "WSRW", true },3603 { "SUSURWR", true },3604 { "USUSRWR", true },3605 };3606 foreach (KeyValuePair<string, bool> scenario in scenarios)3607 {3608 this.Logger.WriteLine("Testing {1} scenario: {0}", scenario.Key, scenario.Value ? "valid" : "invalid");3609 await this.NestedLockHelper(scenario.Key, scenario.Value);3610 }3611 }3612 [Fact]3613 public async Task AmbientLockReflectsCurrentLock()3614 {3615 var asyncLock = new LockDerived();3616 using (await asyncLock.UpgradeableReadLockAsync())3617 {3618 Assert.True(asyncLock.AmbientLockInternal.IsUpgradeableReadLock);3619 using (await asyncLock.WriteLockAsync())3620 {3621 Assert.True(asyncLock.AmbientLockInternal.IsWriteLock);3622 }3623 Assert.True(asyncLock.AmbientLockInternal.IsUpgradeableReadLock);3624 }3625 }3626 [Fact]3627 public async Task WriteLockForksAndAsksForReadLock()3628 {3629 using (TestUtilities.DisableAssertionDialog())3630 {3631 using (await this.asyncLock.WriteLockAsync())3632 {3633 await Task.Run(async delegate3634 {3635 // This throws because it's a dangerous pattern for a read lock to fork off3636 // from a write lock. For instance, if this Task.Run hadn't had an "await"3637 // in front of it (which the lock can't have insight into), then this would3638 // represent concurrent execution within what should be an exclusive lock.3639 // Also, if the read lock out-lived the nesting write lock, we'd have real3640 // trouble on our hands as it's impossible to prepare resources for concurrent3641 // access (as the exclusive lock gets released) while an existing read lock is held.3642 await Assert.ThrowsAsync<InvalidOperationException>(async delegate3643 {3644 using (await this.asyncLock.ReadLockAsync())3645 {3646 }3647 });3648 });3649 }3650 }3651 }3652 [Fact]3653 public async Task WriteNestsReadWithWriteReleasedFirst()3654 {3655 await Assert.ThrowsAsync<InvalidOperationException>(async delegate3656 {3657 using (TestUtilities.DisableAssertionDialog())3658 {3659 var readLockAcquired = new AsyncManualResetEvent();3660 var readLockReleased = new AsyncManualResetEvent();3661 var writeLockCallbackBegun = new AsyncManualResetEvent();3662 var asyncLock = new LockDerived();3663 this.asyncLock = asyncLock;3664 Task readerTask;3665 using (AsyncReaderWriterLock.Releaser access = await this.asyncLock.WriteLockAsync())3666 {3667 asyncLock.OnExclusiveLockReleasedAsyncDelegate = async delegate3668 {3669 writeLockCallbackBegun.Set();3670 // Stay in the critical region until the read lock has been released.3671 await readLockReleased;3672 };3673 // Kicking off a concurrent task while holding a write lock is not allowed3674 // unless the original execution awaits that task. Otherwise, it introduces3675 // concurrency in what is supposed to be an exclusive lock.3676 // So what we're doing here is Bad, but it's how we get the repro.3677 readerTask = Task.Run(async delegate3678 {3679 try3680 {3681 using (await this.asyncLock.ReadLockAsync())3682 {3683 readLockAcquired.Set();3684 // Hold the read lock until the lock class has entered the3685 // critical region called reenterConcurrencyPrep.3686 await writeLockCallbackBegun;3687 }3688 }3689 finally3690 {3691 // Signal the read lock is released. Actually, it may not have been3692 // (if a bug causes the read lock release call to throw and the lock gets3693 // orphaned), but we want to avoid a deadlock in the test itself.3694 // If an exception was thrown, the test will still fail because we rethrow it.3695 readLockAcquired.Set();3696 readLockReleased.Set();3697 }3698 });3699 // Don't release the write lock until the nested read lock has been acquired.3700 await readLockAcquired;3701 }3702 // Wait for, and rethrow any exceptions from our child task.3703 await readerTask;3704 }3705 });3706 }3707 [Fact]3708 public async Task WriteNestsReadWithWriteReleasedFirstWithoutTaskRun()3709 {3710 using (TestUtilities.DisableAssertionDialog())3711 {3712 var readLockReleased = new AsyncManualResetEvent();3713 var writeLockCallbackBegun = new AsyncManualResetEvent();3714 var asyncLock = new LockDerived();3715 this.asyncLock = asyncLock;3716 Task writeLockReleaseTask;3717 AsyncReaderWriterLock.Releaser writerLock = await this.asyncLock.WriteLockAsync();3718 asyncLock.OnExclusiveLockReleasedAsyncDelegate = async delegate3719 {3720 writeLockCallbackBegun.Set();3721 // Stay in the critical region until the read lock has been released.3722 await readLockReleased;3723 };3724 AsyncReaderWriterLock.Releaser readerLock = await this.asyncLock.ReadLockAsync();3725 // This is really unnatural, to release a lock without awaiting it.3726 // In fact I daresay we could call it illegal.3727 // It is critical for the repro that code either execute concurrently3728 // or that we don't await while releasing this lock.3729 writeLockReleaseTask = writerLock.ReleaseAsync();3730 // Hold the read lock until the lock class has entered the3731 // critical region called reenterConcurrencyPrep.3732 Task completingTask = await Task.WhenAny(writeLockCallbackBegun.WaitAsync(), writeLockReleaseTask);3733 try3734 {3735 await completingTask; // observe any exception.3736 Assert.True(false, "Expected exception not thrown.");3737 }3738 catch (CriticalErrorException ex)3739 {3740 Assert.True(asyncLock.CriticalErrorDetected, "The lock should have raised a critical error.");3741 Assert.IsType<InvalidOperationException>(ex.InnerException);3742 return; // the test is over3743 }3744 // The rest of this never executes, but serves to illustrate the anti-pattern that lock users3745 // may try to use, that this test verifies the lock detects and throws exceptions about.3746 await readerLock.ReleaseAsync();3747 readLockReleased.Set();3748 await writerLock.ReleaseAsync();3749 // Wait for, and rethrow any exceptions from our child task.3750 await writeLockReleaseTask;3751 }3752 }3753 [Fact]3754 public void SetLockDataWithoutLock()3755 {3756 var lck = new LockDerived();3757 Assert.Throws<InvalidOperationException>(() => lck.SetLockData(null));3758 }3759 [Fact]3760 public void GetLockDataWithoutLock()3761 {3762 var lck = new LockDerived();3763 Assert.Null(lck.GetLockData());3764 }3765 [Fact]3766 public async Task SetLockDataNoLock()3767 {3768 var lck = new LockDerived();3769 using (await lck.WriteLockAsync())3770 {3771 lck.SetLockData(null);3772 Assert.Null(lck.GetLockData());3773 var value1 = new object();3774 lck.SetLockData(value1);3775 Assert.Equal(value1, lck.GetLockData());3776 using (await lck.WriteLockAsync())3777 {3778 Assert.Null(lck.GetLockData());3779 var value2 = new object();3780 lck.SetLockData(value2);3781 Assert.Equal(value2, lck.GetLockData());3782 }3783 Assert.Equal(value1, lck.GetLockData());3784 }3785 }3786 [Fact]3787 public async Task DisposeWhileExclusiveLockContextCaptured()3788 {3789 var signal = new AsyncManualResetEvent();3790 Task helperTask;3791 using (await this.asyncLock.WriteLockAsync())3792 {3793 helperTask = this.DisposeWhileExclusiveLockContextCaptured_HelperAsync(signal);3794 }3795 signal.Set();3796 this.asyncLock.Dispose();3797 await helperTask;3798 }3799 [Fact]3800 public void GetHangReportSimple()3801 {3802 IHangReportContributor reportContributor = this.asyncLock;3803 HangReportContribution? report = reportContributor.GetHangReport();3804 Assert.NotNull(report);3805 Assert.NotNull(report.Content);3806 Assert.NotNull(report.ContentType);3807 Assert.NotNull(report.ContentName);3808 this.Logger.WriteLine(report.Content);3809 }3810 [Fact]3811 public async Task GetHangReportWithReleasedNestingOuterLock()3812 {3813 using (AsyncReaderWriterLock.Releaser lock1 = await this.asyncLock.ReadLockAsync())3814 {3815 using (AsyncReaderWriterLock.Releaser lock2 = await this.asyncLock.ReadLockAsync())3816 {3817 using (AsyncReaderWriterLock.Releaser lock3 = await this.asyncLock.ReadLockAsync())3818 {3819 await lock1.ReleaseAsync();3820 this.PrintHangReport();3821 }3822 }3823 }3824 }3825 [Fact]3826 public async Task GetHangReportWithReleasedNestingMiddleLock()3827 {3828 using (AsyncReaderWriterLock.Releaser lock1 = await this.asyncLock.ReadLockAsync())3829 {3830 using (AsyncReaderWriterLock.Releaser lock2 = await this.asyncLock.ReadLockAsync())3831 {3832 using (AsyncReaderWriterLock.Releaser lock3 = await this.asyncLock.ReadLockAsync())3833 {3834 await lock2.ReleaseAsync();3835 this.PrintHangReport();3836 }3837 }3838 }3839 }3840 [Fact]3841 public async Task GetHangReportWithWriteLockUpgradeWaiting()3842 {3843 var readLockObtained = new AsyncManualResetEvent();3844 var hangReportComplete = new AsyncManualResetEvent();3845 var writerTask = Task.Run(async delegate3846 {3847 using (await this.asyncLock.UpgradeableReadLockAsync())3848 {3849 await readLockObtained;3850 AsyncReaderWriterLock.Awaiter? writeWaiter = this.asyncLock.WriteLockAsync().GetAwaiter();3851 writeWaiter.OnCompleted(delegate3852 {3853 writeWaiter.GetResult().Dispose();3854 });3855 this.PrintHangReport();3856 hangReportComplete.Set();3857 }3858 });3859 using (AsyncReaderWriterLock.Releaser lock1 = await this.asyncLock.ReadLockAsync())3860 {3861 using (AsyncReaderWriterLock.Releaser lock2 = await this.asyncLock.ReadLockAsync())3862 {3863 readLockObtained.Set();3864 await hangReportComplete;3865 }3866 }3867 await writerTask;3868 }3869 [Fact]3870 public async Task ReadLockAsync_Await_CapturesExecutionContext()3871 {3872 var asyncLocal = new Microsoft.VisualStudio.Threading.AsyncLocal<string>();3873 asyncLocal.Value = "expected";3874 using (AsyncReaderWriterLock.Releaser lck = await this.asyncLock.ReadLockAsync())3875 {3876 Assert.Equal("expected", asyncLocal.Value);3877 }3878 }3879 [Fact]3880 public async Task ReadLockAsync_OnCompleted_CapturesExecutionContext()3881 {3882 var asyncLocal = new Microsoft.VisualStudio.Threading.AsyncLocal<string>();3883 asyncLocal.Value = "expected";3884 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter();3885 Assumes.False(awaiter.IsCompleted);3886 var testResultSource = new TaskCompletionSource<object?>();3887 awaiter.OnCompleted(delegate3888 {3889 try3890 {3891 using (awaiter.GetResult())3892 {3893 Assert.Equal("expected", asyncLocal.Value);3894 testResultSource.SetResult(null);3895 }3896 }3897 catch (Exception ex)3898 {3899 testResultSource.SetException(ex);3900 }3901 finally3902 {3903 }3904 });3905 await testResultSource.Task;3906 }3907 [Fact]3908 public async Task ReadLockAsync_UnsafeOnCompleted_DoesNotCaptureExecutionContext()3909 {3910 var asyncLocal = new Microsoft.VisualStudio.Threading.AsyncLocal<string>();3911 asyncLocal.Value = "expected";3912 AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter();3913 Assumes.False(awaiter.IsCompleted);3914 var testResultSource = new TaskCompletionSource<object?>();3915 awaiter.UnsafeOnCompleted(delegate3916 {3917 try3918 {3919 using (awaiter.GetResult())3920 {3921 Assert.Null(asyncLocal.Value);3922 testResultSource.SetResult(null);3923 }3924 }3925 catch (Exception ex)3926 {3927 testResultSource.SetException(ex);3928 }3929 finally3930 {3931 }3932 });3933 await testResultSource.Task;3934 }3935 [Fact]3936 public async Task ReadLockAsync_UseTaskScheduler()3937 {3938 var asyncLock = new AsyncReaderWriterLockWithSpecialScheduler();3939 Assumes.Equals(0, asyncLock.StartedTaskCount);3940 // A reader lock issued immediately will not be rescheduled.3941 using (await asyncLock.ReadLockAsync())3942 {3943 }3944 Assumes.Equals(0, asyncLock.StartedTaskCount);3945 var writeLockObtained = new AsyncManualResetEvent();3946 var readLockObtained = new AsyncManualResetEvent();3947 var writeLockToRelease = new AsyncManualResetEvent();3948 var writeLockTask = Task.Run(async () =>3949 {3950 using (await asyncLock.WriteLockAsync())3951 {3952 // Write lock is not scheduled through the read lock scheduler.3953 Assumes.Equals(0, asyncLock.StartedTaskCount);3954 writeLockObtained.Set();3955 await writeLockToRelease.WaitAsync();3956 }3957 });3958 await writeLockObtained.WaitAsync();3959 var readLockTask = Task.Run(async () =>3960 {3961 using (await asyncLock.ReadLockAsync())3962 {3963 // Newly issued read lock is using the task scheduler.3964 Assumes.Equals(1, asyncLock.StartedTaskCount);3965 readLockObtained.Set();3966 }3967 });3968 await asyncLock.ScheduleSemaphore.WaitAsync();3969 writeLockToRelease.Set();3970 await writeLockTask;3971 await readLockTask;3972 Assumes.Equals(1, asyncLock.StartedTaskCount);3973 }3974 [Fact]3975 public void Disposable()3976 {3977 IDisposable disposable = this.asyncLock;3978 disposable.Dispose();3979 }3980 private async Task DisposeWhileExclusiveLockContextCaptured_HelperAsync(AsyncManualResetEvent signal)3981 {3982 await signal;3983 await Task.Yield();3984 }3985 private void PrintHangReport()3986 {3987 IHangReportContributor reportContributor = this.asyncLock;3988 HangReportContribution? report = reportContributor.GetHangReport();3989 this.Logger.WriteLine(report.Content);3990 }3991 private void LockReleaseTestHelper(AsyncReaderWriterLock.Awaitable initialLock)3992 {3993 TestUtilities.Run(async delegate3994 {...
SubscriptionsFailover.cs
Source:SubscriptionsFailover.cs
...59 Database = defaultDatabase60 }.Initialize())61 {62 var usersCountInAck = new List<User>();63 var reachedMaxDocCountInAckMre = new AsyncManualResetEvent();64 var usersCountInBatch = new List<User>();65 var reachedMaxDocCountInBatchMre = new AsyncManualResetEvent();66 await GenerateDocuments(store);67 (var subscription, var subsTask) = await CreateAndInitiateSubscription(store, defaultDatabase, usersCountInAck, reachedMaxDocCountInAckMre, usersCountInBatch, reachedMaxDocCountInBatchMre, batchSize);68 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInBatchMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"1. Reached in batch {usersCountInBatch.Count}/10");69 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInAckMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"1. Reached in ack {usersCountInAck.Count}/10");70 Assert.False(subsTask.IsFaulted, subsTask?.Exception?.ToString());71 usersCountInBatch.Clear();72 reachedMaxDocCountInAckMre.Reset();73 usersCountInAck.Clear();74 reachedMaxDocCountInBatchMre.Reset();75 var sp = Stopwatch.StartNew();76 WaitForUserToContinueTheTest(store);77 var fallenNode = await KillServerWhereSubscriptionWorks(defaultDatabase, subscription.SubscriptionName);78 await GenerateDocuments(store);79 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInBatchMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"2. Reached in batch {usersCountInBatch.Count}/10");80 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInAckMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"2. Reached in ack {usersCountInAck.Count}/10");81 Assert.False(subsTask.IsFaulted, subsTask?.Exception?.ToString());82 usersCountInBatch.Clear();83 reachedMaxDocCountInAckMre.Reset();84 usersCountInAck.Clear();85 reachedMaxDocCountInBatchMre.Reset();86 fallenNode = await KillServerWhereSubscriptionWorks(defaultDatabase, subscription.SubscriptionName);87 await GenerateDocuments(store);88 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInBatchMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"3. Reached in batch {usersCountInBatch.Count}/10");89 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInAckMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"3. Reached in ack {usersCountInAck.Count}/10");90 Assert.False(subsTask.IsFaulted, subsTask?.Exception?.ToString());91 }92 }93 [Theory]94 [InlineData(1, 20)]95 [InlineData(5, 10)]96 [InlineData(10, 5)]97 [InlineData(20, 2)]98 public async Task ContinueFromThePointIStoppedConcurrentSubscription(int batchSize, int numberOfConnections)99 {100 DebuggerAttachedTimeout.DisableLongTimespan = true;101 const int nodesAmount = 5;102 var (_, leader) = await CreateRaftCluster(nodesAmount);103 var defaultDatabase = GetDatabaseName();104 await CreateDatabaseInCluster(defaultDatabase, nodesAmount, leader.WebUrl).ConfigureAwait(false);105 using (var store = new DocumentStore106 {107 Urls = new[] { leader.WebUrl },108 Database = defaultDatabase109 }.Initialize())110 {111 var usersCountInAck = new List<User>();112 var reachedMaxDocCountInAckMre = new AsyncManualResetEvent();113 var usersCountInBatch = new List<User>();114 var reachedMaxDocCountInBatchMre = new AsyncManualResetEvent();115 await GenerateDocuments(store);116 (var subscription, var subsTask) = await CreateAndInitiateSubscription(store, defaultDatabase, usersCountInAck, reachedMaxDocCountInAckMre, usersCountInBatch, reachedMaxDocCountInBatchMre, batchSize, numberOfConnections);117 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInBatchMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"1. Reached in batch {usersCountInBatch.Count}/10");118 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInAckMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"1. Reached in ack {usersCountInAck.Count}/10");119 Assert.False(subsTask.IsFaulted, subsTask?.Exception?.ToString());120 usersCountInBatch.Clear();121 reachedMaxDocCountInAckMre.Reset();122 usersCountInAck.Clear();123 reachedMaxDocCountInBatchMre.Reset();124 var sp = Stopwatch.StartNew();125 var fallenNode = await KillServerWhereSubscriptionWorks(defaultDatabase, subscription.SubscriptionName);126 await GenerateDocuments(store);127 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInBatchMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"2. Reached in batch {usersCountInBatch.Count}/10");128 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInAckMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"2. Reached in ack {usersCountInAck.Count}/10");129 Assert.False(subsTask.IsFaulted, subsTask?.Exception?.ToString());130 usersCountInBatch.Clear();131 reachedMaxDocCountInAckMre.Reset();132 usersCountInAck.Clear();133 reachedMaxDocCountInBatchMre.Reset();134 fallenNode = await KillServerWhereSubscriptionWorks(defaultDatabase, subscription.SubscriptionName);135 136 await GenerateDocuments(store);137 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInBatchMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"3. Reached in batch {usersCountInBatch.Count}/10");138 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInAckMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"3. Reached in ack {usersCountInAck.Count}/10");139 Assert.False(subsTask.IsFaulted, subsTask?.Exception?.ToString());140 }141 }142 [Fact]143 public async Task SubscripitonDeletionFromCluster()144 {145 const int nodesAmount = 5;146 var (_, leader) = await CreateRaftCluster(nodesAmount);147 var defaultDatabase = GetDatabaseName();148 await CreateDatabaseInCluster(defaultDatabase, nodesAmount, leader.WebUrl).ConfigureAwait(false);149 using (var store = new DocumentStore150 {151 Urls = new[] { leader.WebUrl },152 Database = defaultDatabase153 }.Initialize())154 {155 var usersCount = new List<User>();156 var reachedMaxDocCountMre = new AsyncManualResetEvent();157 var subscriptionId = await store.Subscriptions.CreateAsync<User>();158 using (var session = store.OpenAsyncSession())159 {160 await session.StoreAsync(new User161 {162 Name = "Peter"163 });164 await session.SaveChangesAsync();165 }166 var subscription = store.Subscriptions.GetSubscriptionWorker<User>(new SubscriptionWorkerOptions(subscriptionId)167 {168 TimeToWaitBeforeConnectionRetry = TimeSpan.FromSeconds(5)169 });170 subscription.AfterAcknowledgment += b => { reachedMaxDocCountMre.Set(); return Task.CompletedTask; };171 GC.KeepAlive(subscription.Run(x => { }));172 Assert.True(await reachedMaxDocCountMre.WaitAsync(TimeSpan.FromSeconds(60)));173 foreach (var ravenServer in Servers)174 {175 using (ravenServer.ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context))176 using (context.OpenReadTransaction())177 {178 Assert.NotNull(ravenServer.ServerStore.Cluster.Read(context, SubscriptionState.GenerateSubscriptionItemKeyName(defaultDatabase, subscriptionId.ToString())));179 }180 }181 await subscription.DisposeAsync();182 var deleteResult = store.Maintenance.Server.Send(new DeleteDatabasesOperation(defaultDatabase, hardDelete: true));183 foreach (var ravenServer in Servers)184 {185 await ravenServer.ServerStore.WaitForCommitIndexChange(RachisConsensus.CommitIndexModification.GreaterOrEqual, deleteResult.RaftCommandIndex + nodesAmount).WaitWithTimeout(TimeSpan.FromSeconds(60));186 }187 await Task.Delay(2000);188 foreach (var ravenServer in Servers)189 {190 using (ravenServer.ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context))191 using (context.OpenReadTransaction())192 {193 Assert.Null(ravenServer.ServerStore.Cluster.Read(context, SubscriptionState.GenerateSubscriptionItemKeyName(defaultDatabase, subscriptionId.ToString())));194 }195 }196 }197 }198 [Fact]199 public async Task SetMentorToSubscriptionWithFailover()200 {201 const int nodesAmount = 5;202 var (_, leader) = await CreateRaftCluster(nodesAmount);203 var defaultDatabase = GetDatabaseName();204 await CreateDatabaseInCluster(defaultDatabase, nodesAmount, leader.WebUrl).ConfigureAwait(false);205 string mentor = "C";206 using (var store = new DocumentStore207 {208 Urls = new[] { leader.WebUrl },209 Database = defaultDatabase210 }.Initialize())211 {212 var usersCountInAck = new List<User>();213 var reachedMaxDocCountInAckMre = new AsyncManualResetEvent();214 var usersCountInBatch = new List<User>();215 var reachedMaxDocCountInBatchMre = new AsyncManualResetEvent();216 await GenerateDocuments(store);217 (var subscription, var subsTask) = await CreateAndInitiateSubscription(store, defaultDatabase, usersCountInAck, reachedMaxDocCountInAckMre, usersCountInBatch, reachedMaxDocCountInBatchMre, 20, mentor: mentor);218 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInBatchMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"1. Reached in batch {usersCountInBatch.Count}/10");219 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInAckMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"1. Reached in ack {usersCountInAck.Count}/10");220 Assert.False(subsTask.IsFaulted, subsTask?.Exception?.ToString());221 usersCountInBatch.Clear();222 reachedMaxDocCountInAckMre.Reset();223 usersCountInAck.Clear();224 reachedMaxDocCountInBatchMre.Reset();225 await KillServerWhereSubscriptionWorks(defaultDatabase, subscription.SubscriptionName);226 await GenerateDocuments(store);227 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInBatchMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"2. Reached in batch {usersCountInBatch.Count}/10");228 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInAckMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"2. Reached in ack {usersCountInAck.Count}/10");229 Assert.False(subsTask.IsFaulted, subsTask?.Exception?.ToString());230 usersCountInBatch.Clear();231 reachedMaxDocCountInAckMre.Reset();232 usersCountInAck.Clear();233 reachedMaxDocCountInBatchMre.Reset();234 await KillServerWhereSubscriptionWorks(defaultDatabase, subscription.SubscriptionName);235 await GenerateDocuments(store);236 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInBatchMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"3. Reached in batch {usersCountInBatch.Count}/10");237 Assert.True(await Task.WhenAny(subsTask, reachedMaxDocCountInAckMre.WaitAsync(_reasonableWaitTime)).WaitWithoutExceptionAsync(_reasonableWaitTime), $"3. Reached in ack {usersCountInAck.Count}/10");238 Assert.False(subsTask.IsFaulted, subsTask?.Exception?.ToString());239 }240 }241 [MultiplatformTheory(RavenArchitecture.AllX64)]242 [InlineData(3)]243 [InlineData(5)]244 public async Task DistributedRevisionsSubscription(int nodesAmount)245 {246 var uniqueRevisions = new HashSet<string>();247 var uniqueDocs = new HashSet<string>();248 var (_, leader) = await CreateRaftCluster(nodesAmount).ConfigureAwait(false);249 var defaultDatabase = GetDatabaseName();250 await CreateDatabaseInCluster(defaultDatabase, nodesAmount, leader.WebUrl).ConfigureAwait(false);251 using (var store = new DocumentStore252 {253 Urls = new[] { leader.WebUrl },254 Database = defaultDatabase255 }.Initialize())256 {257 await SetupRevisions(leader, defaultDatabase).ConfigureAwait(false);258 var reachedMaxDocCountMre = new AsyncManualResetEvent();259 var ackSent = new AsyncManualResetEvent();260 var continueMre = new AsyncManualResetEvent();261 await GenerateDistributedRevisionsDataAsync(defaultDatabase);262 var subscriptionId = await store.Subscriptions.CreateAsync<Revision<User>>().ConfigureAwait(false);263 var docsCount = 0;264 var revisionsCount = 0;265 var expectedRevisionsCount = 0;266 SubscriptionWorker<Revision<User>> subscription = null;267 int i;268 for (i = 0; i < 10; i++)269 {270 subscription = store.Subscriptions.GetSubscriptionWorker<Revision<User>>(new SubscriptionWorkerOptions(subscriptionId)271 {272 MaxErroneousPeriod = nodesAmount == 5 ? TimeSpan.FromSeconds(15) : TimeSpan.FromSeconds(5),273 MaxDocsPerBatch = 1,274 TimeToWaitBeforeConnectionRetry = TimeSpan.FromMilliseconds(100)275 });276 subscription.AfterAcknowledgment += async b =>277 {278 Assert.True(await continueMre.WaitAsync(TimeSpan.FromSeconds(60)));279 try280 {281 if (revisionsCount == expectedRevisionsCount)282 {283 continueMre.Reset();284 ackSent.Set();285 }286 Assert.True(await continueMre.WaitAsync(TimeSpan.FromSeconds(60)));287 }288 catch (Exception)289 {290 }291 };292 var started = new AsyncManualResetEvent();293 var task = subscription.Run(b =>294 {295 started.Set();296 HandleSubscriptionBatch(nodesAmount, b, uniqueDocs, ref docsCount, uniqueRevisions, reachedMaxDocCountMre, ref revisionsCount);297 });298 var cont = task.ContinueWith(t =>299 {300 reachedMaxDocCountMre.SetException(t.Exception);301 ackSent.SetException(t.Exception);302 }, TaskContinuationOptions.OnlyOnFaulted);303 await Task.WhenAny(task, started.WaitAsync(TimeSpan.FromSeconds(60)));304 if (started.IsSet)305 break;306 Assert.IsType<SubscriptionDoesNotExistException>(task.Exception.InnerException);307 subscription.Dispose();308 }309 Assert.NotEqual(i, 10);310 expectedRevisionsCount = nodesAmount + 2;311 continueMre.Set();312 Assert.True(await ackSent.WaitAsync(_reasonableWaitTime).ConfigureAwait(false), $"Doc count is {docsCount} with revisions {revisionsCount}/{expectedRevisionsCount} (1st assert)");313 ackSent.Reset(true);314 var disposedTag = await KillServerWhereSubscriptionWorks(defaultDatabase, subscription.SubscriptionName).ConfigureAwait(false);315 await WaitForResponsibleNodeToChange(defaultDatabase, subscription.SubscriptionName, disposedTag);316 continueMre.Set();317 expectedRevisionsCount += 2;318 Assert.True(await ackSent.WaitAsync(_reasonableWaitTime).ConfigureAwait(false), $"Doc count is {docsCount} with revisions {revisionsCount}/{expectedRevisionsCount} (2nd assert)");319 ackSent.Reset(true);320 continueMre.Set();321 expectedRevisionsCount = (int)Math.Pow(nodesAmount, 2);322 if (nodesAmount == 5)323 {324 var secondDisposedTag = await KillServerWhereSubscriptionWorks(defaultDatabase, subscription.SubscriptionName).ConfigureAwait(false);325 await WaitForResponsibleNodeToChange(defaultDatabase, subscription.SubscriptionName, secondDisposedTag);326 }327 Assert.True(await reachedMaxDocCountMre.WaitAsync(_reasonableWaitTime).ConfigureAwait(false), $"Doc count is {docsCount} with revisions {revisionsCount}/{expectedRevisionsCount} (3rd assert)");328 }329 }330 [MultiplatformFact(RavenArchitecture.AllX86)]331 public async Task DistributedRevisionsSubscription32Bit()332 {333 await DistributedRevisionsSubscription(3);334 }335 private static void HandleSubscriptionBatch(int nodesAmount, SubscriptionBatch<Revision<User>> b, HashSet<string> uniqueDocs, ref int docsCount, HashSet<string> uniqueRevisions,336 AsyncManualResetEvent reachedMaxDocCountMre, ref int revisionsCount)337 {338 foreach (var item in b.Items)339 {340 var x = item.Result;341 try342 {343 if (x == null)344 {345 }346 else if (x.Previous == null)347 {348 if (uniqueDocs.Add(x.Current.Id))349 docsCount++;350 if (uniqueRevisions.Add(x.Current.Name))351 revisionsCount++;352 }353 else if (x.Current == null)354 {355 }356 else357 {358 if (x.Current.Age > x.Previous.Age)359 {360 if (uniqueRevisions.Add(x.Current.Name))361 revisionsCount++;362 }363 }364 if (docsCount == nodesAmount && revisionsCount == Math.Pow(nodesAmount, 2))365 reachedMaxDocCountMre.Set();366 }367 catch (Exception)368 {369 }370 }371 }372 private async Task SetupRevisions(RavenServer server, string defaultDatabase)373 {374 using (var context = JsonOperationContext.ShortTermSingleUse())375 {376 var configuration = new RevisionsConfiguration377 {378 Default = new RevisionsCollectionConfiguration379 {380 Disabled = false,381 MinimumRevisionsToKeep = 10,382 },383 Collections = new Dictionary<string, RevisionsCollectionConfiguration>384 {385 ["Users"] = new RevisionsCollectionConfiguration386 {387 Disabled = false,388 MinimumRevisionsToKeep = 10389 }390 }391 };392 var documentDatabase = await server.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(defaultDatabase);393 var res = await documentDatabase.ServerStore.ModifyDatabaseRevisions(context,394 defaultDatabase,395 DocumentConventions.Default.Serialization.DefaultConverter.ToBlittable(configuration, context), Guid.NewGuid().ToString());396 foreach (var s in Servers)// need to wait for it on all servers397 {398 documentDatabase = await s.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(defaultDatabase);399 await documentDatabase.RachisLogIndexNotifications.WaitForIndexNotification(res.Item1, s.ServerStore.Engine.OperationTimeout);400 }401 }402 }403 private async Task GenerateDistributedRevisionsDataAsync(string defaultDatabase)404 {405 IReadOnlyList<ServerNode> nodes;406 using (var store = new DocumentStore407 {408 Urls = new[] { Servers[0].WebUrl },409 Database = defaultDatabase410 }.Initialize())411 {412 await store.GetRequestExecutor().UpdateTopologyAsync(new RequestExecutor.UpdateTopologyParameters(new ServerNode413 {414 Url = store.Urls[0],415 Database = defaultDatabase,416 })417 {418 TimeoutInMs = Timeout.Infinite419 });420 nodes = store.GetRequestExecutor().TopologyNodes;421 }422 var rnd = new Random(1);423 for (var index = 0; index < Servers.Count; index++)424 {425 var curVer = 0;426 foreach (var server in Servers.OrderBy(x => rnd.Next()))427 {428 using (var curStore = new DocumentStore429 {430 Urls = new[] { server.WebUrl },431 Database = defaultDatabase,432 Conventions = new DocumentConventions433 {434 DisableTopologyUpdates = true435 }436 }.Initialize())437 {438 var curDocName = $"user {index} revision {curVer}";439 using (var session = (DocumentSession)curStore.OpenSession())440 {441 if (curVer == 0)442 {443 session.Store(new User444 {445 Name = curDocName,446 Age = curVer,447 Id = $"users/{index}"448 }, $"users/{index}");449 }450 else451 {452 var user = session.Load<User>($"users/{index}");453 user.Age = curVer;454 user.Name = curDocName;455 session.Store(user, $"users/{index}");456 }457 session.SaveChanges();458 Assert.True(459 AsyncHelpers.RunSync(() => WaitForDocumentInClusterAsync<User>(nodes, "users/" + index, x => x.Name == curDocName, _reasonableWaitTime))460 );461 }462 }463 curVer++;464 }465 }466 }467 private async Task<(SubscriptionWorker<User> worker, Task subsTask)> CreateAndInitiateSubscription(IDocumentStore store, string defaultDatabase, List<User> usersCountInAck, 468 AsyncManualResetEvent reachedMaxDocCountInAckMre, List<User> usersCountInBatch, AsyncManualResetEvent reachedMaxDocCountInBatchMre, int batchSize, int numberOfConnections = 1, string mentor = null)469 {470 var proggress = new SubscriptionProggress()471 {472 MaxId = 0473 };474 var subscriptionName = await store.Subscriptions.CreateAsync<User>(options: new SubscriptionCreationOptions475 {476 MentorNode = mentor477 }).ConfigureAwait(false);478 SubscriptionWorker<User> subscription;479 List<Task> subTasks = new();480 int connections = numberOfConnections;481 do482 {483 subscription = store.Subscriptions.GetSubscriptionWorker<User>(new SubscriptionWorkerOptions(subscriptionName)484 {485 TimeToWaitBeforeConnectionRetry = TimeSpan.FromMilliseconds(500), 486 MaxDocsPerBatch = batchSize,487 Strategy = numberOfConnections > 1 ? SubscriptionOpeningStrategy.Concurrent : SubscriptionOpeningStrategy.OpenIfFree488 });489 subscription.AfterAcknowledgment += b =>490 {491 try492 {493 foreach (var item in b.Items)494 {495 var x = item.Result;496 int curId = 0;497 var afterSlash = x.Id.Substring(x.Id.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1);498 curId = int.Parse(afterSlash.Substring(0, afterSlash.Length - 2));499 Assert.True(curId >= proggress.MaxId);500 usersCountInAck.Add(x);501 proggress.MaxId = curId;502 }503 504 if (usersCountInAck.Count == 10)505 {506 reachedMaxDocCountInAckMre.Set();507 }508 }509 catch (Exception)510 {511 }512 return Task.CompletedTask;513 };514 515 subTasks.Add(subscription.Run(batch =>516 {517 foreach (var item in batch.Items)518 {519 usersCountInBatch.Add(item.Result);520 if (usersCountInBatch.Count == 10)521 {522 reachedMaxDocCountInBatchMre.Set();523 }524 }525 }));526 connections--;527 } while (connections > 0);528 var subscripitonState = await store.Subscriptions.GetSubscriptionStateAsync(subscriptionName, store.Database);529 var getDatabaseTopologyCommand = new GetDatabaseRecordOperation(defaultDatabase);530 var record = await store.Maintenance.Server.SendAsync(getDatabaseTopologyCommand).ConfigureAwait(false);531 foreach (var server in Servers.Where(s => record.Topology.RelevantFor(s.ServerStore.NodeTag)))532 {533 await server.ServerStore.Cluster.WaitForIndexNotification(subscripitonState.SubscriptionId).ConfigureAwait(false);534 }535 if (mentor != null)536 {537 Assert.Equal(mentor, record.Topology.WhoseTaskIsIt(RachisState.Follower, subscripitonState, null));538 }539 540 //await Task.WhenAny(task, Task.Delay(_reasonableWaitTime)).ConfigureAwait(false);541 return (subscription, Task.WhenAll(subTasks));542 }543 private async Task<string> KillServerWhereSubscriptionWorks(string defaultDatabase, string subscriptionName)544 {545 var sp = Stopwatch.StartNew();546 try547 {548 while (sp.ElapsedMilliseconds < _reasonableWaitTime.TotalMilliseconds)549 {550 string tag = null;551 var someServer = Servers.First(x => x.Disposed == false);552 using (someServer.ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context))553 using (context.OpenReadTransaction())554 {555 var databaseRecord = someServer.ServerStore.Cluster.ReadDatabase(context, defaultDatabase);556 var db = await someServer.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(defaultDatabase).ConfigureAwait(false);557 var subscriptionState = db.SubscriptionStorage.GetSubscriptionFromServerStore(subscriptionName);558 tag = databaseRecord.Topology.WhoseTaskIsIt(someServer.ServerStore.Engine.CurrentState, subscriptionState, null);559 }560 if (tag == null)561 {562 await Task.Delay(100);563 continue;564 }565 var server = Servers.First(x => x.ServerStore.NodeTag == tag);566 if (server.Disposed)567 {568 await Task.Delay(100);569 continue;570 }571 await DisposeServerAndWaitForFinishOfDisposalAsync(server);572 return tag;573 }574 return null;575 }576 finally577 {578 Assert.True(sp.ElapsedMilliseconds < _reasonableWaitTime.TotalMilliseconds);579 }580 }581 private async Task WaitForResponsibleNodeToChange(string database, string subscriptionName, string responsibleNode)582 {583 var sp = Stopwatch.StartNew();584 try585 {586 while (sp.ElapsedMilliseconds < _reasonableWaitTime.TotalMilliseconds)587 {588 string tag;589 var someServer = Servers.First(x => x.Disposed == false);590 using (someServer.ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context))591 using (context.OpenReadTransaction())592 {593 var databaseRecord = someServer.ServerStore.Cluster.ReadDatabase(context, database);594 var db = await someServer.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(database).ConfigureAwait(false);595 var subscriptionState = db.SubscriptionStorage.GetSubscriptionFromServerStore(subscriptionName);596 tag = databaseRecord.Topology.WhoseTaskIsIt(someServer.ServerStore.Engine.CurrentState, subscriptionState, null);597 }598 if (tag == null || tag == responsibleNode)599 {600 await Task.Delay(333);601 continue;602 }603 return;604 }605 }606 finally607 {608 Assert.True(sp.ElapsedMilliseconds < _reasonableWaitTime.TotalMilliseconds);609 }610 }611 private static async Task GenerateDocuments(IDocumentStore store)612 {613 using (var session = store.OpenAsyncSession())614 {615 for (int i = 0; i < 10; i++)616 {617 await session.StoreAsync(new User()618 {619 Name = "John" + i620 })621 .ConfigureAwait(false);622 }623 await session.SaveChangesAsync().ConfigureAwait(false);624 }625 }626 [Fact]627 public async Task SubscriptionWorkerShouldNotFailoverToErroredNodes()628 {629 var cluster = await CreateRaftCluster(numberOfNodes: 3);630 using (var store = GetDocumentStore(new Options631 {632 ReplicationFactor = 3,633 Server = cluster.Leader,634 DeleteDatabaseOnDispose = false635 }))636 {637 Servers.ForEach(x => x.ForTestingPurposesOnly().GatherVerboseDatabaseDisposeInformation = true);638 var mre = new AsyncManualResetEvent();639 using (var subscriptionManager = new DocumentSubscriptions(store))640 {641 var reqEx = store.GetRequestExecutor();642 var name = subscriptionManager.Create(new SubscriptionCreationOptions<User>());643 var subs = await SubscriptionFailoverWithWaitingChains.GetSubscription(name, store.Database, cluster.Nodes);644 Assert.NotNull(subs);645 await WaitForRaftIndexToBeAppliedOnClusterNodes(subs.SubscriptionId, cluster.Nodes);646 await ActionWithLeader(async l => await WaitForResponsibleNode(l.ServerStore, store.Database, name, toBecomeNull: false));647 Assert.True(WaitForValue(() => reqEx.Topology != null, true));648 var topology = reqEx.Topology;649 var serverNode1 = topology.Nodes[0];650 await reqEx.UpdateTopologyAsync(new RequestExecutor.UpdateTopologyParameters(serverNode1) { TimeoutInMs = 10_000 });651 var node1 = Servers.First(x => x.WebUrl.Equals(serverNode1.Url, StringComparison.InvariantCultureIgnoreCase));652 var (_, __, disposedTag) = await DisposeServerAndWaitForFinishOfDisposalAsync(node1);...
SubscriptionsFailoverStress.cs
Source:SubscriptionsFailoverStress.cs
...238 MaxDocsPerBatch = 20,239 MaxErroneousPeriod = TimeSpan.FromSeconds(6)240 }))241 {242 var batchProccessed = new AsyncManualResetEvent();243 var task = subscription.Run(async a =>244 {245 batchProccessed.Set();246 await DisposeServerAndWaitForFinishOfDisposalAsync(leader);247 });248 await GenerateDocuments(store);249 Assert.True(await batchProccessed.WaitAsync(_reasonableWaitTime));250 Assert.True(await ThrowsAsync<SubscriptionInvalidStateException>(task).WaitWithTimeout(TimeSpan.FromSeconds(120)).ConfigureAwait(false));251 }252 }253 }254 [Fact]255 public async Task SubscriptionShouldNotFailIfLeaderIsDownButItStillHasEnoughTimeToRetry()256 {257 const int nodesAmount = 2;258 var (_, leader) = await CreateRaftCluster(nodesAmount, shouldRunInMemory: false);259 var indexLeader = Servers.FindIndex(x => x == leader);260 var defaultDatabase = "SubscriptionShouldNotFailIfLeaderIsDownButItStillHasEnoughTimeToRetry";261 await CreateDatabaseInCluster(defaultDatabase, nodesAmount, leader.WebUrl).ConfigureAwait(false);262 string mentor = Servers.First(x => x.ServerStore.NodeTag != x.ServerStore.LeaderTag).ServerStore.NodeTag;263 using (var store = new DocumentStore264 {265 Urls = new[] { leader.WebUrl },266 Database = defaultDatabase267 }.Initialize())268 {269 var subscriptionName = await store.Subscriptions.CreateAsync<User>(options: new SubscriptionCreationOptions270 {271 MentorNode = mentor272 }).ConfigureAwait(false);273 var subscripitonState = await store.Subscriptions.GetSubscriptionStateAsync(subscriptionName, store.Database);274 var getDatabaseTopologyCommand = new GetDatabaseRecordOperation(defaultDatabase);275 var record = await store.Maintenance.Server.SendAsync(getDatabaseTopologyCommand).ConfigureAwait(false);276 foreach (var server in Servers.Where(s => record.Topology.RelevantFor(s.ServerStore.NodeTag)))277 {278 await server.ServerStore.Cluster.WaitForIndexNotification(subscripitonState.SubscriptionId).ConfigureAwait(false);279 }280 if (mentor != null)281 {282 Assert.Equal(mentor, record.Topology.WhoseTaskIsIt(RachisState.Follower, subscripitonState, null));283 }284 using (var subscription = store.Subscriptions.GetSubscriptionWorker<User>(new SubscriptionWorkerOptions(subscriptionName)285 {286 TimeToWaitBeforeConnectionRetry = TimeSpan.FromMilliseconds(500),287 MaxDocsPerBatch = 20,288 MaxErroneousPeriod = TimeSpan.FromSeconds(120)289 }))290 {291 var batchProccessed = new AsyncManualResetEvent();292 var subscriptionRetryBegins = new AsyncManualResetEvent();293 var batchedAcked = new AsyncManualResetEvent();294 var disposedOnce = false;295 subscription.AfterAcknowledgment += x =>296 {297 batchedAcked.Set();298 return Task.CompletedTask;299 };300 (string DataDirectory, string Url, string NodeTag) result = default;301 var task = subscription.Run(batch =>302 {303 if (disposedOnce == false)304 {305 disposedOnce = true;306 subscription.OnSubscriptionConnectionRetry += x => subscriptionRetryBegins.SetAndResetAtomically();307 result = DisposeServerAndWaitForFinishOfDisposal(leader);...
TestUtilities.cs
Source:TestUtilities.cs
...111 /// <param name="baseAwaiter">The awaiter to extend.</param>112 /// <param name="yieldingSignal">The signal to set after the continuation has been pended.</param>113 /// <param name="resumingSignal">The signal to set when the continuation has been invoked.</param>114 /// <returns>A new awaitable.</returns>115 internal static YieldAndNotifyAwaitable YieldAndNotify(this INotifyCompletion baseAwaiter, AsyncManualResetEvent? yieldingSignal = null, AsyncManualResetEvent? resumingSignal = null)116 {117 Requires.NotNull(baseAwaiter, nameof(baseAwaiter));118 return new YieldAndNotifyAwaitable(baseAwaiter, yieldingSignal, resumingSignal);119 }120 /// <summary>121 /// Flood the threadpool with requests that will just block the threads122 /// until the returned value is disposed of.123 /// </summary>124 /// <returns>A value to dispose of to unblock the threadpool.</returns>125 /// <remarks>126 /// This can provide a unique technique for influencing execution order127 /// of synchronous code vs. async code.128 /// </remarks>129 internal static IDisposable StarveThreadpool()130 {131 ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);132 var disposalTokenSource = new CancellationTokenSource();133 var unblockThreadpool = new ManualResetEventSlim();134 for (int i = 0; i < workerThreads; i++)135 {136 Task.Run(137 () => unblockThreadpool.Wait(disposalTokenSource.Token),138 disposalTokenSource.Token);139 }140 return new DisposalAction(disposalTokenSource.Cancel);141 }142 /// <summary>143 /// Executes the specified test method in its own process, offering maximum isolation from ambient noise from other threads144 /// and GC.145 /// </summary>146 /// <param name="testClass">The instance of the test class containing the method to be run in isolation.</param>147 /// <param name="testMethodName">The name of the test method.</param>148 /// <param name="logger">An optional logger to forward any <see cref="ITestOutputHelper"/> output to from the isolated test runner.</param>149 /// <returns>150 /// A task whose result is <c>true</c> if test execution is already isolated and should therefore proceed with the body of the test,151 /// or <c>false</c> after the isolated instance of the test has completed execution.152 /// </returns>153 /// <exception cref="Xunit.Sdk.XunitException">Thrown if the isolated test result is a Failure.</exception>154 /// <exception cref="SkipException">Thrown if on a platform that we do not yet support test isolation on.</exception>155 internal static Task<bool> ExecuteInIsolationAsync(object testClass, string testMethodName, ITestOutputHelper logger)156 {157 Requires.NotNull(testClass, nameof(testClass));158 return ExecuteInIsolationAsync(testClass.GetType().FullName!, testMethodName, logger);159 }160 /// <summary>161 /// Executes the specified test method in its own process, offering maximum isolation from ambient noise from other threads162 /// and GC.163 /// </summary>164 /// <param name="testClassName">The full name of the test class.</param>165 /// <param name="testMethodName">The name of the test method.</param>166 /// <param name="logger">An optional logger to forward any <see cref="ITestOutputHelper"/> output to from the isolated test runner.</param>167 /// <returns>168 /// A task whose result is <c>true</c> if test execution is already isolated and should therefore proceed with the body of the test,169 /// or <c>false</c> after the isolated instance of the test has completed execution.170 /// </returns>171 /// <exception cref="Xunit.Sdk.XunitException">Thrown if the isolated test result is a Failure.</exception>172 /// <exception cref="SkipException">Thrown if on a platform that we do not yet support test isolation on.</exception>173#pragma warning disable CA1801 // Review unused parameters174 internal static Task<bool> ExecuteInIsolationAsync(string testClassName, string testMethodName, ITestOutputHelper logger)175#pragma warning restore CA1801 // Review unused parameters176 {177 Requires.NotNullOrEmpty(testClassName, nameof(testClassName));178 Requires.NotNullOrEmpty(testMethodName, nameof(testMethodName));179#if NETFRAMEWORK180 const string testHostProcessName = "IsolatedTestHost.exe";181 if (Process.GetCurrentProcess().ProcessName == Path.GetFileNameWithoutExtension(testHostProcessName))182 {183 return TplExtensions.TrueTask;184 }185 var startInfo = new ProcessStartInfo(186 testHostProcessName,187 AssemblyCommandLineArguments(188 Assembly.GetExecutingAssembly().Location,189 testClassName,190 testMethodName))191 {192 RedirectStandardError = logger is object,193 RedirectStandardOutput = logger is object,194 CreateNoWindow = true,195 UseShellExecute = false,196 };197 Process isolatedTestProcess = new Process198 {199 StartInfo = startInfo,200 EnableRaisingEvents = true,201 };202 var processExitCode = new TaskCompletionSource<IsolatedTestHost.ExitCode>();203 isolatedTestProcess.Exited += (s, e) =>204 {205 processExitCode.SetResult((IsolatedTestHost.ExitCode)isolatedTestProcess.ExitCode);206 };207 if (logger is object)208 {209 isolatedTestProcess.OutputDataReceived += (s, e) => logger.WriteLine(e.Data ?? string.Empty);210 isolatedTestProcess.ErrorDataReceived += (s, e) => logger.WriteLine(e.Data ?? string.Empty);211 }212 Assert.True(isolatedTestProcess.Start());213 if (logger is object)214 {215 isolatedTestProcess.BeginOutputReadLine();216 isolatedTestProcess.BeginErrorReadLine();217 }218 return processExitCode.Task.ContinueWith(219 t =>220 {221 switch (t.Result)222 {223 case IsolatedTestHost.ExitCode.TestSkipped:224 throw new SkipException("Test skipped. See output of isolated task for details.");225 case IsolatedTestHost.ExitCode.TestPassed:226 default:227 Assert.Equal(IsolatedTestHost.ExitCode.TestPassed, t.Result);228 break;229 }230 return false;231 },232 TaskScheduler.Default);233#else234 return Task.FromException<bool>(new SkipException("Test isolation is not yet supported on this platform."));235#endif236 }237 /// <summary>238 /// Wait on a task without possibly inlining it to the current thread.239 /// </summary>240 /// <param name="task">The task to wait on.</param>241 /// <param name="throwOriginalException"><c>true</c> to throw the original (inner) exception when the <paramref name="task"/> faults; <c>false</c> to throw <see cref="AggregateException"/>.</param>242 /// <exception cref="AggregateException">Thrown if <paramref name="task"/> completes in a faulted state if <paramref name="throwOriginalException"/> is <c>false</c>.</exception>243 internal static void WaitWithoutInlining(this Task task, bool throwOriginalException)244 {245 Requires.NotNull(task, nameof(task));246 if (!task.IsCompleted)247 {248 // Waiting on a continuation of a task won't ever inline the predecessor (in .NET 4.x anyway).249 Task? continuation = task.ContinueWith(t => { }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);250 continuation.Wait();251 }252 // Rethrow the exception if the task faulted.253 if (throwOriginalException)254 {255 task.GetAwaiter().GetResult();256 }257 else258 {259 task.Wait();260 }261 }262 /// <summary>263 /// Wait on a task without possibly inlining it to the current thread and returns its result.264 /// </summary>265 /// <typeparam name="T">The type of result returned from the <paramref name="task"/>.</typeparam>266 /// <param name="task">The task to wait on.</param>267 /// <param name="throwOriginalException"><c>true</c> to throw the original (inner) exception when the <paramref name="task"/> faults; <c>false</c> to throw <see cref="AggregateException"/>.</param>268 /// <returns>The result of the <see cref="Task{T}"/>.</returns>269 /// <exception cref="AggregateException">Thrown if <paramref name="task"/> completes in a faulted state if <paramref name="throwOriginalException"/> is <c>false</c>.</exception>270 internal static T GetResultWithoutInlining<T>(this Task<T> task, bool throwOriginalException = true)271 {272 WaitWithoutInlining(task, throwOriginalException);273 return task.Result;274 }275 private static string AssemblyCommandLineArguments(params string[] args) => string.Join(" ", args.Select(a => $"\"{a}\""));276 internal readonly struct YieldAndNotifyAwaitable277 {278 private readonly INotifyCompletion baseAwaiter;279 private readonly AsyncManualResetEvent? yieldingSignal;280 private readonly AsyncManualResetEvent? resumingSignal;281 internal YieldAndNotifyAwaitable(INotifyCompletion baseAwaiter, AsyncManualResetEvent? yieldingSignal, AsyncManualResetEvent? resumingSignal)282 {283 Requires.NotNull(baseAwaiter, nameof(baseAwaiter));284 this.baseAwaiter = baseAwaiter;285 this.yieldingSignal = yieldingSignal;286 this.resumingSignal = resumingSignal;287 }288 public YieldAndNotifyAwaiter GetAwaiter()289 {290 return new YieldAndNotifyAwaiter(this.baseAwaiter, this.yieldingSignal, this.resumingSignal);291 }292 }293 internal readonly struct YieldAndNotifyAwaiter : INotifyCompletion294 {295 private readonly INotifyCompletion baseAwaiter;296 private readonly AsyncManualResetEvent? yieldingSignal;297 private readonly AsyncManualResetEvent? resumingSignal;298 internal YieldAndNotifyAwaiter(INotifyCompletion baseAwaiter, AsyncManualResetEvent? yieldingSignal, AsyncManualResetEvent? resumingSignal)299 {300 Requires.NotNull(baseAwaiter, nameof(baseAwaiter));301 this.baseAwaiter = baseAwaiter;302 this.yieldingSignal = yieldingSignal;303 this.resumingSignal = resumingSignal;304 }305 public bool IsCompleted306 {307 get { return false; }308 }309 public void OnCompleted(Action continuation)310 {311 YieldAndNotifyAwaiter that = this;312 this.baseAwaiter.OnCompleted(delegate...
SubscriptionFailoverWIthWaitingChains.cs
Source:SubscriptionFailoverWIthWaitingChains.cs
...179 Server = server,180 ModifyDocumentStore = s => s.Conventions.ReadBalanceBehavior = Raven.Client.Http.ReadBalanceBehavior.RoundRobin,181 }))182 {183 var mre = new AsyncManualResetEvent();184 using (var session = store.OpenSession())185 {186 session.Store(new User());187 session.SaveChanges();188 }189 var subsId = await store.Subscriptions.CreateAsync<User>();190 using var subsWorker = store.Subscriptions.GetSubscriptionWorker<User>(new SubscriptionWorkerOptions(subsId)191 {192 TimeToWaitBeforeConnectionRetry = TimeSpan.FromMilliseconds(16)193 });194 subsWorker.OnSubscriptionConnectionRetry += ex =>195 {196 Assert.NotNull(ex);197 ...
AsyncTrackingSyncContext.cs
Source:AsyncTrackingSyncContext.cs
...38 return synchronizationContext;39 }40 [SecuritySafeCritical]41 void SetCurrentAsSynchronizationContext() => SetSynchronizationContext(this);42 readonly AsyncManualResetEvent @event = new(true);43 readonly SynchronizationContext innerContext;44 int operationCount;45 private AsyncTrackingSyncContext()46 {47 innerContext = Current ?? new SynchronizationContext();48 }49 public override void OperationCompleted()50 {51 var result = Interlocked.Decrement(ref operationCount);52 if (result == 0)53 @event.Set();54 innerContext.OperationCompleted();55 }56 public override void OperationStarted()...
AsyncTestSyncContext.cs
Source:AsyncTestSyncContext.cs
...8 /// of outstanding "async void" operations, and wait for them all to complete.9 /// </summary>10 public class AsyncTestSyncContext : SynchronizationContext11 {12 readonly AsyncManualResetEvent @event = new AsyncManualResetEvent(true);13 Exception exception;14 readonly SynchronizationContext innerContext;15 int operationCount;16 /// <summary>17 /// Initializes a new instance of the <see cref="AsyncTestSyncContext"/> class.18 /// </summary>19 /// <param name="innerContext">The existing synchronization context (may be <c>null</c>).</param>20 public AsyncTestSyncContext(SynchronizationContext innerContext)21 {22 this.innerContext = innerContext;23 }24 /// <inheritdoc/>25 public override void OperationCompleted()26 {...
AsyncManualResetEvent.cs
Source:AsyncManualResetEvent.cs
1using System.Threading.Tasks;2namespace Shard.Shared.Web.IntegrationTests.Clock.TaskTracking3{4 // From https://github.com/xunit/xunit/blob/master/src/xunit.execution/Sdk/Utility/AsyncManualResetEvent.cs5 class AsyncManualResetEvent6 {7 volatile TaskCompletionSource<bool> taskCompletionSource = new();8 public AsyncManualResetEvent(bool signaled = false)9 {10 if (signaled)11 taskCompletionSource.TrySetResult(true);12 }13 public bool IsSet14 {15 get { return taskCompletionSource.Task.IsCompleted; }16 }17 public Task WaitAsync()18 {19 return taskCompletionSource.Task;20 }21 public void Set()22 {...
AsyncManualResetEvent
Using AI Code Generation
1using Xunit;2using System;3using System.Threading.Tasks;4{5 {6 static void Main(string[] args)7 {8 AsyncManualResetEvent amre = new AsyncManualResetEvent();9 Task t = amre.WaitAsync();10 Console.WriteLine("Before Set");11 amre.Set();12 Console.WriteLine("After Set");13 Console.ReadKey();14 }15 }16}
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!