-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3602 from ogoshen/development
C# Development
- Loading branch information
Showing
132 changed files
with
5,934 additions
and
2,516 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Platform Invoke | ||
|
||
This document contains notes on interoperability with the native SDK. | ||
|
||
The .NET wrapper uses Platform Invoke (P/Invoke) to call functions from the unmanaged `realsense2` library. | ||
It's a bit more tedious and error-prone than C++/CLI, but has better support across .NET implementations (.NET Framework, Core, Mono & Xamarin) | ||
|
||
This means that the .NET wrapper exposes an object oriented API by calling into C functions, which is similar to what the C++ API does, with all the benefits and shortcomings of running in a managed environment, some of which discussed here. | ||
|
||
## Lifetime & Resources | ||
|
||
Just as the standard `System.IO.FileStream` is a wrapper around native OS file handles, most objects in the library are wrappers around their native counterparts. | ||
|
||
Unmanaged resources are handled by the .NET [IDisposable](https://docs.microsoft.com/en-us/dotnet/api/system.idisposable.dispose) interface, which provides a predictable way to release resources. | ||
You should either directly call `Dispose` as soon as you're finished using the instance, or indirectly with the [using](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement) statement. | ||
|
||
This is especially important for the `Frame` class and its derivatives, as the RealSense™ SDK uses a native frame pool, failing to release frames will cause the queue to fill, and no new frames will arrive. | ||
|
||
> You cannot count on the GC to release these objects, it's unpredictable, it will not keep up, and isn't even guaranteed to run. | ||
Trying to use a disposed object for a native call should raise an `ObjectDisposedException`. | ||
|
||
## Memory & GC | ||
|
||
Garbage collection means a *stop-the-world* event pauses all running threads while looking for unreferenced objects. This will lead to extreme spikes in frame times and can cause the RealSense™ device to drop frames. | ||
|
||
We use an object pool to avoid allocating memory, and relieve GC pressure. Objects are rented from the pool, reinitialized to wrap a new unmanaged resource, and are released back to the pool when disposed. | ||
|
||
Wrapped objects are finalizable, and will free their unmanaged resources before being collected, this should avoid native resource leaks in most but catastrophic failures. | ||
|
||
## NativeMethods & Pointers | ||
|
||
The `NativeMethods` class holds the extern function definitions (`rs2_*`) calling into the native `realsense2` library. | ||
|
||
There are two types of `IntPtr` pointers used in these calls: | ||
|
||
1. **Unmanaged** pointers coming from the native SDK itself | ||
|
||
They are usually wrapped and operated on by the exposed API, or passed around in the internals of library. | ||
|
||
These pointers are invisible to the GC, they won't be relocated, so they are safe to use in pinvoke calls, and should be freed when required by the SDK. | ||
|
||
For instance: | ||
* The pointer returned from `NativeMethods.rs2_pipeline_wait_for_frames` which is wrapped in a `Frame` object, and must be released with `NativeMethods.rs2_release_frame` | ||
|
||
* `Frame.Data` returns a pointer to the start of the frame data, it comes from a call to `NativeMethods.rs2_get_frame_data` with the above pointer. | ||
It's lifetime is bound to the frame and will be released along with it. | ||
|
||
2. Pointers to **managed** objects | ||
|
||
These can be relocated or freed by the GC (even while being used in native code) and special care is taken to ensure their safe use in unmanaged calls. | ||
|
||
Some examples: | ||
* In `Pipeline.Start(FrameCallback cb)` the `cb` delegate has to be kept alive since it will be called from unmanaged code. | ||
|
||
* In `VideoFrame.CopyTo<T>(T[] array)` the array has to pinned so the GC won't move it while we're copying frame data. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
target_sources(${LRS_DOTNET_TARGET} | ||
PRIVATE | ||
"${CMAKE_CURRENT_LIST_DIR}/DeleterHandle.cs" | ||
"${CMAKE_CURRENT_LIST_DIR}/Object.cs" | ||
"${CMAKE_CURRENT_LIST_DIR}/PooledObject.cs" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// License: Apache 2.0. See LICENSE file in root directory. | ||
// Copyright(c) 2017 Intel Corporation. All Rights Reserved. | ||
|
||
namespace Intel.RealSense.Base | ||
{ | ||
using System; | ||
using System.Diagnostics; | ||
using System.Runtime.InteropServices; | ||
using System.Security; | ||
|
||
[SuppressUnmanagedCodeSecurity] | ||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] | ||
public delegate void Deleter(IntPtr ptr); | ||
|
||
/// <summary> | ||
/// Native handle with deleter delegate to release unmanaged resources | ||
/// </summary> | ||
// TODO: CriticalFinalizerObject & CER | ||
//[DebuggerDisplay("{deleter?.Method.Name,nq}", Name = nameof(Deleter))] | ||
internal sealed class DeleterHandle : IDisposable | ||
{ | ||
private IntPtr handle; | ||
private Deleter deleter; | ||
|
||
public IntPtr Handle => handle; | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether this handle is invalid | ||
/// </summary> | ||
public bool IsInvalid => handle == IntPtr.Zero; | ||
|
||
public DeleterHandle(IntPtr ptr, Deleter deleter) | ||
{ | ||
handle = ptr; | ||
this.deleter = deleter; | ||
} | ||
|
||
public void SetHandleAsInvalid() | ||
{ | ||
handle = IntPtr.Zero; | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
Dispose(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
public void Dispose(bool disposing) | ||
{ | ||
if (handle == IntPtr.Zero) | ||
{ | ||
return; | ||
} | ||
|
||
deleter?.Invoke(handle); | ||
handle = IntPtr.Zero; | ||
} | ||
|
||
internal void Reset(IntPtr ptr) | ||
{ | ||
handle = ptr; | ||
GC.ReRegisterForFinalize(this); | ||
} | ||
|
||
internal void Reset(IntPtr ptr, Deleter deleter) | ||
{ | ||
this.handle = ptr; | ||
this.deleter = deleter; | ||
//GC.ReRegisterForFinalize(this); | ||
} | ||
|
||
~DeleterHandle() | ||
{ | ||
//Console.WriteLine($"~{handle} {deleter?.Method}"); | ||
Dispose(false); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// License: Apache 2.0. See LICENSE file in root directory. | ||
// Copyright(c) 2017 Intel Corporation. All Rights Reserved. | ||
|
||
namespace Intel.RealSense.Base | ||
{ | ||
using System; | ||
using System.Diagnostics; | ||
|
||
/// <summary> | ||
/// Base class for disposable objects with native resources | ||
/// </summary> | ||
public abstract class Object : IDisposable | ||
{ | ||
// TODO: rename, kept for backwards compatiblity | ||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] | ||
internal readonly DeleterHandle m_instance; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="Object"/> class. | ||
/// </summary> | ||
/// <param name="ptr">native pointer</param> | ||
/// <param name="deleter">optional deleter</param> | ||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="ptr"/> is null</exception> | ||
protected Object(IntPtr ptr, Deleter deleter) | ||
{ | ||
if (ptr == IntPtr.Zero) | ||
{ | ||
throw new ArgumentNullException(nameof(ptr)); | ||
} | ||
|
||
m_instance = new DeleterHandle(ptr, deleter); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the native handle | ||
/// </summary> | ||
/// <exception cref="ObjectDisposedException">Thrown when <see cref="DeleterHandle.IsInvalid"/></exception> | ||
public IntPtr Handle | ||
{ | ||
get | ||
{ | ||
if (m_instance.IsInvalid) | ||
{ | ||
throw new ObjectDisposedException(GetType().Name); | ||
} | ||
|
||
return m_instance.Handle; | ||
} | ||
} | ||
|
||
/// <inheritdoc/> | ||
public void Dispose() | ||
{ | ||
this.Dispose(true); | ||
} | ||
|
||
protected virtual void Dispose(bool disposing) | ||
{ | ||
m_instance.Dispose(); | ||
} | ||
|
||
internal void Reset(IntPtr ptr, Deleter deleter) | ||
{ | ||
m_instance.Reset(ptr, deleter); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// License: Apache 2.0. See LICENSE file in root directory. | ||
// Copyright(c) 2017 Intel Corporation. All Rights Reserved. | ||
|
||
namespace Intel.RealSense.Base | ||
{ | ||
using System; | ||
|
||
/// <summary> | ||
/// Base class for objects in an <cref see="ObjectPool">ObjectPool</cref> | ||
/// </summary> | ||
public abstract class PooledObject : Object | ||
{ | ||
protected PooledObject(IntPtr ptr, Deleter deleter) | ||
: base(ptr, deleter) | ||
{ | ||
} | ||
|
||
internal abstract void Initialize(); | ||
|
||
protected override void Dispose(bool disposing) | ||
{ | ||
if (m_instance.IsInvalid) | ||
{ | ||
return; | ||
} | ||
|
||
base.Dispose(disposing); | ||
ObjectPool.Release(this); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.