//----------------------------------------------------------------------- // // 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; } } }