Skip to content

Commit

Permalink
Merge pull request #451 from JetBrains/backend-listeners-support-usov
Browse files Browse the repository at this point in the history
Backend listeners support
  • Loading branch information
Iliya-usov authored Dec 4, 2023
2 parents b198ed8 + 6cdf84d commit 1823afd
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.jetbrains.rd.framework

import com.jetbrains.rd.framework.base.AllowBindingCookie
import com.jetbrains.rd.framework.base.RdExtBase
import com.jetbrains.rd.framework.base.bind
import com.jetbrains.rd.framework.base.bindTopLevel
import com.jetbrains.rd.framework.impl.InternRoot
import com.jetbrains.rd.framework.impl.ProtocolContexts
Expand Down Expand Up @@ -48,6 +47,8 @@ class Protocol internal constructor(

override val isMaster: Boolean = identity.dynamicKind == IdKind.Client

val rdEntitiesRegistrar: RdEntitiesRegistrar = parentProtocol?.rdEntitiesRegistrar ?: RdEntitiesRegistrar()

companion object {
val logCategory = "protocol"
fun sublogger(subcategory: String) = getLogger("$logCategory.$subcategory")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.jetbrains.rd.framework

import com.jetbrains.rd.util.ConcurrentHashMap
import com.jetbrains.rd.util.addUnique
import com.jetbrains.rd.util.lifetime.Lifetime

class RdEntitiesRegistrar {
private val map = ConcurrentHashMap<RdId, IRdDynamic>()

internal fun register(lifetime: Lifetime, rdId: RdId, dynamic: IRdDynamic) {
require(!rdId.isNull)
map.addUnique(lifetime, rdId, dynamic)
}

fun tryGetDynamic(rdId: RdId): IRdDynamic? = map[rdId]
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ abstract class RdBindableBase : IRdBindable, IPrintable {

assertBindingThread()

if (proto is Protocol && !rdid.isNull)
proto.rdEntitiesRegistrar.register(bindLifetime, rdid, this)

Signal.priorityAdviseSection {
preInit(lf, proto)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ abstract class RdExtBase : RdReactiveBase() {
super.init(lifetime, proto, ctx)
}

val info = ExtCreationInfo(location, (parent as? RdBindableBase)?.containingExt?.rdid, serializationHash, this)
val info = ExtCreationInfo(location, (parent as? RdBindableBase)?.rdid, serializationHash, this)

if (scheduler is CustomExtScheduler) {
assert(customSchedulerWrapper == null)
Expand Down
25 changes: 24 additions & 1 deletion rd-net/Lifetimes/Collections/Viewable/Signal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class Signal

internal static bool IsPriorityAdvise => ourPriorityCookie > 0;

public struct PriorityAdviseCookie : IDisposable
public ref struct PriorityAdviseCookie
{
public static PriorityAdviseCookie Create()
{
Expand All @@ -25,6 +25,29 @@ public void Dispose()
ourPriorityCookie--;
}
}

public ref struct NonPriorityAdviseCookie
{
private readonly int myOldValue;

private NonPriorityAdviseCookie(int oldValue)
{
myOldValue = oldValue;
}

public static NonPriorityAdviseCookie Create()
{
var oldValue = ourPriorityCookie;
ourPriorityCookie = 0;
return new NonPriorityAdviseCookie(oldValue);
}

public void Dispose()
{
Assertion.Assert(ourPriorityCookie == 0);
ourPriorityCookie = myOldValue;
}
}
}


Expand Down
10 changes: 5 additions & 5 deletions rd-net/Lifetimes/Diagnostics/RName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ public class RName
[PublicAPI] public static readonly RName Empty = new RName(null, "", "");

[PublicAPI]
public readonly object? Parent;
public readonly RName? Parent;
[PublicAPI]
public readonly string Separator;
[PublicAPI]
public readonly object LocalName;
public readonly string LocalName;


public RName(object? parent, object localName, string separator)
public RName(RName? parent, string localName, string separator)
{
Parent = parent;
Separator = separator ?? throw new ArgumentNullException(nameof(separator));
LocalName = localName ?? throw new ArgumentNullException(nameof(localName));
}

public RName(object localName) : this(Empty, localName, "") {}
public RName(string localName) : this(Empty, localName, "") {}

/// <summary>
/// Separator doesn't count if localName is empty or parent is empty.
Expand All @@ -34,7 +34,7 @@ public RName(object localName) : this(Empty, localName, "") {}
/// <param name="separator"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public RName Sub(object localName, string separator=".")
public RName Sub(string localName, string separator=".")
{
if (localName == null) throw new ArgumentNullException(nameof(localName));
if (localName is string s && s.Length == 0)
Expand Down
6 changes: 6 additions & 0 deletions rd-net/RdFramework/Base/RdBindableBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using JetBrains.Collections.Viewable;
using JetBrains.Diagnostics;
using JetBrains.Lifetimes;
using JetBrains.Rd.Impl;
using JetBrains.Rd.Util;
using JetBrains.Util.Internal;
using JetBrains.Util.Util;
Expand Down Expand Up @@ -90,6 +91,11 @@ public void PreBind(Lifetime lf, IRdDynamic parent, string name)

AssertBindingThread();

if (proto is Protocol proto2 && !RdId.IsNil)
{
proto2.RdEntitiesRegistrar.Register(lf, RdId, this);
}

using (Signal.PriorityAdviseCookie.Create())
PreInit(lf, proto);

Expand Down
19 changes: 14 additions & 5 deletions rd-net/RdFramework/Base/RdExtBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,23 @@ protected override void Init(Lifetime lifetime, IProtocol parentProto, Serializa
lifetime.TryBracket(
() =>
{
var proto = new Protocol(parentProto.Name, parentProto.Serializers, parentProto.Identities, extScheduler, myExtWire, lifetime, serializationContext, parentProto.Contexts, parentProto.ExtCreated, this.CreateExtSignal());
var parentProtocolImpl = (Protocol)parentProto;
var proto = new Protocol(parentProto.Name, parentProto.Serializers, parentProto.Identities, extScheduler, myExtWire, lifetime, parentProtocolImpl, this.CreateExtSignal());
myExtProtocol = proto;

//protocol must be set first to allow bindable bind to it
base.PreInit(lifetime, proto);
base.Init(lifetime, proto, ctx);
using (AllowBindCookie.Create())
{
base.PreInit(lifetime, proto);
base.Init(lifetime, proto, ctx);
}

var bindableParent = Parent as RdBindableBase;
var info = new ExtCreationInfo(Location, bindableParent?.ContainingExt?.RdId, SerializationHash, this);
((Protocol)parentProto).SubmitExtCreated(info);
var info = new ExtCreationInfo(Location, bindableParent?.RdId, SerializationHash, this);
using (Signal.NonPriorityAdviseCookie.Create())
{
parentProtocolImpl.SubmitExtCreated(info);
}

parentWire.Advise(lifetime, this);
SendState(parentWire, ExtState.Ready);
Expand All @@ -85,6 +92,8 @@ protected override void Init(Lifetime lifetime, IProtocol parentProto, Serializa
);
}

protected override void AssertBindingThread() { }


public override void OnWireReceived(IProtocol proto, SerializationCtx ctx, UnsafeReader reader, IRdWireableDispatchHelper dispatchHelper)
{
Expand Down
22 changes: 17 additions & 5 deletions rd-net/RdFramework/ExtCreationInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

namespace JetBrains.Rd
{
public struct ExtCreationInfo
public readonly struct ExtCreationInfo
{
public RName Name;
public RdId? Id;
public long Hash;
public RdExtBase? Ext;
public readonly RName Name;
public readonly RdId? Id;
public readonly long Hash;
public readonly RdExtBase? Ext;

public ExtCreationInfo(RName name, RdId? id, long hash, RdExtBase? ext)
{
Expand All @@ -18,4 +18,16 @@ public ExtCreationInfo(RName name, RdId? id, long hash, RdExtBase? ext)
Ext = ext;
}
}

public readonly struct ExtCreationInfoEx
{
public readonly ExtCreationInfo Info;
public readonly bool IsLocal;

public ExtCreationInfoEx(ExtCreationInfo info, bool isLocal)
{
Info = info;
IsLocal = isLocal;
}
}
}
7 changes: 5 additions & 2 deletions rd-net/RdFramework/IProtocol.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using JetBrains.Annotations;
using System;
using JetBrains.Collections.Viewable;
using JetBrains.Lifetimes;
using JetBrains.Rd.Base;
Expand All @@ -17,6 +17,9 @@ public interface IProtocol : IRdDynamic
IScheduler Scheduler { get; }
IWire Wire { get; }
ProtocolContexts Contexts { get; }
ISignal<ExtCreationInfo> ExtCreated { get; }
ISignal<ExtCreationInfoEx> ExtCreated { get; }

public T? GetExtension<T>() where T : RdExtBase;
public T GetOrCreateExtension<T>(Func<T> create) where T : RdExtBase;
}
}
1 change: 1 addition & 0 deletions rd-net/RdFramework/Impl/ExtCreatedUtils.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using JetBrains.Annotations;
using JetBrains.Collections.Viewable;
using JetBrains.Diagnostics;
using JetBrains.Rd.Base;
using JetBrains.Serialization;
Expand Down
72 changes: 61 additions & 11 deletions rd-net/RdFramework/Impl/Protocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Threading;
using JetBrains.Annotations;
using JetBrains.Collections.Synchronized;
using JetBrains.Collections.Viewable;
using JetBrains.Diagnostics;
using JetBrains.Lifetimes;
Expand Down Expand Up @@ -29,14 +30,18 @@ public class Protocol : IProtocol

public Lifetime Lifetime { get; }

public RdEntitiesRegistrar RdEntitiesRegistrar { get; }

private readonly Protocol? myParentProtocol;
private readonly Dictionary<string, object> myExtensions = new();

public Protocol(string name, ISerializers serializers, IIdentities identities, IScheduler scheduler,
IWire wire, Lifetime lifetime, params RdContextBase[] initialContexts)
: this(name, serializers, identities, scheduler, wire, lifetime, null, null, null, null, initialContexts)
: this(name, serializers, identities, scheduler, wire, lifetime, null, null, initialContexts)
{ }

internal Protocol(string name, ISerializers serializers, IIdentities identities, IScheduler scheduler,
IWire wire, Lifetime lifetime, SerializationCtx? serializationCtx = null, ProtocolContexts? parentContexts = null,
ISignal<ExtCreationInfo>? parentExtCreated = null, RdSignal<ExtCreationInfo>? parentExtConfirmation = null, params RdContextBase[] initialContexts)
IWire wire, Lifetime lifetime, Protocol? parentProtocol, RdSignal<ExtCreationInfo>? parentExtConfirmation = null, params RdContextBase[] initialContexts)
{
Lifetime = lifetime;
Name = name ?? throw new ArgumentNullException(nameof(name));
Expand All @@ -46,22 +51,23 @@ internal Protocol(string name, ISerializers serializers, IIdentities identities,
Identities = identities ?? throw new ArgumentNullException(nameof(identities));
Scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
Wire = wire ?? throw new ArgumentNullException(nameof(wire));
SerializationContext = serializationCtx ?? new SerializationCtx(this, new Dictionary<string, IInternRoot<object>>() {{ProtocolInternScopeStringId, CreateProtocolInternRoot(lifetime)}});
Contexts = parentContexts ?? new ProtocolContexts(SerializationContext);
myParentProtocol = parentProtocol;
RdEntitiesRegistrar = parentProtocol?.RdEntitiesRegistrar ?? new RdEntitiesRegistrar();
SerializationContext = parentProtocol?.SerializationContext ?? new SerializationCtx(this, new Dictionary<string, IInternRoot<object>>() {{ProtocolInternScopeStringId, CreateProtocolInternRoot(lifetime)}});
Contexts = parentProtocol?.Contexts ?? new ProtocolContexts(SerializationContext);
wire.Contexts = Contexts;
if (serializationCtx == null)
if (parentProtocol?.SerializationContext == null)
SerializationContext.InternRoots[ProtocolInternScopeStringId].BindTopLevel(lifetime, this, ProtocolInternRootRdId);
foreach (var rdContextBase in initialContexts) rdContextBase.RegisterOn(Contexts);
if (parentContexts == null)
if (parentProtocol?.Contexts == null)
BindContexts(lifetime);
OutOfSyncModels = new ViewableSet<RdExtBase>();
ExtCreated = parentExtCreated ?? new Signal<ExtCreationInfo>();
ExtCreated = parentProtocol?.ExtCreated ?? new Signal<ExtCreationInfoEx>();
ExtConfirmation = parentExtConfirmation ?? this.CreateExtSignal();
ExtIsLocal = new ThreadLocal<bool>(() => false);
ExtConfirmation.Advise(lifetime, message =>
{
if (ExtIsLocal.Value) return;
ExtCreated.Fire(message);
ExtCreated.Fire(new ExtCreationInfoEx(message, ExtIsLocal.Value));
});
using (AllowBindCookie.Create())
ExtConfirmation.BindTopLevel(lifetime, this, ProtocolExtCreatedRdId);
Expand Down Expand Up @@ -119,7 +125,7 @@ internal void SubmitExtCreated(ExtCreationInfo info)

public ProtocolContexts Contexts { get; }

public ISignal<ExtCreationInfo> ExtCreated { get; }
public ISignal<ExtCreationInfoEx> ExtCreated { get; }

private RdSignal<ExtCreationInfo> ExtConfirmation { get; }

Expand All @@ -130,5 +136,49 @@ internal void SubmitExtCreated(ExtCreationInfo info)

public RName Location { get; }
IProtocol IRdDynamic.TryGetProto() => this;

public virtual T? GetExtension<T>() where T : RdExtBase
{
var parentProtocol = myParentProtocol;
if (parentProtocol != null)
return parentProtocol.GetExtension<T>();

lock (myExtensions)
{
return myExtensions.TryGetValue(typeof(T).Name, out var extension) ? (T)extension : default;
}
}

public virtual T GetOrCreateExtension<T>(Func<T> create) where T : RdExtBase
{
if (create == null) throw new ArgumentNullException(nameof(create));

var parentProtocol = myParentProtocol;
if (parentProtocol != null)
return parentProtocol.GetOrCreateExtension(create);

lock (myExtensions)
{
var name = typeof(T).Name;
if (myExtensions.TryGetValue(name, out var existing))
{
var val = existing.NotNull("Found null value for key: '{0}'", name) as T;
Assertion.Require(val != null, $"Found bad value for key '{name}'. Expected type: '{typeof(T).FullName}', actual:'{existing.GetType().FullName}");
return val;
}

var res = create().NotNull("'Create' result must not be null");

myExtensions[name] = res;
if (res is IRdBindable rdBindable)
{
rdBindable.Identify(Identities, RdId.Root.Mix(name));
rdBindable.PreBind(Lifetime, this, name);
rdBindable.Bind();
}

return res;
}
}
}
}
27 changes: 27 additions & 0 deletions rd-net/RdFramework/Impl/RdEntitiesRegistrar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using JetBrains.Diagnostics;
using JetBrains.Lifetimes;
using JetBrains.Rd.Base;
using JetBrains.Rd.Util;

namespace JetBrains.Rd.Impl;

public class RdEntitiesRegistrar
{
private readonly Dictionary<RdId, IRdDynamic> myMap = new();

internal void Register(Lifetime lifetime, RdId rdId, IRdDynamic dynamic)
{
Assertion.Assert(!rdId.IsNil);

myMap.BlockingAddUnique(lifetime, myMap, rdId, dynamic);
}

public bool TryGetEntity(RdId rdId, out IRdDynamic entity)
{
lock (myMap)
{
return myMap.TryGetValue(rdId, out entity);
}
}
}
Loading

0 comments on commit 1823afd

Please sign in to comment.