Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qx rd outer lifetime #219

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions rd-net/RdFramework.Reflection/RdOuterLifetime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using JetBrains.Annotations;
using JetBrains.Lifetimes;
using JetBrains.Rd.Base;
using JetBrains.Rd.Impl;
using JetBrains.Serialization;

namespace JetBrains.Rd.Reflection
{
/// <summary>
/// Abstraction to send <see cref="OuterLifetime"/> over the protocol
/// </summary>
public sealed class RdOuterLifetime: RdReactiveBase
{
[NonSerialized] private LifetimeDefinition myLifetimeDefinition;
[NonSerialized] private bool myIsClientSide = false;

public static implicit operator OuterLifetime (RdOuterLifetime rdOuterLifetime)
{
return rdOuterLifetime.myLifetimeDefinition;
}

[UsedImplicitly]
public RdOuterLifetime()
{
}

protected override void Init(Lifetime lifetime)
{
base.Init(lifetime);
myLifetimeDefinition = Lifetime.Define(lifetime);
myLifetimeDefinition.AllowTerminationUnderExecution = true;
Proto.Wire.Advise(lifetime, this);

if (!myIsClientSide)
myLifetimeDefinition.Lifetime.OnTermination(() => Proto.Wire.Send(RdId, writer => writer.Write(1)));
}

public override void OnWireReceived(UnsafeReader reader)
{
myLifetimeDefinition.Terminate();
}

/// <summary>
/// Used on a sender side to
/// </summary>
public void AttachToLifetime(Lifetime lifetime)
{
if (!lifetime.TryOnTermination(myLifetimeDefinition))
myLifetimeDefinition.Terminate();
}

#region Intrinsic

public static RdOuterLifetime Read(SerializationCtx ctx, UnsafeReader reader)
{
var id = reader.ReadRdId();
var rdOuterLifetime = new RdOuterLifetime()
{
myIsClientSide = true
}.WithId(id);
return rdOuterLifetime;
}

public static void Write(SerializationCtx ctx, UnsafeWriter writer, RdOuterLifetime value)
{
writer.Write(value.RdId);
}

#endregion
}
}
4 changes: 3 additions & 1 deletion rd-net/RdFramework.Reflection/ReflectionRdActivator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ private object ActivateRd(Type type)
#endif

var typeInfo = type.GetTypeInfo();
ReflectionSerializerVerifier.AssertEitherExtModelAttribute(typeInfo);
if (!ReflectionSerializerVerifier.IsSealedClassAssignableFromIRdBindable(typeInfo))
ReflectionSerializerVerifier.AssertEitherExtModelAttribute(typeInfo);

var implementingType = ReflectionSerializerVerifier.GetImplementingType(typeInfo);
Assertion.Assert(typeof(RdBindableBase).GetTypeInfo().IsAssignableFrom(implementingType),
$"Unable to activate {type.FullName}: type should be {nameof(RdBindableBase)}");
Expand Down
13 changes: 11 additions & 2 deletions rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ private static bool IsMemberType(TypeInfo typeInfo)
genericDefinition == typeof(RdMap<,>) || // TResponse can be LiveModel
(genericDefinition == typeof(RdCall<,>) && IsScalar(arguments[0]) /*&& IsScalar(arguments[1])*/) ||
// Custom classes support
(typeInfo.IsClass && typeInfo.IsSealed && typeof(IRdBindable).IsAssignableFrom(typeInfo));
(IsSealedClassAssignableFromIRdBindable(typeInfo));
}

if (IsScalar(typeInfo))
Expand All @@ -201,9 +201,18 @@ private static bool IsMemberType(TypeInfo typeInfo)
if (hasRdModel)
return true;

// Custom classes support
if (IsSealedClassAssignableFromIRdBindable(typeInfo))
return true;

return false;
}

public static bool IsSealedClassAssignableFromIRdBindable(TypeInfo typeInfo)
{
return typeInfo.IsClass && typeInfo.IsSealed && typeof(IRdBindable).IsAssignableFrom(typeInfo);
}

public static bool IsScalar(Type type)
{
return !typeof(IRdBindable).IsAssignableFrom(type);
Expand All @@ -212,7 +221,7 @@ public static bool IsScalar(Type type)
public static void AssertEitherExtModelAttribute(TypeInfo type)
{
/*Assertion.Assert((HasRdExtAttribute(type) || HasRdModelAttribute(type)), $"Invalid RdModel: expected to have either {nameof(RdModelAttribute)} or {nameof(RdExtAttribute)} ({type.ToString(true)}).");*/
Assertion.Assert(HasRdExtAttribute(type) ^ HasRdModelAttribute(type), $"Invalid RdModel {type.ToString(true)}: expected to have only one of {nameof(RdModelAttribute)} or {nameof(RdExtAttribute)}.");
Assertion.Assert(HasRdExtAttribute(type) ^ HasRdModelAttribute(type), $"Invalid RdModel {type.ToString(true)}: expected to have only one of {nameof(RdModelAttribute)} or {nameof(RdExtAttribute)} or be a sealed class assignable from {nameof(IRdBindable)}.");
}

public static void AssertRoot(TypeInfo type)
Expand Down
69 changes: 69 additions & 0 deletions rd-net/Test.RdFramework/RdOuterLifetimeTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using JetBrains.Collections.Viewable;
using JetBrains.Lifetimes;
using JetBrains.Rd.Reflection;
using NUnit.Framework;
using Test.RdFramework.Reflection;

namespace Test.RdFramework
{
[TestFixture]
public class RdOuterLifetimeTest: RdReflectionTestBase
{
private Root myS;
private Root myC;

[RdExt]
public sealed class Root : RdExtReflectionBindableBase
{
public IViewableProperty<Model> PolyProperty { get; }
}

[RdModel]
public sealed class Model : RdReflectionBindableBase
{
public RdOuterLifetime OuterLifetime { get; }
}

public override void SetUp()
{
base.SetUp();
myS = SFacade.InitBind(new Root(), TestLifetime, ClientProtocol);
myC = CFacade.InitBind(new Root(), TestLifetime, ServerProtocol);
}

[Test]
public void Test01()
{
Lifetime.Using(lifetime =>
{
bool sTerminated = false;
var m = CFacade.Activator.Activate<Model>();
myC.PolyProperty.Value = m;
var ld2 = OuterLifetime.DefineIntersection(lifetime, myS.PolyProperty.Value.OuterLifetime);
ld2.Lifetime.OnTermination(() => sTerminated = true);

Lifetime.Using(outerLifetime => m.OuterLifetime.AttachToLifetime(outerLifetime));

Assert.IsTrue(sTerminated, "sTerminated");
});
}

[Test]
public void TestTerminatedLifetime01()
{
Lifetime.Using(lifetime =>
{
bool sTerminated = false;
var m = CFacade.Activator.Activate<Model>();
myC.PolyProperty.Value = m;
var ld2 = OuterLifetime.DefineIntersection(lifetime, myS.PolyProperty.Value.OuterLifetime);
ld2.Lifetime.OnTermination(() => sTerminated = true);

var lifetimeDefinition = Lifetime.Define(lifetime);
lifetimeDefinition.Terminate();
m.OuterLifetime.AttachToLifetime(lifetimeDefinition.Lifetime);
Assert.IsTrue(sTerminated, "sTerminated");
});
}
}
}