RefCounted.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.InteropServices;
  4. namespace AtomicEngine
  5. {
  6. /// <summary>
  7. /// Class which ensures Disposed is called, without needing to implement Dispose in class Finalizer
  8. /// </summary>
  9. internal class RefCountedSafeFileHandle : SafeHandle
  10. {
  11. public RefCountedSafeFileHandle(IntPtr handle, bool ownsHandle = true)
  12. : base(handle, ownsHandle)
  13. {
  14. if (handle == IntPtr.Zero)
  15. {
  16. throw new InvalidOperationException("RefCountedSafeFileHandle - native == IntPtr.Zero");
  17. }
  18. NativeCore.csi_AtomicEngine_AddRef(handle);
  19. }
  20. override public bool IsInvalid { get { return handle == IntPtr.Zero; } }
  21. /// <summary>
  22. /// Release the handle, which will release the native instance immediately if in main thread
  23. /// otherwise, will queue
  24. /// </summary>
  25. override protected bool ReleaseHandle()
  26. {
  27. if (handle == IntPtr.Zero)
  28. {
  29. throw new InvalidOperationException("RefCountedSafeFileHandle.ReleaseHandle - native == IntPtr.Zero");
  30. }
  31. // We can be called from Dispose in main thread or from finalizers, which aren't in the main thread
  32. if (AtomicNET.IsMainThread())
  33. {
  34. NativeCore.csi_AtomicEngine_ReleaseRef(handle);
  35. }
  36. else
  37. {
  38. // We're in a finalizer, need to add to queue to release when
  39. // back in main thread
  40. lock (RefCounted.refCountedFinalizerQueue)
  41. {
  42. RefCounted.refCountedFinalizerQueue.Add(handle);
  43. }
  44. }
  45. handle = IntPtr.Zero;
  46. return true;
  47. }
  48. }
  49. [ComVisible(true)]
  50. public partial class RefCounted : IDisposable
  51. {
  52. /// <summary>
  53. /// If instance has been disposed, native object is in an undefined state, and instance should not be accessed
  54. /// </summary>
  55. public bool Disposed => disposed;
  56. // _handle is set to null to indicate disposal of this instance.
  57. private RefCountedSafeFileHandle refHandle;
  58. public RefCounted()
  59. {
  60. }
  61. /// <summary>
  62. /// WARNING: C# finalizers can be called in any thread!!!
  63. /// Don't need native cleanup code in the finalizer as use SafeHandle
  64. /// </summary>
  65. ~RefCounted()
  66. {
  67. }
  68. protected RefCounted(IntPtr native)
  69. {
  70. nativeInstance = native;
  71. }
  72. internal void InternalInit()
  73. {
  74. if (refHandle != null)
  75. throw new InvalidOperationException("RefCounted.Init - refHandle already initialized");
  76. refHandle = new RefCountedSafeFileHandle(nativeInstance);
  77. }
  78. /// <summary>
  79. /// Dispose method of IDisposible interface, note that native code can hold references to
  80. /// RefCounted derived instances, disposing RefCounted instances from managed code releases
  81. /// the managed reference and will only release the native RefCounted instance if no other references
  82. /// are held in native code.
  83. /// </summary>
  84. public void Dispose()
  85. {
  86. Dispose(true);
  87. }
  88. protected virtual void Dispose(bool disposing)
  89. {
  90. disposed = true;
  91. if (refHandle != null && !refHandle.IsInvalid)
  92. {
  93. NativeCore.RemoveNative(nativeInstance);
  94. // Free the handle
  95. refHandle.Dispose();
  96. }
  97. nativeInstance = IntPtr.Zero;
  98. }
  99. static internal List<IntPtr> refCountedFinalizerQueue = new List<IntPtr>();
  100. /// <summary>
  101. /// Releases RefCounted instances which were finalized (which can happen on any thread)
  102. /// </summary>
  103. static internal void ReleaseFinalized()
  104. {
  105. lock (refCountedFinalizerQueue)
  106. {
  107. foreach (var native in refCountedFinalizerQueue)
  108. {
  109. NativeCore.RemoveNative(native);
  110. NativeCore.csi_AtomicEngine_ReleaseRef(native);
  111. }
  112. refCountedFinalizerQueue.Clear();
  113. }
  114. }
  115. /// <summary>
  116. /// This method may be called multiple times, called on instance after it is either registered as a new native created in C# (InstantiationType == InstantiationType.INSTANTIATION_NET)
  117. /// or a native which has been wrapped ((InstantiationType != InstantiationType.INSTANTIATION_NET)
  118. /// Note that RefCounted that get GC'd from script, can still live in native code, and get rewrapped
  119. /// </summary>
  120. internal virtual void PostNativeUpdate()
  121. {
  122. }
  123. public static implicit operator IntPtr(RefCounted refCounted)
  124. {
  125. if (refCounted == null)
  126. return IntPtr.Zero;
  127. return refCounted.nativeInstance;
  128. }
  129. public IntPtr NativeInstance { get { return nativeInstance; } }
  130. public IntPtr nativeInstance = IntPtr.Zero;
  131. private bool disposed = false;
  132. [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
  133. public static extern IntPtr csi_Atomic_RefCounted_GetClassID(IntPtr self);
  134. }
  135. }