@@ -45,12 +45,13 @@ public abstract partial class ExchangeAPI : BaseAPI, IExchangeAPI
4545 #region Private methods
4646
4747 private static readonly IReadOnlyCollection < Type > exchangeTypes = typeof ( ExchangeAPI ) . Assembly . GetTypes ( ) . Where ( type => type . IsSubclassOf ( typeof ( ExchangeAPI ) ) && ! type . IsAbstract ) . ToArray ( ) ;
48- private static readonly ConcurrentDictionary < Type , IExchangeAPI > apis = new ConcurrentDictionary < Type , IExchangeAPI > ( ) ;
48+ private static readonly ConcurrentDictionary < Type , Task < IExchangeAPI > > apis = new ConcurrentDictionary < Type , Task < IExchangeAPI > > ( ) ;
49+ private static readonly SemaphoreSlim semaphore = new SemaphoreSlim ( 1 ) ;
4950
5051 private bool initialized ;
5152 private bool disposed ;
5253
53- private static IExchangeAPI InitializeAPI ( Type ? type , Type ? knownType = null )
54+ private static async Task < IExchangeAPI > InitializeAPIAsync ( Type ? type , Type ? knownType = null )
5455 {
5556 if ( type is null )
5657 {
@@ -64,30 +65,23 @@ private static IExchangeAPI InitializeAPI(Type? type, Type? knownType = null)
6465 }
6566
6667 const int retryCount = 3 ;
67- Exception ? ex = null ;
68-
68+
6969 // try up to 3 times to init
70- for ( int i = 1 ; i <= 3 ; i ++ )
70+ for ( int i = 1 ; i <= retryCount ; i ++ )
7171 {
7272 try
7373 {
74- api . InitializeAsync ( ) . Sync ( ) ;
75- ex = null ;
76- break ;
74+ await api . InitializeAsync ( ) ;
7775 }
78- catch ( Exception _ex )
76+ catch ( Exception )
7977 {
80- ex = _ex ;
81- if ( i != retryCount )
78+ if ( i == retryCount )
8279 {
83- Thread . Sleep ( 5000 ) ;
80+ throw ;
8481 }
85- }
86- }
8782
88- if ( ex != null )
89- {
90- throw ex ;
83+ Thread . Sleep ( 5000 ) ;
84+ }
9185 }
9286
9387 return api ;
@@ -365,7 +359,7 @@ public void Dispose()
365359 Cache ? . Dispose ( ) ;
366360
367361 // take out of global api dictionary if disposed and we are the current exchange in the dictionary
368- if ( apis . TryGetValue ( GetType ( ) , out IExchangeAPI existing ) && this == existing )
362+ if ( apis . TryGetValue ( GetType ( ) , out Task < IExchangeAPI > existing ) && this == existing . Result )
369363 {
370364 apis . TryRemove ( GetType ( ) , out _ ) ;
371365 }
@@ -386,9 +380,17 @@ private async Task InitializeAsync()
386380 initialized = true ;
387381 }
388382
389- private static IExchangeAPI CreateExchangeAPI ( Type ? type )
383+ /// <summary>
384+ /// Create an exchange api, by-passing any cache. Use this method for cases
385+ /// where you need multiple instances of the same exchange, for example
386+ /// multiple credentials.
387+ /// </summary>
388+ /// <typeparam name="T">Type of exchange api to create</typeparam>
389+ /// <returns>Created exchange api</returns>
390+ [ Obsolete ( "Use the async version" ) ]
391+ public static T CreateExchangeAPI < T > ( ) where T : ExchangeAPI
390392 {
391- return InitializeAPI ( type ) ;
393+ return CreateExchangeAPIAsync < T > ( ) . Result ;
392394 }
393395
394396 /// <summary>
@@ -398,75 +400,117 @@ private static IExchangeAPI CreateExchangeAPI(Type? type)
398400 /// </summary>
399401 /// <typeparam name="T">Type of exchange api to create</typeparam>
400402 /// <returns>Created exchange api</returns>
401- public static T CreateExchangeAPI < T > ( ) where T : ExchangeAPI
403+ public static async Task < T > CreateExchangeAPIAsync < T > ( ) where T : ExchangeAPI
402404 {
403- return ( T ) CreateExchangeAPI ( typeof ( T ) ) ;
405+ return ( T ) await InitializeAPIAsync ( typeof ( T ) ) ;
404406 }
405407
406408 /// <summary>
407409 /// Get a cached exchange API given an exchange name (see ExchangeName class)
408410 /// </summary>
409411 /// <param name="exchangeName">Exchange name. Must match the casing of the ExchangeName class name exactly.</param>
410412 /// <returns>Exchange API or null if not found</returns>
413+ [ Obsolete ( "Use the async version" ) ]
411414 public static IExchangeAPI GetExchangeAPI ( string exchangeName )
415+ {
416+ return GetExchangeAPIAsync ( exchangeName ) . Result ;
417+ }
418+
419+ /// <summary>
420+ /// Get a cached exchange API given an exchange name (see ExchangeName class)
421+ /// </summary>
422+ /// <param name="exchangeName">Exchange name. Must match the casing of the ExchangeName class name exactly.</param>
423+ /// <returns>Exchange API or null if not found</returns>
424+ public static Task < IExchangeAPI > GetExchangeAPIAsync ( string exchangeName )
412425 {
413426 Type type = ExchangeName . GetExchangeType ( exchangeName ) ;
414- return GetExchangeAPI ( type ) ;
427+ return GetExchangeAPIAsync ( type ) ;
415428 }
416429
417430 /// <summary>
418431 /// Get a cached exchange API given a type
419432 /// </summary>
420433 /// <typeparam name="T">Type of exchange to get</typeparam>
421434 /// <returns>Exchange API or null if not found</returns>
435+ [ Obsolete ( "Use the async version" ) ]
422436 public static IExchangeAPI GetExchangeAPI < T > ( ) where T : ExchangeAPI
437+ {
438+ return GetExchangeAPIAsync < T > ( ) . Result ;
439+ }
440+
441+ public static Task < IExchangeAPI > GetExchangeAPIAsync < T > ( ) where T : ExchangeAPI
423442 {
424443 // note: this method will be slightly slow (milliseconds) the first time it is called due to cache miss and initialization
425444 // subsequent calls with cache hits will be nanoseconds
426445 Type type = typeof ( T ) ! ;
427- return GetExchangeAPI ( type ) ;
446+ return GetExchangeAPIAsync ( type ) ;
428447 }
429448
430449 /// <summary>
431450 /// Get a cached exchange API given a type
432451 /// </summary>
433452 /// <param name="type">Type of exchange</param>
434453 /// <returns>Exchange API or null if not found</returns>
454+ [ Obsolete ( "Use the async version" ) ]
435455 public static IExchangeAPI GetExchangeAPI ( Type type )
436456 {
437- // note: this method will be slightly slow (milliseconds) the first time it is called due to cache miss and initialization
438- // subsequent calls with cache hits will be nanoseconds
439- return apis . GetOrAdd ( type , _exchangeName =>
457+ return GetExchangeAPIAsync ( type ) . Result ;
458+ }
459+
460+ /// <summary>
461+ /// Get a cached exchange API given a type
462+ /// </summary>
463+ /// <param name="type">Type of exchange</param>
464+ /// <returns>Exchange API or null if not found</returns>
465+ public static async Task < IExchangeAPI > GetExchangeAPIAsync ( Type type )
466+ {
467+ if ( apis . TryGetValue ( type , out var result ) ) return await result ;
468+
469+ await semaphore . WaitAsync ( ) ;
470+ try
440471 {
441- // find the api type
442- Type ? foundType = exchangeTypes . FirstOrDefault ( t => t == type ) ;
443- return InitializeAPI ( foundType , type ) ;
444- } ) ;
472+ // try again inside semaphore
473+ if ( apis . TryGetValue ( type , out result ) ) return await result ;
474+
475+ // still not found, initialize it
476+ var foundType = exchangeTypes . FirstOrDefault ( t => t == type ) ;
477+ return await ( apis [ type ] = InitializeAPIAsync ( foundType , type ) ) ;
478+ }
479+ finally
480+ {
481+ semaphore . Release ( ) ;
482+ }
445483 }
446484
447485 /// <summary>
448486 /// Get all cached versions of exchange APIs
449487 /// </summary>
450488 /// <returns>All APIs</returns>
489+ [ Obsolete ( "Use the async version" ) ]
451490 public static IExchangeAPI [ ] GetExchangeAPIs ( )
452491 {
453- foreach ( Type type in exchangeTypes )
492+ return GetExchangeAPIsAsync ( ) . Result ;
493+ }
494+
495+ /// <summary>
496+ /// Get all cached versions of exchange APIs
497+ /// </summary>
498+ /// <returns>All APIs</returns>
499+ public static async Task < IExchangeAPI [ ] > GetExchangeAPIsAsync ( )
500+ {
501+ var apiList = new List < IExchangeAPI > ( ) ;
502+ foreach ( var kv in apis . ToArray ( ) )
454503 {
455- List < IExchangeAPI > apiList = new List < IExchangeAPI > ( ) ;
456- foreach ( var kv in apis . ToArray ( ) )
504+ if ( kv . Value == null )
457505 {
458- if ( kv . Value == null )
459- {
460- apiList . Add ( GetExchangeAPI ( kv . Key ) ) ;
461- }
462- else
463- {
464- apiList . Add ( kv . Value ) ;
465- }
506+ apiList . Add ( await GetExchangeAPIAsync ( kv . Key ) ) ;
507+ }
508+ else
509+ {
510+ apiList . Add ( await kv . Value ) ;
466511 }
467- return apiList . ToArray ( ) ;
468512 }
469- return apis . Values . ToArray ( ) ;
513+ return apiList . ToArray ( ) ;
470514 }
471515
472516 /// <summary>
0 commit comments