@@ -5,7 +5,7 @@ namespace Tests;
55public class Tests
66{
77 [ Fact ]
8- public void GivenBalanceWithoutSync_WhenMultipleWithdrawals_ThenFinalBalanceIsIncorrect ( )
8+ public void GivenBalanceWithoutSync_WhenMultipleWithdrawals_ThenFinalBalanceIsNotGuaranteed ( )
99 {
1010 var account = new Account
1111 {
@@ -14,11 +14,12 @@ public void GivenBalanceWithoutSync_WhenMultipleWithdrawals_ThenFinalBalanceIsIn
1414
1515 AccountService . WithdrawBalance ( account . Withdraw ) ;
1616
17- Assert . NotEqual ( 100000 , account . Balance ) ;
17+ // A race is not deterministic: this demonstrates the race, it cannot prove it happened.
18+ Assert . InRange ( account . Balance , 0 , 100000 ) ;
1819 }
1920
2021 [ Fact ]
21- public void GivenBalanceVolatile_WhenMultipleWithdrawals_ThenFinalBalanceIsIncorrect ( )
22+ public void GivenBalanceVolatile_WhenMultipleWithdrawals_ThenFinalBalanceIsNotGuaranteed ( )
2223 {
2324 var account = new Account
2425 {
@@ -27,7 +28,8 @@ public void GivenBalanceVolatile_WhenMultipleWithdrawals_ThenFinalBalanceIsIncor
2728
2829 AccountService . WithdrawBalance ( account . WithdrawVolatile ) ;
2930
30- Assert . NotEqual ( 1000 , account . BalanceVolatile ) ;
31+ // A race is not deterministic: this demonstrates the race, it cannot prove it happened.
32+ Assert . InRange ( account . BalanceVolatile , 0 , 100000 ) ;
3133 }
3234
3335 [ Fact ]
@@ -55,4 +57,39 @@ public void GivenBalanceWithInterlocked_WhenMultipleWithdrawals_ThenFinalBalance
5557
5658 Assert . Equal ( 0 , account . BalanceInterlocked ) ;
5759 }
60+
61+ [ Fact ]
62+ public void GivenCompareExchangeLoop_WhenConcurrentWithdrawals_ThenEveryWithdrawalSucceedsAndBalanceIsZero ( )
63+ {
64+ var account = new Account
65+ {
66+ BalanceInterlocked = 100000
67+ } ;
68+ var succeeded = 0 ;
69+
70+ Parallel . For ( 0 , 1000 , _ =>
71+ {
72+ if ( account . WithdrawIfSufficient ( 100 ) )
73+ Interlocked . Increment ( ref succeeded ) ;
74+ } ) ;
75+
76+ Assert . Equal ( 1000 , succeeded ) ;
77+ Assert . Equal ( 0 , account . BalanceInterlocked ) ;
78+ Assert . False ( account . WithdrawIfSufficient ( 100 ) ) ;
79+ }
80+
81+ [ Fact ]
82+ public void GivenOneShotInitializer_WhenManyThreadsTryToStart_ThenExactlyOneWins ( )
83+ {
84+ var initializer = new OneShotInitializer ( ) ;
85+ var winners = 0 ;
86+
87+ Parallel . For ( 0 , 1000 , _ =>
88+ {
89+ if ( initializer . TryStart ( ) )
90+ Interlocked . Increment ( ref winners ) ;
91+ } ) ;
92+
93+ Assert . Equal ( 1 , winners ) ;
94+ }
5895}
0 commit comments