@@ -17,14 +17,13 @@ namespace Microsoft.Framework.DependencyInjection
17
17
/// </summary>
18
18
internal class ServiceProvider : IServiceProvider , IDisposable
19
19
{
20
- private readonly object _sync = new object ( ) ;
21
-
22
- private readonly Func < Type , Func < ServiceProvider , object > > _createServiceAccessor ;
23
20
private readonly ServiceProvider _root ;
24
21
private readonly ServiceTable _table ;
25
22
26
23
private readonly Dictionary < IService , object > _resolvedServices = new Dictionary < IService , object > ( ) ;
27
- private ConcurrentBag < IDisposable > _disposables = new ConcurrentBag < IDisposable > ( ) ;
24
+ private List < IDisposable > _transientDisposables ;
25
+
26
+ private static readonly Func < Type , ServiceProvider , Func < ServiceProvider , object > > _createServiceAccessor = CreateServiceAccessor ;
28
27
29
28
public ServiceProvider ( IEnumerable < ServiceDescriptor > serviceDescriptors )
30
29
{
@@ -34,38 +33,35 @@ public ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors)
34
33
_table . Add ( typeof ( IServiceProvider ) , new ServiceProviderService ( ) ) ;
35
34
_table . Add ( typeof ( IServiceScopeFactory ) , new ServiceScopeService ( ) ) ;
36
35
_table . Add ( typeof ( IEnumerable < > ) , new OpenIEnumerableService ( _table ) ) ;
37
-
38
- // Caching to avoid allocating a Func<,> object per call to GetService
39
- _createServiceAccessor = CreateServiceAccessor ;
40
36
}
41
37
42
38
// This constructor is called exclusively to create a child scope from the parent
43
39
internal ServiceProvider ( ServiceProvider parent )
44
40
{
45
41
_root = parent . _root ;
46
42
_table = parent . _table ;
47
-
48
- // Caching to avoid allocating a Func<,> object per call to GetService
49
- _createServiceAccessor = CreateServiceAccessor ;
50
43
}
51
44
45
+ // Reusing _resolvedServices as an implementation detail of the lock
46
+ private object SyncObject => _resolvedServices ;
47
+
52
48
/// <summary>
53
49
/// Gets the service object of the specified type.
54
50
/// </summary>
55
51
/// <param name="serviceType"></param>
56
52
/// <returns></returns>
57
53
public object GetService ( Type serviceType )
58
54
{
59
- var realizedService = _table . RealizedServices . GetOrAdd ( serviceType , _createServiceAccessor ) ;
55
+ var realizedService = _table . RealizedServices . GetOrAdd ( serviceType , _createServiceAccessor , this ) ;
60
56
return realizedService . Invoke ( this ) ;
61
57
}
62
58
63
- private Func < ServiceProvider , object > CreateServiceAccessor ( Type serviceType )
59
+ private static Func < ServiceProvider , object > CreateServiceAccessor ( Type serviceType , ServiceProvider serviceProvider )
64
60
{
65
- var callSite = GetServiceCallSite ( serviceType , new HashSet < Type > ( ) ) ;
61
+ var callSite = serviceProvider . GetServiceCallSite ( serviceType , new HashSet < Type > ( ) ) ;
66
62
if ( callSite != null )
67
63
{
68
- return RealizeService ( _table , serviceType , callSite ) ;
64
+ return RealizeService ( serviceProvider . _table , serviceType , callSite ) ;
69
65
}
70
66
71
67
return _ => null ;
@@ -145,14 +141,27 @@ internal IServiceCallSite GetResolveCallSite(IService service, ISet<Type> callSi
145
141
146
142
public void Dispose ( )
147
143
{
148
- var disposables = Interlocked . Exchange ( ref _disposables , null ) ;
149
-
150
- if ( disposables != null )
144
+ lock ( SyncObject )
151
145
{
152
- foreach ( var disposable in disposables )
146
+ if ( _transientDisposables != null )
147
+ {
148
+ foreach ( var disposable in _transientDisposables )
149
+ {
150
+ disposable . Dispose ( ) ;
151
+ }
152
+
153
+ _transientDisposables . Clear ( ) ;
154
+ }
155
+
156
+ // PERF: We've enumerating the dictionary so that we don't allocate to enumerate.
157
+ // .Values allocates a KeyCollection on the heap, enumerating the dictionary allocates
158
+ // a struct enumerator
159
+ foreach ( var entry in _resolvedServices )
153
160
{
154
- disposable . Dispose ( ) ;
161
+ ( entry . Value as IDisposable ) ? . Dispose ( ) ;
155
162
}
163
+
164
+ _resolvedServices . Clear ( ) ;
156
165
}
157
166
}
158
167
@@ -163,7 +172,15 @@ private object CaptureDisposable(object service)
163
172
var disposable = service as IDisposable ;
164
173
if ( disposable != null )
165
174
{
166
- _disposables . Add ( disposable ) ;
175
+ lock ( SyncObject )
176
+ {
177
+ if ( _transientDisposables == null )
178
+ {
179
+ _transientDisposables = new List < IDisposable > ( ) ;
180
+ }
181
+
182
+ _transientDisposables . Add ( disposable ) ;
183
+ }
167
184
}
168
185
}
169
186
return service ;
@@ -255,11 +272,11 @@ public ScopedCallSite(IService key, IServiceCallSite serviceCallSite)
255
272
public virtual object Invoke ( ServiceProvider provider )
256
273
{
257
274
object resolved ;
258
- lock ( provider . _sync )
275
+ lock ( provider . _resolvedServices )
259
276
{
260
277
if ( ! provider . _resolvedServices . TryGetValue ( _key , out resolved ) )
261
278
{
262
- resolved = provider . CaptureDisposable ( _serviceCallSite . Invoke ( provider ) ) ;
279
+ resolved = _serviceCallSite . Invoke ( provider ) ;
263
280
provider . _resolvedServices . Add ( _key , resolved ) ;
264
281
}
265
282
}
@@ -284,12 +301,8 @@ public virtual Expression Build(Expression providerExpression)
284
301
keyExpression ,
285
302
resolvedExpression ) ;
286
303
287
- var captureDisposableExpression = Expression . Assign (
288
- resolvedExpression ,
289
- Expression . Call (
290
- providerExpression ,
291
- CaptureDisposableMethodInfo ,
292
- _serviceCallSite . Build ( providerExpression ) ) ) ;
304
+ var assignExpression = Expression . Assign (
305
+ resolvedExpression , _serviceCallSite . Build ( providerExpression ) ) ;
293
306
294
307
var addValueExpression = Expression . Call (
295
308
resolvedServicesExpression ,
@@ -302,7 +315,7 @@ public virtual Expression Build(Expression providerExpression)
302
315
new [ ] { resolvedExpression } ,
303
316
Expression . IfThen (
304
317
Expression . Not ( tryGetValueExpression ) ,
305
- Expression . Block ( captureDisposableExpression , addValueExpression ) ) ,
318
+ Expression . Block ( assignExpression , addValueExpression ) ) ,
306
319
resolvedExpression ) ;
307
320
308
321
return Lock ( providerExpression , blockExpression ) ;
@@ -312,7 +325,7 @@ private static Expression Lock(Expression providerExpression, Expression body)
312
325
{
313
326
// The C# compiler would copy the lock object to guard against mutation.
314
327
// We don't, since we know the lock object is readonly.
315
- var syncField = Expression . Field ( providerExpression , "_sync " ) ;
328
+ var syncField = Expression . Field ( providerExpression , "_resolvedServices " ) ;
316
329
var lockWasTaken = Expression . Variable ( typeof ( bool ) , "lockWasTaken" ) ;
317
330
318
331
var monitorEnter = Expression . Call ( MonitorEnterMethodInfo , syncField , lockWasTaken ) ;
0 commit comments