16
16
using System . Linq ;
17
17
using System . Threading ;
18
18
using System . Threading . Tasks ;
19
- using Cleary . AsyncExtensions ;
19
+ using Nito . AsyncEx ;
20
20
using ServiceStack . Caching ;
21
21
using ServiceStack . Logging ;
22
22
using ServiceStack . Text ;
@@ -36,8 +36,8 @@ public partial class PooledRedisClientManager
36
36
private const string PoolTimeoutError =
37
37
"Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use." ;
38
38
39
- private AsyncMonitor readMonitor ;
40
- private AsyncMonitor writeMonitor ;
39
+ private AsyncManualResetEvent readAsyncEvent ;
40
+ private AsyncManualResetEvent writeAsyncEvent ;
41
41
42
42
protected readonly int PoolSizeMultiplier = 20 ;
43
43
public int RecheckPoolAfterMs = 100 ;
@@ -217,30 +217,44 @@ protected virtual void OnStart()
217
217
this . Start ( ) ;
218
218
}
219
219
220
+ private void pulseAllRead ( )
221
+ {
222
+ readAsyncEvent ? . Set ( ) ;
223
+ readAsyncEvent ? . Reset ( ) ;
224
+ Monitor . PulseAll ( readClients ) ;
225
+ }
226
+
227
+ private void pulseAllWrite ( )
228
+ {
229
+ writeAsyncEvent ? . Set ( ) ;
230
+ writeAsyncEvent ? . Reset ( ) ;
231
+ Monitor . PulseAll ( writeClients ) ;
232
+ }
233
+
220
234
private async Task < bool > waitForWriter ( int msTimeout )
221
235
{
222
- if ( writeMonitor == null )
223
- writeMonitor = new AsyncMonitor ( ) ;
224
- var delayTask = Task . Delay ( msTimeout ) ;
225
- var result = await Task . WhenAny ( writeMonitor . WaitAsync ( ) , delayTask ) ;
226
- if ( result == delayTask ) return false ;
236
+ if ( writeAsyncEvent == null ) // If we're not doing async, no need to create this till we need it.
237
+ writeAsyncEvent = new AsyncManualResetEvent ( false ) ;
238
+ var cts = new CancellationTokenSource ( TimeSpan . FromMilliseconds ( msTimeout ) ) ;
239
+ try
240
+ {
241
+ await writeAsyncEvent . WaitAsync ( cts . Token ) ;
242
+ }
243
+ catch ( OperationCanceledException ) { return false ; }
227
244
return true ;
228
245
}
229
246
230
247
/// <summary>
231
248
/// Returns a Read/Write client (The default) using the hosts defined in ReadWriteHosts
232
249
/// </summary>
233
250
/// <returns></returns>
234
- public IRedisClient GetClient ( ) => GetClient ( false ) ;
251
+ public IRedisClient GetClient ( ) => GetClientBlocking ( ) ;
235
252
236
253
private async ValueTask < IRedisClientAsync > GetClientAsync ( )
237
254
{
238
255
try
239
256
{
240
- var poolTimedOut = false ;
241
257
var inactivePoolIndex = - 1 ;
242
-
243
- var timeoutsTests = 300 ;
244
258
do
245
259
{
246
260
RedisClient inActiveClient ;
@@ -269,33 +283,22 @@ private async ValueTask<IRedisClientAsync> GetClientAsync()
269
283
}
270
284
}
271
285
272
- timeoutsTests -- ;
273
- if ( timeoutsTests <= 0 )
286
+ if ( PoolTimeout . HasValue )
287
+ {
288
+ // We have a timeout value set - so try to not wait longer than this.
289
+ if ( ! await waitForWriter ( PoolTimeout . Value ) )
290
+ {
291
+ throw new TimeoutException ( PoolTimeoutError ) ;
292
+ }
293
+ }
294
+ else
274
295
{
275
- poolTimedOut = true ;
276
- break ;
296
+ // Wait forever, so just retry till we get one.
297
+ await waitForWriter ( RecheckPoolAfterMs ) ;
277
298
}
278
- await Task . Delay ( 10 ) ;
279
-
280
- // // Didn't get one, so let's wait a bit for a new one.
281
- // if (PoolTimeout.HasValue)
282
- // {
283
- // if (!await waitForWriter(PoolTimeout.Value))
284
- // {
285
- // poolTimedOut = true;
286
- // break;
287
- // }
288
- // }
289
- // else
290
- // {
291
- // await waitForWriter(RecheckPoolAfterMs);
292
- // }
293
- } while ( true ) ; // Just keep repeating until we get a
299
+ } while ( true ) ; // Just keep repeating until we get a slot.
294
300
295
- if ( poolTimedOut )
296
- throw new TimeoutException ( PoolTimeoutError ) ;
297
-
298
- //Reaches here when there's no Valid InActive Clients
301
+ //Reaches here when there's no Valid InActive Clients, but we have a slot for one!
299
302
try
300
303
{
301
304
//inactivePoolIndex = index of reservedSlot || index of invalid client
@@ -343,9 +346,8 @@ private async ValueTask<IRedisClientAsync> GetClientAsync()
343
346
}
344
347
}
345
348
346
- private RedisClient GetClient ( bool forAsync )
349
+ private RedisClient GetClientBlocking ( )
347
350
{
348
- if ( forAsync ) throw new Exception ( "Call GetClientAsync instead" ) ;
349
351
try
350
352
{
351
353
var poolTimedOut = false ;
@@ -380,7 +382,7 @@ private RedisClient GetClient(bool forAsync)
380
382
381
383
InitClient ( inActiveClient ) ;
382
384
383
- return ( ! AssertAccessOnlyOnSameThread || forAsync )
385
+ return ( ! AssertAccessOnlyOnSameThread )
384
386
? inActiveClient
385
387
: inActiveClient . LimitAccessToThread ( Thread . CurrentThread . ManagedThreadId , Environment . StackTrace ) ;
386
388
}
@@ -416,7 +418,7 @@ private RedisClient GetClient(bool forAsync)
416
418
WritePoolIndex ++ ;
417
419
writeClients [ inactivePoolIndex ] = newClient ;
418
420
419
- return ( ! AssertAccessOnlyOnSameThread || forAsync )
421
+ return ( ! AssertAccessOnlyOnSameThread )
420
422
? newClient
421
423
: newClient . LimitAccessToThread ( Thread . CurrentThread . ManagedThreadId , Environment . StackTrace ) ;
422
424
}
@@ -672,8 +674,7 @@ public void DisposeClient(RedisNativeClient client)
672
674
client . Deactivate ( ) ;
673
675
}
674
676
675
- Monitor . PulseAll ( readClients ) ;
676
- readMonitor ? . PulseAll ( ) ;
677
+ pulseAllRead ( ) ;
677
678
return ;
678
679
}
679
680
}
@@ -694,23 +695,20 @@ public void DisposeClient(RedisNativeClient client)
694
695
client . Deactivate ( ) ;
695
696
}
696
697
697
- Monitor . PulseAll ( writeClients ) ;
698
- writeMonitor ? . PulseAll ( ) ;
698
+ pulseAllWrite ( ) ;
699
699
return ;
700
700
}
701
701
}
702
702
703
703
//Client not found in any pool, pulse both pools.
704
704
lock ( readClients )
705
705
{
706
- Monitor . PulseAll ( readClients ) ;
707
- readMonitor ? . PulseAll ( ) ;
706
+ pulseAllRead ( ) ;
708
707
}
709
708
710
709
lock ( writeClients )
711
710
{
712
- Monitor . PulseAll ( writeClients ) ;
713
- writeMonitor ? . PulseAll ( ) ;
711
+ pulseAllWrite ( ) ;
714
712
}
715
713
}
716
714
@@ -723,8 +721,7 @@ public void DisposeReadOnlyClient(RedisNativeClient client)
723
721
lock ( readClients )
724
722
{
725
723
client . Deactivate ( ) ;
726
- Monitor . PulseAll ( readClients ) ;
727
- readMonitor ? . PulseAll ( ) ;
724
+ pulseAllRead ( ) ;
728
725
}
729
726
}
730
727
@@ -737,8 +734,7 @@ public void DisposeWriteClient(RedisNativeClient client)
737
734
lock ( writeClients )
738
735
{
739
736
client . Deactivate ( ) ;
740
- Monitor . PulseAll ( writeClients ) ;
741
- writeMonitor ? . PulseAll ( ) ;
737
+ pulseAllWrite ( ) ;
742
738
}
743
739
}
744
740
0 commit comments