//-----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.
//
//-----------------------------------------------------------------------
namespace Microsoft.Isam.Esent.Interop
{
using System.Collections.Generic;
///
///
/// A collection of wrapped callbacks. This is used when the wrapped callback
/// can be garbage collected. The wrappers should be removed from the collection
/// when the callback is collected.
///
///
/// Removing the wrappers can lead to crashes. In this case we trust
/// the client code to keep its callback alive until ESENT doesn't need it any
/// more. Once the wrapped callback is garbage collected we allow the wrapper
/// to be collected as well. If ESENT subsequently uses the callback there will
/// be a crash.
///
///
/// The reason this is hard to deal with is that the lifetime of a JET_CALLBACK
/// isn't very clear. Table callbacks can stick around until the table meta-data
/// is purged, while a JetDefragment callback can be used until defrag ends. On
/// the other hand, keeping the callback wrapper alive indefinitely would lead
/// to unbounded memory use.
///
///
internal sealed class CallbackWrappers
{
///
/// Used to synchronize access to this object.
///
private readonly object lockObject = new object();
///
/// A list of the wrapped callbacks.
///
private readonly List callbackWrappers = new List();
///
/// Wrap a callback and returns its wrapper. If the callback is
/// already wrapped then the existing wrapper is returned.
///
/// The callback to add.
/// The callback wrapper for the callback.
public JetCallbackWrapper Add(JET_CALLBACK callback)
{
lock (this.lockObject)
{
JetCallbackWrapper wrapper;
if (!this.TryFindWrapperFor(callback, out wrapper))
{
wrapper = new JetCallbackWrapper(callback);
this.callbackWrappers.Add(wrapper);
}
return wrapper;
}
}
///
/// Go through the collection of callback wrappers and remove any dead callbacks.
///
public void Collect()
{
lock (this.lockObject)
{
this.callbackWrappers.RemoveAll(wrapper => !wrapper.IsAlive);
}
}
///
/// Look in the list of callback wrappers to see if there is already an entry for
/// this callback.
///
/// The callback to look for.
/// Returns the wrapper, if found.
/// True if a wrapper was found, false otherwise.
private bool TryFindWrapperFor(JET_CALLBACK callback, out JetCallbackWrapper wrapper)
{
foreach (JetCallbackWrapper w in this.callbackWrappers)
{
if (w.IsWrapping(callback))
{
wrapper = w;
return true;
}
}
wrapper = null;
return false;
}
}
}