Skip to content

Commit

Permalink
feat: save uncomplete work - ipc contract interface support the cshar…
Browse files Browse the repository at this point in the history
…p event. (#12)
  • Loading branch information
DingpingZhang committed Nov 15, 2021
1 parent 67c43fc commit 2ae431a
Show file tree
Hide file tree
Showing 21 changed files with 497 additions and 80 deletions.
21 changes: 20 additions & 1 deletion HandyIpc.Generator/ClientProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace HandyIpc.Generator
{
public static class ClientProxy
{
public static string Generate(INamedTypeSymbol @interface, IReadOnlyCollection<IMethodSymbol> methods)
public static string Generate(INamedTypeSymbol @interface, IReadOnlyCollection<IMethodSymbol> methods, List<IEventSymbol> events)
{
var (@namespace, className, typeParameters) = @interface.GenerateNameFromInterface();
string interfaceType = @interface.ToFullDeclaration();
Expand All @@ -30,12 +30,31 @@ public class {nameof(ClientProxy)}{className}{typeParameters} : {interfaceType}
private readonly Sender _sender;
private readonly ISerializer _serializer;
private readonly string _key;
private readonly AwaitorManager _awaitorManager;
{events.For(item =>
{
var eSymbol = ((INamedTypeSymbol)item.Type).DelegateInvokeMethod.Parameters[1];
var eType = eSymbol.Type.ToTypeDeclaration();
return $@"
public event {item.Type.ToTypeDeclaration()} {item.Name}
{{
add => _awaitorManager.Subscribe(""{item.Name}"", value.GetHashCode(), args =>
{{
var e = ({eType})_serializer.Deserialize(args, typeof({eType}));
value(this, e);
}});
remove => _awaitorManager.Unsubscribe(""{item.Name}"", value.GetHashCode());
}}
";
}, RemoveLineIfEmpty)}
public {nameof(ClientProxy)}{className}(Sender sender, ISerializer serializer, string key)
{{
_sender = sender;
_serializer = serializer;
_key = key;
_awaitorManager = new AwaitorManager(key, sender, serializer);
}}
{methods.For(method =>
{
Expand Down
8 changes: 7 additions & 1 deletion HandyIpc.Generator/Dispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace HandyIpc.Generator
{
public static class Dispatcher
{
public static string Generate(INamedTypeSymbol @interface, IReadOnlyCollection<IMethodSymbol> methods)
public static string Generate(INamedTypeSymbol @interface, IReadOnlyCollection<IMethodSymbol> methods, List<IEventSymbol> events)
{
var (@namespace, className, typeParameters) = @interface.GenerateNameFromInterface();
string interfaceType = @interface.ToFullDeclaration();
Expand Down Expand Up @@ -36,13 +36,19 @@ public class {nameof(Dispatcher)}{className}{typeParameters} : IMethodDispatcher
private readonly Lazy<IReadOnlyDictionary<string, MethodInfo>> _genericMethodMapping;
" : RemoveLineIfEmpty)}
public NotifierManager NotifierManager {{ get; set; }}
public {nameof(Dispatcher)}{className}({interfaceType} instance)
{{
_instance = instance;
{Text(methods.Any(item => item.TypeParameters.Any()) ? $@"
_genericMethodMapping = new Lazy<IReadOnlyDictionary<string, MethodInfo>>(
() => GeneratorHelper.GetGenericMethodMapping(typeof({interfaceType}), _instance));
" : RemoveLineIfEmpty)}
{events.For(item => $@"
instance.{item.Name} += (_, e) => NotifierManager.Publish(""{item.Name}"", e);
", RemoveLineIfEmpty)}
}}
public async Task Dispatch(Context ctx, Func<Task> next)
Expand Down
10 changes: 9 additions & 1 deletion HandyIpc.Generator/ServerProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace HandyIpc.Generator
{
public static class ServerProxy
{
public static string Generate(INamedTypeSymbol @interface, IReadOnlyCollection<IMethodSymbol> methods)
public static string Generate(INamedTypeSymbol @interface, IReadOnlyCollection<IMethodSymbol> methods, List<IEventSymbol> events)
{
var (@namespace, className, typeParameters) = @interface.GenerateNameFromInterface();
string interfaceType = @interface.ToFullDeclaration();
Expand All @@ -26,9 +26,17 @@ public class {nameof(ServerProxy)}{className}{typeParameters} : {interfaceType}
{{
private readonly {interfaceType} _instance;
{events.For(item => $@"
public event {item.Type.ToTypeDeclaration()} {item.Name};
", RemoveLineIfEmpty)}
public {nameof(ServerProxy)}{className}({interfaceType} instance)
{{
_instance = instance;
{events.For(item => $@"
instance.{item.Name} += (sender, e) => {item.Name}?.Invoke(sender, e);
", RemoveLineIfEmpty)}
}}
{methods.For(method =>
{
Expand Down
28 changes: 20 additions & 8 deletions HandyIpc.Generator/SourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using static HandyIpc.Generator.DiagnosticDescriptors;
using System.Diagnostics;

namespace HandyIpc.Generator
{
Expand Down Expand Up @@ -43,12 +44,23 @@ public void Execute(GeneratorExecutionContext context)
.Select(@interface => @interface!)
.Where(@interface => ContainsAttribute(@interface, ipcContractAttributeSymbol));
})
.Select(@interface => (
@interface,
methods: @interface.GetMembers().OfType<IMethodSymbol>().ToList().AsReadOnly()));
.Select(@interface =>
{
var members = @interface.GetMembers().ToArray();
return (
@interface,
methods: members
.OfType<IMethodSymbol>()
.Where(item => item.MethodKind
is not MethodKind.EventAdd
and not MethodKind.EventRemove
and not MethodKind.EventRaise)
.ToList(),
events: members.OfType<IEventSymbol>().ToList());
});

var fileNameCounter = new Dictionary<string, int>();
foreach (var (@interface, methods) in contractInterfaces)
foreach (var (@interface, methods, events) in contractInterfaces)
{
if (@interface.Interfaces.Length > 0)
{
Expand All @@ -60,7 +72,7 @@ public void Execute(GeneratorExecutionContext context)
continue;
}

if (!methods.Any())
if (!methods.Any() && !events.Any())
{
foreach (Location location in @interface.Locations)
{
Expand All @@ -70,9 +82,9 @@ public void Execute(GeneratorExecutionContext context)
continue;
}

string clientProxySource = ClientProxy.Generate(@interface, methods);
string serverProxySource = ServerProxy.Generate(@interface, methods);
string dispatcherSource = Dispatcher.Generate(@interface, methods);
string clientProxySource = ClientProxy.Generate(@interface, methods, events);
string serverProxySource = ServerProxy.Generate(@interface, methods, events);
string dispatcherSource = Dispatcher.Generate(@interface, methods, events);

string fileName = GetUniqueString(@interface.Name, fileNameCounter);

Expand Down
44 changes: 44 additions & 0 deletions HandyIpc.Tests/EventTypeTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Threading.Tasks;
using HandyIpc;
using HandyIpcTests.Fixtures;
using HandyIpcTests.Interfaces;
using Xunit;

namespace HandyIpcTests
{
[Collection(nameof(CollectionFixture))]
public class EventTypeTest
{
private readonly NamedPipeFixture _namedPipeFixture;
private readonly SocketFixture _socketFixture;

public EventTypeTest(NamedPipeFixture namedPipeFixture, SocketFixture socketFixture)
{
_namedPipeFixture = namedPipeFixture;
_socketFixture = socketFixture;
}

[Fact]
public async Task TestEventHandler()
{
int count = 0;
var instance = _socketFixture.Client.Resolve<IEventType>();
instance.Changed += Instance_Changed;
instance.Changed += (sender, e) => count++;

instance.RaiseChanged(EventArgs.Empty);
instance.RaiseChanged(EventArgs.Empty);
instance.RaiseChanged(EventArgs.Empty);
instance.RaiseChanged(EventArgs.Empty);

await Task.Delay(2000);
Assert.Equal(3, count);
}

private void Instance_Changed(object? sender, EventArgs e)
{

}
}
}
3 changes: 2 additions & 1 deletion HandyIpc.Tests/Fixtures/ProtocolFixtureBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ protected ProtocolFixtureBase(ContainerClientBuilder clientBuilder, ContainerSer
serverBuilder
.Register<IBuildInType, BuildInTypeImpl>()
.Register(typeof(IGenericType<,>), typeof(GenericTypeImpl<,>))
.Register<ITaskReturnType, TaskReturnTypeImpl>();
.Register<ITaskReturnType, TaskReturnTypeImpl>()
.Register<IEventType, EventType>();

_server = serverBuilder.Build();
_server.Start();
Expand Down
15 changes: 15 additions & 0 deletions HandyIpc.Tests/Implementations/EventType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using HandyIpcTests.Interfaces;

namespace HandyIpcTests.Implementations
{
internal class EventType : IEventType
{
public event EventHandler? Changed;

public void RaiseChanged(EventArgs e)
{
Changed?.Invoke(this, e);
}
}
}
9 changes: 0 additions & 9 deletions HandyIpc.Tests/Interfaces/IEmpty.cs

This file was deleted.

13 changes: 13 additions & 0 deletions HandyIpc.Tests/Interfaces/IEventType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using HandyIpc;

namespace HandyIpcTests.Interfaces
{
[IpcContract]
public interface IEventType
{
event EventHandler Changed;

public void RaiseChanged(EventArgs e);
}
}
18 changes: 14 additions & 4 deletions HandyIpc/ContainerServerBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using HandyIpc.Core;

Expand Down Expand Up @@ -50,25 +51,34 @@ public IContainerRegistry Register(Type interfaceType, Func<Type[], object> fact
public IContainerServer Build()
{
Dictionary<string, Middleware> map = new();
ConcurrentDictionary<string, NotifierManager> notifiers = new();
foreach (var (key, type, factory) in _interfaceMap)
{
Middleware methodDispatcher = CreateDispatcher(type, factory).Dispatch;
IMethodDispatcher dispatcher = CreateDispatcher(type, factory);
dispatcher.NotifierManager = notifiers.GetOrAdd(key, _ => new NotifierManager(_serializerFactory()));
Middleware methodDispatcher = dispatcher.Dispatch;
map.Add(key, methodDispatcher);
}

foreach (var (key, type, factory) in _genericInterfaceMap)
{
Middleware methodDispatcher = Middlewares.GetMethodDispatcher(
genericTypes => CreateDispatcher(
type.MakeGenericType(genericTypes),
() => factory(genericTypes)));
genericTypes =>
{
IMethodDispatcher dispatcher = CreateDispatcher(
type.MakeGenericType(genericTypes),
() => factory(genericTypes));
dispatcher.NotifierManager = notifiers.GetOrAdd(key, _ => new NotifierManager(_serializerFactory()));
return dispatcher;
});
map.Add(key, methodDispatcher);
}

Middleware middleware = Middlewares.Compose(
Middlewares.Heartbeat,
Middlewares.ExceptionHandler,
Middlewares.GetHandleRequest(map),
Middlewares.GetHandleEvent(notifiers),
Middlewares.NotFound);

return new ContainerServer(_serverFactory(), middleware, _serializerFactory(), _loggerFactory());
Expand Down
2 changes: 1 addition & 1 deletion HandyIpc/Core/AsyncPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace HandyIpc.Core
{
internal sealed class AsyncPool<TValue> : PoolBase<TValue> where TValue : IDisposable
public sealed class AsyncPool<TValue> : PoolBase<TValue> where TValue : IDisposable
{
private readonly Func<Task<TValue>> _factory;
private readonly Func<TValue, Task<bool>> _checkValue;
Expand Down
Loading

0 comments on commit 2ae431a

Please sign in to comment.