/// <summary> /// Disposes the resources held by the image. /// </summary> /// <param name="disposing"><c>True</c> to release both managed and unmanaged resources; <c>False</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (!this.disposedValue) { lock (this) { if (disposing) { // If we have a native buffer wrapper, dispose it since we // are a wrapper for it's IMemoryOwner<T> interface this.nativeBufferWrapper?.Dispose(); Allocator.Singleton.UnregisterForDisposal(this); this.handle.Close(); this.handle = null; } // Return the buffer during finalization to ensure that the pool // can clean up its references. If the Image was garbage collected, the pool // will continue to hold a reference. if (this.managedBufferCache != null) { unsafe { Allocator.Singleton.ReturnBufferCache((IntPtr)this.GetUnsafeBuffer()); } this.managedBufferCache = null; } this.disposedValue = true; } } }
/// <summary> /// Initializes a new instance of the <see cref="Image"/> class. /// </summary> /// <param name="handle">Handle to initialize the image from.</param> /// <remarks>The handle will be owned by the new image.</remarks> internal Image(NativeMethods.k4a_image_t handle) { // Hook the native allocator and register this object. // .Dispose() will be called on this object when the allocator is shut down. Allocator.Singleton.RegisterForDisposal(this); this.handle = handle; }
/// <summary> /// Retrieves a native image handle from the native API. /// </summary> /// <param name="nativeMethod">Native method to retrieve the image.</param> /// <param name="cachedImage">A cached instance of the Image that we return to callers. (Callers may dispose this image, although they shouldn't).</param> /// <remarks> /// If this is the first time calling the property, we construct a new wrapper. /// If the handle is for an Image we have already constructed a wrapper for, we return the existing wrapper. /// If the handle is for a different Image, we construct a new wrapper and dispose the old one. /// If existing wrapper has been disposed, we throw an exception. /// </remarks> private void UpdateImageWrapperAndDisposePrevious( Func <NativeMethods.k4a_capture_t, NativeMethods.k4a_image_t> nativeMethod, ref Image cachedImage) { // Lock must be held to ensure the Image in cache is not replaced while we are inspecting it // It is still possible for that Image to be accessed or Disposed while this lock is held lock (this) { if (this.disposedValue) { throw new ObjectDisposedException(nameof(Capture)); } // Get the image object from the native SDK // We now own a reference on this image and must eventually release it. NativeMethods.k4a_image_t nativeImageHandle = nativeMethod(this.handle); // This try block ensures that we close image (and release our reference) if we fail // to create a new Image. try { // If this Capture previously had an image if (cachedImage != null) { // Get the native pointer IntPtr imageHandleValue = nativeImageHandle.DangerousGetHandle(); // Get the handle value from the cached image. If it has been disposed, this // will throw an exception, which will be passed to the caller. // Take an extra reference on the cachedImage to ensure it doesn't get disposed // after getting the IntPtr from the handle. using (Image reference = cachedImage.Reference()) { if (reference.DangerousGetHandle().DangerousGetHandle() != imageHandleValue) { // The image has changed, invalidate the current image and construct new wrappers cachedImage.Dispose(); cachedImage = null; } } } if (cachedImage == null && !nativeImageHandle.IsInvalid) { // Construct a new wrapper and return it // The native function may have returned #pragma warning disable CA2000 // Dispose objects before losing scope cachedImage = new Image(nativeImageHandle); #pragma warning restore CA2000 // Dispose objects before losing scope // Since we have wrapped image, it is now owned by Image and we should no longer close it nativeImageHandle = null; } } finally { // Ensure the native handle is closed if we have a failure creating the Image object if (nativeImageHandle != null) { nativeImageHandle.Close(); } } } }
// This function retrieves a native image handle from the native API. // // If this is the first time calling the property, we construct a new wrapper // If the handle is for an Image we have already constructed a wrapper for, we return the existing wrapper // If the handle is for a different Image, or if the wrapper has already been disposed, we construct a new wrapper and dispose the old one private Image GetImageWrapperAndDisposePrevious(Func <NativeMethods.k4a_capture_t, NativeMethods.k4a_image_t> nativeMethod, ref Image cache) { // Lock must be held to ensure the Image in cache is not replaced while we are inspecting it // It is still possible for that Image to be accessed or Disposed while this lock is held lock (this) { if (this.disposedValue) { throw new ObjectDisposedException(nameof(Capture)); } NativeMethods.k4a_image_t image = nativeMethod(this.handle); // This try block ensures that we close image try { if (cache != null) { IntPtr imageHandle = image.DangerousGetHandle(); // Only attempt to access cache in this try block try { // If cache was disposed before we called the native accessor (by the caller or another thread), // the handle could have been reused and the values would incorrectly match. However, cache.DangerousGetHandle() // will throw an exception, and we will correctly construct a new image wrapper. if (cache.DangerousGetHandle().DangerousGetHandle() != imageHandle) { // The image has changed, invalidate the current image and construct a new wrapper cache.Dispose(); cache = null; } } catch (ObjectDisposedException) { // If cache has been disposed by the caller or another thread we will discard // it and construct a new wrapper cache = null; } } if (cache == null && !image.IsInvalid) { // Construct a new wrapper and return it // The native function may have returned cache = new Image(image); // Since we have wrapped image, it is now owned by the UnsafeImage object and we should no longer close it image = null; } } finally { // Ensure the native handle is closed if we have a failure creating the Image object if (image != null) { image.Close(); } } return(cache); } }