How to use AsyncManualResetEvent class of Xunit.Sdk package

Best Xunit code snippet using Xunit.Sdk.AsyncManualResetEvent

AsyncReaderWriterLockTests.cs

Source:AsyncReaderWriterLockTests.cs Github

copy

Full Screen

...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        {...

Full Screen

Full Screen

SubscriptionsFailover.cs

Source:SubscriptionsFailover.cs Github

copy

Full Screen

...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);...

Full Screen

Full Screen

SubscriptionsFailoverStress.cs

Source:SubscriptionsFailoverStress.cs Github

copy

Full Screen

...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);...

Full Screen

Full Screen

TestUtilities.cs

Source:TestUtilities.cs Github

copy

Full Screen

...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...

Full Screen

Full Screen

SubscriptionFailoverWIthWaitingChains.cs

Source:SubscriptionFailoverWIthWaitingChains.cs Github

copy

Full Screen

...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                    ...

Full Screen

Full Screen

AsyncTrackingSyncContext.cs

Source:AsyncTrackingSyncContext.cs Github

copy

Full Screen

...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()...

Full Screen

Full Screen

AsyncTestSyncContext.cs

Source:AsyncTestSyncContext.cs Github

copy

Full Screen

...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        {...

Full Screen

Full Screen

AsyncManualResetEvent.cs

Source:AsyncManualResetEvent.cs Github

copy

Full Screen

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        {...

Full Screen

Full Screen

AsyncManualResetEvent

Using AI Code Generation

copy

Full Screen

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}

Full Screen

Full Screen

Automation Testing Tutorials

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.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Xunit automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Most used methods in AsyncManualResetEvent

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful