Skip to content
This repository was archived by the owner on Nov 2, 2018. It is now read-only.

Commit 799c134

Browse files
committed
Don't allocate disposable list until used
- Only use it for transient objects - Use _resolvedServices as a lockObject to avoid allocating a lockObj
1 parent 2526128 commit 799c134

File tree

2 files changed

+40
-15
lines changed

2 files changed

+40
-15
lines changed

src/Microsoft.Framework.DependencyInjection/ServiceProvider.cs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ internal class ServiceProvider : IServiceProvider, IDisposable
2121
private readonly ServiceTable _table;
2222

2323
private readonly Dictionary<IService, object> _resolvedServices = new Dictionary<IService, object>();
24-
private readonly List<IDisposable> _disposables = new List<IDisposable>();
24+
private List<IDisposable> _transientDisposables;
2525

2626
private static readonly Func<Type, ServiceProvider, Func<ServiceProvider, object>> _createServiceAccessor = CreateServiceAccessor;
2727

@@ -42,6 +42,9 @@ internal ServiceProvider(ServiceProvider parent)
4242
_table = parent._table;
4343
}
4444

45+
// Reusing _resolvedServices as an implementation detail of the lock
46+
private object SyncObject => _resolvedServices;
47+
4548
/// <summary>
4649
/// Gets the service object of the specified type.
4750
/// </summary>
@@ -138,14 +141,24 @@ internal IServiceCallSite GetResolveCallSite(IService service, ISet<Type> callSi
138141

139142
public void Dispose()
140143
{
141-
lock (_disposables)
144+
lock (SyncObject)
142145
{
143-
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+
// Nuke the other service types
157+
foreach (var item in _resolvedServices.Values)
144158
{
145-
disposable.Dispose();
159+
(item as IDisposable)?.Dispose();
146160
}
147161

148-
_disposables.Clear();
149162
_resolvedServices.Clear();
150163
}
151164
}
@@ -163,9 +176,14 @@ private object CaptureDisposable(object service)
163176
var disposable = service as IDisposable;
164177
if (disposable != null)
165178
{
166-
lock (_disposables)
179+
lock (SyncObject)
167180
{
168-
_disposables.Add(disposable);
181+
if (_transientDisposables == null)
182+
{
183+
_transientDisposables = new List<IDisposable>();
184+
}
185+
186+
_transientDisposables.Add(disposable);
169187
}
170188
}
171189
}
@@ -262,7 +280,7 @@ public virtual object Invoke(ServiceProvider provider)
262280
{
263281
if (!provider._resolvedServices.TryGetValue(_key, out resolved))
264282
{
265-
resolved = provider.CaptureDisposable(_serviceCallSite.Invoke(provider));
283+
resolved = _serviceCallSite.Invoke(provider);
266284
provider._resolvedServices.Add(_key, resolved);
267285
}
268286
}
@@ -287,12 +305,8 @@ public virtual Expression Build(Expression providerExpression)
287305
keyExpression,
288306
resolvedExpression);
289307

290-
var captureDisposableExpression = Expression.Assign(
291-
resolvedExpression,
292-
Expression.Call(
293-
providerExpression,
294-
CaptureDisposableMethodInfo,
295-
_serviceCallSite.Build(providerExpression)));
308+
var assignExpression = Expression.Assign(
309+
resolvedExpression, _serviceCallSite.Build(providerExpression));
296310

297311
var addValueExpression = Expression.Call(
298312
resolvedServicesExpression,
@@ -305,7 +319,7 @@ public virtual Expression Build(Expression providerExpression)
305319
new[] { resolvedExpression },
306320
Expression.IfThen(
307321
Expression.Not(tryGetValueExpression),
308-
Expression.Block(captureDisposableExpression, addValueExpression)),
322+
Expression.Block(assignExpression, addValueExpression)),
309323
resolvedExpression);
310324

311325
return Lock(providerExpression, blockExpression);

test/Microsoft.Framework.DependencyInjection.Tests/ScopingContainerTestBase.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,22 +78,33 @@ public void DisposingScopeDisposesService()
7878
FakeService disposableService;
7979
FakeService transient1;
8080
FakeService transient2;
81+
FakeService singleton;
8182

8283
var scopeFactory = container.GetService<IServiceScopeFactory>();
8384
using (var scope = scopeFactory.CreateScope())
8485
{
8586
disposableService = (FakeService)scope.ServiceProvider.GetService<IFakeScopedService>();
8687
transient1 = (FakeService)scope.ServiceProvider.GetService<IFakeService>();
8788
transient2 = (FakeService)scope.ServiceProvider.GetService<IFakeService>();
89+
singleton = (FakeService)scope.ServiceProvider.GetService<IFakeSingletonService>();
8890

8991
Assert.False(disposableService.Disposed);
9092
Assert.False(transient1.Disposed);
9193
Assert.False(transient2.Disposed);
94+
Assert.False(singleton.Disposed);
9295
}
9396

9497
Assert.True(disposableService.Disposed);
9598
Assert.True(transient1.Disposed);
9699
Assert.True(transient2.Disposed);
100+
Assert.False(singleton.Disposed);
101+
102+
var disposableContainer = container as IDisposable;
103+
if (disposableContainer != null)
104+
{
105+
disposableContainer.Dispose();
106+
Assert.True(singleton.Disposed);
107+
}
97108
}
98109

99110
[Fact]

0 commit comments

Comments
 (0)