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