Skip to content

Commit

Permalink
- Burden.RequiresDecommission is now settable, and if set to false Li…
Browse files Browse the repository at this point in the history
…fecycledComponentsReleasePolicy will not try to Release the burden, and CreationContext will not add it as child to its potential parents. This enables us:

- ILifestyleManager.Release is now called not whenever someone calls kernel.ReleaseComponent or some external signal comes in. It has now more stricter semantics - if it's called, it means that the component really should be released, so { return false; } is rarely a good implementation idea now.
  • Loading branch information
kkozmic committed Dec 11, 2010
1 parent 5eed741 commit 1145acb
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 60 deletions.
2 changes: 2 additions & 0 deletions src/Castle.Windsor.Tests/Castle.Windsor.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@
<Compile Include="ByRefDependenciesTestCase.cs" />
<Compile Include="ClassComponents\CBA.cs" />
<Compile Include="ClassComponents\HasByRefCtorArgument.cs" />
<Compile Include="ClassComponents\HasCtorDependency.cs" />
<Compile Include="Components\ArrayRefDepAsConstructor.cs" />
<Compile Include="Components\CalculatorServiceWithStandartInterceptorTyped.cs" />
<Compile Include="Components\CalculatorServiceWithFooInterceptorNamed.cs" />
Expand Down Expand Up @@ -641,6 +642,7 @@
<Compile Include="Facilities\Startable\Components\StartableChainWithGenerics.cs" />
<Compile Include="Facilities\Startable\Components\StartableComponentWithCustomDependencies.cs" />
<Compile Include="Registration\DynamicParametersTestCase.cs" />
<Compile Include="Windsor.Tests\LifecycledComponentsReleasePolicyComponentTrackingTestCase.cs" />
<Compile Include="Windsor.Tests\GenericVarianceTestCase.cs" />
<Compile Include="Windsor.Tests\LitestylePerThreadTestCase.cs" />
<Compile Include="Windsor.Tests\MultiServiceComponentsTestCase.cs" />
Expand Down
28 changes: 28 additions & 0 deletions src/Castle.Windsor.Tests/ClassComponents/HasCtorDependency.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2004-2010 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.Windsor.Tests.ClassComponents
{
using Castle.Windsor.Tests.Components;

public class HasCtorDependency
{
public HasCtorDependency(ISimpleService dependency)
{
Dependency = dependency;
}

public ISimpleService Dependency { get; private set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2004-2010 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.Windsor.Tests
{
using Castle.MicroKernel.Registration;
using Castle.Windsor.Tests.ClassComponents;
using Castle.Windsor.Tests.Components;

using NUnit.Framework;

public class LifecycledComponentsReleasePolicyComponentTrackingTestCase : AbstractContainerTestFixture
{
[Test]
[Ignore("We don't need release policy to track this guy. If we don't though, we can't check if it's tracked by its lifestyle manager...")]
public void Disposable_singleton_as_dependency_of_non_disposable_transient_is_not_tracked()
{
SimpleServiceDisposable.DisposedCount = 0;
Container.Register(Component.For<HasCtorDependency>().LifeStyle.Transient,
Component.For<ISimpleService>().ImplementedBy<SimpleServiceDisposable>());

var root = Container.Resolve<HasCtorDependency>();

Assert.IsFalse(Kernel.ReleasePolicy.HasTrack(root.Dependency));
}

[Test]
public void Disposable_singleton_as_dependency_of_non_disposable_transient_is_decommissionsed_with_container()
{
SimpleServiceDisposable.DisposedCount = 0;
Container.Register(Component.For<HasCtorDependency>().LifeStyle.Transient,
Component.For<ISimpleService>().ImplementedBy<SimpleServiceDisposable>());

Container.Resolve<HasCtorDependency>();
CleanUp();

Assert.AreEqual(1, SimpleServiceDisposable.DisposedCount);
;
}
}
}
13 changes: 4 additions & 9 deletions src/Castle.Windsor/MicroKernel/Burden.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.MicroKernel
{
using System;
Expand All @@ -27,7 +26,6 @@ public class Burden

private object instance;
private List<Burden> items;
private bool requiresDecommission;

public Burden(IHandler handler)
{
Expand All @@ -44,10 +42,7 @@ public ComponentModel Model
get { return handler.ComponentModel; }
}

public bool RequiresDecommission
{
get { return requiresDecommission; }
}
public bool RequiresDecommission { get; set; }

public void AddChild(Burden child)
{
Expand All @@ -59,7 +54,7 @@ public void AddChild(Burden child)

if (child.RequiresDecommission)
{
requiresDecommission = true;
RequiresDecommission = true;
}
}

Expand Down Expand Up @@ -87,9 +82,9 @@ public void SetRootInstance(object instance, bool hasDecomission)
}

this.instance = instance;
requiresDecommission = requiresDecommission || hasDecomission;
RequiresDecommission = RequiresDecommission || hasDecomission;
}

public event BurdenReleased Released = delegate{};
public event BurdenReleased Released = delegate { };
}
}
5 changes: 4 additions & 1 deletion src/Castle.Windsor/MicroKernel/Context/CreationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,10 @@ private void ExitResolutionContext(Burden burden)
}

resolutionStack.Pop();

