From 2b460eaecad1dc3faabb3bc65aa9553670052637 Mon Sep 17 00:00:00 2001 From: Dmitri Smirnov Date: Thu, 27 Aug 2020 09:17:42 -0700 Subject: [PATCH] Revise IDisposable implementation in C# interfaces (#4915) Revise IDisposable implementation in C# interfaces --- .../DisposableNamedOnnxValue.cs | 9 +- .../FixedBufferOnnxValue.cs | 7 ++ .../InferenceSession.cs | 24 +++- .../NativeApiStatus.cs | 18 ++- .../NativeOnnxTensorMemory.cs | 54 +++----- .../Microsoft.ML.OnnxRuntime/OnnxRuntime.cs | 24 +--- .../Microsoft.ML.OnnxRuntime/OrtAllocator.cs | 117 ++++++++---------- .../Microsoft.ML.OnnxRuntime/OrtIoBinding.cs | 43 +++---- .../src/Microsoft.ML.OnnxRuntime/OrtValue.cs | 46 +++---- .../Microsoft.ML.OnnxRuntime/RunOptions.cs | 42 +++---- .../SessionOptions.cs | 85 ++++++------- 11 files changed, 211 insertions(+), 258 deletions(-) diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/DisposableNamedOnnxValue.cs b/csharp/src/Microsoft.ML.OnnxRuntime/DisposableNamedOnnxValue.cs index 859e543e7b..18b6977394 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/DisposableNamedOnnxValue.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/DisposableNamedOnnxValue.cs @@ -55,6 +55,7 @@ namespace Microsoft.ML.OnnxRuntime private NativeMemoryHandler _nativeMemoryManager; private TensorElementType _elementType; private OnnxValueType _onnxValueType; + private bool _disposed = false; private DisposableNamedOnnxValue(string name, Object value, OnnxValueType onnxValueType, TensorElementType elementType, NativeMemoryHandler nativeMemoryManager) : base(name, value) @@ -264,15 +265,21 @@ namespace Microsoft.ML.OnnxRuntime protected virtual void Dispose(bool disposing) { + if(_disposed) + { + return; + } + + // dispose managed state (managed objects). if (disposing) { - // dispose managed state (managed objects). if (_nativeMemoryManager != null) { _nativeMemoryManager.Dispose(); _nativeMemoryManager = null; } } + _disposed = true; } public void Dispose() diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/FixedBufferOnnxValue.cs b/csharp/src/Microsoft.ML.OnnxRuntime/FixedBufferOnnxValue.cs index 8ef1fc119f..4444ed85e6 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/FixedBufferOnnxValue.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/FixedBufferOnnxValue.cs @@ -9,6 +9,7 @@ namespace Microsoft.ML.OnnxRuntime /// public class FixedBufferOnnxValue : IDisposable { + private bool _disposed = false; internal MemoryHandle PinnedMemory { get; private set; } internal OrtValue Value { get; private set; } internal OnnxValueType OnnxValueType { get; private set; } @@ -46,11 +47,17 @@ namespace Microsoft.ML.OnnxRuntime protected virtual void Dispose(bool disposing) { + if(_disposed) + { + return; + } + if (disposing) { Value.Dispose(); PinnedMemory.Dispose(); } + _disposed = true; } public void Dispose() diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/InferenceSession.cs b/csharp/src/Microsoft.ML.OnnxRuntime/InferenceSession.cs index a7b50003ef..a54e6c0f51 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/InferenceSession.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/InferenceSession.cs @@ -21,6 +21,7 @@ namespace Microsoft.ML.OnnxRuntime private SessionOptions _builtInSessionOptions = null; private RunOptions _builtInRunOptions = null; private ModelMetadata _modelMetadata = null; + private bool _disposed = false; #region Public API @@ -902,27 +903,43 @@ namespace Microsoft.ML.OnnxRuntime #endregion - #region IDisposable/ no finalizers needed + #region IDisposable + + /// + /// Finalizer. to cleanup session in case it runs + /// and the user forgets to Dispose() of the session + /// + ~InferenceSession() + { + Dispose(false); + } public void Dispose() { - GC.SuppressFinalize(this); Dispose(true); + GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { + if(_disposed) + { + return; + } + if (disposing) { // cleanup managed resources if (_builtInSessionOptions != null) { _builtInSessionOptions.Dispose(); + _builtInSessionOptions = null; } if (_builtInRunOptions != null) { _builtInRunOptions.Dispose(); + _builtInRunOptions = null; } } @@ -930,11 +947,12 @@ namespace Microsoft.ML.OnnxRuntime if (_nativeHandle != IntPtr.Zero) { NativeMethods.OrtReleaseSession(_nativeHandle); + _nativeHandle = IntPtr.Zero; } + _disposed = true; } #endregion - } diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/NativeApiStatus.cs b/csharp/src/Microsoft.ML.OnnxRuntime/NativeApiStatus.cs index dabe0b4d68..bbd961309a 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/NativeApiStatus.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/NativeApiStatus.cs @@ -10,9 +10,9 @@ namespace Microsoft.ML.OnnxRuntime { private static string GetErrorMessage(IntPtr /*(ONNXStatus*)*/status) { + // nativeString belongs to status, no need for separate release IntPtr nativeString = NativeMethods.OrtGetErrorMessage(status); - string str = Marshal.PtrToStringAnsi(nativeString); //assumes charset = ANSI - return str; + return NativeOnnxValueHelper.StringFromNativeUtf8(nativeString); } /// @@ -25,10 +25,16 @@ namespace Microsoft.ML.OnnxRuntime { if (nativeStatus != IntPtr.Zero) { - ErrorCode statusCode = NativeMethods.OrtGetErrorCode(nativeStatus); - string errorMessage = GetErrorMessage(nativeStatus); - NativeMethods.OrtReleaseStatus(nativeStatus); - throw new OnnxRuntimeException(statusCode, errorMessage); + try + { + ErrorCode statusCode = NativeMethods.OrtGetErrorCode(nativeStatus); + string errorMessage = GetErrorMessage(nativeStatus); + throw new OnnxRuntimeException(statusCode, errorMessage); + } + finally + { + NativeMethods.OrtReleaseStatus(nativeStatus); + } } } } diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/NativeOnnxTensorMemory.cs b/csharp/src/Microsoft.ML.OnnxRuntime/NativeOnnxTensorMemory.cs index 4c1958485f..b62ac98a2b 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/NativeOnnxTensorMemory.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/NativeOnnxTensorMemory.cs @@ -21,7 +21,6 @@ namespace Microsoft.ML.OnnxRuntime internal class NativeOnnxTensorMemory : MemoryManager, NativeMemoryHandler { private bool _disposed; - private int _referenceCount; private IntPtr _onnxValueHandle; // pointer to onnxvalue object in native private IntPtr _dataBufferPointer; // pointer to mutable tensor data in native memory private string[] _dataBufferAsString; // string tensor values copied into managed memory @@ -35,6 +34,7 @@ namespace Microsoft.ML.OnnxRuntime Type type = null; int width = 0; _onnxValueHandle = onnxValueHandle; + _disposed = false; IntPtr typeAndShape = IntPtr.Zero; NativeApiStatus.VerifySuccess(NativeMethods.OrtGetTensorTypeAndShape(onnxValueHandle, out typeAndShape)); try @@ -119,16 +119,8 @@ namespace Microsoft.ML.OnnxRuntime public IntPtr Handle { get { return _onnxValueHandle; } } - public void Dispose() - { - GC.SuppressFinalize(this); - Dispose(true); - } - public bool IsDisposed => _disposed; - protected bool IsRetained => _referenceCount > 0; - public int[] Dimensions => _dimensions; public int Rank => _dimensions.Length; @@ -172,53 +164,35 @@ namespace Microsoft.ML.OnnxRuntime { throw new ArgumentOutOfRangeException(nameof(elementIndex)); } - Retain(); - return new MemoryHandle((void*)((int)_dataBufferPointer + elementIndex * _elementWidth)); //could not use Unsafe.Add } } - public override void Unpin() + // MemoryHandle returned above by Pin() should be disposed. + // Unpin() is purely to satisfy the interface. + // TODO: This class needs work. It is not clear what happens + // if the MemoryHandle remains alive and this class gets Disposed. + public override void Unpin() { } + + public void Dispose() { - Release(); - } - - private bool Release() - { - int newRefCount = Interlocked.Decrement(ref _referenceCount); - - if (newRefCount < 0) - { - throw new InvalidOperationException("Unmatched Release/Retain"); - } - - return newRefCount != 0; - } - - private void Retain() - { - if (IsDisposed) - { - throw new ObjectDisposedException(nameof(NativeOnnxTensorMemory)); - } - - Interlocked.Increment(ref _referenceCount); + Dispose(true); + GC.SuppressFinalize(this); } protected override void Dispose(bool disposing) { - if (_disposed) + if(_disposed) { return; } - if (disposing) + if (_onnxValueHandle != IntPtr.Zero) { - // do managed objects cleanup + NativeMethods.OrtReleaseValue(_onnxValueHandle); + _onnxValueHandle = IntPtr.Zero; } - NativeMethods.OrtReleaseValue(_onnxValueHandle); - _disposed = true; } diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/OnnxRuntime.cs b/csharp/src/Microsoft.ML.OnnxRuntime/OnnxRuntime.cs index d0091f6cbe..a4bf08d3ba 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/OnnxRuntime.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/OnnxRuntime.cs @@ -51,31 +51,13 @@ namespace Microsoft.ML.OnnxRuntime private OnnxRuntime() //Problem: it is not possible to pass any option for a Singleton :base(IntPtr.Zero, true) { - handle = IntPtr.Zero; - try - { - NativeApiStatus.VerifySuccess(NativeMethods.OrtCreateEnv(LogLevel.Warning, @"CSharpOnnxRuntime", out handle)); - } - catch (OnnxRuntimeException e) - { - if (handle != IntPtr.Zero) - { - Delete(handle); - handle = IntPtr.Zero; - } - throw e; - } - - } - - private static void Delete(IntPtr nativePtr) - { - NativeMethods.OrtReleaseEnv(nativePtr); + NativeApiStatus.VerifySuccess(NativeMethods.OrtCreateEnv(LogLevel.Warning, @"CSharpOnnxRuntime", out handle)); } protected override bool ReleaseHandle() { - Delete(handle); + NativeMethods.OrtReleaseEnv(handle); + handle = IntPtr.Zero; return true; } } diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/OrtAllocator.cs b/csharp/src/Microsoft.ML.OnnxRuntime/OrtAllocator.cs index 426a4445e1..0f610d2a23 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/OrtAllocator.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/OrtAllocator.cs @@ -37,10 +37,9 @@ namespace Microsoft.ML.OnnxRuntime /// to pre-allocated memory. In that case, the instance of OrtMemoryInfo contains the information about the allocator /// used to allocate the underlying memory. /// - public class OrtMemoryInfo : IDisposable + public class OrtMemoryInfo : SafeHandle { private static readonly Lazy _defaultCpuAllocInfo = new Lazy(CreateCpuMemoryInfo); - private IntPtr _pointer; private readonly bool _owned; // false if we are exposing OrtMemoryInfo from an allocator which owns it private static OrtMemoryInfo CreateCpuMemoryInfo() @@ -66,10 +65,12 @@ namespace Microsoft.ML.OnnxRuntime { get { - return _pointer; + return handle; } } + public override bool IsInvalid { get { return handle == IntPtr.Zero; } } + /// /// This allocator takes an native pointer to already existing /// instance of OrtMemoryInfo. That instance may either be owned or not @@ -78,8 +79,8 @@ namespace Microsoft.ML.OnnxRuntime /// /// internal OrtMemoryInfo(IntPtr allocInfo, bool owned) + : base(allocInfo, true) { - _pointer = allocInfo; _owned = owned; } @@ -100,6 +101,7 @@ namespace Microsoft.ML.OnnxRuntime /// Device id /// Memory type public OrtMemoryInfo(byte[] utf8AllocatorName, OrtAllocatorType allocatorType, int deviceId, OrtMemType memoryType) + : base(IntPtr.Zero, true) { using (var pinnedName = new PinnedGCHandle(GCHandle.Alloc(utf8AllocatorName, GCHandleType.Pinned))) { @@ -107,7 +109,7 @@ namespace Microsoft.ML.OnnxRuntime allocatorType, deviceId, memoryType, - out _pointer)); + out handle)); } _owned = true; } @@ -132,7 +134,7 @@ namespace Microsoft.ML.OnnxRuntime get { IntPtr utf8Name = IntPtr.Zero; - NativeApiStatus.VerifySuccess(NativeMethods.OrtMemoryInfoGetName(_pointer, out utf8Name)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtMemoryInfoGetName(handle, out utf8Name)); return NativeOnnxValueHelper.StringFromNativeUtf8(utf8Name); } } @@ -145,7 +147,7 @@ namespace Microsoft.ML.OnnxRuntime get { int id = 0; - NativeApiStatus.VerifySuccess(NativeMethods.OrtMemoryInfoGetId(_pointer, out id)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtMemoryInfoGetId(handle, out id)); return id; } } @@ -159,7 +161,7 @@ namespace Microsoft.ML.OnnxRuntime public OrtMemType GetMemoryType() { OrtMemType memoryType = OrtMemType.Default; - NativeApiStatus.VerifySuccess(NativeMethods.OrtMemoryInfoGetMemType(_pointer, out memoryType)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtMemoryInfoGetMemType(handle, out memoryType)); return memoryType; } @@ -170,7 +172,7 @@ namespace Microsoft.ML.OnnxRuntime public OrtAllocatorType GetAllocatorType() { OrtAllocatorType allocatorType = OrtAllocatorType.ArenaAllocator; - NativeApiStatus.VerifySuccess(NativeMethods.OrtMemoryInfoGetType(_pointer, out allocatorType)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtMemoryInfoGetType(handle, out allocatorType)); return allocatorType; } @@ -191,7 +193,7 @@ namespace Microsoft.ML.OnnxRuntime return true; } int result = -1; - NativeApiStatus.VerifySuccess(NativeMethods.OrtCompareMemoryInfo(_pointer, other._pointer, out result)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtCompareMemoryInfo(handle, other.Pointer, out result)); return (result == 0); } @@ -200,25 +202,19 @@ namespace Microsoft.ML.OnnxRuntime return Pointer.ToInt32(); } - #region IDisposable Support - protected virtual void Dispose(bool disposing) + #region SafeHandle + protected override bool ReleaseHandle() { - if (disposing) + // If this instance exposes OrtMemoryInfo that belongs + // to the allocator then the allocator owns it + if (_owned) { - if (_owned) - { - NativeMethods.OrtReleaseMemoryInfo(_pointer); - } - _pointer = IntPtr.Zero; + NativeMethods.OrtReleaseMemoryInfo(handle); } + handle = IntPtr.Zero; + return true; } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - // We intentionally do not provider an finalizer for the class #endregion } @@ -230,8 +226,11 @@ namespace Microsoft.ML.OnnxRuntime /// lifespan of the allocation. Or, if you prefer, all OrtMemoryAllocation instances must be /// disposed of before the corresponding allocator instances are disposed of. /// - public class OrtMemoryAllocation : IDisposable + public class OrtMemoryAllocation : SafeHandle { + // This allocator is used to free this allocation + // This also prevents OrtAllocator GC/finalization in case + // the user forgets to Dispose() of this allocation private OrtAllocator _allocator; /// @@ -245,16 +244,18 @@ namespace Microsoft.ML.OnnxRuntime /// /// internal OrtMemoryAllocation(OrtAllocator allocator, IntPtr pointer, uint size) + : base(pointer, true) { _allocator = allocator; - Pointer = pointer; Size = size; } /// /// Internal accessor to call native methods /// - internal IntPtr Pointer { get; private set; } + internal IntPtr Pointer { get { return handle; } } + + public override bool IsInvalid { get { return handle == IntPtr.Zero; } } /// /// Returns the size of the allocation @@ -268,24 +269,14 @@ namespace Microsoft.ML.OnnxRuntime return _allocator.Info; } } - #region IDisposable Support - protected virtual void Dispose(bool disposing) + #region SafeHandle + protected override bool ReleaseHandle() { - if (disposing) - { - if (_allocator != null) - { - _allocator.FreeMemory(Pointer); - } - Pointer = IntPtr.Zero; - } + _allocator.FreeMemory(handle); + handle = IntPtr.Zero; + return true; } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } #endregion } @@ -294,10 +285,9 @@ namespace Microsoft.ML.OnnxRuntime /// This allocator enables you to allocate memory from the internal /// memory pools including device allocations. Useful for binding. /// - public class OrtAllocator : IDisposable + public class OrtAllocator : SafeHandle { private static readonly Lazy _defaultInstance = new Lazy(GetDefaultCpuAllocator); - private IntPtr _pointer; private readonly bool _owned; private static OrtAllocator GetDefaultCpuAllocator() @@ -324,19 +314,21 @@ namespace Microsoft.ML.OnnxRuntime { get { - return _pointer; + return handle; } } + public override bool IsInvalid { get { return handle == IntPtr.Zero; } } + /// /// Internal constructor wraps existing native allocators /// /// /// internal OrtAllocator(IntPtr allocator, bool owned) + : base(allocator, true) { - this._pointer = allocator; - this._owned = owned; + _owned = owned; } /// @@ -348,8 +340,9 @@ namespace Microsoft.ML.OnnxRuntime /// /// public OrtAllocator(InferenceSession session, OrtMemoryInfo memInfo) + : base(IntPtr.Zero, true) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtCreateAllocator(session.Handle, memInfo.Pointer, out _pointer)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtCreateAllocator(session.Handle, memInfo.Pointer, out handle)); _owned = true; } @@ -361,7 +354,7 @@ namespace Microsoft.ML.OnnxRuntime get { IntPtr memoryInfo = IntPtr.Zero; - NativeApiStatus.VerifySuccess(NativeMethods.OrtAllocatorGetInfo(_pointer, out memoryInfo)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtAllocatorGetInfo(handle, out memoryInfo)); // This serves as an exposure of memory_info owned by the allocator return new OrtMemoryInfo(memoryInfo, false); } @@ -375,7 +368,7 @@ namespace Microsoft.ML.OnnxRuntime public OrtMemoryAllocation Allocate(uint size) { IntPtr allocation = IntPtr.Zero; - NativeApiStatus.VerifySuccess(NativeMethods.OrtAllocatorAlloc(_pointer, (UIntPtr)size, out allocation)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtAllocatorAlloc(handle, (UIntPtr)size, out allocation)); return new OrtMemoryAllocation(this, allocation, size); } @@ -385,29 +378,21 @@ namespace Microsoft.ML.OnnxRuntime /// internal void FreeMemory(IntPtr allocation) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtAllocatorFree(_pointer, allocation)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtAllocatorFree(handle, allocation)); } - #region IDisposable Support - protected virtual void Dispose(bool disposing) + #region SafeHandle + protected override bool ReleaseHandle() { - if (disposing) + // Singleton default allocator is not owned + if (_owned) { - if (_owned) - { - NativeMethods.OrtReleaseAllocator(_pointer); - } - _pointer = IntPtr.Zero; + NativeMethods.OrtReleaseAllocator(handle); } + handle = IntPtr.Zero; + return true; } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - // We intentionally do not provider an finalizer for the class #endregion } } diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/OrtIoBinding.cs b/csharp/src/Microsoft.ML.OnnxRuntime/OrtIoBinding.cs index 364a5ea21c..92ce948251 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/OrtIoBinding.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/OrtIoBinding.cs @@ -14,27 +14,28 @@ namespace Microsoft.ML.OnnxRuntime /// that piece of memory to an input name and shape and onnxruntime will use that as input. /// Other traditional inputs can also be bound that already exists as Tensors /// - public class OrtIoBinding : IDisposable + public class OrtIoBinding : SafeHandle { - private IntPtr _handle; - /// /// Use InferenceSession.CreateIoBinding() /// /// internal OrtIoBinding(InferenceSession session) + : base(IntPtr.Zero, true) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtCreateIoBinding(session.Handle, out _handle)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtCreateIoBinding(session.Handle, out handle)); } internal IntPtr Handle { get { - return _handle; + return handle; } } + public override bool IsInvalid { get { return handle == IntPtr.Zero; } } + /// /// Bind a piece of pre-allocated native memory as a OrtValue Tensor with a given shape /// to an input with a given name. The model will read the specified input from that memory @@ -114,7 +115,7 @@ namespace Microsoft.ML.OnnxRuntime { var utf8NamePinned = GCHandle.Alloc(NativeOnnxValueHelper.StringToZeroTerminatedUtf8(name), GCHandleType.Pinned); using (var pinnedName = new PinnedGCHandle(utf8NamePinned)) - NativeApiStatus.VerifySuccess(NativeMethods.OrtBindOutputToDevice(_handle, pinnedName.Pointer, memInfo.Pointer)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtBindOutputToDevice(handle, pinnedName.Pointer, memInfo.Pointer)); } /// @@ -130,11 +131,11 @@ namespace Microsoft.ML.OnnxRuntime { if (isInput) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtBindInput(_handle, pinnedName.Pointer, ortValue)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtBindInput(handle, pinnedName.Pointer, ortValue)); } else { - NativeApiStatus.VerifySuccess(NativeMethods.OrtBindOutput(_handle, pinnedName.Pointer, ortValue)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtBindOutput(handle, pinnedName.Pointer, ortValue)); } } } @@ -149,7 +150,7 @@ namespace Microsoft.ML.OnnxRuntime IntPtr lengths = IntPtr.Zero; UIntPtr count = UIntPtr.Zero; var allocator = OrtAllocator.DefaultInstance; - NativeApiStatus.VerifySuccess(NativeMethods.OrtGetBoundOutputNames(_handle, allocator.Pointer, out buffer, out lengths, out count)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtGetBoundOutputNames(handle, allocator.Pointer, out buffer, out lengths, out count)); if(count.Equals(UIntPtr.Zero)) { @@ -193,7 +194,7 @@ namespace Microsoft.ML.OnnxRuntime IntPtr ortValues = IntPtr.Zero; UIntPtr count = UIntPtr.Zero; var allocator = OrtAllocator.DefaultInstance; - NativeApiStatus.VerifySuccess(NativeMethods.OrtGetBoundOutputValues(_handle, allocator.Pointer, out ortValues, out count)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtGetBoundOutputValues(handle, allocator.Pointer, out ortValues, out count)); if(count.Equals(UIntPtr.Zero)) { @@ -225,7 +226,7 @@ namespace Microsoft.ML.OnnxRuntime /// public void ClearBoundInputs() { - NativeMethods.OrtClearBoundInputs(_handle); + NativeMethods.OrtClearBoundInputs(handle); } /// @@ -233,25 +234,17 @@ namespace Microsoft.ML.OnnxRuntime /// public void ClearBoundOutputs() { - NativeMethods.OrtClearBoundOutputs(_handle); + NativeMethods.OrtClearBoundOutputs(handle); } - #region Disposable Support - protected virtual void Dispose(bool disposing) + #region SafeHandle + protected override bool ReleaseHandle() { - if(disposing) - { - NativeMethods.OrtReleaseIoBinding(_handle); - _handle = IntPtr.Zero; - } - } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); + NativeMethods.OrtReleaseIoBinding(handle); + handle = IntPtr.Zero; + return true; } - // No need for the finalizer #endregion } } diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/OrtValue.cs b/csharp/src/Microsoft.ML.OnnxRuntime/OrtValue.cs index 5c097eccd7..92c8896b82 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/OrtValue.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/OrtValue.cs @@ -22,20 +22,24 @@ namespace Microsoft.ML.OnnxRuntime /// /// Represents a disposable OrtValue /// - public class OrtValue : IDisposable + public class OrtValue : SafeHandle { /// /// Use factory methods to instantiate /// /// - /// Default true, own the raw handle + /// Default true, own the raw handle + /// However, we use this class to expose OrtValue that is owned by DisposableNamedOnnxValue + /// internal OrtValue(IntPtr handle, bool owned = true) + : base(handle, true) { - Handle = handle; IsOwned = owned; } - internal IntPtr Handle { get; private set; } + internal IntPtr Handle { get { return handle; } } + + public override bool IsInvalid { get { return handle == IntPtr.Zero; } } #region NamedOnnxValue/DisposableOnnxValue accommodations @@ -54,10 +58,10 @@ namespace Microsoft.ML.OnnxRuntime /// internal IntPtr Disown() { - var handle = Handle; - Handle = IntPtr.Zero; + var ret = Handle; + handle = IntPtr.Zero; IsOwned = false; - return handle; + return ret; } internal bool IsOwned { get; private set; } @@ -337,28 +341,18 @@ namespace Microsoft.ML.OnnxRuntime return ortValue; } - #region Disposable Support - protected virtual void Dispose(bool disposing) + #region SafeHandle + protected override bool ReleaseHandle() { - if (disposing) + // We have to surrender ownership to some legacy classes + // Or we never had that ownership to begin with + if (IsOwned) { - // We have to surrender ownership to some legacy classes - // Or we never had that ownership to begin with - if (Handle != IntPtr.Zero) - { - if (IsOwned) - { - NativeMethods.OrtReleaseValue(Handle); - } - // Prevent use after disposal - Handle = IntPtr.Zero; - } + NativeMethods.OrtReleaseValue(handle); } - } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); + // Prevent use after disposal + handle = IntPtr.Zero; + return true; } // No need for the finalizer #endregion diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/RunOptions.cs b/csharp/src/Microsoft.ML.OnnxRuntime/RunOptions.cs index 3bd35e3f94..ed2e8cebee 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/RunOptions.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/RunOptions.cs @@ -6,23 +6,25 @@ using System.Runtime.InteropServices; namespace Microsoft.ML.OnnxRuntime { /// Sets various runtime options. - public class RunOptions : IDisposable + public class RunOptions : SafeHandle { - private IntPtr _nativePtr; internal IntPtr Handle { get { - return _nativePtr; + return handle; } } - public RunOptions() + public RunOptions() + :base(IntPtr.Zero, true) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtCreateRunOptions(out _nativePtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtCreateRunOptions(out handle)); } + public override bool IsInvalid { get { return handle == IntPtr.Zero; } } + /// /// Log Severity Level for the session logs. Default = ORT_LOGGING_LEVEL_WARNING /// @@ -34,7 +36,7 @@ namespace Microsoft.ML.OnnxRuntime } set { - NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsSetRunLogSeverityLevel(_nativePtr, value)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsSetRunLogSeverityLevel(handle, value)); _logSeverityLevel = value; } } @@ -52,7 +54,7 @@ namespace Microsoft.ML.OnnxRuntime } set { - NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsSetRunLogVerbosityLevel(_nativePtr, value)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsSetRunLogVerbosityLevel(handle, value)); _logVerbosityLevel = value; } } @@ -67,14 +69,14 @@ namespace Microsoft.ML.OnnxRuntime { string tag = null; IntPtr tagPtr = IntPtr.Zero; - NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsGetRunTag(_nativePtr, out tagPtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsGetRunTag(handle, out tagPtr)); tag = Marshal.PtrToStringAnsi(tagPtr); // assume ANSI string // should not release the memory of the tagPtr, because it returns the c_str() of the std::string being used inside RunOptions C++ class return tag; } set { - NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsSetRunTag(_nativePtr, value)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsSetRunTag(handle, value)); } } @@ -93,12 +95,12 @@ namespace Microsoft.ML.OnnxRuntime { if (!_terminate && value) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsSetTerminate(_nativePtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsSetTerminate(handle)); _terminate = true; } else if (_terminate && !value) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsUnsetTerminate(_nativePtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtRunOptionsUnsetTerminate(handle)); _terminate = false; } } @@ -106,21 +108,13 @@ namespace Microsoft.ML.OnnxRuntime private bool _terminate = false; //value set to default value of the C++ RunOptions - #region IDisposable + #region SafeHandle - public void Dispose() + protected override bool ReleaseHandle() { - GC.SuppressFinalize(this); - Dispose(true); - } - - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - NativeMethods.OrtReleaseRunOptions(_nativePtr); - } + NativeMethods.OrtReleaseRunOptions(handle); + handle = IntPtr.Zero; + return true; } #endregion diff --git a/csharp/src/Microsoft.ML.OnnxRuntime/SessionOptions.cs b/csharp/src/Microsoft.ML.OnnxRuntime/SessionOptions.cs index b04a7e4fec..16ff02aed0 100644 --- a/csharp/src/Microsoft.ML.OnnxRuntime/SessionOptions.cs +++ b/csharp/src/Microsoft.ML.OnnxRuntime/SessionOptions.cs @@ -34,9 +34,8 @@ namespace Microsoft.ML.OnnxRuntime /// /// Holds the options for creating an InferenceSession /// - public class SessionOptions : IDisposable + public class SessionOptions : SafeHandle { - private IntPtr _nativePtr; private static string[] cudaDelayLoadedLibs = { "cublas64_10.dll", "cudnn64_7.dll", "curand64_10.dll" }; #region Constructor and Factory methods @@ -45,8 +44,9 @@ namespace Microsoft.ML.OnnxRuntime /// Constructs an empty SessionOptions /// public SessionOptions() + :base(IntPtr.Zero, true) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtCreateSessionOptions(out _nativePtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtCreateSessionOptions(out handle)); } /// @@ -69,8 +69,8 @@ namespace Microsoft.ML.OnnxRuntime { CheckCudaExecutionProviderDLLs(); SessionOptions options = new SessionOptions(); - NativeMethods.OrtSessionOptionsAppendExecutionProvider_CUDA(options._nativePtr, deviceId); - NativeMethods.OrtSessionOptionsAppendExecutionProvider_CPU(options._nativePtr, 1); + NativeMethods.OrtSessionOptionsAppendExecutionProvider_CUDA(options.Handle, deviceId); + NativeMethods.OrtSessionOptionsAppendExecutionProvider_CPU(options.Handle, 1); return options; } @@ -83,7 +83,7 @@ namespace Microsoft.ML.OnnxRuntime public static SessionOptions MakeSessionOptionWithNupharProvider(String settings = "") { SessionOptions options = new SessionOptions(); - NativeMethods.OrtSessionOptionsAppendExecutionProvider_Nuphar(options._nativePtr, 1, settings); + NativeMethods.OrtSessionOptionsAppendExecutionProvider_Nuphar(options.Handle, 1, settings); return options; } @@ -92,7 +92,7 @@ namespace Microsoft.ML.OnnxRuntime #region ExecutionProviderAppends public void AppendExecutionProvider_CPU(int useArena) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_CPU(_nativePtr, useArena)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_CPU(handle, useArena)); } /// @@ -100,7 +100,7 @@ namespace Microsoft.ML.OnnxRuntime /// public void AppendExecutionProvider_Dnnl(int useArena) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_Dnnl(_nativePtr, useArena)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_Dnnl(handle, useArena)); } /// @@ -108,7 +108,7 @@ namespace Microsoft.ML.OnnxRuntime /// public void AppendExecutionProvider_CUDA(int deviceId) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_CUDA(_nativePtr, deviceId)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_CUDA(handle, deviceId)); } /// @@ -116,7 +116,7 @@ namespace Microsoft.ML.OnnxRuntime /// public void AppendExecutionProvider_NGraph(string nGraphBackendType) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_NGraph(_nativePtr, nGraphBackendType)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_NGraph(handle, nGraphBackendType)); } /// @@ -124,7 +124,7 @@ namespace Microsoft.ML.OnnxRuntime /// public void AppendExecutionProvider_OpenVINO(string deviceId = "") { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_OpenVINO(_nativePtr, deviceId)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_OpenVINO(handle, deviceId)); } /// @@ -132,7 +132,7 @@ namespace Microsoft.ML.OnnxRuntime /// public void AppendExecutionProvider_Tensorrt(int deviceId) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_Tensorrt(_nativePtr, deviceId)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_Tensorrt(handle, deviceId)); } /// @@ -140,7 +140,7 @@ namespace Microsoft.ML.OnnxRuntime /// public void AppendExecutionProvider_MIGraphX(int deviceId) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_MIGraphX(_nativePtr, deviceId)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_MIGraphX(handle, deviceId)); } /// @@ -148,7 +148,7 @@ namespace Microsoft.ML.OnnxRuntime /// public void AppendExecutionProvider_Nnapi() { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_Nnapi(_nativePtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_Nnapi(handle)); } /// @@ -156,7 +156,7 @@ namespace Microsoft.ML.OnnxRuntime /// public void AppendExecutionProvider_Nuphar(string settings = "") { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_Nuphar(_nativePtr, 1, settings)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSessionOptionsAppendExecutionProvider_Nuphar(handle, 1, settings)); } #endregion //ExecutionProviderAppends @@ -164,7 +164,7 @@ namespace Microsoft.ML.OnnxRuntime public void RegisterCustomOpLibrary(string libraryPath) { IntPtr libraryHandle = IntPtr.Zero; - NativeApiStatus.VerifySuccess(NativeMethods.OrtRegisterCustomOpsLibrary(_nativePtr, libraryPath, out libraryHandle)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtRegisterCustomOpsLibrary(handle, libraryPath, out libraryHandle)); } #endregion @@ -173,11 +173,13 @@ namespace Microsoft.ML.OnnxRuntime { get { - return _nativePtr; + return handle; } } - #region Public Properties + + public override bool IsInvalid { get { return handle == IntPtr.Zero; } } + /// /// Enables the use of the memory allocation patterns in the first Run() call for subsequent runs. Default = true. /// @@ -191,19 +193,18 @@ namespace Microsoft.ML.OnnxRuntime { if (!_enableMemoryPattern && value) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtEnableMemPattern(_nativePtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtEnableMemPattern(handle)); _enableMemoryPattern = true; } else if (_enableMemoryPattern && !value) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtDisableMemPattern(_nativePtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtDisableMemPattern(handle)); _enableMemoryPattern = false; } } } private bool _enableMemoryPattern = true; - /// /// Path prefix to use for output of profiling data /// @@ -227,12 +228,12 @@ namespace Microsoft.ML.OnnxRuntime { if (!_enableProfiling && value) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtEnableProfiling(_nativePtr, NativeMethods.GetPlatformSerializedString(ProfileOutputPathPrefix))); + NativeApiStatus.VerifySuccess(NativeMethods.OrtEnableProfiling(handle, NativeMethods.GetPlatformSerializedString(ProfileOutputPathPrefix))); _enableProfiling = true; } else if (_enableProfiling && !value) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtDisableProfiling(_nativePtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtDisableProfiling(handle)); _enableProfiling = false; } } @@ -252,7 +253,7 @@ namespace Microsoft.ML.OnnxRuntime { if (value != _optimizedModelFilePath) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSetOptimizedModelFilePath(_nativePtr, NativeMethods.GetPlatformSerializedString(value))); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSetOptimizedModelFilePath(handle, NativeMethods.GetPlatformSerializedString(value))); _optimizedModelFilePath = value; } } @@ -274,12 +275,12 @@ namespace Microsoft.ML.OnnxRuntime { if (!_enableCpuMemArena && value) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtEnableCpuMemArena(_nativePtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtEnableCpuMemArena(handle)); _enableCpuMemArena = true; } else if (_enableCpuMemArena && !value) { - NativeApiStatus.VerifySuccess(NativeMethods.OrtDisableCpuMemArena(_nativePtr)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtDisableCpuMemArena(handle)); _enableCpuMemArena = false; } } @@ -300,7 +301,7 @@ namespace Microsoft.ML.OnnxRuntime set { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSetSessionLogId(_nativePtr, value)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSetSessionLogId(handle, value)); _logId = value; } } @@ -317,7 +318,7 @@ namespace Microsoft.ML.OnnxRuntime } set { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSetSessionLogSeverityLevel(_nativePtr, value)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSetSessionLogSeverityLevel(handle, value)); _logSeverityLevel = value; } } @@ -335,7 +336,7 @@ namespace Microsoft.ML.OnnxRuntime } set { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSetSessionLogVerbosityLevel(_nativePtr, value)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSetSessionLogVerbosityLevel(handle, value)); _logVerbosityLevel = value; } } @@ -354,7 +355,7 @@ namespace Microsoft.ML.OnnxRuntime } set { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSetIntraOpNumThreads(_nativePtr, value)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSetIntraOpNumThreads(handle, value)); _intraOpNumThreads = value; } } @@ -373,7 +374,7 @@ namespace Microsoft.ML.OnnxRuntime } set { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSetInterOpNumThreads(_nativePtr, value)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSetInterOpNumThreads(handle, value)); _interOpNumThreads = value; } } @@ -390,7 +391,7 @@ namespace Microsoft.ML.OnnxRuntime } set { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSetSessionGraphOptimizationLevel(_nativePtr, value)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSetSessionGraphOptimizationLevel(handle, value)); _graphOptimizationLevel = value; } } @@ -408,7 +409,7 @@ namespace Microsoft.ML.OnnxRuntime } set { - NativeApiStatus.VerifySuccess(NativeMethods.OrtSetSessionExecutionMode(_nativePtr, value)); + NativeApiStatus.VerifySuccess(NativeMethods.OrtSetSessionExecutionMode(handle, value)); _executionMode = value; } } @@ -448,21 +449,13 @@ namespace Microsoft.ML.OnnxRuntime #endregion - #region IDisposable + #region SafeHandle - public void Dispose() + protected override bool ReleaseHandle() { - GC.SuppressFinalize(this); - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - NativeMethods.OrtReleaseSessionOptions(_nativePtr); - _nativePtr = IntPtr.Zero; - } + NativeMethods.OrtReleaseSessionOptions(handle); + handle = IntPtr.Zero; + return true; } #endregion