11using System ;
22using System . Collections . Generic ;
33using System . Diagnostics ;
4+ using System . Diagnostics . CodeAnalysis ;
5+ using System . Runtime . ExceptionServices ;
46using System . Threading ;
57
68namespace BitFaster . Caching . Atomic
@@ -98,8 +100,17 @@ private V CreateValue<TFactory>(K key, TFactory valueFactory) where TFactory : s
98100
99101 if ( init != null )
100102 {
101- value = init . CreateValue ( key , valueFactory ) ;
102- Volatile . Write ( ref initializer , null ) ; // volatile write must occur after setting value
103+ try
104+ {
105+ value = init . CreateValue ( key , valueFactory ) ;
106+ Volatile . Write ( ref initializer , null ) ; // volatile write must occur after setting value
107+ }
108+ catch
109+ {
110+ // Overwrite the initializer with a fresh copy. New threads will start from a clean state.
111+ Volatile . Write ( ref initializer , new Initializer ( ) ) ;
112+ throw ;
113+ }
103114 }
104115
105116 return value ;
@@ -138,6 +149,7 @@ private class Initializer
138149 {
139150 private bool isInitialized ;
140151 private V value ;
152+ private ExceptionDispatchInfo exceptionDispatch ;
141153
142154 public V CreateValue < TFactory > ( K key , TFactory valueFactory ) where TFactory : struct , IValueFactory < K , V >
143155 {
@@ -148,9 +160,24 @@ public V CreateValue<TFactory>(K key, TFactory valueFactory) where TFactory : st
148160 return value ;
149161 }
150162
151- value = valueFactory . Create ( key ) ;
152- isInitialized = true ;
153- return value ;
163+ // If a previous thread called the factory and failed, throw the same error instead
164+ // of calling the factory again.
165+ if ( exceptionDispatch != null )
166+ {
167+ exceptionDispatch . Throw ( ) ;
168+ }
169+
170+ try
171+ {
172+ value = valueFactory . Create ( key ) ;
173+ isInitialized = true ;
174+ return value ;
175+ }
176+ catch ( Exception ex )
177+ {
178+ exceptionDispatch = ExceptionDispatchInfo . Capture ( ex ) ;
179+ throw ;
180+ }
154181 }
155182 }
156183 }
0 commit comments