11using System ;
22#if COMPAT_BOOTSTRAP_USING_REFLECTION && HAS_SYSTEM_APPDOMAIN_GETASSEMBLIES && HAS_SYSTEM_REFLECTION_ASSEMBLY_GETEXPORTEDTYPES
33using System . Reflection ;
4- using System . Threading ;
54#endif
65
76namespace GeoAPI
@@ -12,22 +11,88 @@ namespace GeoAPI
1211 public static class GeometryServiceProvider
1312 {
1413 private static volatile IGeometryServices s_instance ;
15- private static readonly object s_lock = new object ( ) ;
14+
15+ /// <summary>
16+ /// Make sure only one thread runs <see cref="InitializeInstance"/> at a time.
17+ /// </summary>
18+ private static readonly object s_autoInitLock = new object ( ) ;
19+
20+ /// <summary>
21+ /// Make sure that anyone who directly sets <see cref="Instance"/>, including the automatic
22+ /// initializer, behaves consistently, regarding <see cref="s_instanceSetDirectly"/> and the
23+ /// semantics of <see cref="SetInstanceIfNotAlreadySetDirectly"/>.
24+ /// </summary>
25+ private static readonly object s_explicitInitLock = new object ( ) ;
26+
27+ /// <summary>
28+ /// Indicates whether or not <see cref="s_instance"/> has been set directly (i.e., outside
29+ /// of the reflection-based initializer).
30+ /// </summary>
31+ private static bool s_instanceSetDirectly = false ;
1632
1733 /// <summary>
1834 /// Gets or sets the <see cref="IGeometryServices"/> instance.
1935 /// </summary>
2036 public static IGeometryServices Instance
2137 {
2238 get => s_instance ?? InitializeInstance ( ) ;
23- set => s_instance = value ?? throw new ArgumentNullException ( nameof ( value ) ) ;
39+ set
40+ {
41+ if ( value == null )
42+ {
43+ throw new ArgumentNullException ( nameof ( value ) ) ;
44+ }
45+
46+ lock ( s_explicitInitLock )
47+ {
48+ s_instance = value ;
49+ s_instanceSetDirectly = true ;
50+ }
51+ }
52+ }
53+
54+ /// <summary>
55+ /// Sets <see cref="Instance"/> to the given value, unless it has already been set directly.
56+ /// Both this method and the property's setter itself count as setting it "directly".
57+ /// </summary>
58+ /// <param name="instance">
59+ /// The new value to put into <see cref="Instance"/> if it hasn't already been set directly.
60+ /// </param>
61+ /// <returns>
62+ /// <c>true</c> if <see cref="Instance"/> was set, <c>false</c> otherwise.
63+ /// </returns>
64+ /// <exception cref="ArgumentNullException">
65+ /// Thrown when <paramref name="instance"/> is <see langword="null"/>.
66+ /// </exception>
67+ public static bool SetInstanceIfNotAlreadySetDirectly ( IGeometryServices instance )
68+ {
69+ if ( instance == null )
70+ {
71+ throw new ArgumentNullException ( nameof ( instance ) ) ;
72+ }
73+
74+ lock ( s_explicitInitLock )
75+ {
76+ if ( s_instanceSetDirectly )
77+ {
78+ // someone has already set the value directly before.
79+ return false ;
80+ }
81+
82+ s_instance = instance ;
83+
84+ // calling this method counts.
85+ s_instanceSetDirectly = true ;
86+ return true ;
87+ }
2488 }
2589
2690 private static IGeometryServices InitializeInstance ( )
2791 {
2892#if COMPAT_BOOTSTRAP_USING_REFLECTION && HAS_SYSTEM_APPDOMAIN_GETASSEMBLIES && HAS_SYSTEM_REFLECTION_ASSEMBLY_GETEXPORTEDTYPES
29- lock ( s_lock )
93+ lock ( s_autoInitLock )
3094 {
95+ // see if someone has already set it while we were waiting for the lock.
3196 var instance = s_instance ;
3297 if ( instance != null ) return instance ;
3398 foreach ( var assembly in AppDomain . CurrentDomain . GetAssemblies ( ) )
@@ -64,8 +129,16 @@ private static IGeometryServices InitializeInstance()
64129 foreach ( var constructor in type . GetConstructors ( ) )
65130 if ( constructor . IsPublic && constructor . GetParameters ( ) . Length == 0 )
66131 {
67- Interlocked . CompareExchange ( ref s_instance , ( IGeometryServices ) Activator . CreateInstance ( type ) , null ) ;
68- return s_instance ;
132+ instance = ( IGeometryServices ) Activator . CreateInstance ( type ) ;
133+ lock ( s_explicitInitLock )
134+ {
135+ if ( ! s_instanceSetDirectly )
136+ {
137+ s_instance = instance ;
138+ }
139+
140+ return s_instance ;
141+ }
69142 }
70143 }
71144 }
0 commit comments