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 could lead to unbounded memory use.

        public void VerifyCallbackWrappersCollectsUnusedWrappers()
        {
            DateTime endTime = DateTime.Now + TimeSpan.FromSeconds(19);

            var callbackWrappers = new CallbackWrappers();

            RunFullGarbageCollection();
            long memoryAtStart = GC.GetTotalMemory(true);

            while (DateTime.Now < endTime)
            {
                for (int i = 0; i < 128; ++i)
                {
                    CreateCallbackWrapper(callbackWrappers);
                }

                RunFullGarbageCollection();
                callbackWrappers.Collect();
                RunFullGarbageCollection();
            }

            RunFullGarbageCollection();
            long memoryAtEnd = GC.GetTotalMemory(true);
            GC.KeepAlive(callbackWrappers);

            long memory = memoryAtEnd - memoryAtStart;
            Console.WriteLine("{0:N0} bytes used", memory);
            Assert.IsTrue(memory < 1024 * 1024, "Test used too much memory. JetCallbackWrapper objects weren't collected.");
        }
        public void VerifyAddingSameCallbackReturnsSameWrapper()
        {
            JET_CALLBACK callback = CreateCallback();

            var callbackWrappers = new CallbackWrappers();
            var wrapper = callbackWrappers.Add(callback);

            Assert.AreEqual(wrapper, callbackWrappers.Add(callback));
        }
        public void VerifyAddingCallbackReturnsWrapper()
        {
            JET_CALLBACK callback = CreateCallback();

            var callbackWrappers = new CallbackWrappers();
            var wrapper = callbackWrappers.Add(callback);

            Assert.IsNotNull(callbackWrappers.Add(callback));
        }
 /// <summary>
 /// Create a new wrapped callback.
 /// </summary>
 /// <param name="callbackWrappers">The CallbackWrappers to add the callback to.</param>
 private static void CreateCallbackWrapper(CallbackWrappers callbackWrappers)
 {
     callbackWrappers.Add(CreateCallback());
 }
        public void VerifyWrapperIsCollectedWhenCallbackIsCollected()
        {
            JET_CALLBACK callback = CreateCallback();
            var callbackRef = new WeakReference(callback);

            var callbackWrappers = new CallbackWrappers();
            var wrapperRef = new WeakReference(callbackWrappers.Add(callback));

            callback = null;

            RunFullGarbageCollection();
            callbackWrappers.Collect();
            RunFullGarbageCollection();

            Assert.IsFalse(callbackRef.IsAlive);
            Assert.IsFalse(wrapperRef.IsAlive);

            // Avoid premature collection of this objects
            GC.KeepAlive(callbackWrappers);
        }
Esempio n. 6
0
        public void VerifyNativeCallbackIsNotGarbageCollected()
        {
            JET_CALLBACK callback = CreateCallback();

            var callbackWrappers = new CallbackWrappers();
            JetCallbackWrapper wrapper = callbackWrappers.Add(callback);
            WeakReference weakRef = new WeakReference(wrapper.NativeCallback);
            RunFullGarbageCollection();
            Assert.IsTrue(weakRef.IsAlive);
            GC.KeepAlive(wrapper);
        }