@@ -44,11 +44,21 @@ public sealed class CertificateManager : IDisposable
4444 /// <summary>
4545 /// Cache dictionary
4646 /// </summary>
47- private readonly ConcurrentDictionary < string , CachedCertificate > cachedCertificates ;
47+ private readonly ConcurrentDictionary < string , CachedCertificate > cachedCertificates
48+ = new ConcurrentDictionary < string , CachedCertificate > ( ) ;
4849
49- private readonly CancellationTokenSource clearCertificatesTokenSource ;
50+ /// <summary>
51+ /// A list of pending certificate creation tasks.
52+ /// Usefull to prevent multiple threads working on same certificate generation
53+ /// when burst certificate generation requests happen for same certificate.
54+ /// </summary>
55+ private readonly ConcurrentDictionary < string , Task < X509Certificate2 > > pendingCertificateCreationTasks
56+ = new ConcurrentDictionary < string , Task < X509Certificate2 > > ( ) ;
57+
58+ private readonly CancellationTokenSource clearCertificatesTokenSource
59+ = new CancellationTokenSource ( ) ;
5060
51- private readonly object rootCertCreationLock ;
61+ private readonly object rootCertCreationLock = new object ( ) ;
5262
5363 private ICertificateMaker certEngine ;
5464
@@ -60,7 +70,7 @@ public sealed class CertificateManager : IDisposable
6070
6171 private string rootCertificateName ;
6272
63- private ICertificateCache certificateCache ;
73+ private ICertificateCache certificateCache = new DefaultCertificateDiskCache ( ) ;
6474
6575 /// <summary>
6676 /// Initializes a new instance of the <see cref="CertificateManager"/> class.
@@ -99,14 +109,6 @@ internal CertificateManager(string rootCertificateName, string rootCertificateIs
99109 }
100110
101111 CertificateEngine = CertificateEngine . BouncyCastle ;
102-
103- cachedCertificates = new ConcurrentDictionary < string , CachedCertificate > ( ) ;
104-
105- clearCertificatesTokenSource = new CancellationTokenSource ( ) ;
106-
107- certificateCache = new DefaultCertificateDiskCache ( ) ;
108-
109- rootCertCreationLock = new object ( ) ;
110112 }
111113
112114 /// <summary>
@@ -225,8 +227,9 @@ public X509Certificate2 RootCertificate
225227 public bool SaveFakeCertificates { get ; set ; } = false ;
226228
227229 /// <summary>
228- /// The service to save fake certificates.
229- /// The default storage saves certificates in folder "crts" (will be created in proxy dll directory).
230+ /// The fake certificate cache storage.
231+ /// The default cache storage implementation saves certificates in folder "crts" (will be created in proxy dll directory).
232+ /// Implement ICertificateCache interface and assign concrete class here to customize.
230233 /// </summary>
231234 public ICertificateCache CertificateStorage
232235 {
@@ -436,41 +439,40 @@ internal X509Certificate2 CreateCertificate(string certificateName, bool isRootC
436439 internal async Task < X509Certificate2 > CreateCertificateAsync ( string certificateName )
437440 {
438441 // check in cache first
439- var item = cachedCertificates . GetOrAdd ( certificateName , _ =>
442+ if ( cachedCertificates . TryGetValue ( certificateName , out var cached ) )
440443 {
441- var cached = new CachedCertificate ( ) ;
442- cached . CreationTask = Task . Run ( ( ) =>
443- {
444- var certificate = CreateCertificate ( certificateName , false ) ;
445-
446- // see http://www.albahari.com/threading/part4.aspx for the explanation
447- // why Thread.MemoryBarrier is used here and below
448- cached . Certificate = certificate ;
449- Thread . MemoryBarrier ( ) ;
450- cached . CreationTask = null ;
451- Thread . MemoryBarrier ( ) ;
452- return certificate ;
453- } ) ;
454-
455- return cached ;
456- } ) ;
457-
458- item . LastAccess = DateTime . Now ;
444+ cached . LastAccess = DateTime . Now ;
445+ return cached . Certificate ;
446+ }
459447
460- if ( item . Certificate != null )
448+ // handle burst requests with same certificate name
449+ // by checking for existing task for same certificate name
450+ if ( pendingCertificateCreationTasks . TryGetValue ( certificateName , out var task ) )
461451 {
462- return item . Certificate ;
452+ return await task ;
463453 }
464454
465- // handle burst requests with same certificate name
466- // by checking for existing task
467- Thread . MemoryBarrier ( ) ;
468- var task = item . CreationTask ;
455+ // run certificate creation task & add it to pending tasks
456+ task = Task . Run ( ( ) =>
457+ {
458+ var result = CreateCertificate ( certificateName , false ) ;
459+ if ( result != null )
460+ {
461+ cachedCertificates . TryAdd ( certificateName , new CachedCertificate
462+ {
463+ Certificate = result
464+ } ) ;
465+ }
469466
470- Thread . MemoryBarrier ( ) ;
467+ return result ;
468+ } ) ;
469+ pendingCertificateCreationTasks . TryAdd ( certificateName , task ) ;
471470
472- // return result
473- return item . Certificate ?? await task ;
471+ // cleanup pending tasks & return result
472+ var certificate = await task ;
473+ pendingCertificateCreationTasks . TryRemove ( certificateName , out task ) ;
474+
475+ return certificate ;
474476 }
475477
476478 /// <summary>
0 commit comments