Skip to content

Instantly share code, notes, and snippets.

@cameronism
Created May 20, 2015 21:52
Show Gist options
  • Select an option

  • Save cameronism/15058503a3d9b3565e7a to your computer and use it in GitHub Desktop.

Select an option

Save cameronism/15058503a3d9b3565e7a to your computer and use it in GitHub Desktop.
Safe helper to defer setup until needed and teardown until after a delay
/// <summary>
/// Safe helper to defer setup until needed and teardown until after a delay
/// </summary>
/// <remarks>
/// You're on your own if setup or teardown throws or if Dispose is not called on each Acquire result
/// </remarks>
public class DelayedTeardown
{
// -1 indicates that setup needs to be run
private int _count = -1;
private object _lock = new object();
private Timer _timer;
private int _delay;
private Action _teardown;
private Action _setup;
public struct Disposable : IDisposable
{
DelayedTeardown _parent;
/// <summary>Be kind. Don't call this. Get it from Acquire</summary>
internal Disposable(DelayedTeardown instance)
{
_parent = instance;
}
public void Dispose()
{
_parent.Release();
}
}
public DelayedTeardown(TimeSpan delay, Action setup, Action teardown)
{
_delay = (int)delay.TotalMilliseconds;
_teardown = teardown;
_setup = setup;
_timer = new Timer(Teardown);
}
private void Teardown(object state)
{
lock (_lock)
{
if (_count > 0)
return;
_teardown.Invoke();
_count = -1;
}
}
private void Release()
{
lock (_lock)
{
if (--_count == 0)
_timer.Change(_delay, Timeout.Infinite);
}
}
public Disposable Acquire()
{
lock (_lock)
{
if (_count == -1)
{
_setup.Invoke();
_count = 1;
}
else
{
_count++;
}
}
return new Disposable(this);
}
}
var counter = new DelayedTeardown(TimeSpan.FromSeconds(0.5), () => "setup".Dump(), () => "teardown".Dump());
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(state =>
{
using (counter.Acquire())
{
state.Dump();
}
}, i.ToString());
}
// let teardown happen in the middle around half the time (on my machine)
Thread.Sleep(517);
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(state =>
{
using (counter.Acquire())
{
state.Dump();
}
}, i.ToString());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment