static internal void UnsubscribeFromEvent(AObject receiver, RefCounted sender, uint eventType) { List <EventSubscription> eventReceivers; if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers)) { return; } AObject obj; foreach (EventSubscription er in eventReceivers) { if (!er.Receiver.TryGetTarget(out obj)) { continue; // GC'd } if (obj == receiver && (sender == null || er.Sender == sender.nativeInstance)) { eventReceivers.Remove(er); return; } } }
public void Add(RefCounted refCounted, RefType refType = RefType.REF_DEFAULT) { lock (knownObjects) { ReferenceHolder <RefCounted> knownObject; if (knownObjects.TryGetValue(refCounted.nativeInstance, out knownObject)) { var existingObj = knownObject?.Reference; // this is another check verifying correct RefCounted by using type, which isn't a good test if (existingObj != null && !IsInHierarchy(existingObj.GetType(), refCounted.GetType())) { throw new InvalidOperationException($"NativeInstance '{refCounted.nativeInstance}' is in use by '{existingObj.GetType().Name}' (IsDeleted={existingObj.nativeInstance == IntPtr.Zero}). {refCounted.GetType()}"); } } // first check if explicit strong reference bool strongRef = refType == RefType.REF_STRONG; if (!strongRef) { strongRef = StrongRefByDefault(refCounted); } knownObjects[refCounted.nativeInstance] = new ReferenceHolder <RefCounted>(refCounted, !strongRef); } }
// register a newly created native public static IntPtr RegisterNative(IntPtr native, RefCounted r, InstantiationType instantiationType = InstantiationType.INSTANTIATION_NET) { if (native == IntPtr.Zero || r == null) { throw new InvalidOperationException("NativeCore.RegisterNative - native == IntPtr.Zero || RefCounted instance == null"); } if (instantiationType == InstantiationType.INSTANTIATION_NET) { if (r.nativeInstance != IntPtr.Zero) { throw new InvalidOperationException("NativeCore.RegisterNative - NET Instantiated RefCounted with initialized nativeInstance"); } r.nativeInstance = native; } r.InstantiationType = instantiationType; r.InternalInit(); refCountedCache.Add(r); r.PostNativeUpdate(); return(native); }
// wraps an existing native instance, with downcast support public static T WrapNative <T>(IntPtr native) where T : RefCounted { if (native == IntPtr.Zero) { return(null); } RefCounted r; WeakReference <RefCounted> w; // first see if we're already available if (nativeLookup.TryGetValue(native, out w)) { if (w.TryGetTarget(out r)) { // we're alive! return((T)r); } else { // we were seen before, but have since been GC'd, remove! nativeLookup.Remove(native); if (csi_Atomic_RefCounted_Refs(native) == 1) { // only managed ref remains, so release and return null csi_AtomicEngine_ReleaseRef(native); return(null); } csi_AtomicEngine_ReleaseRef(native); } } IntPtr classID = RefCounted.csi_Atomic_RefCounted_GetClassID(native); // and store, with downcast support for instance Component -> StaticModel // we never want to hit this path for script inherited natives NativeType nativeType; if (!nativeClassIDToNativeType.TryGetValue(classID, out nativeType)) { throw new InvalidOperationException("NativeCore.WrapNative - Attempting to wrap unknown native class id"); } r = nativeType.managedConstructor(native); w = new WeakReference <RefCounted>(r); NativeCore.nativeLookup[native] = w; // store a ref, so native side will not be released while we still have a reference in managed code r.AddRef(); return((T)r); }
public static IntPtr RegisterNative(IntPtr native, RefCounted r) { r.nativeInstance = native; var w = new WeakReference(r); NativeCore.nativeLookup [r.RefID] = w; r.AddRef(); return(native); }
// register a newly created native public static IntPtr RegisterNative(IntPtr native, RefCounted r) { r.nativeInstance = native; var w = new WeakReference(r); NativeCore.nativeLookup [native] = w; // keep native side alive r.AddRef(); return(native); }
/// <summary> /// Runs a GC collection, waits for any finalizers, and then expires any natives collected /// </summary> public static void RunGC() { // run a GC collection GC.Collect(); // finalizers can run on any thread, we're explicitly running a GC here // so wait for all the finalizers to finish GC.WaitForPendingFinalizers(); // Anything finalized on another thread will now be available to release // in main thread RefCounted.ReleaseFinalized(); ExpireNatives(); }
// wraps an existing native instance, with downcast support public static T WrapNative <T>(IntPtr native) where T : RefCounted { if (native == IntPtr.Zero) { throw new InvalidOperationException("NativeCore.WrapNative - Attempting to wrap native instance IntPtr.Zero"); } var reference = refCountedCache.Get(native)?.Reference; // This isn't really a good test to verify right object, better to test if not a T and error? if (reference is T) { return((T)reference); } IntPtr classID = RefCounted.csi_Atomic_RefCounted_GetClassID(native); // Check whether this is a valid native class to wrap NativeType nativeType; if (!nativeClassIDToNativeType.TryGetValue(classID, out nativeType)) { if (logWrapUnknownNative) { Log.Info("WrapNative returning null for unknown class: " + GetNativeTypeName(native)); } return(null); } // TODO: make CSComponent abstract and have general abstract logic here? if (nativeType.Type == typeof(CSComponent)) { return(null); } // Construct managed instance wrapper for native instance // this has downcast support for instance Component -> StaticModel // note, we never want to hit this path for script inherited natives var r = nativeType.managedConstructor(native); // IMPORTANT: if a RefCounted instance is created in managed code, has reference count increased in native code // and managed side is GC'd, the original NET created instance will be gone and we can get it back here reported // as instantiated in native code. May want a transative boolean to be able to tell when an object has passed this "barrier" // which is somewhat common RegisterNative(native, r, InstantiationType.INSTANTIATION_NATIVE); return((T)r); }
// register a newly created native public static IntPtr RegisterNative(IntPtr native, RefCounted r) { if (nativeLookup.ContainsKey(native)) { throw new InvalidOperationException("NativeCore.RegisterNative - Duplicate IntPtr key"); } r.nativeInstance = native; // keep native side alive r.AddRef(); nativeLookup[native] = new WeakReference <RefCounted>(r); return(native); }
// wraps an existing native instance, with downcast support public static T WrapNative <T> (IntPtr native) where T : RefCounted { if (native == IntPtr.Zero) { return(null); } // instance id uint id = csb_Atomic_RefCounted_GetRefID(native); WeakReference w; // first see if we're already available if (nativeLookup.TryGetValue(id, out w)) { if (w.IsAlive) { // we're alive! return((T)w.Target); } else { // we were seen before, but have since been GC'd, remove! nativeLookup.Remove(id); } } IntPtr classID = RefCounted.csb_Atomic_RefCounted_GetClassID(native); // and store, with downcast support for instance Component -> StaticModel // we never want to hit this path for script inherited natives RefCounted r = nativeClassIDToManagedConstructor[classID](native); w = new WeakReference(r); NativeCore.nativeLookup [id] = w; // store a ref, so native side will not be released while we still have a reference in managed code r.AddRef(); return((T)r); }
/// <summary> /// Some types are stored with a StrongRef by default, to help avoid Object churn and support explicit Disposing /// </summary> bool StrongRefByDefault(RefCounted refCounted) { if (refCounted is Scene) { return(true); } if (refCounted is Node) { return(true); } if (refCounted is Context) { return(true); } if (refCounted is Component) { return(true); } return(false); }
public static void ReleaseExpiredNativeReferences() { List <uint> released = new List <uint> (); foreach (KeyValuePair <uint, WeakReference> entry in nativeLookup) { if (entry.Value.Target == null || !entry.Value.IsAlive) { released.Add(entry.Key); } else { } } foreach (uint id in released) { // use safe release RefCounted.SafeReleaseRef(id); nativeLookup.Remove(id); } }
// wraps an existing native instance, with downcast support public static T WrapNative <T>(IntPtr native) where T : RefCounted { if (native == IntPtr.Zero) { return(null); } RefCounted r; WeakReference <RefCounted> w; // first see if we're already available if (nativeLookup.TryGetValue(native, out w)) { if (w.TryGetTarget(out r)) { // we're alive! return((T)r); } else { // we were seen before, but have since been GC'd, remove! nativeLookup.Remove(native); if (csi_Atomic_RefCounted_Refs(native) == 1) { // only managed ref remains, so release and return null csi_AtomicEngine_ReleaseRef(native); return(null); } csi_AtomicEngine_ReleaseRef(native); } } IntPtr classID = RefCounted.csi_Atomic_RefCounted_GetClassID(native); // and store, with downcast support for instance Component -> StaticModel // we never want to hit this path for script inherited natives NativeType nativeType; if (!nativeClassIDToNativeType.TryGetValue(classID, out nativeType)) { throw new InvalidOperationException("NativeCore.WrapNative - Attempting to wrap unknown native class id"); } // TODO: make CSComponent abstract and have general abstract logic here? if (nativeType.Type == typeof(CSComponent)) { return(null); } r = nativeType.managedConstructor(native); w = new WeakReference <RefCounted>(r); NativeCore.nativeLookup[native] = w; // store a ref, so native side will not be released while we still have a reference in managed code r.AddRef(); // Note: r.InstantiationType may be INSTANTIATION_NET here is we were GC'd, native still had a reference, and we came back if (r.InstantiationType == InstantiationType.INSTANTIATION_NET) { //Log.Warn($"Wrapped {r.GetType().Name} was originally instantiated in NET, changing to native, this is likely an error"); //r.InstantiationType = InstantiationType.INSTANTIATION_NATIVE; } r.PostNativeUpdate(); return((T)r); }
static private void registerSubsystem(RefCounted subsystem) { subSystems[subsystem.GetType()] = subsystem; }
public Skeleton(RefCounted owner, IntPtr nativeSkeleton) { this.owner = owner; this.nativeSkeleton = nativeSkeleton; }
public static void UpdateDispatch(float timeStep) { RefCounted.ReleaseFinalized(); }