if(burden.RequiresDecommission == false)
{
return;
}
if (resolutionStack.Count != 0)
{
resolutionStack.Peek().Burden.AddChild(burden);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2004-2009 Castle Project - http://www.castleproject.org/
// Copyright 2004-2010 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -21,24 +21,36 @@ namespace Castle.MicroKernel.Lifestyle
using Castle.MicroKernel.Registration;

/// <summary>
/// Implements a Poolable Lifestyle Manager.
/// Manages a pool of objects.
/// </summary>
[Serializable]
public class PoolableLifestyleManager : AbstractLifestyleManager
{
private IPool pool;
private readonly int initialSize;
private readonly int maxSize;
private IPool pool;

public PoolableLifestyleManager(int initialSize, int maxSize)
{
this.initialSize = initialSize;
this.maxSize = maxSize;
}

public override void Track(Burden burden, IReleasePolicy releasePolicy)
public override void Dispose()
{
releasePolicy.Track(burden.Instance, burden);
if (pool != null)
{
pool.Dispose();
}
}

public override bool Release(object instance)
{
if (pool != null)
{
return pool.Release(instance);
}
return false;
}

public override object Resolve(CreationContext context)
Expand All @@ -57,21 +69,10 @@ public override object Resolve(CreationContext context)
return pool.Request(context);
}

public override bool Release(object instance)
{
if (pool != null)
{
return pool.Release(instance);
}
return false;
}

public override void Dispose()
public override void Track(Burden burden, IReleasePolicy releasePolicy)
{
if (pool != null)
{
pool.Dispose();
}
burden.RequiresDecommission = true;
releasePolicy.Track(burden.Instance, burden);
}

protected IPool CreatePool(int initialSize, int maxSize)
Expand All @@ -88,4 +89,4 @@ protected IPool CreatePool(int initialSize, int maxSize)
return factory.Create(initialSize, maxSize, ComponentActivator);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
namespace Castle.MicroKernel.Lifestyle
{
using System;
using System.Threading;

using Castle.MicroKernel.Context;

Expand All @@ -25,22 +26,29 @@ namespace Castle.MicroKernel.Lifestyle
public class SingletonLifestyleManager : AbstractLifestyleManager
{
private Object instance;
private Burden burden;

public override void Dispose()
{
var localInstance = instance;
if (localInstance != null)
{
instance = null;

base.Release(localInstance);
burden.Release();
}
}

public override bool Release(object instance)
public override void Track(Burden burden, IReleasePolicy releasePolicy)
{
// Do nothing
return false;
var track = burden.RequiresDecommission;
burden.RequiresDecommission = false;
if(Interlocked.CompareExchange(ref this.burden, burden, null) == null)
{
if(track)
{
releasePolicy.Track(burden.Instance, burden);
}
}
}

public override object Resolve(CreationContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.MicroKernel.Releasers
{
using System;
Expand All @@ -31,36 +30,31 @@ public class LifecycledComponentsReleasePolicy : IReleasePolicy

private readonly Lock @lock = Lock.Create();

public virtual void Track(object instance, Burden burden)
public void Dispose()
{
using(@lock.ForWriting())
using (@lock.ForWriting())
{
var oldCount = instance2Burden.Count;
instance2Burden[instance] = burden;
if(oldCount < instance2Burden.Count)
var burdens = instance2Burden.ToArray();
instance2Burden.Clear();
// NOTE: This is relying on a undocumented behavior that order of items when enumerating Dictionary<> will be oldest --> latest
foreach (var burden in burdens.Reverse())
{
burden.Released += OnInstanceReleased;
if(burden.Value.RequiresDecommission)
{
burden.Value.Release();
}
}
}
}

private void OnInstanceReleased(Burden burden)
{
using (@lock.ForWriting())
{
instance2Burden.Remove(burden.Instance);
burden.Released -= OnInstanceReleased;
}
}

public bool HasTrack(object instance)
{
if (instance == null)
{
return false;
}

using(@lock.ForReading())
using (@lock.ForReading())
{
return instance2Burden.ContainsKey(instance);
}
Expand All @@ -77,11 +71,19 @@ public void Release(object instance)
{
Burden burden;
if (!instance2Burden.TryGetValue(instance, out burden))
{
return;
}

locker.Upgrade();
if (!instance2Burden.TryGetValue(instance, out burden))
{
return;
}
if (burden.RequiresDecommission == false)
{
return;
}

// we remove first, then release so that if we recursively end up here again, the first TryGetValue call breaks the circuit
var existed = instance2Burden.Remove(instance);
Expand All @@ -99,18 +101,26 @@ public void Release(object instance)
}
}

public void Dispose()
public virtual void Track(object instance, Burden burden)
{
using(@lock.ForWriting())
using (@lock.ForWriting())
{
var burdens = instance2Burden.ToArray();
instance2Burden.Clear();
// NOTE: This is relying on a undocumented behavior that order of items when enumerating Dictionary<> will be oldest --> latest
foreach (var burden in burdens.Reverse())
var oldCount = instance2Burden.Count;
instance2Burden[instance] = burden;
if (oldCount < instance2Burden.Count)
{
burden.Value.Release();
burden.Released += OnInstanceReleased;
}
}
}

private void OnInstanceReleased(Burden burden)
{
using (@lock.ForWriting())
{
instance2Burden.Remove(burden.Instance);
burden.Released -= OnInstanceReleased;
}
}
}
}
}

0 comments on commit 1145acb

Please sign in to comment.