NetReference gets created for each .Net ref

(instad of for each .Net object)
Object Tags get passed as objectIds and this id is used to find a NetValue (which represents the .Net instance)
In contrast to before now a GCHandle gets created every time a .Net object enters the QML boundary
This commit is contained in:
Michael Lamers 2018-07-21 18:34:44 +02:00
parent ee7765dfe8
commit 177aec0537
12 changed files with 63 additions and 59 deletions

View file

@ -5,10 +5,10 @@
NetValue::~NetValue()
{
auto hit = netValues.find(instance.data());
if(hit != netValues.end())
auto hit = objectIdNetValuesMap.find(instance->getObjectId());
if(hit != objectIdNetValuesMap.end())
{
netValues.erase(hit);
objectIdNetValuesMap.erase(hit);
}
qDebug("NetValue deleted: %s", qPrintable(instance->getTypeInfo()->getClassName()));
if(instance != nullptr) {
@ -78,9 +78,10 @@ bool NetValue::activateSignal(QString signalName, QSharedPointer<NetVariantList>
NetValue* NetValue::forInstance(QSharedPointer<NetReference> instance, bool autoCreate)
{
if(netValues.find(instance.data()) != netValues.end())
auto objectId = instance->getObjectId();
if(objectIdNetValuesMap.find(objectId) != objectIdNetValuesMap.end())
{
return netValues.at(instance.data());
return objectIdNetValuesMap.at(objectId);
}
if(!autoCreate)
{
@ -96,8 +97,8 @@ NetValue::NetValue(QSharedPointer<NetReference> instance, QObject *parent)
{
valueMeta = new NetValueMetaObject(this, instance);
setParent(parent);
netValues[instance.data()] = this;
objectIdNetValuesMap[instance->getObjectId()] = this;
qDebug("NetValue created: %s", qPrintable(instance->getTypeInfo()->getClassName()));
}
std::map<NetReference*, NetValue*> NetValue::netValues = std::map<NetReference*, NetValue*>();
std::map<uint64_t, NetValue*> NetValue::objectIdNetValuesMap = std::map<uint64_t, NetValue*>();

View file

@ -36,7 +36,7 @@ private:
QSharedPointer<NetReference> instance;
NetValueMetaObject* valueMeta;
static std::map<NetReference*, NetValue*> netValues;
static std::map<uint64_t, NetValue*> objectIdNetValuesMap;
};
#endif // NETVALUE_H

View file

@ -3,8 +3,9 @@
#include <QmlNet/qml/NetValue.h>
#include <QDebug>
NetReference::NetReference(NetGCHandle* gcHandle, QSharedPointer<NetTypeInfo> typeInfo) :
NetReference::NetReference(NetGCHandle* gcHandle, uint64_t objectId, QSharedPointer<NetTypeInfo> typeInfo) :
gcHandle(gcHandle),
objectId(objectId),
typeInfo(typeInfo)
{
qDebug("NetReference created: %s", qPrintable(typeInfo->getClassName()));
@ -20,6 +21,11 @@ NetGCHandle* NetReference::getGCHandle()
return gcHandle;
}
uint64_t NetReference::getObjectId()
{
return objectId;
}
QSharedPointer<NetTypeInfo> NetReference::getTypeInfo()
{
return typeInfo;
@ -38,9 +44,9 @@ void NetReference::release()
extern "C" {
Q_DECL_EXPORT NetReferenceContainer* net_instance_create(NetGCHandle* handle, NetTypeInfoContainer* typeContainer) {
Q_DECL_EXPORT NetReferenceContainer* net_instance_create(NetGCHandle* handle, uint64_t objectId, NetTypeInfoContainer* typeContainer) {
NetReferenceContainer* result = new NetReferenceContainer();
result->instance = QSharedPointer<NetReference>(new NetReference(handle, typeContainer->netTypeInfo));
result->instance = QSharedPointer<NetReference>(new NetReference(handle, objectId, typeContainer->netTypeInfo));
return result;
}
@ -57,6 +63,10 @@ Q_DECL_EXPORT NetGCHandle* net_instance_getHandle(NetReferenceContainer* contain
return container->instance->getGCHandle();
}
Q_DECL_EXPORT uint64_t net_instance_getObjectId(NetReferenceContainer* container) {
return container->instance->getObjectId();
}
Q_DECL_EXPORT bool net_instance_activateSignal(NetReferenceContainer* container, LPWSTR signalName, NetVariantListContainer* parametersContainer) {
NetValue* existing = NetValue::forInstance(container->instance);
if(existing == NULL) {

View file

@ -6,14 +6,16 @@
class NetReference
{
public:
NetReference(NetGCHandle* gcHandle, QSharedPointer<NetTypeInfo> typeInfo);
NetReference(NetGCHandle* gcHandle, uint64_t objectId, QSharedPointer<NetTypeInfo> typeInfo);
~NetReference();
NetGCHandle* getGCHandle();
uint64_t getObjectId();
QSharedPointer<NetTypeInfo> getTypeInfo();
void release();
private:
NetGCHandle* gcHandle;
uint64_t objectId;
QSharedPointer<NetTypeInfo> typeInfo;
};

View file

@ -26,7 +26,7 @@ namespace Qml.Net.Tests.Qml
var testObject = new TestObject();
var variant = new NetVariant();
variant.Instance.Should().BeNull();
variant.Instance = NetReference.GetForObject(testObject);
variant.Instance = NetReference.CreateForObject(testObject);
variant.Instance.Should().NotBeNull();
variant.Instance.Instance.Should().Be(testObject);
variant.VariantType.Should().Be(NetVariantType.Object);

View file

@ -35,7 +35,7 @@ namespace Qml.Net.Tests.Types
{
var o = new TestObject();
reference = new WeakReference(o);
instance = NetReference.GetForObject(o);
instance = NetReference.CreateForObject(o);
}).Wait();
// NetReference is still alive, so the weak reference must be alive as well.
@ -67,7 +67,7 @@ namespace Qml.Net.Tests.Types
var o = new TestObject();
var type = NetTypeManager.GetTypeInfo<TestObject>();
var method = type.GetMethod(0);
var instance = NetReference.GetForObject(o);
var instance = NetReference.CreateForObject(o);
// This will jump to native, to then call the .NET delegate (round trip).
// The purpose is to simulate Qml invoking a method, sending .NET instance back.

View file

@ -15,7 +15,7 @@ namespace Qml.Net.Tests.Types
public void Can_create_net_instance()
{
var o = new TestObject();
var instance = NetReference.GetForObject(o);
var instance = NetReference.CreateForObject(o);
var returnedInstance = instance.Instance;

View file

@ -142,7 +142,7 @@ namespace Qml.Net.Internal
var typeCreator = NetReference.TypeCreator;
var instance = typeCreator != null ? typeCreator.Create(typeInfo) : Activator.CreateInstance(typeInfo);
var netReference = NetReference.GetForObject(instance);
var netReference = NetReference.CreateForObject(instance);
// When .NET collects this NetReference, we don't want it to delete this
// handle. Ownership has been passed to the caller.
return Interop.NetReference.Clone(netReference.Handle);

View file

@ -38,7 +38,7 @@ namespace Qml.Net.Internal
destination.DateTime = (DateTime)source;
else
{
destination.Instance = NetReference.GetForObject(source);
destination.Instance = NetReference.CreateForObject(source);
}
}
}

View file

@ -13,14 +13,14 @@ namespace Qml.Net.Internal
{
}
private ObjectId(ulong id)
private ObjectId(UInt64 id)
{
Id = id;
}
public ulong Id { get; private set; }
public UInt64 Id { get; private set; }
public static implicit operator ulong(ObjectId oId)
public static implicit operator UInt64(ObjectId oId)
{
return oId.Id;
}
@ -32,23 +32,23 @@ namespace Qml.Net.Internal
#region Id management
private static ulong NextId = 1;
private static HashSet<ulong> UsedIds = new HashSet<ulong>();
private static UInt64 NextId = 1;
private static HashSet<UInt64> UsedIds = new HashSet<UInt64>();
private static ulong TakeNextFreeId()
private static UInt64 TakeNextFreeId()
{
ulong nextId = NextId;
UInt64 nextId = NextId;
UsedIds.Add(nextId);
NextId = CalculateNextFreeId(nextId);
return nextId;
}
private static ulong CalculateNextFreeId(ulong nextId)
private static UInt64 CalculateNextFreeId(UInt64 nextId)
{
bool firstPass = true;
while (UsedIds.Contains(nextId))
{
if(nextId == ulong.MaxValue)
if(nextId == UInt64.MaxValue)
{
if(!firstPass)
{
@ -65,7 +65,7 @@ namespace Qml.Net.Internal
return nextId;
}
private static void FreeId(ulong id)
private static void FreeId(UInt64 id)
{
UsedIds.Remove(id);
}
@ -82,7 +82,7 @@ namespace Qml.Net.Internal
{
private static readonly ConditionalWeakTable<object, ObjectId> ObjectIdRefs = new ConditionalWeakTable<object, ObjectId>();
public static ulong GetOrCreateTag(this object obj)
public static UInt64 GetOrCreateTag(this object obj)
{
var result = GetTag(obj);
if(result.HasValue)
@ -94,7 +94,7 @@ namespace Qml.Net.Internal
return newObjId;
}
public static ulong? GetTag(this object obj)
public static UInt64? GetTag(this object obj)
{
if (ObjectIdRefs.TryGetValue(obj, out var objId))
{

View file

@ -6,7 +6,7 @@ namespace Qml.Net
{
public static bool ActivateSignal(this object instance, string signalName, params object[] args)
{
var existing = NetReference.GetForObject(instance, false /*don't create one if doesn't exist*/);
var existing = NetReference.CreateForObject(instance);
return existing != null && existing.ActivateSignal(signalName, args);
}
}

View file

@ -1,6 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using AdvancedDLSupport;
using Qml.Net.Internal;
using Qml.Net.Qml;
@ -9,8 +10,8 @@ namespace Qml.Net.Types
{
public class NetReference : BaseDisposable
{
private NetReference(IntPtr gcHandle, NetTypeInfo type, bool ownsHandle = true)
:base(Interop.NetReference.Create(gcHandle, type.Handle), ownsHandle)
private NetReference(IntPtr gcHandle, UInt64 objectId, NetTypeInfo type, bool ownsHandle = true)
:base(Interop.NetReference.Create(gcHandle, objectId, type.Handle), ownsHandle)
{
}
@ -29,6 +30,14 @@ namespace Qml.Net.Types
}
}
public ulong ObjectId
{
get
{
return Interop.NetReference.GetObjectId(Handle);
}
}
public NetReference Clone()
{
return new NetReference(Interop.NetReference.Clone(Handle));
@ -74,37 +83,17 @@ namespace Qml.Net.Types
return type;
}
private static readonly ConditionalWeakTable<object, NetReference> ObjectNetReferenceConnections = new ConditionalWeakTable<object, NetReference>();
public static bool ExistsForObject(object value)
{
return ObjectNetReferenceConnections.TryGetValue(value, out NetReference NetReference);
}
public static NetReference GetForObject(object value, bool autoCreate = true)
public static NetReference CreateForObject(object value)
{
if (value == null) return null;
var alreadyExists = false;
if (ObjectNetReferenceConnections.TryGetValue(value, out var NetReference))
{
alreadyExists = true;
if (GCHandle.FromIntPtr(NetReference.Handle).IsAllocated)
{
return NetReference;
}
}
if (!autoCreate) return null;
var typeInfo = NetTypeManager.GetTypeInfo(GetUnproxiedType(value.GetType()).AssemblyQualifiedName);
if(typeInfo == null) throw new InvalidOperationException($"Couldn't create type info from {value.GetType().AssemblyQualifiedName}");
var handle = GCHandle.Alloc(value);
var newNetReference = new NetReference(GCHandle.ToIntPtr(handle), typeInfo);
if(alreadyExists)
{
ObjectNetReferenceConnections.Remove(value);
}
ObjectNetReferenceConnections.Add(value, newNetReference);
var objectId = value.GetOrCreateTag();
var newNetReference = new NetReference(GCHandle.ToIntPtr(handle), objectId, typeInfo);
return newNetReference;
}
@ -114,7 +103,7 @@ namespace Qml.Net.Types
public interface INetReferenceInterop
{
[NativeSymbol(Entrypoint = "net_instance_create")]
IntPtr Create(IntPtr handle, IntPtr type);
IntPtr Create(IntPtr handle, UInt64 objectId, IntPtr type);
[NativeSymbol(Entrypoint = "net_instance_destroy")]
void Destroy(IntPtr instance);
[NativeSymbol(Entrypoint = "net_instance_clone")]
@ -122,6 +111,8 @@ namespace Qml.Net.Types
[NativeSymbol(Entrypoint = "net_instance_getHandle")]
IntPtr GetHandle(IntPtr instance);
[NativeSymbol(Entrypoint = "net_instance_getObjectId")]
UInt64 GetObjectId(IntPtr instance);
[NativeSymbol(Entrypoint = "net_instance_activateSignal")]
bool ActivateSignal(IntPtr instance, [MarshalAs(UnmanagedType.LPWStr)]string signalName, IntPtr variants);
}