Permalink
Browse files

- fixed IOC-319 - Concurrency problem when child container is used

  • Loading branch information...
1 parent 60367ab commit eed5084306447cf122f350ae001baef9863e1712 @kkozmic kkozmic committed Nov 19, 2011
View
3 .gitignore
@@ -18,4 +18,5 @@ _ReSharper*
#leftovers from merge
*.orig
*.bak
-*.sln.DotSettings.user
+*.sln.DotSettings.user
+*.DotSettings.user
View
1 Changes.txt
@@ -5,6 +5,7 @@
- implemented IOC-312 - Add shortcut methods to API to register types from given namespace
- fixed IOC-320 - System.ArgumentNullException at Castle.MicroKernel.Burden.Release(IReleasePolicy policy)
+- fixed IOC-319 - Concurrency problem when child container is used
- fixed IOC-315 - ResolveAll should not ignore generic constraint violations on dependencies of resolved component
- fixed IOC-314 - Parsing container configuration uses the current culture
- fixed IOC-311 - OptimizeDependencyResolutionDisposable eats exceptions thrown during installation
View
1 src/Castle.Windsor.Tests/GraphNodeTests.cs
@@ -31,7 +31,6 @@ public void SimpleUsage()
parent.AddDependent(child);
- Assert.AreSame( parent, child.Dependers[0] );
Assert.AreSame( child, parent.Dependents[0] );
}
View
29 src/Castle.Windsor.Tests/Lifestyle/ScopedLifestyleTestCase.cs
@@ -17,8 +17,14 @@ namespace CastleTests.Lifestyle
using System;
using Castle.Core;
+
+ using Castle.MicroKernel;
+
using Castle.MicroKernel.Lifestyle;
using Castle.MicroKernel.Registration;
+
+ using Castle.Windsor;
+
using Castle.Windsor.Tests.ClassComponents;
using CastleTests.Components;
@@ -219,5 +225,28 @@ public void Requiring_scope_within_parent_scope_uses_parent_scope()
}
}
}
+
+ [Test]
+ [Bug("IOC-319")]
+ public void Nested_container_and_scope_used_together_dont_cause_components_to_be_released_twice()
+ {
+ DisposableFoo.ResetDisposedCount();
+ Container.Register(Component.For<IWindsorContainer>().LifeStyle.Scoped()
+ .UsingFactoryMethod(k =>
+ {
+ var container = new WindsorContainer();
+ container.Register(Component.For<DisposableFoo>().LifestyleScoped());
+
+ k.AddChildKernel(container.Kernel);
+ return container;
+ }));
+ using (Container.BeginScope())
+ {
+ var child = Container.Resolve<IWindsorContainer>();
+ child.Resolve<DisposableFoo>();
+ }
+
+ Assert.AreEqual(1, DisposableFoo.DisposedCount);
+ }
}
}
View
3 src/Castle.Windsor.Tests/Lifestyle/ScopedLifetyleCustomScopesTestCase.cs
@@ -28,6 +28,7 @@ public class ScopedLifetyleCustomScopesTestCase : AbstractContainerTestCase
[Test]
public void Can_use_custom_scope_accessor_with_scoped_lifestyle()
{
+ StaticScopeAccessor.ResetScope();
Container.Register(Component.For<A>().LifestyleScoped(scopeAccessorType: typeof(StaticScopeAccessor)));
var a1 = Container.Resolve<A>();
@@ -39,6 +40,7 @@ public void Can_use_custom_scope_accessor_with_scoped_lifestyle()
[Test]
public void Can_use_custom_scope_accessor_with_scoped_lifestyle_generic()
{
+ StaticScopeAccessor.ResetScope();
Container.Register(Component.For<A>().LifestyleScoped<StaticScopeAccessor>());
var a1 = Container.Resolve<A>();
@@ -50,6 +52,7 @@ public void Can_use_custom_scope_accessor_with_scoped_lifestyle_generic()
[Test]
public void Can_use_custom_scope_accessor_with_scoped_lifestyle_multiple()
{
+ StaticScopeAccessor.ResetScope();
Container.Register(Classes.FromThisAssembly()
.Where(c => c.Is<A>())
.LifestyleScoped<StaticScopeAccessor>());
View
7 src/Castle.Windsor.Tests/TestInfrastructure/StaticScopeAccessor.cs
@@ -19,7 +19,7 @@ namespace CastleTests.TestInfrastructure
public class StaticScopeAccessor : IScopeAccessor
{
- private static readonly DefaultLifetimeScope scope = new DefaultLifetimeScope();
+ private static DefaultLifetimeScope scope = new DefaultLifetimeScope();
public void Dispose()
{
@@ -35,5 +35,10 @@ public static DefaultLifetimeScope Scope
{
get { return scope; }
}
+
+ public static void ResetScope()
+ {
+ scope = new DefaultLifetimeScope();
+ }
}
}
View
69 src/Castle.Windsor/Core/Internal/GraphNode.cs
@@ -17,73 +17,27 @@ namespace Castle.Core.Internal
using System;
using System.Collections.Generic;
-#if SILVERLIGHT
- public class GraphNode : IVertex
-#else
[Serializable]
- public class GraphNode : MarshalByRefObject, IVertex
+ public class GraphNode :
+#if !SILVERLIGHT
+ MarshalByRefObject,
#endif
+ IVertex
{
- private List<GraphNode> incoming;
- private List<GraphNode> outgoing;
-
- #region IVertex Members
-
- public IVertex[] Adjacencies
- {
- get { return Dependents; }
- }
- #endregion
+ private List<GraphNode> outgoing;
public void AddDependent(GraphNode node)
{
- Outgoing.Add(node);
- node.Incoming.Add(this);
- }
-
- private List<GraphNode> Incoming
- {
- get
- {
- if (incoming == null)
- {
- incoming = new List<GraphNode>();
- }
-
- return incoming;
- }
- }
-
- private List<GraphNode> Outgoing
- {
- get
+ if (outgoing == null)
{
- if (outgoing == null)
- {
- outgoing = new List<GraphNode>();
- }
- return outgoing;
+ outgoing = new List<GraphNode>();
}
+ outgoing.Add(node);
}
/// <summary>
- /// The nodes that depends on this node
- /// </summary>
- public GraphNode[] Dependers
- {
- get
- {
- if (incoming == null)
- {
- return new GraphNode[0];
- }
- return incoming.ToArray();
- }
- }
-
- /// <summary>
- /// The nodes that this node depends
+ /// The nodes that this node depends on
/// </summary>
public GraphNode[] Dependents
{
@@ -96,5 +50,10 @@ public GraphNode[] Dependents
return outgoing.ToArray();
}
}
+
+ IVertex[] IVertex.Adjacencies
+ {
+ get { return Dependents; }
+ }
}
}
View
10 src/Castle.Windsor/MicroKernel/Burden.cs
@@ -79,7 +79,13 @@ public bool RequiresDecommission
}
/// <summary>
- /// If <c>true</c> requires release by <see cref = "IReleasePolicy" />. If <c>false</c>, the object has a well defined, detectable end of life (web-request end, disposal of the container etc), and will be released externally.
+ /// If
+ /// <c>true</c>
+ /// requires release by
+ /// <see cref="IReleasePolicy" />
+ /// . If
+ /// <c>false</c>
+ /// , the object has a well defined, detectable end of life (web-request end, disposal of the container etc), and will be released externally.
/// </summary>
public bool RequiresPolicyRelease
{
@@ -156,7 +162,7 @@ private bool IsLateBound(IDecommissionConcern arg)
public event BurdenReleaseDelegate Releasing;
public event BurdenReleaseDelegate GraphReleased;
- private enum Decommission
+ private enum Decommission : byte
{
No,
Yes,
View
37 src/Castle.Windsor/MicroKernel/Lifestyle/Scoped/ScopeCache.cs
@@ -17,29 +17,52 @@ namespace Castle.MicroKernel.Lifestyle.Scoped
using System;
using System.Collections.Generic;
using System.Linq;
+ using System.Threading;
using Castle.Core.Internal;
public class ScopeCache : IScopeCache, IDisposable
{
// NOTE: does that need to be thread safe?
- private readonly IDictionary<object, Burden> cache = new Dictionary<object, Burden>();
+ private IDictionary<object, Burden> cache = new Dictionary<object, Burden>();
public Burden this[object id]
{
- set { cache.Add(id, value); }
+ set
+ {
+ try
+ {
+ cache.Add(id, value);
+ }
+ catch (NullReferenceException)
+ {
+ throw new ObjectDisposedException("Scope cache was already disposed. This is most likely a bug in the calling code.");
+ }
+ }
get
{
- Burden burden;
- cache.TryGetValue(id, out burden);
- return burden;
+ try
+ {
+ Burden burden;
+ cache.TryGetValue(id, out burden);
+ return burden;
+ }
+ catch (NullReferenceException)
+ {
+ throw new ObjectDisposedException("Scope cache was already disposed. This is most likely a bug in the calling code.");
+ }
}
}
public void Dispose()
{
- cache.Values.Reverse().ForEach(b => b.Release());
- cache.Clear();
+ var localCache = Interlocked.Exchange(ref cache, null);
+ if (localCache == null)
+ {
+ // that should never happen but Dispose in general is expected to be safe to call so... let's obey the rules
+ return;
+ }
+ localCache.Values.Reverse().ForEach(b => b.Release());
}
}
}
View
18 src/Castle.Windsor/MicroKernel/Lifestyle/ScopedLifestyleManager.cs
@@ -14,12 +14,15 @@
namespace Castle.MicroKernel.Lifestyle
{
+ using System;
+ using System.Threading;
+
using Castle.MicroKernel.Context;
using Castle.MicroKernel.Lifestyle.Scoped;
public class ScopedLifestyleManager : AbstractLifestyleManager
{
- private readonly IScopeAccessor accessor;
+ private IScopeAccessor accessor;
public ScopedLifestyleManager()
: this(new LifetimeScopeAccessor())
@@ -33,12 +36,21 @@ public ScopedLifestyleManager(IScopeAccessor accessor)
public override void Dispose()
{
- accessor.Dispose();
+ var scope = Interlocked.Exchange(ref accessor, null);
+ if (scope != null)
+ {
+ scope.Dispose();
+ }
}
public override object Resolve(CreationContext context, IReleasePolicy releasePolicy)
{
- var scope = accessor.GetScope(context);
+ var localScope = accessor;
+ if(localScope == null)
+ {
+ throw new ObjectDisposedException("Scope was already disposed. This is most likely a bug in the calling code.");
+ }
+ var scope = localScope.GetScope(context);
var burden = scope.GetCachedInstance(Model, afterCreated =>
{
var localBurden = base.CreateInstance(context, trackedExternally: true);

0 comments on commit eed5084

Please sign in to comment.