From cbae4df12fa996a44466d25f15620c42f5600679 Mon Sep 17 00:00:00 2001 From: Vladimir Rudnev Date: Tue, 22 Oct 2024 19:14:50 +0300 Subject: [PATCH 1/6] Updated TargetFramework to net8.0 --- .../BindableChildrenUtil.cs | 4 ++-- .../BuiltInSerializers.cs | 4 ++-- .../CollectionSerializers.cs | 6 ++--- .../RdFramework.Reflection/ProxyGenerator.cs | 11 +++++----- .../ProxyGeneratorCache.cs | 2 +- .../RdFramework.Reflection.csproj | 2 +- .../ReflectionRdActivator.cs | 5 +++-- .../ReflectionSerializerVerifier.cs | 5 ++++- .../ReflectionSerializers.cs | 4 +++- .../ScalarCollectionExtension.cs | 2 +- .../ScalarSerializer.cs | 6 +++-- .../RdFramework.Reflection/SerializerPair.cs | 22 +++++++++---------- .../SerializerReflectionUtil.cs | 12 +++++----- rd-net/RdFramework/Base/IRdBindable.cs | 4 ++-- rd-net/RdFramework/Base/RdBindableBase.cs | 2 +- .../RdFramework/Impl/RdEntitiesRegistrar.cs | 2 +- rd-net/RdFramework/Impl/RdPerContextMap.cs | 2 +- rd-net/RdFramework/Impl/RdSecureString.cs | 2 +- rd-net/RdFramework/Impl/Serializers.cs | 2 +- rd-net/RdFramework/RdFramework.csproj | 2 +- rd-net/RdFramework/Tasks/RdFault.cs | 12 +++++----- .../Text/Intrinsics/TextBufferVersion.cs | 2 +- rd-net/RdFramework/Util/ConcurrentSet.cs | 2 +- rd-net/Test.Cross/Test.Cross.csproj | 2 +- .../Test.RdFramework/Test.RdFramework.csproj | 2 +- .../Test.Reflection.App.csproj | 2 +- rd-net/global.json | 6 ----- 27 files changed, 67 insertions(+), 62 deletions(-) delete mode 100644 rd-net/global.json diff --git a/rd-net/RdFramework.Reflection/BindableChildrenUtil.cs b/rd-net/RdFramework.Reflection/BindableChildrenUtil.cs index a665b4703..cfe172c1b 100644 --- a/rd-net/RdFramework.Reflection/BindableChildrenUtil.cs +++ b/rd-net/RdFramework.Reflection/BindableChildrenUtil.cs @@ -24,7 +24,7 @@ internal static class BindableChildrenUtil internal static void PrettyPrint(PrettyPrinter p, IReflectionBindable instance) { - Action prettyPrinter; + Action? prettyPrinter; lock (ourPrettyPrintersLock) { ourPrettyPrinters.TryGetValue(instance.GetType(), out prettyPrinter); @@ -65,7 +65,7 @@ internal static void PrettyPrint(PrettyPrinter p, IReflectionBindable instance) internal static void FillBindableFields(IReflectionBindable instance) { var type = instance.GetType(); - Action fillBindableFields; + Action? fillBindableFields; lock (ourFillBindableChildren) { ourFillBindableChildren.TryGetValue(type, out fillBindableFields); diff --git a/rd-net/RdFramework.Reflection/BuiltInSerializers.cs b/rd-net/RdFramework.Reflection/BuiltInSerializers.cs index e47eb50cb..e99fb50c5 100644 --- a/rd-net/RdFramework.Reflection/BuiltInSerializers.cs +++ b/rd-net/RdFramework.Reflection/BuiltInSerializers.cs @@ -244,8 +244,8 @@ public static bool HasBuiltInFields(TypeInfo t) Assertion.Fail($"Invalid BuiltIn serializer for type {typeInfo}. Static field 'Read' with type {typeof(CtxReadDelegate<>).ToString(true)} not found"); if (writeField == null) Assertion.Fail($"Invalid BuiltIn serializer for type {typeInfo}. Static field 'Write' with type {typeof(CtxWriteDelegate<>).ToString(true)} not found"); - var reader = readField.GetValue(null); - var writer = writeField.GetValue(null); + var reader = readField.GetValue(null)!; + var writer = writeField.GetValue(null)!; return new SerializerPair(reader, writer); } diff --git a/rd-net/RdFramework.Reflection/CollectionSerializers.cs b/rd-net/RdFramework.Reflection/CollectionSerializers.cs index bcaf2ab55..0504523c2 100644 --- a/rd-net/RdFramework.Reflection/CollectionSerializers.cs +++ b/rd-net/RdFramework.Reflection/CollectionSerializers.cs @@ -22,7 +22,7 @@ public static SerializerPair CreateListSerializerPair(SerializerPair itemSeri } public static SerializerPair CreateDictionarySerializerPair( - SerializerPair keySerializer, SerializerPair valueSerializer) + SerializerPair keySerializer, SerializerPair valueSerializer) where TKey : notnull { var read = CreateReadDictionary(keySerializer, valueSerializer); @@ -53,7 +53,7 @@ public static SerializerPair CreateDictionarySerializerPair( } public static SerializerPair CreateReadOnlyDictionarySerializerPair( - SerializerPair keySerializer, SerializerPair valueSerializer) + SerializerPair keySerializer, SerializerPair valueSerializer) where TKey : notnull { #if NET35 throw new NotSupportedException(); @@ -88,7 +88,7 @@ public static SerializerPair CreateReadOnlyDictionarySerializerPair?> CreateReadDictionary( - SerializerPair keySerializer, SerializerPair valueSerializer) + SerializerPair keySerializer, SerializerPair valueSerializer) where TKey : notnull { CtxReadDelegate?> read = (context, reader) => { diff --git a/rd-net/RdFramework.Reflection/ProxyGenerator.cs b/rd-net/RdFramework.Reflection/ProxyGenerator.cs index cde603d64..87e074a1c 100644 --- a/rd-net/RdFramework.Reflection/ProxyGenerator.cs +++ b/rd-net/RdFramework.Reflection/ProxyGenerator.cs @@ -83,7 +83,7 @@ public struct FakeTuple { public ProxyGenerator(bool allowSave = false) { myAllowSave = allowSave; -#if NETSTANDARD +#if NETSTANDARD || NETCOREAPP myAssemblyBuilder = new Lazy(() => AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DynamicAssemblyName), AssemblyBuilderAccess.Run)); myModuleBuilder = new Lazy(() => myAssemblyBuilder.Value.DefineDynamicModule(DynamicAssemblyName)); #else @@ -325,14 +325,15 @@ private void ImplementProperty(TypeBuilderContext ctx, PropertyInfo propertyInfo throw new Exception("Setter for properties in proxy interface is prohibited due to unclear semantic"); } - if (propertyInfo.GetGetMethod() != null) + var methodInfo = propertyInfo.GetGetMethod(); + if (methodInfo != null) { - var getMethod = typebuilder.DefineMethod(propertyInfo.GetGetMethod().Name, MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.Private, type, EmptyArray.Instance); + var getMethod = typebuilder.DefineMethod(methodInfo.Name, MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.Private, type, EmptyArray.Instance); var il = getMethod.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Ret); - typebuilder.DefineMethodOverride(getMethod, propertyInfo.GetGetMethod()); + typebuilder.DefineMethodOverride(getMethod, methodInfo); } } @@ -636,7 +637,7 @@ internal class ProxyGeneratorMembers // ReSharper disable once PossibleNullReferenceException public readonly MethodInfo EternalLifetimeGet = typeof(Lifetime) - .GetProperty(nameof(Lifetime.Eternal), BindingFlags.Static | BindingFlags.Public) + .GetProperty(nameof(Lifetime.Eternal), BindingFlags.Static | BindingFlags.Public)! .GetGetMethod() .NotNull(nameof(EternalLifetimeGet)); diff --git a/rd-net/RdFramework.Reflection/ProxyGeneratorCache.cs b/rd-net/RdFramework.Reflection/ProxyGeneratorCache.cs index ed812a0b8..6cfbc7641 100644 --- a/rd-net/RdFramework.Reflection/ProxyGeneratorCache.cs +++ b/rd-net/RdFramework.Reflection/ProxyGeneratorCache.cs @@ -15,7 +15,7 @@ public class ProxyGeneratorCache : IProxyGenerator private sealed class TokenComparer : IComparer { public static IComparer Instance { get; } = new TokenComparer(); - public int Compare(MethodInfo x, MethodInfo y) => (x?.MetadataToken ?? -1).CompareTo(y?.MetadataToken ?? -1); + public int Compare(MethodInfo? x, MethodInfo? y) => (x?.MetadataToken ?? -1).CompareTo(y?.MetadataToken ?? -1); } public ProxyGeneratorCache(ProxyGenerator generator) diff --git a/rd-net/RdFramework.Reflection/RdFramework.Reflection.csproj b/rd-net/RdFramework.Reflection/RdFramework.Reflection.csproj index 94e0e498a..a984e243b 100644 --- a/rd-net/RdFramework.Reflection/RdFramework.Reflection.csproj +++ b/rd-net/RdFramework.Reflection/RdFramework.Reflection.csproj @@ -1,7 +1,7 @@  - netstandard2.0;net35;net472 + net8.0 JetBrains.RdFramework.Reflection JetBrains.Rd.Reflection diff --git a/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs b/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs index f2d588496..db794c071 100644 --- a/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs +++ b/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs @@ -148,7 +148,8 @@ private object ActivateRd(Type type) object instance; try { - instance = Activator.CreateInstance(implementingType); + instance = Activator.CreateInstance(implementingType) + ?? throw new InvalidOperationException($"Unable to create instance of: {implementingType.ToString(true)}"); } catch (MissingMethodException e) { @@ -454,7 +455,7 @@ public static string GetTypeName(Type type) return rpcInterface.AssemblyQualifiedName; } - return typename; + return typename!; } } } \ No newline at end of file diff --git a/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs b/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs index dbfbe016b..0e0b0164f 100644 --- a/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs +++ b/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs @@ -148,7 +148,10 @@ bool IsValidArray() if (!typeInfo.IsArray) return false; if (typeInfo.GetArrayRank() != 1) return false; - var arrayType = typeInfo.GetElementType().GetTypeInfo(); + var elementType = typeInfo.GetElementType(); + if (elementType == null) return false; + + var arrayType = elementType.GetTypeInfo(); return IsFieldType(arrayType, false); } diff --git a/rd-net/RdFramework.Reflection/ReflectionSerializers.cs b/rd-net/RdFramework.Reflection/ReflectionSerializers.cs index 11bf16eaa..ea35f0c97 100644 --- a/rd-net/RdFramework.Reflection/ReflectionSerializers.cs +++ b/rd-net/RdFramework.Reflection/ReflectionSerializers.cs @@ -226,11 +226,13 @@ private void RegisterModelSerializer() object instance; if (isScalar) { +#pragma warning disable SYSLIB0050 instance = FormatterServices.GetUninitializedObject(type); +#pragma warning restore SYSLIB0050 } else { - instance = Activator.CreateInstance(type); + instance = Activator.CreateInstance(type)!; } var bindableInstance = instance as IRdBindable; diff --git a/rd-net/RdFramework.Reflection/ScalarCollectionExtension.cs b/rd-net/RdFramework.Reflection/ScalarCollectionExtension.cs index 8a1bef542..6e6a461ea 100644 --- a/rd-net/RdFramework.Reflection/ScalarCollectionExtension.cs +++ b/rd-net/RdFramework.Reflection/ScalarCollectionExtension.cs @@ -44,7 +44,7 @@ public static void AttachCollectionSerializers(ReflectionSerializers self) } else if (type.IsArray) { - var result = (SerializerPair)ReflectionUtil.InvokeStaticGeneric(typeof(ScalarCollectionExtension), nameof(CreateArraySerializer), type.GetElementType(), new object[] { self })!; + var result = (SerializerPair)ReflectionUtil.InvokeStaticGeneric(typeof(ScalarCollectionExtension), nameof(CreateArraySerializer), type.GetElementType()!, self)!; self.Register(type, result); } }); diff --git a/rd-net/RdFramework.Reflection/ScalarSerializer.cs b/rd-net/RdFramework.Reflection/ScalarSerializer.cs index 7ebc4fb2b..e8cfb06a4 100644 --- a/rd-net/RdFramework.Reflection/ScalarSerializer.cs +++ b/rd-net/RdFramework.Reflection/ScalarSerializer.cs @@ -124,7 +124,9 @@ private SerializerPair CreateCustomScalar(ISerializersSource serializers) if (allowNullable && !unsafeReader.ReadNullness()) return default; +#pragma warning disable SYSLIB0050 object instance = FormatterServices.GetUninitializedObject(typeof(T)); +#pragma warning restore SYSLIB0050 try { @@ -247,7 +249,7 @@ private SerializerPair CreateValueTupleSerializer(ISerializersSource serializ } var type = typeInfo.AsType(); - CtxReadDelegate readerDelegate = (ctx, unsafeReader) => + CtxReadDelegate readerDelegate = (ctx, unsafeReader) => { // todo: consider using IL emit var activatorArgs = new object[argumentTypes.Length]; @@ -258,7 +260,7 @@ private SerializerPair CreateValueTupleSerializer(ISerializersSource serializ } var instance = Activator.CreateInstance(type, activatorArgs); - return (T) instance; + return (T?) instance; }; CtxWriteDelegate writerDelegate = (ctx, unsafeWriter, value) => diff --git a/rd-net/RdFramework.Reflection/SerializerPair.cs b/rd-net/RdFramework.Reflection/SerializerPair.cs index feb34d468..44f7a690e 100644 --- a/rd-net/RdFramework.Reflection/SerializerPair.cs +++ b/rd-net/RdFramework.Reflection/SerializerPair.cs @@ -95,10 +95,10 @@ void WriterDelegate(SerializationCtx ctx, UnsafeWriter writer, T value) => void WriterDelegateStatic(SerializationCtx ctx, UnsafeWriter writer, T value) => writeMethod.Invoke(null, new object?[] { ctx, writer, value, }); - T ReaderDelegate(SerializationCtx ctx, UnsafeReader reader) => - (T) readMethod.Invoke(null, new object?[] { ctx, reader }); + T? ReaderDelegate(SerializationCtx ctx, UnsafeReader reader) => + (T?) readMethod.Invoke(null, new object?[] { ctx, reader }); - CtxReadDelegate ctxReadDelegate = ReaderDelegate; + CtxReadDelegate ctxReadDelegate = ReaderDelegate; CtxWriteDelegate ctxWriteDelegate = writeMethod.IsStatic ? WriterDelegateStatic : WriterDelegate; return new SerializerPair(ctxReadDelegate, ctxWriteDelegate); } @@ -111,13 +111,13 @@ private static SerializerPair CreateFromMethodsImpl1(MethodInfo readMethod, M void WriterDelegate(SerializationCtx ctx, UnsafeWriter writer, T value) => writeMethod.Invoke(null, new object?[] {ctx, writer, value}); - T ReaderDelegate(SerializationCtx ctx, UnsafeReader reader) + T? ReaderDelegate(SerializationCtx ctx, UnsafeReader reader) { - return (T)readMethod.Invoke(null, + return (T?)readMethod.Invoke(null, new[] {ctx, reader, ctxKeyReadDelegate, ctxKeyWriteDelegate}); } - CtxReadDelegate ctxReadDelegate = ReaderDelegate; + CtxReadDelegate ctxReadDelegate = ReaderDelegate; CtxWriteDelegate ctxWriteDelegate = WriterDelegate; return new SerializerPair(ctxReadDelegate, ctxWriteDelegate); } @@ -133,13 +133,13 @@ private static SerializerPair CreateFromMethodsImpl2(MethodInfo readMethod, M void WriterDelegate(SerializationCtx ctx, UnsafeWriter writer, T value) => writeMethod.Invoke(null, new object?[] {ctx, writer, value}); - T ReaderDelegate(SerializationCtx ctx, UnsafeReader reader) + T? ReaderDelegate(SerializationCtx ctx, UnsafeReader reader) { - return (T)readMethod.Invoke(null, + return (T?)readMethod.Invoke(null, new[] {ctx, reader, ctxKeyReadDelegate, ctxKeyWriteDelegate, ctxValueReadDelegate, ctxValueWriteDelegate}); } - CtxReadDelegate ctxReadDelegate = ReaderDelegate; + CtxReadDelegate ctxReadDelegate = ReaderDelegate; CtxWriteDelegate ctxWriteDelegate = WriterDelegate; return new SerializerPair(ctxReadDelegate, ctxWriteDelegate); } @@ -153,7 +153,7 @@ public static SerializerPair FromMarshaller(IBuiltInMarshaller marshaller) private static SerializerPair CreateFromNonProtocolMethodsT(MethodInfo readMethod, MethodInfo writeMethod) { - Assertion.Require(readMethod.IsStatic, $"Read method should be static ({readMethod.DeclaringType.ToString(true)})"); + Assertion.Require(readMethod.IsStatic, $"Read method should be static ({readMethod.DeclaringType?.ToString(true)})"); void WriterDelegate(SerializationCtx ctx, UnsafeWriter writer, T value) { @@ -174,7 +174,7 @@ void WriterDelegateStatic(SerializationCtx ctx, UnsafeWriter writer, T value) if (!typeof(T).IsValueType && !reader.ReadNullness()) return default; - return (T) readMethod.Invoke(null, new object[] {reader}); + return (T?) readMethod.Invoke(null, new object[] {reader}); } CtxReadDelegate ctxReadDelegate = ReaderDelegate; diff --git a/rd-net/RdFramework.Reflection/SerializerReflectionUtil.cs b/rd-net/RdFramework.Reflection/SerializerReflectionUtil.cs index 229d62550..c3e375ac4 100644 --- a/rd-net/RdFramework.Reflection/SerializerReflectionUtil.cs +++ b/rd-net/RdFramework.Reflection/SerializerReflectionUtil.cs @@ -68,13 +68,13 @@ internal static FieldInfo[] GetBindableFields(TypeInfo typeInfo) return list.ToArray(); } - private static IEnumerable GetFields(Type type, Type baseType) + private static IEnumerable GetFields(Type? type, Type baseType) { - foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) + foreach (var field in type?.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) ?? Array.Empty()) yield return field; // private fields only being returned for the current type - while ((type = type.BaseType) != baseType && type != null) + while ((type = type?.BaseType) != baseType && type != null) { // but protected fields are returned in first step foreach (var baseField in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic)) @@ -85,7 +85,7 @@ private static IEnumerable GetFields(Type type, Type baseType) internal static SerializerPair ConvertPair(SerializerPair serializers, Type desiredType) { - return (SerializerPair)ourConvertSerializerPair.MakeGenericMethod(serializers.Writer.GetType().GetGenericArguments()[0], desiredType).Invoke(null, new object[] { serializers }); + return (SerializerPair)ourConvertSerializerPair.MakeGenericMethod(serializers.Writer.GetType().GetGenericArguments()[0], desiredType).Invoke(null, new object[] { serializers })!; } private static readonly MethodInfo ourConvertSerializerPair = typeof(SerializerReflectionUtil).GetTypeInfo().GetMethod(nameof(ConvertPairGeneric), BindingFlags.Static | BindingFlags.NonPublic)!; @@ -113,7 +113,7 @@ internal static CtxReadDelegate ConvertReader(object reader) var genericTypedRead = ourConvertTypedCtxRead.MakeGenericMethod(reader.GetType().GetGenericArguments()[0], typeof(object)); var result = genericTypedRead.Invoke(null, new[] { reader }); - return (CtxReadDelegate)result; + return (CtxReadDelegate)result!; } internal static CtxWriteDelegate ConvertWriter(object writer) @@ -121,7 +121,7 @@ internal static CtxWriteDelegate ConvertWriter(object writer) if (writer is CtxWriteDelegate objWriter) return objWriter; - return (CtxWriteDelegate)ourConvertTypedCtxWrite.MakeGenericMethod(writer.GetType().GetGenericArguments()[0], typeof(TOut)).Invoke(null, new[] { writer }); + return (CtxWriteDelegate)ourConvertTypedCtxWrite.MakeGenericMethod(writer.GetType().GetGenericArguments()[0], typeof(TOut)).Invoke(null, new[] { writer })!; } private static readonly MethodInfo ourConvertTypedCtxRead = typeof(SerializerReflectionUtil).GetTypeInfo().GetMethod(nameof(CtxReadTypedToObject), BindingFlags.Static | BindingFlags.NonPublic)!; diff --git a/rd-net/RdFramework/Base/IRdBindable.cs b/rd-net/RdFramework/Base/IRdBindable.cs index f39f7f3ea..f11832c66 100644 --- a/rd-net/RdFramework/Base/IRdBindable.cs +++ b/rd-net/RdFramework/Base/IRdBindable.cs @@ -24,7 +24,7 @@ public static class RdDynamicEx { public static IProtocol GetProtoOrThrow(this IRdDynamic dynamic) { - return dynamic.TryGetProto() ?? throw new ProtocolNotBoundException(dynamic.ToString()); + return dynamic.TryGetProto() ?? throw new ProtocolNotBoundException(dynamic.ToString() ?? "'dynamic.ToString() was null'"); } } @@ -322,7 +322,7 @@ public static void PrintEx(this object? me, PrettyPrinter printer) break; } default: - printer.Print(me.ToString()); + printer.Print(me.ToString() ?? ""); break; } } diff --git a/rd-net/RdFramework/Base/RdBindableBase.cs b/rd-net/RdFramework/Base/RdBindableBase.cs index a6e4a0209..97b42d265 100644 --- a/rd-net/RdFramework/Base/RdBindableBase.cs +++ b/rd-net/RdFramework/Base/RdBindableBase.cs @@ -297,7 +297,7 @@ private T GetOrCreateExtension(string name, bool highPriorityExtension, Func< // NOTE: dummy implementation which prevents WPF from hanging the viewmodel forever on reflection property descriptor fabricated change events: // when it sees PropertyChanged, it does not look for property descriptor events - public virtual event PropertyChangedEventHandler PropertyChanged { add { } remove { } } + public virtual event PropertyChangedEventHandler? PropertyChanged { add { } remove { } } } public enum BindState diff --git a/rd-net/RdFramework/Impl/RdEntitiesRegistrar.cs b/rd-net/RdFramework/Impl/RdEntitiesRegistrar.cs index 9a29f0241..a95159454 100644 --- a/rd-net/RdFramework/Impl/RdEntitiesRegistrar.cs +++ b/rd-net/RdFramework/Impl/RdEntitiesRegistrar.cs @@ -17,7 +17,7 @@ internal void Register(Lifetime lifetime, RdId rdId, IRdDynamic dynamic) myMap.BlockingAddUnique(lifetime, myMap, rdId, dynamic); } - public bool TryGetEntity(RdId rdId, out IRdDynamic entity) + public bool TryGetEntity(RdId rdId, out IRdDynamic? entity) { lock (myMap) { diff --git a/rd-net/RdFramework/Impl/RdPerContextMap.cs b/rd-net/RdFramework/Impl/RdPerContextMap.cs index 683968fbd..a0b828cab 100644 --- a/rd-net/RdFramework/Impl/RdPerContextMap.cs +++ b/rd-net/RdFramework/Impl/RdPerContextMap.cs @@ -59,7 +59,7 @@ protected override void PreInit(Lifetime lifetime, IProtocol proto) if (!cookie.Succeed) return; - value.WithId(RdId.Mix(contextValue.ToString())); + value.WithId(RdId.Mix(contextValue.ToString() ?? "")); value.PreBind(contextValueLifetime, this, $"[{contextValue.ToString()}]"); } diff --git a/rd-net/RdFramework/Impl/RdSecureString.cs b/rd-net/RdFramework/Impl/RdSecureString.cs index ddfc0f8b6..ad664c727 100644 --- a/rd-net/RdFramework/Impl/RdSecureString.cs +++ b/rd-net/RdFramework/Impl/RdSecureString.cs @@ -26,7 +26,7 @@ public bool Equals(RdSecureString other) return string.Equals(Contents, other.Contents); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; return obj is RdSecureString && Equals((RdSecureString) obj); diff --git a/rd-net/RdFramework/Impl/Serializers.cs b/rd-net/RdFramework/Impl/Serializers.cs index f8f58f3e8..02acc0918 100644 --- a/rd-net/RdFramework/Impl/Serializers.cs +++ b/rd-net/RdFramework/Impl/Serializers.cs @@ -257,7 +257,7 @@ public void Register(CtxReadDelegate reader, CtxWriteDelegate writer, l bool TryGetReader(RdId rdId, out CtxReadDelegate readDelegate) { lock (myLock) - return myReaders.TryGetValue(rdId, out readDelegate); + return myReaders.TryGetValue(rdId, out readDelegate!); } #if !NET35 myBackgroundRegistrar.Join(); diff --git a/rd-net/RdFramework/RdFramework.csproj b/rd-net/RdFramework/RdFramework.csproj index 6d2e119ab..f0f09422b 100644 --- a/rd-net/RdFramework/RdFramework.csproj +++ b/rd-net/RdFramework/RdFramework.csproj @@ -1,7 +1,7 @@  - netstandard2.0;net35;net472 + net8.0 JetBrains.RdFramework JetBrains.Rd diff --git a/rd-net/RdFramework/Tasks/RdFault.cs b/rd-net/RdFramework/Tasks/RdFault.cs index df156e784..6d8ead731 100644 --- a/rd-net/RdFramework/Tasks/RdFault.cs +++ b/rd-net/RdFramework/Tasks/RdFault.cs @@ -11,9 +11,9 @@ namespace JetBrains.Rd.Tasks [Serializable] public class RdFault : Exception { - public string ReasonTypeFqn { get; private set; } - public string ReasonText { get; private set; } - public string ReasonMessage { get; private set; } + public string? ReasonTypeFqn { get; private set; } + public string? ReasonText { get; private set; } + public string? ReasonMessage { get; private set; } public RdFault(Exception inner) : base(inner.Message, inner) { @@ -22,7 +22,8 @@ public RdFault(Exception inner) : base(inner.Message, inner) ReasonText = inner.ToString(); //todo Use system capabilities, stack traces, etc } - [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] + // [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] + [Obsolete("Obsolete")] protected RdFault(SerializationInfo info, StreamingContext context) : base(info, context) { ReasonTypeFqn = info.GetString(nameof(ReasonTypeFqn)); @@ -30,7 +31,8 @@ protected RdFault(SerializationInfo info, StreamingContext context) : base(info, ReasonMessage = info.GetString(nameof(ReasonMessage)); } - [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] + // [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] + [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue(nameof(ReasonTypeFqn), ReasonTypeFqn); diff --git a/rd-net/RdFramework/Text/Intrinsics/TextBufferVersion.cs b/rd-net/RdFramework/Text/Intrinsics/TextBufferVersion.cs index 2fb33b983..c13e610d9 100644 --- a/rd-net/RdFramework/Text/Intrinsics/TextBufferVersion.cs +++ b/rd-net/RdFramework/Text/Intrinsics/TextBufferVersion.cs @@ -33,7 +33,7 @@ public bool Equals(TextBufferVersion other) return !left.Equals(right); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; return obj is TextBufferVersion && Equals((TextBufferVersion) obj); diff --git a/rd-net/RdFramework/Util/ConcurrentSet.cs b/rd-net/RdFramework/Util/ConcurrentSet.cs index f3834b264..547379f54 100644 --- a/rd-net/RdFramework/Util/ConcurrentSet.cs +++ b/rd-net/RdFramework/Util/ConcurrentSet.cs @@ -8,7 +8,7 @@ internal class ConcurrentSet : #if NET35 ICollection #else - ISet + ISet where T : notnull #endif { private readonly ConcurrentDictionary myDictionary = new ConcurrentDictionary(); diff --git a/rd-net/Test.Cross/Test.Cross.csproj b/rd-net/Test.Cross/Test.Cross.csproj index afc6426bf..d6b227cb1 100644 --- a/rd-net/Test.Cross/Test.Cross.csproj +++ b/rd-net/Test.Cross/Test.Cross.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net8.0 Test.RdCross CrossTests AnyCPU diff --git a/rd-net/Test.RdFramework/Test.RdFramework.csproj b/rd-net/Test.RdFramework/Test.RdFramework.csproj index a42fd71cc..cf51b4579 100644 --- a/rd-net/Test.RdFramework/Test.RdFramework.csproj +++ b/rd-net/Test.RdFramework/Test.RdFramework.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net8.0 net472;$(TargetFrameworks);net35 Full diff --git a/rd-net/Test.Reflection.App/Test.Reflection.App.csproj b/rd-net/Test.Reflection.App/Test.Reflection.App.csproj index 5fdf0a655..d29a43d50 100644 --- a/rd-net/Test.Reflection.App/Test.Reflection.App.csproj +++ b/rd-net/Test.Reflection.App/Test.Reflection.App.csproj @@ -2,7 +2,7 @@ Exe - net472 + net8.0 false LatestMajor disable diff --git a/rd-net/global.json b/rd-net/global.json deleted file mode 100644 index d73dacb40..000000000 --- a/rd-net/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sdk": { - "version": "6.0.100", - "rollForward": "major" - } -} From 948c03d6693d6c1d49c0051e3595abc2ffadfc51 Mon Sep 17 00:00:00 2001 From: Vladimir Rudnev Date: Tue, 22 Oct 2024 19:18:24 +0300 Subject: [PATCH 2/6] Add support of UnixDomainSocketEndPoint --- rd-net/RdFramework/Impl/EndPointWrapper.cs | 49 ++ rd-net/RdFramework/Impl/SocketWire.cs | 63 ++- rd-net/RdFramework/WireEx.cs | 5 +- rd-net/Test.RdFramework/SocketProxyTest.cs | 254 ++++----- .../SocketWireIpEndpointTest.cs | 141 +++++ rd-net/Test.RdFramework/SocketWireTest.cs | 503 ------------------ rd-net/Test.RdFramework/SocketWireTestBase.cs | 398 ++++++++++++++ .../SocketWireUnixEndpointTest.cs | 47 ++ rd-net/Test.Reflection.App/Program.cs | 6 +- 9 files changed, 807 insertions(+), 659 deletions(-) create mode 100644 rd-net/RdFramework/Impl/EndPointWrapper.cs create mode 100644 rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs delete mode 100644 rd-net/Test.RdFramework/SocketWireTest.cs create mode 100644 rd-net/Test.RdFramework/SocketWireTestBase.cs create mode 100644 rd-net/Test.RdFramework/SocketWireUnixEndpointTest.cs diff --git a/rd-net/RdFramework/Impl/EndPointWrapper.cs b/rd-net/RdFramework/Impl/EndPointWrapper.cs new file mode 100644 index 000000000..4a5ada28f --- /dev/null +++ b/rd-net/RdFramework/Impl/EndPointWrapper.cs @@ -0,0 +1,49 @@ +using System.IO; +using System.Net; +using System.Net.Sockets; + +namespace JetBrains.Rd.Impl; + +public class EndPointWrapper +{ + public EndPoint EndPointImpl { get; } + public IPAddress? LocalAddress { get; private set; } + public int? LocalPort { get; private set; } + public string? LocalPath { get; private set; } + public AddressFamily AddressFamily { get; private set; } + public SocketType SocketType { get; private set; } + public ProtocolType ProtocolType { get; private set; } + + private EndPointWrapper(EndPoint endPoint) + { + EndPointImpl = endPoint; + } + + public static EndPointWrapper CreateIpEndPoint(IPAddress? address = null, int? port = null) + { + var address1 = address ?? IPAddress.Loopback; + var port1 = port ?? 0; + return new EndPointWrapper(new IPEndPoint(address1, port1)) + { + AddressFamily = AddressFamily.InterNetwork, + SocketType = SocketType.Stream, + ProtocolType = ProtocolType.Tcp, + LocalAddress = address1, + LocalPort = port1, + LocalPath = null, + }; + } + + public static EndPointWrapper CreateUnixEndPoint(string? path = null) + { + var path1 = path ?? Path.GetTempFileName(); + return new EndPointWrapper(new UnixDomainSocketEndPoint(path1)) { + AddressFamily = AddressFamily.Unix, + SocketType = SocketType.Stream, + ProtocolType = ProtocolType.Unspecified, + LocalAddress = null, + LocalPort = null, + LocalPath = path1, + }; + } +} \ No newline at end of file diff --git a/rd-net/RdFramework/Impl/SocketWire.cs b/rd-net/RdFramework/Impl/SocketWire.cs index fbef69a70..a1f33b508 100644 --- a/rd-net/RdFramework/Impl/SocketWire.cs +++ b/rd-net/RdFramework/Impl/SocketWire.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; @@ -113,7 +114,7 @@ protected Base(string id, Lifetime lifetime, IScheduler scheduler) private Timer StartHeartbeat() { var timer = new Timer(HeartBeatInterval.TotalMilliseconds) { AutoReset = false }; - void OnTimedEvent(object sender, ElapsedEventArgs e) + void OnTimedEvent(object? sender, ElapsedEventArgs e) { Ping(); timer.Start(); @@ -458,7 +459,7 @@ protected override void SendPkg(UnsafeWriter.Cookie cookie) //It's a kind of magic... protected static void SetSocketOptions(Socket s) { - s.NoDelay = true; + if (s.ProtocolType == ProtocolType.Tcp) s.NoDelay = true; // if (!TimeoutForbidden()) // s.ReceiveTimeout = TimeoutMs; //sometimes shutdown and close doesn't lead Receive to throw exception @@ -509,7 +510,7 @@ protected void AddTerminationActions(Thread receiverThread) ); } - public int Port { get; protected set; } + public int? Port { get; protected set; } protected virtual bool AcceptHandshake(Socket socket) @@ -522,9 +523,12 @@ protected virtual bool AcceptHandshake(Socket socket) public class Client : Base { public Client(Lifetime lifetime, IScheduler scheduler, int port, string? optId = null) : - this(lifetime, scheduler, new IPEndPoint(IPAddress.Loopback, port), optId) {} + this(lifetime, scheduler, EndPointWrapper.CreateIpEndPoint(IPAddress.Loopback, port), optId) {} + + public Client(Lifetime lifetime, IScheduler scheduler, string path, string? optId = null) : + this(lifetime, scheduler, EndPointWrapper.CreateUnixEndPoint(path), optId) {} - public Client(Lifetime lifetime, IScheduler scheduler, IPEndPoint endPoint, string? optId = null) : + public Client(Lifetime lifetime, IScheduler scheduler, EndPointWrapper endPointWrapper, string? optId = null) : base("ClientSocket-"+(optId ?? ""), lifetime, scheduler) { var thread = new Thread(() => @@ -538,12 +542,12 @@ public Client(Lifetime lifetime, IScheduler scheduler, IPEndPoint endPoint, stri { try { - var s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + var s = new Socket(endPointWrapper.AddressFamily, endPointWrapper.SocketType, endPointWrapper.ProtocolType); Socket = s; SetSocketOptions(s); - Log.Verbose("{0}: connecting to {1}.", Id, endPoint); - s.Connect(endPoint); + Log.Verbose("{0}: connecting to {1}.", Id, endPointWrapper.EndPointImpl); + s.Connect(endPointWrapper.EndPointImpl); lock (Lock) { @@ -569,11 +573,11 @@ public Client(Lifetime lifetime, IScheduler scheduler, IPEndPoint endPoint, stri { lastReportedErrorHash = errorHashCode; if (Log.IsVersboseEnabled()) - Log.Verbose(ex, $"{Id}: connection error for endpoint \"{endPoint}\"."); + Log.Verbose(ex, $"{Id}: connection error for endpoint \"{endPointWrapper.EndPointImpl}\"."); } else { - Log.Verbose("{0}: connection error for endpoint \"{1}\" ({2}).", Id, endPoint, ex.Message); + Log.Verbose("{0}: connection error for endpoint \"{1}\" ({2}).", Id, endPointWrapper.EndPointImpl, ex.Message); } lock (Lock) @@ -613,9 +617,9 @@ public Client(Lifetime lifetime, IScheduler scheduler, IPEndPoint endPoint, stri public class Server : Base { - public Server(Lifetime lifetime, IScheduler scheduler, IPEndPoint? endPoint = null, string? optId = null) : this(lifetime, scheduler, optId) + public Server(Lifetime lifetime, IScheduler scheduler, EndPointWrapper? endPointWrapper = null, string? optId = null) : this(lifetime, scheduler, optId) { - var serverSocket = CreateServerSocket(endPoint); + var serverSocket = CreateServerSocket(endPointWrapper); StartServerSocket(lifetime, serverSocket); @@ -639,17 +643,22 @@ public Server(Lifetime lifetime, IScheduler scheduler, Socket serverSocket, stri private Server(Lifetime lifetime, IScheduler scheduler, string? optId = null) : base("ServerSocket-"+(optId ?? ""), lifetime, scheduler) {} - public static Socket CreateServerSocket(IPEndPoint? endPoint) + public static Socket CreateServerSocket(EndPointWrapper? endPointWrapper) { - Protocol.InitLogger.Verbose("Creating server socket on endpoint: {0}", endPoint); + Protocol.InitLogger.Verbose("Creating server socket on endpoint: {0}", endPointWrapper?.EndPointImpl); + // by default we will use IPEndpoint? + endPointWrapper ??= EndPointWrapper.CreateIpEndPoint(IPAddress.Loopback, 0); - var serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + var serverSocket = new Socket(endPointWrapper.AddressFamily, endPointWrapper.SocketType, endPointWrapper.ProtocolType); SetSocketOptions(serverSocket); - endPoint = endPoint ?? new IPEndPoint(IPAddress.Loopback, 0); - serverSocket.Bind(endPoint); + if (endPointWrapper.LocalPath != null) + { + if (File.Exists(endPointWrapper.LocalPath)) File.Delete(endPointWrapper.LocalPath); + } + serverSocket.Bind(endPointWrapper.EndPointImpl); serverSocket.Listen(1); - Protocol.InitLogger.Verbose("Server socket created, listening started on endpoint: {0}", endPoint); + Protocol.InitLogger.Verbose("Server socket created, listening started on endpoint: {0}", endPointWrapper.EndPointImpl); return serverSocket; } @@ -657,7 +666,9 @@ public static Socket CreateServerSocket(IPEndPoint? endPoint) private void StartServerSocket(Lifetime lifetime, Socket serverSocket) { if (serverSocket == null) throw new ArgumentNullException(nameof(serverSocket)); - Port = ((IPEndPoint) serverSocket.LocalEndPoint).Port; + Port = serverSocket.LocalEndPoint is IPEndPoint ipEndPoint ? ipEndPoint.Port : null; + // TODO do we want to store Path from Unix here? Only via reflection. + Log.Verbose("{0} : started, port: {1}", Id, Port); var thread = new Thread(() => @@ -741,28 +752,30 @@ public void Deconstruct(out IScheduler scheduler, out string? id) public class ServerFactory { - [PublicAPI] public readonly int LocalPort; + [PublicAPI] public readonly int? LocalPort; + [PublicAPI] public readonly string? LocalPath; [PublicAPI] public readonly IViewableSet Connected = new ViewableSet(); - public ServerFactory(Lifetime lifetime, IScheduler scheduler, IPEndPoint? endpoint = null) - : this(lifetime, () => new WireParameters(scheduler, null), endpoint) {} + public ServerFactory(Lifetime lifetime, IScheduler scheduler, EndPointWrapper? endpointWrapper = null) + : this(lifetime, () => new WireParameters(scheduler, null), endpointWrapper) {} public ServerFactory( Lifetime lifetime, Func wireParametersFactory, - IPEndPoint? endpoint = null + EndPointWrapper? endpointWrapper = null ) { - var serverSocket = Server.CreateServerSocket(endpoint); + var serverSocket = Server.CreateServerSocket(endpointWrapper); var serverSocketLifetimeDef = new LifetimeDefinition(lifetime); serverSocketLifetimeDef.Lifetime.OnTermination(() => { ourStaticLog.Verbose("closing server socket"); Base.CloseSocket(serverSocket); }); - LocalPort = ((IPEndPoint) serverSocket.LocalEndPoint).Port; + LocalPort = (serverSocket.LocalEndPoint as IPEndPoint)?.Port; + LocalPath = endpointWrapper?.EndPointImpl is UnixDomainSocketEndPoint ? endpointWrapper.LocalPath : null; void Rec() { diff --git a/rd-net/RdFramework/WireEx.cs b/rd-net/RdFramework/WireEx.cs index f772f40d4..e7380945c 100644 --- a/rd-net/RdFramework/WireEx.cs +++ b/rd-net/RdFramework/WireEx.cs @@ -11,7 +11,10 @@ public static int GetServerPort(this IWire wire) var serverSocketWire = wire as SocketWire.Server; if (serverSocketWire == null) throw new ArgumentException("You must use SocketWire.Server to get server port"); - return serverSocketWire.Port; + var port = serverSocketWire.Port; + if (!port.HasValue) + throw new ArgumentException("You must use SocketWire.Server with connection over TCP to get server port"); + return port.Value; } public static void Send(this IWire wire, RdId id, Action writer) diff --git a/rd-net/Test.RdFramework/SocketProxyTest.cs b/rd-net/Test.RdFramework/SocketProxyTest.cs index cbc0aa6f7..69e91b6c3 100644 --- a/rd-net/Test.RdFramework/SocketProxyTest.cs +++ b/rd-net/Test.RdFramework/SocketProxyTest.cs @@ -1,127 +1,127 @@ -#if !NET35 -using System.Collections.Generic; -using System.Threading; -using JetBrains.Collections.Viewable; -using JetBrains.Lifetimes; -using JetBrains.Rd.Base; -using JetBrains.Rd.Impl; -using JetBrains.Threading; -using NUnit.Framework; -using Test.Lifetimes; - -namespace Test.RdFramework -{ - [TestFixture] - [Ignore("TODO: this test tends to hang")] - class SocketProxyTest : LifetimesTestBase - { - [Test] - public void TestSimple() - { - // using var factory = Log.UsingLogFactory(new TextWriterLogFactory(Console.Out, LoggingLevel.TRACE)); - Lifetime.Using(lifetime => - { - var proxyLifetimeDefinition = lifetime.CreateNested(); - var proxyLifetime = proxyLifetimeDefinition.Lifetime; - { - SynchronousScheduler.Instance.SetActive(lifetime); - - var serverProtocol = SocketWireTest.Server(lifetime); - - var proxy = new SocketProxy("TestProxy", proxyLifetime, serverProtocol).With(socketProxy => - socketProxy.Start()); - Thread.Sleep(SocketWireTest.DefaultTimeout); - - var clientProtocol = SocketWireTest.Client(lifetime, proxy.Port); - - var sp = NewRdSignal().Static(1); - sp.BindTopLevel(lifetime, serverProtocol, SocketWireTest.Top); - - var cp = NewRdSignal().Static(1); - cp.BindTopLevel(lifetime, clientProtocol, SocketWireTest.Top); - - var serverLog = new List(); - var clientLog = new List(); - - sp.Advise(lifetime, i => serverLog.Add(i)); - cp.Advise(lifetime, i => clientLog.Add(i)); - - //Connection is established for now - - sp.Fire(1); - - SpinWaitEx.SpinUntil(() => serverLog.Count == 1); - SpinWaitEx.SpinUntil(() => clientLog.Count == 1); - Assert.AreEqual(new List {1}, serverLog); - Assert.AreEqual(new List {1}, clientLog); - - cp.Fire(2); - - SpinWaitEx.SpinUntil(() => serverLog.Count == 2); - SpinWaitEx.SpinUntil(() => clientLog.Count == 2); - Assert.AreEqual(new List {1, 2}, serverLog); - Assert.AreEqual(new List {1, 2}, clientLog); - - proxy.StopServerToClientMessaging(); - - cp.Advise(lifetime, i => Assert.AreNotSame(3, i, "Value {0} mustn't be received", 3)); - - sp.Fire(3); - - SpinWaitEx.SpinUntil(() => serverLog.Count == 3); - Assert.AreEqual(new List {1, 2, 3}, serverLog); - - - proxy.StopClientToServerMessaging(); - - sp.Advise(lifetime, i => Assert.AreNotSame(4, i, "Value {0} mustn't be received", 4)); - - cp.Fire(4); - - SpinWaitEx.SpinUntil(() => clientLog.Count == 3); - Assert.AreEqual(new List {1, 2, 4}, clientLog); - - //Connection is broken for now - - proxy.StartServerToClientMessaging(); - - sp.Fire(5); - SpinWaitEx.SpinUntil(() => serverLog.Count == 4); - SpinWaitEx.SpinUntil(() => clientLog.Count == 4); - Assert.AreEqual(new List {1, 2, 3, 5}, serverLog); - Assert.AreEqual(new List {1, 2, 4, 5}, clientLog); - - - proxy.StartClientToServerMessaging(); - - cp.Fire(6); - SpinWaitEx.SpinUntil(() => serverLog.Count == 5); - SpinWaitEx.SpinUntil(() => clientLog.Count == 5); - Assert.AreEqual(new List {1, 2, 3, 5, 6}, serverLog); - Assert.AreEqual(new List {1, 2, 4, 5, 6}, clientLog); - - //Connection is established for now - - proxyLifetimeDefinition.Terminate(); - - - cp.Advise(lifetime, i => Assert.AreNotSame(7, i, "Value {0} mustn't be received", 7)); - sp.Fire(7); - - SpinWaitEx.SpinUntil(() => serverLog.Count == 6); - Assert.AreEqual(new List {1, 2, 3, 5, 6, 7}, serverLog); - - - sp.Advise(lifetime, i => Assert.AreNotSame(8, i, "Value {0} mustn't be received", 8)); - cp.Fire(8); - - SpinWaitEx.SpinUntil(() => clientLog.Count == 6); - Assert.AreEqual(new List {1, 2, 4, 5, 6, 8}, clientLog); - - //Connection is broken for now, proxy is not alive - } - }); - } - } -} -#endif +// #if !NET35 +// using System.Collections.Generic; +// using System.Threading; +// using JetBrains.Collections.Viewable; +// using JetBrains.Lifetimes; +// using JetBrains.Rd.Base; +// using JetBrains.Rd.Impl; +// using JetBrains.Threading; +// using NUnit.Framework; +// using Test.Lifetimes; +// +// namespace Test.RdFramework +// { +// [TestFixture] +// [Ignore("TODO: this test tends to hang")] +// class SocketProxyTest : LifetimesTestBase +// { +// [Test] +// public void TestSimple() +// { +// // using var factory = Log.UsingLogFactory(new TextWriterLogFactory(Console.Out, LoggingLevel.TRACE)); +// Lifetime.Using(lifetime => +// { +// var proxyLifetimeDefinition = lifetime.CreateNested(); +// var proxyLifetime = proxyLifetimeDefinition.Lifetime; +// { +// SynchronousScheduler.Instance.SetActive(lifetime); +// +// var (serverProtocol, _) = SocketWireIpEndpointTest.Server(lifetime, default); +// +// var proxy = new SocketProxy("TestProxy", proxyLifetime, serverProtocol).With(socketProxy => +// socketProxy.Start()); +// Thread.Sleep(SocketWireIpEndpointTest.DefaultTimeout); +// +// var clientProtocol = SocketWireIpEndpointTest.Client(lifetime, proxy.Port); +// +// var sp = NewRdSignal().Static(1); +// sp.BindTopLevel(lifetime, serverProtocol, SocketWireIpEndpointTest.Top); +// +// var cp = NewRdSignal().Static(1); +// cp.BindTopLevel(lifetime, clientProtocol, SocketWireIpEndpointTest.Top); +// +// var serverLog = new List(); +// var clientLog = new List(); +// +// sp.Advise(lifetime, i => serverLog.Add(i)); +// cp.Advise(lifetime, i => clientLog.Add(i)); +// +// //Connection is established for now +// +// sp.Fire(1); +// +// SpinWaitEx.SpinUntil(() => serverLog.Count == 1); +// SpinWaitEx.SpinUntil(() => clientLog.Count == 1); +// Assert.AreEqual(new List {1}, serverLog); +// Assert.AreEqual(new List {1}, clientLog); +// +// cp.Fire(2); +// +// SpinWaitEx.SpinUntil(() => serverLog.Count == 2); +// SpinWaitEx.SpinUntil(() => clientLog.Count == 2); +// Assert.AreEqual(new List {1, 2}, serverLog); +// Assert.AreEqual(new List {1, 2}, clientLog); +// +// proxy.StopServerToClientMessaging(); +// +// cp.Advise(lifetime, i => Assert.AreNotSame(3, i, "Value {0} mustn't be received", 3)); +// +// sp.Fire(3); +// +// SpinWaitEx.SpinUntil(() => serverLog.Count == 3); +// Assert.AreEqual(new List {1, 2, 3}, serverLog); +// +// +// proxy.StopClientToServerMessaging(); +// +// sp.Advise(lifetime, i => Assert.AreNotSame(4, i, "Value {0} mustn't be received", 4)); +// +// cp.Fire(4); +// +// SpinWaitEx.SpinUntil(() => clientLog.Count == 3); +// Assert.AreEqual(new List {1, 2, 4}, clientLog); +// +// //Connection is broken for now +// +// proxy.StartServerToClientMessaging(); +// +// sp.Fire(5); +// SpinWaitEx.SpinUntil(() => serverLog.Count == 4); +// SpinWaitEx.SpinUntil(() => clientLog.Count == 4); +// Assert.AreEqual(new List {1, 2, 3, 5}, serverLog); +// Assert.AreEqual(new List {1, 2, 4, 5}, clientLog); +// +// +// proxy.StartClientToServerMessaging(); +// +// cp.Fire(6); +// SpinWaitEx.SpinUntil(() => serverLog.Count == 5); +// SpinWaitEx.SpinUntil(() => clientLog.Count == 5); +// Assert.AreEqual(new List {1, 2, 3, 5, 6}, serverLog); +// Assert.AreEqual(new List {1, 2, 4, 5, 6}, clientLog); +// +// //Connection is established for now +// +// proxyLifetimeDefinition.Terminate(); +// +// +// cp.Advise(lifetime, i => Assert.AreNotSame(7, i, "Value {0} mustn't be received", 7)); +// sp.Fire(7); +// +// SpinWaitEx.SpinUntil(() => serverLog.Count == 6); +// Assert.AreEqual(new List {1, 2, 3, 5, 6, 7}, serverLog); +// +// +// sp.Advise(lifetime, i => Assert.AreNotSame(8, i, "Value {0} mustn't be received", 8)); +// cp.Fire(8); +// +// SpinWaitEx.SpinUntil(() => clientLog.Count == 6); +// Assert.AreEqual(new List {1, 2, 4, 5, 6, 8}, clientLog); +// +// //Connection is broken for now, proxy is not alive +// } +// }); +// } +// } +// } +// #endif diff --git a/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs b/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs new file mode 100644 index 000000000..ed9650727 --- /dev/null +++ b/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs @@ -0,0 +1,141 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using JetBrains.Collections.Viewable; +using JetBrains.Diagnostics; +using JetBrains.Diagnostics.Internal; +using JetBrains.Lifetimes; +using JetBrains.Rd; +using JetBrains.Rd.Impl; +using NUnit.Framework; + +namespace Test.RdFramework; + +[TestFixture] +public class SocketWireIpEndpointTest : SocketWireTestBase +{ + internal override int GetPortOrPath() + { + var l = new TcpListener(IPAddress.Loopback, 0); + l.Start(); + int port = ((IPEndPoint) l.LocalEndpoint).Port; + l.Stop(); + return port; + } + + internal override (IProtocol ServerProtocol, int portOrPath) Server(Lifetime lifetime, int port = 0) + { + var id = "TestServer"; + var endPointWrapper = EndPointWrapper.CreateIpEndPoint(IPAddress.Loopback, port); + var server = new SocketWire.Server(lifetime, SynchronousScheduler.Instance, endPointWrapper, id); + var protocol = new Protocol(id, new Serializers(), new Identities(IdKind.Server), SynchronousScheduler.Instance, server, lifetime); + return (protocol, server.Port!.Value); + } + + internal override IProtocol Client(Lifetime lifetime, int port) + { + var id = "TestClient"; + var client = new SocketWire.Client(lifetime, SynchronousScheduler.Instance, port, id); + return new Protocol(id, new Serializers(), new Identities(IdKind.Server), SynchronousScheduler.Instance, client, lifetime); + } + + internal override EndPointWrapper CreateEndpointWrapper() => EndPointWrapper.CreateIpEndPoint(); + + internal IProtocol Client(Lifetime lifetime, IProtocol serverProtocol) + { + // ReSharper disable once PossibleNullReferenceException + // ReSharper disable once PossibleInvalidOperationException + return Client(lifetime, (serverProtocol.Wire as SocketWire.Server).Port.Value); + } + + internal override (IProtocol ServerProtocol, IProtocol ClientProtocol) CreateServerClient(Lifetime lifetime) + { + var (serverProtocol, _) = Server(lifetime); + var clientProtocol = Client(lifetime, serverProtocol); + return (serverProtocol, clientProtocol); + } + + [TestCase(true)] + [TestCase(false)] + public void TestPacketLoss(bool isClientToServer) + { + using (Log.UsingLogFactory(new TextWriterLogFactory(Console.Out, LoggingLevel.TRACE))) + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + + var (serverProtocol, _) = Server(lifetime); + var serverWire = (SocketWire.Base) serverProtocol.Wire; + + var proxy = new SocketProxy("TestProxy", lifetime, serverProtocol); + proxy.Start(); + + var clientProtocol = Client(lifetime, proxy.Port); + var clientWire = (SocketWire.Base) clientProtocol.Wire; + + Thread.Sleep(DefaultTimeout); + + if (isClientToServer) + proxy.StopClientToServerMessaging(); + else + proxy.StopServerToClientMessaging(); + + var detectionTimeoutTicks = ((SocketWire.Base) clientProtocol.Wire).HeartBeatInterval.Ticks * + (SocketWire.Base.MaximumHeartbeatDelay + 3); + var detectionTimeout = TimeSpan.FromTicks(detectionTimeoutTicks); + + Thread.Sleep(detectionTimeout); + + Assert.IsTrue(serverWire.Connected.Value); + Assert.IsTrue(clientWire.Connected.Value); + + Assert.IsFalse(serverWire.HeartbeatAlive.Value); + Assert.IsFalse(clientWire.HeartbeatAlive.Value); + + if (isClientToServer) + proxy.StartClientToServerMessaging(); + else + proxy.StartServerToClientMessaging(); + + Thread.Sleep(detectionTimeout); + + Assert.IsTrue(serverWire.Connected.Value); + Assert.IsTrue(clientWire.Connected.Value); + + Assert.IsTrue(serverWire.HeartbeatAlive.Value); + Assert.IsTrue(clientWire.HeartbeatAlive.Value); + + }); + } + + // [Test] + // [Ignore("Not enough timeout to get the correct test")] + // public void TestStressHeartbeat() + // { + // // using (Log.UsingLogFactory(new TextWriterLogFactory(Console.Out, LoggingLevel.TRACE))) + // Lifetime.Using(lifetime => + // { + // SynchronousScheduler.Instance.SetActive(lifetime); + // + // var interval = TimeSpan.FromMilliseconds(50); + // + // var serverProtocol = Server(lifetime); + // var serverWire = ((SocketWire.Base) serverProtocol.Wire).With(wire => wire.HeartBeatInterval = interval); + // + // var latency = TimeSpan.FromMilliseconds(40); + // var proxy = new SocketProxy("TestProxy", lifetime, serverProtocol) {Latency = latency}; + // proxy.Start(); + // + // var clientProtocol = Client(lifetime, proxy.Port); + // var clientWire = ((SocketWire.Base) clientProtocol.Wire).With(wire => wire.HeartBeatInterval = interval); + // + // Thread.Sleep(DefaultTimeout); + // + // serverWire.HeartbeatAlive.WhenFalse(lifetime, _ => Assert.Fail("Detected false disconnect on server side")); + // clientWire.HeartbeatAlive.WhenFalse(lifetime, _ => Assert.Fail("Detected false disconnect on client side")); + // + // Thread.Sleep(TimeSpan.FromSeconds(50)); + // }); + // } +} \ No newline at end of file diff --git a/rd-net/Test.RdFramework/SocketWireTest.cs b/rd-net/Test.RdFramework/SocketWireTest.cs deleted file mode 100644 index 40d06ffc4..000000000 --- a/rd-net/Test.RdFramework/SocketWireTest.cs +++ /dev/null @@ -1,503 +0,0 @@ -#if !NET35 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using JetBrains.Collections.Viewable; -using JetBrains.Core; -using JetBrains.Diagnostics; -using JetBrains.Diagnostics.Internal; -using JetBrains.Lifetimes; -using JetBrains.Rd; -using JetBrains.Rd.Base; -using JetBrains.Rd.Impl; -using JetBrains.Threading; -using NUnit.Framework; -using Test.Lifetimes; - -namespace Test.RdFramework -{ - [TestFixture] - public class SocketWireTest : LifetimesTestBase - { - internal static TimeSpan DefaultTimeout = TimeSpan.FromMilliseconds(100); - - internal const string Top = "top"; - private void WaitAndAssert(RdProperty property, T expected, T prev) - { - WaitAndAssert(property, expected, new Maybe(prev)); - } - - - private void WaitAndAssert(RdProperty property, T expected, Maybe prev = default(Maybe)) - { - var start = Environment.TickCount; - const int timeout = 5000; - while (Environment.TickCount - start < timeout && property.Maybe == prev) Thread.Sleep(10); - if (property.Maybe == prev) - throw new TimeoutException($"Timeout {timeout} ms while waiting for value '{expected}'"); - Assert.AreEqual(expected, property.Value); - } - - - static int FindFreePort() - { - TcpListener l = new TcpListener(IPAddress.Loopback, 0); - l.Start(); - int port = ((IPEndPoint) l.LocalEndpoint).Port; - l.Stop(); - return port; - } - - - internal static IProtocol Server(Lifetime lifetime, int? port = null) - { - var id = "TestServer"; - var server = new SocketWire.Server(lifetime, SynchronousScheduler.Instance, new IPEndPoint(IPAddress.Loopback, port ?? 0), id); - return new Protocol(id, new Serializers(), new Identities(IdKind.Server), SynchronousScheduler.Instance, server, lifetime); - } - - internal static IProtocol Client(Lifetime lifetime, int port) - { - var id = "TestClient"; - var client = new SocketWire.Client(lifetime, SynchronousScheduler.Instance, port, id); - return new Protocol(id, new Serializers(), new Identities(IdKind.Server), SynchronousScheduler.Instance, client, lifetime); - } - - internal static IProtocol Client(Lifetime lifetime, IProtocol serverProtocol) - { - // ReSharper disable once PossibleNullReferenceException - return Client(lifetime, (serverProtocol.Wire as SocketWire.Server).Port); - } - - [Test] - public void TestBasicRun() - { - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - var serverProtocol = Server(lifetime); - var clientProtocol = Client(lifetime, serverProtocol); - - var sp = NewRdProperty().Static(1); - sp.BindTopLevel(lifetime, serverProtocol, Top); - var cp = NewRdProperty().Static(1); - cp.BindTopLevel(lifetime, clientProtocol, Top); - - cp.SetValue(1); - WaitAndAssert(sp, 1); - }); - } - - - [Test] - public void TestOrdering() - { - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - var serverProtocol = Server(lifetime); - var clientProtocol = Client(lifetime, serverProtocol); - - var sp = NewRdProperty().Static(1); - sp.BindTopLevel(lifetime, serverProtocol, Top); - var cp = NewRdProperty().Static(1); - cp.BindTopLevel(lifetime, clientProtocol, Top); - - var log = new List(); - sp.Advise(lifetime, it => log.Add(it)); - sp.SetValue(1); - sp.SetValue(2); - sp.SetValue(3); - sp.SetValue(4); - sp.SetValue(5); - - while (log.Count < 5) Thread.Sleep(10); - CollectionAssert.AreEqual(new[] {1, 2, 3, 4, 5}, log); - }); - } - - - [Test] - public void TestBigBuffer() - { - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - var serverProtocol = Server(lifetime); - var clientProtocol = Client(lifetime, serverProtocol); - - var sp = NewRdProperty().Static(1); - sp.BindTopLevel(lifetime, serverProtocol, Top); - var cp = NewRdProperty().Static(1); - cp.BindTopLevel(lifetime, clientProtocol, Top); - - cp.SetValue("1"); - WaitAndAssert(sp, "1"); - - sp.SetValue(new string('a', 100000)); - WaitAndAssert(cp, new string('a', 100000), "1"); - - cp.SetValue("a"); - WaitAndAssert(sp, "a", new string('a', 100000)); - - cp.SetValue("ab"); - WaitAndAssert(sp, "ab", "a"); - - cp.SetValue("abc"); - WaitAndAssert(sp, "abc", "ab"); - }); - } - - - [Test] - public void TestRunWithSlowpokeServer() - { - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - - var port = FindFreePort(); - var clientProtocol = Client(lifetime, port); - - var cp = NewRdProperty().Static(1); - cp.BindTopLevel(lifetime, clientProtocol, Top); - cp.SetValue(1); - - Thread.Sleep(2000); - var serverProtocol = Server(lifetime, port); - var sp = NewRdProperty().Static(1); - sp.BindTopLevel(lifetime, serverProtocol, Top); - - var prev = sp.Maybe; - - - cp.SetValue(4); - Thread.Sleep(200); - WaitAndAssert(sp, 4, prev); - }); - } - - - [Test] - [Timeout(5000)] - public void TestServerWithoutClient() - { - Lifetime.Using(lifetime => - { - WithLongTimeout(lifetime); - SynchronousScheduler.Instance.SetActive(lifetime); - Server(lifetime); - }); - } - - [Test] - public void TestServerWithoutClientWithDelay() - { - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - Server(lifetime); - Thread.Sleep(100); - }); - } - - [Test] - public void TestServerWithoutClientWithDelayAndMessages() - { - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - var protocol = Server(lifetime); - Thread.Sleep(100); - var p = NewRdProperty().Static(1); - p.BindTopLevel(lifetime, protocol, Top); - p.SetValue(1); - p.SetValue(2); - Thread.Sleep(50); - }); - } - - - [Test] - [Timeout(5000)] - public void TestClientWithoutServer() - { - Lifetime.Using(lifetime => - { - WithLongTimeout(lifetime); - SynchronousScheduler.Instance.SetActive(lifetime); - Client(lifetime, FindFreePort()); - }); - } - - [Test] - public void TestClientWithoutServerWithDelay() - { - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - Client(lifetime, FindFreePort()); - Thread.Sleep(100); - }); - } - - [Test] - public void TestClientWithoutServerWithDelayAndMessages() - { - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - var protocol = Client(lifetime, FindFreePort()); - Thread.Sleep(100); - var p = NewRdProperty().Static(1); - p.BindTopLevel(lifetime, protocol, Top); - p.SetValue(1); - p.SetValue(2); - Thread.Sleep(50); - }); - } - - - [Test, Ignore("https://github.com/JetBrains/rd/issues/69")] - public void TestDisconnect() => TestDisconnectBase((list, i) => list.Add(i)); - - [Test] - public void TestDisconnect_AllowDuplicates() => TestDisconnectBase((list, i) => - { - // values may be duplicated due to asynchronous acknowledgement - if (list.LastOrDefault() < i) - list.Add(i); - }); - - private void TestDisconnectBase(Action, int> advise) - { - var timeout = TimeSpan.FromSeconds(1); - - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - var serverProtocol = Server(lifetime); - var clientProtocol = Client(lifetime, serverProtocol); - - var sp = NewRdSignal().Static(1); - sp.BindTopLevel(lifetime, serverProtocol, Top); - - var cp = NewRdSignal().Static(1); - cp.BindTopLevel(lifetime, clientProtocol, Top); - - var log = new List(); - sp.Advise(lifetime, i => advise(log, i)); - - cp.Fire(1); - cp.Fire(2); - Assert.True(SpinWaitEx.SpinUntil(timeout, () => log.Count == 2)); - Assert.AreEqual(new List {1, 2}, log); - - CloseSocket(clientProtocol); - cp.Fire(3); - cp.Fire(4); - - Assert.True(SpinWaitEx.SpinUntil(timeout, () => log.Count == 4)); - Assert.AreEqual(new List {1, 2, 3, 4}, log); - - CloseSocket(serverProtocol); - cp.Fire(5); - cp.Fire(6); - - Assert.True(SpinWaitEx.SpinUntil(timeout, () => log.Count == 6)); - Assert.AreEqual(new List {1, 2, 3, 4, 5, 6}, log); - }); - } - - [Test] - public void TestReconnect() - { - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - var serverProtocol = Server(lifetime, null); - - var sp = NewRdProperty().Static(1); - sp.BindTopLevel(lifetime, serverProtocol, Top); - sp.IsMaster = false; - - var wire = serverProtocol.Wire as SocketWire.Base; - int clientCount = 0; - wire.NotNull().Connected.WhenTrue(lifetime, _ => - { - clientCount++; - }); - - Assert.AreEqual(0, clientCount); - - Lifetime.Using(lf => - { - var clientProtocol = Client(lf, serverProtocol); - var cp = NewRdProperty().Static(1); - cp.IsMaster = true; - cp.BindTopLevel(lf, clientProtocol, Top); - cp.SetValue(1); - WaitAndAssert(sp, 1); - Assert.AreEqual(1, clientCount); - }); - - - Lifetime.Using(lf => - { - sp = NewRdProperty().Static(2); - sp.BindTopLevel(lifetime, serverProtocol, Top); - - var clientProtocol = Client(lf, serverProtocol); - var cp = NewRdProperty().Static(2); - cp.BindTopLevel(lf, clientProtocol, Top); - cp.SetValue(2); - WaitAndAssert(sp, 2); - Assert.AreEqual(2, clientCount); - }); - - - Lifetime.Using(lf => - { - var clientProtocol = Client(lf, serverProtocol); - var cp = NewRdProperty().Static(2); - cp.BindTopLevel(lf, clientProtocol, Top); - cp.SetValue(3); - WaitAndAssert(sp, 3, 2); - Assert.AreEqual(3, clientCount); - }); - - }); - - } - - [TestCase(true)] - [TestCase(false)] - public void TestPacketLoss(bool isClientToServer) - { - using (Log.UsingLogFactory(new TextWriterLogFactory(Console.Out, LoggingLevel.TRACE))) - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - - var serverProtocol = Server(lifetime); - var serverWire = (SocketWire.Base) serverProtocol.Wire; - - var proxy = new SocketProxy("TestProxy", lifetime, serverProtocol); - proxy.Start(); - - var clientProtocol = Client(lifetime, proxy.Port); - var clientWire = (SocketWire.Base) clientProtocol.Wire; - - Thread.Sleep(DefaultTimeout); - - if (isClientToServer) - proxy.StopClientToServerMessaging(); - else - proxy.StopServerToClientMessaging(); - - var detectionTimeoutTicks = ((SocketWire.Base) clientProtocol.Wire).HeartBeatInterval.Ticks * - (SocketWire.Base.MaximumHeartbeatDelay + 3); - var detectionTimeout = TimeSpan.FromTicks(detectionTimeoutTicks); - - Thread.Sleep(detectionTimeout); - - Assert.IsTrue(serverWire.Connected.Value); - Assert.IsTrue(clientWire.Connected.Value); - - Assert.IsFalse(serverWire.HeartbeatAlive.Value); - Assert.IsFalse(clientWire.HeartbeatAlive.Value); - - if (isClientToServer) - proxy.StartClientToServerMessaging(); - else - proxy.StartServerToClientMessaging(); - - Thread.Sleep(detectionTimeout); - - Assert.IsTrue(serverWire.Connected.Value); - Assert.IsTrue(clientWire.Connected.Value); - - Assert.IsTrue(serverWire.HeartbeatAlive.Value); - Assert.IsTrue(clientWire.HeartbeatAlive.Value); - - }); - } - - [Test] - [Ignore("Not enough timeout to get the correct test")] - public void TestStressHeartbeat() - { - // using (Log.UsingLogFactory(new TextWriterLogFactory(Console.Out, LoggingLevel.TRACE))) - Lifetime.Using(lifetime => - { - SynchronousScheduler.Instance.SetActive(lifetime); - - var interval = TimeSpan.FromMilliseconds(50); - - var serverProtocol = Server(lifetime); - var serverWire = ((SocketWire.Base) serverProtocol.Wire).With(wire => wire.HeartBeatInterval = interval); - - var latency = TimeSpan.FromMilliseconds(40); - var proxy = new SocketProxy("TestProxy", lifetime, serverProtocol) {Latency = latency}; - proxy.Start(); - - var clientProtocol = Client(lifetime, proxy.Port); - var clientWire = ((SocketWire.Base) clientProtocol.Wire).With(wire => wire.HeartBeatInterval = interval); - - Thread.Sleep(DefaultTimeout); - - serverWire.HeartbeatAlive.WhenFalse(lifetime, _ => Assert.Fail("Detected false disconnect on server side")); - clientWire.HeartbeatAlive.WhenFalse(lifetime, _ => Assert.Fail("Detected false disconnect on client side")); - - Thread.Sleep(TimeSpan.FromSeconds(50)); - }); - } - - - - [Test] - public void TestSocketFactory() - { - var sLifetime = new LifetimeDefinition(); - var factory = new SocketWire.ServerFactory(sLifetime.Lifetime, SynchronousScheduler.Instance); - - var lf1 = new LifetimeDefinition(); - new SocketWire.Client(lf1.Lifetime, SynchronousScheduler.Instance, factory.LocalPort); - SpinWaitEx.SpinUntil(() => factory.Connected.Count == 1); - - var lf2 = new LifetimeDefinition(); - new SocketWire.Client(lf2.Lifetime, SynchronousScheduler.Instance, factory.LocalPort); - SpinWaitEx.SpinUntil(() => factory.Connected.Count == 2); - - - lf1.Terminate(); - SpinWaitEx.SpinUntil(() => factory.Connected.Count == 1); - - sLifetime.Terminate(); - SpinWaitEx.SpinUntil(() => factory.Connected.Count == 0); - } - - - private static void CloseSocket(IProtocol protocol) - { - if (!(protocol.Wire is SocketWire.Base socketWire)) - { - Assert.Fail(); - return; - } - - SocketWire.Base.CloseSocket(socketWire.Socket.NotNull()); - } - - private static void WithLongTimeout(Lifetime lifetime) - { - var oldValue = SocketWire.Base.TimeoutMs; - lifetime.Bracket(() => SocketWire.Base.TimeoutMs = 100_000, () => SocketWire.Base.TimeoutMs = oldValue); - } - } -} -#endif \ No newline at end of file diff --git a/rd-net/Test.RdFramework/SocketWireTestBase.cs b/rd-net/Test.RdFramework/SocketWireTestBase.cs new file mode 100644 index 000000000..51f79714f --- /dev/null +++ b/rd-net/Test.RdFramework/SocketWireTestBase.cs @@ -0,0 +1,398 @@ +#if !NET35 + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading; +using JetBrains.Collections.Viewable; +using JetBrains.Core; +using JetBrains.Diagnostics; +using JetBrains.Lifetimes; +using JetBrains.Rd; +using JetBrains.Rd.Base; +using JetBrains.Rd.Impl; +using JetBrains.Threading; +using NUnit.Framework; +using Test.Lifetimes; + +namespace Test.RdFramework; + +public abstract class SocketWireTestBase : LifetimesTestBase +{ + internal static TimeSpan DefaultTimeout = TimeSpan.FromMilliseconds(100); + + internal const string Top = "top"; + private void WaitAndAssert(RdProperty property, T expected, T prev) + { + WaitAndAssert(property, expected, new Maybe(prev)); + } + + + private void WaitAndAssert(RdProperty property, T expected, Maybe prev = default(Maybe)) + { + var start = Environment.TickCount; + const int timeout = 5000; + while (Environment.TickCount - start < timeout && property.Maybe == prev) Thread.Sleep(10); + if (property.Maybe == prev) + throw new TimeoutException($"Timeout {timeout} ms while waiting for value '{expected}'"); + Assert.AreEqual(expected, property.Value); + } + + internal abstract (IProtocol ServerProtocol, IProtocol ClientProtocol) CreateServerClient(Lifetime lifetime); + internal abstract T GetPortOrPath(); + internal abstract (IProtocol ServerProtocol, T portOrPath) Server(Lifetime lifetime, T portOrPath = default); + internal abstract IProtocol Client(Lifetime lifetime, T portOrPath); + internal abstract EndPointWrapper CreateEndpointWrapper(); + + [Test] + public void TestBasicRun() + { + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + var (serverProtocol, clientProtocol) = CreateServerClient(lifetime); + + var sp = NewRdProperty().Static(1); + sp.BindTopLevel(lifetime, serverProtocol, Top); + var cp = NewRdProperty().Static(1); + cp.BindTopLevel(lifetime, clientProtocol, Top); + + cp.SetValue(1); + WaitAndAssert(sp, 1); + }); + } + + [Test] + public void TestOrdering() + { + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + var (serverProtocol, clientProtocol) = CreateServerClient(lifetime); + + var sp = NewRdProperty().Static(1); + sp.BindTopLevel(lifetime, serverProtocol, Top); + var cp = NewRdProperty().Static(1); + cp.BindTopLevel(lifetime, clientProtocol, Top); + + var log = new List(); + sp.Advise(lifetime, it => log.Add(it)); + sp.SetValue(1); + sp.SetValue(2); + sp.SetValue(3); + sp.SetValue(4); + sp.SetValue(5); + + while (log.Count < 5) Thread.Sleep(10); + CollectionAssert.AreEqual(new[] {1, 2, 3, 4, 5}, log); + }); + } + + + [Test] + public void TestBigBuffer() + { + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + var (serverProtocol, clientProtocol) = CreateServerClient(lifetime); + + var sp = NewRdProperty().Static(1); + sp.BindTopLevel(lifetime, serverProtocol, Top); + var cp = NewRdProperty().Static(1); + cp.BindTopLevel(lifetime, clientProtocol, Top); + + cp.SetValue("1"); + WaitAndAssert(sp, "1"); + + sp.SetValue(new string('a', 100000)); + WaitAndAssert(cp, new string('a', 100000), "1"); + + cp.SetValue("a"); + WaitAndAssert(sp, "a", new string('a', 100000)); + + cp.SetValue("ab"); + WaitAndAssert(sp, "ab", "a"); + + cp.SetValue("abc"); + WaitAndAssert(sp, "abc", "ab"); + }); + } + + + [Test] + public void TestRunWithSlowpokeServer() + { + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + + var portOrPath = GetPortOrPath(); + var clientProtocol = Client(lifetime, portOrPath); + + var cp = NewRdProperty().Static(1); + cp.BindTopLevel(lifetime, clientProtocol, Top); + cp.SetValue(1); + + Thread.Sleep(2000); + var (serverProtocol, _) = Server(lifetime, portOrPath); + var sp = NewRdProperty().Static(1); + sp.BindTopLevel(lifetime, serverProtocol, Top); + + var prev = sp.Maybe; + + + cp.SetValue(4); + Thread.Sleep(200); + WaitAndAssert(sp, 4, prev); + }); + } + + + [Test] + [Timeout(5000)] + public void TestServerWithoutClient() + { + Lifetime.Using(lifetime => + { + WithLongTimeout(lifetime); + SynchronousScheduler.Instance.SetActive(lifetime); + Server(lifetime); + }); + } + + [Test] + public void TestServerWithoutClientWithDelay() + { + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + Server(lifetime); + Thread.Sleep(100); + }); + } + + [Test] + public void TestServerWithoutClientWithDelayAndMessages() + { + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + var (protocol, _) = Server(lifetime); + Thread.Sleep(100); + var p = NewRdProperty().Static(1); + p.BindTopLevel(lifetime, protocol, Top); + p.SetValue(1); + p.SetValue(2); + Thread.Sleep(50); + }); + } + + + [Test] + [Timeout(5000)] + public void TestClientWithoutServer() + { + Lifetime.Using(lifetime => + { + WithLongTimeout(lifetime); + SynchronousScheduler.Instance.SetActive(lifetime); + Client(lifetime, GetPortOrPath()); + }); + } + + [Test] + public void TestClientWithoutServerWithDelay() + { + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + Client(lifetime, GetPortOrPath()); + Thread.Sleep(100); + }); + } + + [Test] + public void TestClientWithoutServerWithDelayAndMessages() + { + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + var protocol = Client(lifetime, GetPortOrPath()); + Thread.Sleep(100); + var p = NewRdProperty().Static(1); + p.BindTopLevel(lifetime, protocol, Top); + p.SetValue(1); + p.SetValue(2); + Thread.Sleep(50); + }); + } + + + [Test, Ignore("https://github.com/JetBrains/rd/issues/69")] + public void TestDisconnect() => TestDisconnectBase((list, i) => list.Add(i)); + + [Test] + public void TestDisconnect_AllowDuplicates() => TestDisconnectBase((list, i) => + { + // values may be duplicated due to asynchronous acknowledgement + if (list.LastOrDefault() < i) + list.Add(i); + }); + + private void TestDisconnectBase(Action, int> advise) + { + var timeout = TimeSpan.FromSeconds(1); + + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + var (serverProtocol, clientProtocol) = CreateServerClient(lifetime); + + var sp = NewRdSignal().Static(1); + sp.BindTopLevel(lifetime, serverProtocol, Top); + + var cp = NewRdSignal().Static(1); + cp.BindTopLevel(lifetime, clientProtocol, Top); + + var log = new List(); + sp.Advise(lifetime, i => advise(log, i)); + + cp.Fire(1); + cp.Fire(2); + Assert.True(SpinWaitEx.SpinUntil(timeout, () => log.Count == 2)); + Assert.AreEqual(new List {1, 2}, log); + + CloseSocket(clientProtocol); + cp.Fire(3); + cp.Fire(4); + + Assert.True(SpinWaitEx.SpinUntil(timeout, () => log.Count == 4)); + Assert.AreEqual(new List {1, 2, 3, 4}, log); + + CloseSocket(serverProtocol); + cp.Fire(5); + cp.Fire(6); + + Assert.True(SpinWaitEx.SpinUntil(timeout, () => log.Count == 6)); + Assert.AreEqual(new List {1, 2, 3, 4, 5, 6}, log); + }); + } + + [Test] + public void TestReconnect() + { + Lifetime.Using(lifetime => + { + SynchronousScheduler.Instance.SetActive(lifetime); + var (serverProtocol, portOrPath) = Server(lifetime, default); + + var sp = NewRdProperty().Static(1); + sp.BindTopLevel(lifetime, serverProtocol, Top); + sp.IsMaster = false; + + var wire = serverProtocol.Wire as SocketWire.Base; + int clientCount = 0; + wire.NotNull().Connected.WhenTrue(lifetime, _ => + { + clientCount++; + }); + + Assert.AreEqual(0, clientCount); + + Lifetime.Using(lf => + { + var clientProtocol = Client(lf, portOrPath); + var cp = NewRdProperty().Static(1); + cp.IsMaster = true; + cp.BindTopLevel(lf, clientProtocol, Top); + cp.SetValue(1); + WaitAndAssert(sp, 1); + Assert.AreEqual(1, clientCount); + }); + + + Lifetime.Using(lf => + { + sp = NewRdProperty().Static(2); + sp.BindTopLevel(lifetime, serverProtocol, Top); + + var clientProtocol = Client(lf, portOrPath); + var cp = NewRdProperty().Static(2); + cp.BindTopLevel(lf, clientProtocol, Top); + cp.SetValue(2); + WaitAndAssert(sp, 2); + Assert.AreEqual(2, clientCount); + }); + + + Lifetime.Using(lf => + { + var clientProtocol = Client(lf, portOrPath); + var cp = NewRdProperty().Static(2); + cp.BindTopLevel(lf, clientProtocol, Top); + cp.SetValue(3); + WaitAndAssert(sp, 3, 2); + Assert.AreEqual(3, clientCount); + }); + + }); + + } + + + [Test] + public void TestSocketFactory() + { + var sLifetime = new LifetimeDefinition(); + var endPointWrapper = CreateEndpointWrapper(); + var factory = new SocketWire.ServerFactory(sLifetime.Lifetime, SynchronousScheduler.Instance, endPointWrapper); + + var lf1 = new LifetimeDefinition(); + // ReSharper disable once PossibleInvalidOperationException + if (endPointWrapper.EndPointImpl is IPEndPoint) + { + new SocketWire.Client(lf1.Lifetime, SynchronousScheduler.Instance, factory.LocalPort.Value); + SpinWaitEx.SpinUntil(() => factory.Connected.Count == 1); + + var lf2 = new LifetimeDefinition(); + new SocketWire.Client(lf2.Lifetime, SynchronousScheduler.Instance, factory.LocalPort.Value); + SpinWaitEx.SpinUntil(() => factory.Connected.Count == 2); + } + else + { + new SocketWire.Client(lf1.Lifetime, SynchronousScheduler.Instance, factory.LocalPath!); + SpinWaitEx.SpinUntil(() => factory.Connected.Count == 1); + + var lf2 = new LifetimeDefinition(); + new SocketWire.Client(lf2.Lifetime, SynchronousScheduler.Instance, factory.LocalPath); + SpinWaitEx.SpinUntil(() => factory.Connected.Count == 2); + } + + lf1.Terminate(); + SpinWaitEx.SpinUntil(() => factory.Connected.Count == 1); + + sLifetime.Terminate(); + SpinWaitEx.SpinUntil(() => factory.Connected.Count == 0); + } + + + private static void CloseSocket(IProtocol protocol) + { + if (!(protocol.Wire is SocketWire.Base socketWire)) + { + Assert.Fail(); + return; + } + + SocketWire.Base.CloseSocket(socketWire.Socket.NotNull()); + } + + private static void WithLongTimeout(Lifetime lifetime) + { + var oldValue = SocketWire.Base.TimeoutMs; + lifetime.Bracket(() => SocketWire.Base.TimeoutMs = 100_000, () => SocketWire.Base.TimeoutMs = oldValue); + } +} +#endif \ No newline at end of file diff --git a/rd-net/Test.RdFramework/SocketWireUnixEndpointTest.cs b/rd-net/Test.RdFramework/SocketWireUnixEndpointTest.cs new file mode 100644 index 000000000..f333cd30d --- /dev/null +++ b/rd-net/Test.RdFramework/SocketWireUnixEndpointTest.cs @@ -0,0 +1,47 @@ +using System.IO; +using JetBrains.Collections.Viewable; +using JetBrains.Lifetimes; +using JetBrains.Rd; +using JetBrains.Rd.Impl; +using NUnit.Framework; + +namespace Test.RdFramework; + +[TestFixture] +public class SocketWireUnixEndpointTest : SocketWireTestBase +{ + internal override string GetPortOrPath() => Path.GetTempFileName(); + + internal override (IProtocol ServerProtocol, string portOrPath) Server(Lifetime lifetime, string path = null) + { + var id = "TestServer"; + var endPointWrapper = EndPointWrapper.CreateUnixEndPoint(path); + var server = new SocketWire.Server(lifetime, SynchronousScheduler.Instance, endPointWrapper, id); + var protocol = new Protocol(id, new Serializers(), new Identities(IdKind.Server), SynchronousScheduler.Instance, server, lifetime); + return (protocol, endPointWrapper.LocalPath); + } + + internal override IProtocol Client(Lifetime lifetime, string path) + { + var id = "TestClient"; + var client = new SocketWire.Client(lifetime, SynchronousScheduler.Instance, path, id); + return new Protocol(id, new Serializers(), new Identities(IdKind.Server), SynchronousScheduler.Instance, client, lifetime); + } + + internal override EndPointWrapper CreateEndpointWrapper() => EndPointWrapper.CreateUnixEndPoint(); + + // internal IProtocol Client(Lifetime lifetime, IProtocol serverProtocol) + // { + // // ReSharper disable once PossibleNullReferenceException + // // ReSharper disable once PossibleInvalidOperationException + // return Client(lifetime, (serverProtocol.Wire as SocketWire.Server).Port.Value); + // } + + internal override (IProtocol ServerProtocol, IProtocol ClientProtocol) CreateServerClient(Lifetime lifetime) + { + var path = GetPortOrPath(); + var (serverProtocol, _) = Server(lifetime, path); + var clientProtocol = Client(lifetime, path); + return (serverProtocol, clientProtocol); + } +} \ No newline at end of file diff --git a/rd-net/Test.Reflection.App/Program.cs b/rd-net/Test.Reflection.App/Program.cs index bd5f386e4..a331859c4 100644 --- a/rd-net/Test.Reflection.App/Program.cs +++ b/rd-net/Test.Reflection.App/Program.cs @@ -36,7 +36,7 @@ static class Program public static event Action OnChar; - private static readonly IPEndPoint ourIpEndPoint = new IPEndPoint(IPAddress.Loopback, ourPort); + private static readonly EndPointWrapper ourWrapper = EndPointWrapper.CreateIpEndPoint(IPAddress.Loopback, ourPort); public static void StartClient() => Main(new [] {"client"}); public static void StartServer() => Main(new [] {"server"}); @@ -63,13 +63,13 @@ private static void MainLifetime(string[] args, LifetimeDefinition lifetimeDefin if (isServer) { Console.Title = "Server"; - wire = new SocketWire.Server(lifetime, scheduler, ourIpEndPoint); + wire = new SocketWire.Server(lifetime, scheduler, ourWrapper); protocol = new Protocol("Server", reflectionSerializers.Serializers, new Identities(IdKind.Server), scheduler, wire, lifetime); } else { Console.Title = "Client"; - wire = new SocketWire.Client(lifetime, scheduler, ourIpEndPoint); + wire = new SocketWire.Client(lifetime, scheduler, ourWrapper); protocol = new Protocol("Client", reflectionSerializers.Serializers, new Identities(IdKind.Client), scheduler, wire, lifetime); } From 6dcf8a0a70bd38c5dce883eb077c94384cc5cdf3 Mon Sep 17 00:00:00 2001 From: Vladimir Rudnev Date: Tue, 7 Jan 2025 18:06:13 +0200 Subject: [PATCH 3/6] fix Accept + Close deadlock. --- rd-net/RdFramework/Impl/SocketWire.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rd-net/RdFramework/Impl/SocketWire.cs b/rd-net/RdFramework/Impl/SocketWire.cs index a1f33b508..21dec86ee 100644 --- a/rd-net/RdFramework/Impl/SocketWire.cs +++ b/rd-net/RdFramework/Impl/SocketWire.cs @@ -673,14 +673,14 @@ private void StartServerSocket(Lifetime lifetime, Socket serverSocket) var thread = new Thread(() => { - Log.Catch(() => + Log.Catch(async () => { while (lifetime.IsAlive) { try { Log.Verbose("{0} : accepting, port: {1}", Id, Port); - var s = serverSocket.Accept(); + var s = await serverSocket.AcceptAsync(lifetime); lock (Lock) { if (!lifetime.IsAlive) @@ -712,6 +712,10 @@ private void StartServerSocket(Lifetime lifetime, Socket serverSocket) { Log.Verbose("{0}: ObjectDisposedException with message {1}", Id, e.Message); } + catch (OperationCanceledException e) + { + Log.Verbose("{0} : OperationCanceledException with message {1}", Id, e.Message); + } catch (Exception e) { Log.Error(e, Id); From d79a483e364476b38bab6c6d2ab828bc4bef6fa6 Mon Sep 17 00:00:00 2001 From: Vladimir Rudnev Date: Tue, 7 Jan 2025 19:35:08 +0200 Subject: [PATCH 4/6] Review fixes. Return old frameworks. Marked everything related to `UnixDomainSocketEndPoint` with `#if NET8_0_OR_GREATER` pragma. --- .../RdFramework.Reflection.csproj | 2 +- .../ReflectionRdActivator.cs | 3 +- .../ReflectionSerializerVerifier.cs | 5 +- .../SerializerReflectionUtil.cs | 7 +- rd-net/RdFramework/Impl/EndPointWrapper.cs | 11 +- rd-net/RdFramework/Impl/SocketWire.cs | 23 +- rd-net/RdFramework/RdFramework.csproj | 2 +- rd-net/RdFramework/Tasks/RdFault.cs | 5 +- rd-net/Test.Cross/Test.Cross.csproj | 2 +- rd-net/Test.RdFramework/SocketProxyTest.cs | 254 +++++++++--------- .../SocketWireIpEndpointTest.cs | 8 +- rd-net/Test.RdFramework/SocketWireTestBase.cs | 7 +- .../SocketWireUnixEndpointTest.cs | 16 +- .../Test.RdFramework/Test.RdFramework.csproj | 2 +- .../Test.Reflection.App.csproj | 2 +- rd-net/global.json | 6 + 16 files changed, 197 insertions(+), 158 deletions(-) create mode 100644 rd-net/global.json diff --git a/rd-net/RdFramework.Reflection/RdFramework.Reflection.csproj b/rd-net/RdFramework.Reflection/RdFramework.Reflection.csproj index a984e243b..e768ed7b4 100644 --- a/rd-net/RdFramework.Reflection/RdFramework.Reflection.csproj +++ b/rd-net/RdFramework.Reflection/RdFramework.Reflection.csproj @@ -1,7 +1,7 @@  - net8.0 + netstandard2.0;net35;net472;net8.0; JetBrains.RdFramework.Reflection JetBrains.Rd.Reflection diff --git a/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs b/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs index db794c071..b23d7b414 100644 --- a/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs +++ b/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs @@ -148,8 +148,7 @@ private object ActivateRd(Type type) object instance; try { - instance = Activator.CreateInstance(implementingType) - ?? throw new InvalidOperationException($"Unable to create instance of: {implementingType.ToString(true)}"); + instance = Activator.CreateInstance(implementingType)!; } catch (MissingMethodException e) { diff --git a/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs b/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs index 0e0b0164f..bff7d55e0 100644 --- a/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs +++ b/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs @@ -148,10 +148,7 @@ bool IsValidArray() if (!typeInfo.IsArray) return false; if (typeInfo.GetArrayRank() != 1) return false; - var elementType = typeInfo.GetElementType(); - if (elementType == null) return false; - - var arrayType = elementType.GetTypeInfo(); + var arrayType = typeInfo.GetElementType()!.GetTypeInfo(); return IsFieldType(arrayType, false); } diff --git a/rd-net/RdFramework.Reflection/SerializerReflectionUtil.cs b/rd-net/RdFramework.Reflection/SerializerReflectionUtil.cs index c3e375ac4..6e1811ee6 100644 --- a/rd-net/RdFramework.Reflection/SerializerReflectionUtil.cs +++ b/rd-net/RdFramework.Reflection/SerializerReflectionUtil.cs @@ -68,14 +68,15 @@ internal static FieldInfo[] GetBindableFields(TypeInfo typeInfo) return list.ToArray(); } - private static IEnumerable GetFields(Type? type, Type baseType) + private static IEnumerable GetFields(Type type, Type baseType) { - foreach (var field in type?.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) ?? Array.Empty()) + foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) yield return field; // private fields only being returned for the current type - while ((type = type?.BaseType) != baseType && type != null) + while (type.BaseType != baseType && type.BaseType != null) { + type = type.BaseType; // but protected fields are returned in first step foreach (var baseField in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic)) if (baseField.IsPrivate) diff --git a/rd-net/RdFramework/Impl/EndPointWrapper.cs b/rd-net/RdFramework/Impl/EndPointWrapper.cs index 4a5ada28f..2f40c2410 100644 --- a/rd-net/RdFramework/Impl/EndPointWrapper.cs +++ b/rd-net/RdFramework/Impl/EndPointWrapper.cs @@ -34,9 +34,10 @@ public static EndPointWrapper CreateIpEndPoint(IPAddress? address = null, int? p }; } - public static EndPointWrapper CreateUnixEndPoint(string? path = null) +#if NET8_0_OR_GREATER + public static EndPointWrapper CreateUnixEndPoint(UnixSocketConnectionParams connectionParams) { - var path1 = path ?? Path.GetTempFileName(); + var path1 = connectionParams.Path ?? Path.GetTempFileName(); return new EndPointWrapper(new UnixDomainSocketEndPoint(path1)) { AddressFamily = AddressFamily.Unix, SocketType = SocketType.Stream, @@ -46,4 +47,10 @@ public static EndPointWrapper CreateUnixEndPoint(string? path = null) LocalPath = path1, }; } + + public struct UnixSocketConnectionParams + { + public string? Path { get; set; } + } +#endif } \ No newline at end of file diff --git a/rd-net/RdFramework/Impl/SocketWire.cs b/rd-net/RdFramework/Impl/SocketWire.cs index 21dec86ee..12c3754d7 100644 --- a/rd-net/RdFramework/Impl/SocketWire.cs +++ b/rd-net/RdFramework/Impl/SocketWire.cs @@ -525,8 +525,10 @@ public class Client : Base public Client(Lifetime lifetime, IScheduler scheduler, int port, string? optId = null) : this(lifetime, scheduler, EndPointWrapper.CreateIpEndPoint(IPAddress.Loopback, port), optId) {} - public Client(Lifetime lifetime, IScheduler scheduler, string path, string? optId = null) : - this(lifetime, scheduler, EndPointWrapper.CreateUnixEndPoint(path), optId) {} +#if NET8_0_OR_GREATER + public Client(Lifetime lifetime, IScheduler scheduler, EndPointWrapper.UnixSocketConnectionParams connectionParams, string? optId = null) : + this(lifetime, scheduler, EndPointWrapper.CreateUnixEndPoint(connectionParams), optId) {} +#endif public Client(Lifetime lifetime, IScheduler scheduler, EndPointWrapper endPointWrapper, string? optId = null) : base("ClientSocket-"+(optId ?? ""), lifetime, scheduler) @@ -646,7 +648,7 @@ private Server(Lifetime lifetime, IScheduler scheduler, string? optId = null) : public static Socket CreateServerSocket(EndPointWrapper? endPointWrapper) { Protocol.InitLogger.Verbose("Creating server socket on endpoint: {0}", endPointWrapper?.EndPointImpl); - // by default we will use IPEndpoint? + // by default we will use IPEndpoint endPointWrapper ??= EndPointWrapper.CreateIpEndPoint(IPAddress.Loopback, 0); var serverSocket = new Socket(endPointWrapper.AddressFamily, endPointWrapper.SocketType, endPointWrapper.ProtocolType); @@ -673,14 +675,22 @@ private void StartServerSocket(Lifetime lifetime, Socket serverSocket) var thread = new Thread(() => { +#if NET8_0_OR_GREATER Log.Catch(async () => +#else + Log.Catch(() => +#endif { while (lifetime.IsAlive) { try { Log.Verbose("{0} : accepting, port: {1}", Id, Port); +#if NET8_0_OR_GREATER var s = await serverSocket.AcceptAsync(lifetime); +#else + var s = serverSocket.Accept(); +#endif lock (Lock) { if (!lifetime.IsAlive) @@ -751,13 +761,12 @@ public void Deconstruct(out IScheduler scheduler, out string? id) } } - - - public class ServerFactory { [PublicAPI] public readonly int? LocalPort; +#if NET8_0_OR_GREATER [PublicAPI] public readonly string? LocalPath; +#endif [PublicAPI] public readonly IViewableSet Connected = new ViewableSet(); @@ -779,7 +788,9 @@ public ServerFactory( Base.CloseSocket(serverSocket); }); LocalPort = (serverSocket.LocalEndPoint as IPEndPoint)?.Port; +#if NET8_0_OR_GREATER LocalPath = endpointWrapper?.EndPointImpl is UnixDomainSocketEndPoint ? endpointWrapper.LocalPath : null; +#endif void Rec() { diff --git a/rd-net/RdFramework/RdFramework.csproj b/rd-net/RdFramework/RdFramework.csproj index f0f09422b..e6ef3b546 100644 --- a/rd-net/RdFramework/RdFramework.csproj +++ b/rd-net/RdFramework/RdFramework.csproj @@ -1,7 +1,7 @@  - net8.0 + netstandard2.0;net35;net472;net8.0 JetBrains.RdFramework JetBrains.Rd diff --git a/rd-net/RdFramework/Tasks/RdFault.cs b/rd-net/RdFramework/Tasks/RdFault.cs index 6d8ead731..b9df4eccf 100644 --- a/rd-net/RdFramework/Tasks/RdFault.cs +++ b/rd-net/RdFramework/Tasks/RdFault.cs @@ -31,8 +31,11 @@ protected RdFault(SerializationInfo info, StreamingContext context) : base(info, ReasonMessage = info.GetString(nameof(ReasonMessage)); } - // [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] +#if NET8_0_OR_GREATER [Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] +#else + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] +#endif public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue(nameof(ReasonTypeFqn), ReasonTypeFqn); diff --git a/rd-net/Test.Cross/Test.Cross.csproj b/rd-net/Test.Cross/Test.Cross.csproj index d6b227cb1..73f5b25bc 100644 --- a/rd-net/Test.Cross/Test.Cross.csproj +++ b/rd-net/Test.Cross/Test.Cross.csproj @@ -1,7 +1,7 @@  - net8.0 + netcoreapp3.1;net8.0 Test.RdCross CrossTests AnyCPU diff --git a/rd-net/Test.RdFramework/SocketProxyTest.cs b/rd-net/Test.RdFramework/SocketProxyTest.cs index 69e91b6c3..82f572faf 100644 --- a/rd-net/Test.RdFramework/SocketProxyTest.cs +++ b/rd-net/Test.RdFramework/SocketProxyTest.cs @@ -1,127 +1,127 @@ -// #if !NET35 -// using System.Collections.Generic; -// using System.Threading; -// using JetBrains.Collections.Viewable; -// using JetBrains.Lifetimes; -// using JetBrains.Rd.Base; -// using JetBrains.Rd.Impl; -// using JetBrains.Threading; -// using NUnit.Framework; -// using Test.Lifetimes; -// -// namespace Test.RdFramework -// { -// [TestFixture] -// [Ignore("TODO: this test tends to hang")] -// class SocketProxyTest : LifetimesTestBase -// { -// [Test] -// public void TestSimple() -// { -// // using var factory = Log.UsingLogFactory(new TextWriterLogFactory(Console.Out, LoggingLevel.TRACE)); -// Lifetime.Using(lifetime => -// { -// var proxyLifetimeDefinition = lifetime.CreateNested(); -// var proxyLifetime = proxyLifetimeDefinition.Lifetime; -// { -// SynchronousScheduler.Instance.SetActive(lifetime); -// -// var (serverProtocol, _) = SocketWireIpEndpointTest.Server(lifetime, default); -// -// var proxy = new SocketProxy("TestProxy", proxyLifetime, serverProtocol).With(socketProxy => -// socketProxy.Start()); -// Thread.Sleep(SocketWireIpEndpointTest.DefaultTimeout); -// -// var clientProtocol = SocketWireIpEndpointTest.Client(lifetime, proxy.Port); -// -// var sp = NewRdSignal().Static(1); -// sp.BindTopLevel(lifetime, serverProtocol, SocketWireIpEndpointTest.Top); -// -// var cp = NewRdSignal().Static(1); -// cp.BindTopLevel(lifetime, clientProtocol, SocketWireIpEndpointTest.Top); -// -// var serverLog = new List(); -// var clientLog = new List(); -// -// sp.Advise(lifetime, i => serverLog.Add(i)); -// cp.Advise(lifetime, i => clientLog.Add(i)); -// -// //Connection is established for now -// -// sp.Fire(1); -// -// SpinWaitEx.SpinUntil(() => serverLog.Count == 1); -// SpinWaitEx.SpinUntil(() => clientLog.Count == 1); -// Assert.AreEqual(new List {1}, serverLog); -// Assert.AreEqual(new List {1}, clientLog); -// -// cp.Fire(2); -// -// SpinWaitEx.SpinUntil(() => serverLog.Count == 2); -// SpinWaitEx.SpinUntil(() => clientLog.Count == 2); -// Assert.AreEqual(new List {1, 2}, serverLog); -// Assert.AreEqual(new List {1, 2}, clientLog); -// -// proxy.StopServerToClientMessaging(); -// -// cp.Advise(lifetime, i => Assert.AreNotSame(3, i, "Value {0} mustn't be received", 3)); -// -// sp.Fire(3); -// -// SpinWaitEx.SpinUntil(() => serverLog.Count == 3); -// Assert.AreEqual(new List {1, 2, 3}, serverLog); -// -// -// proxy.StopClientToServerMessaging(); -// -// sp.Advise(lifetime, i => Assert.AreNotSame(4, i, "Value {0} mustn't be received", 4)); -// -// cp.Fire(4); -// -// SpinWaitEx.SpinUntil(() => clientLog.Count == 3); -// Assert.AreEqual(new List {1, 2, 4}, clientLog); -// -// //Connection is broken for now -// -// proxy.StartServerToClientMessaging(); -// -// sp.Fire(5); -// SpinWaitEx.SpinUntil(() => serverLog.Count == 4); -// SpinWaitEx.SpinUntil(() => clientLog.Count == 4); -// Assert.AreEqual(new List {1, 2, 3, 5}, serverLog); -// Assert.AreEqual(new List {1, 2, 4, 5}, clientLog); -// -// -// proxy.StartClientToServerMessaging(); -// -// cp.Fire(6); -// SpinWaitEx.SpinUntil(() => serverLog.Count == 5); -// SpinWaitEx.SpinUntil(() => clientLog.Count == 5); -// Assert.AreEqual(new List {1, 2, 3, 5, 6}, serverLog); -// Assert.AreEqual(new List {1, 2, 4, 5, 6}, clientLog); -// -// //Connection is established for now -// -// proxyLifetimeDefinition.Terminate(); -// -// -// cp.Advise(lifetime, i => Assert.AreNotSame(7, i, "Value {0} mustn't be received", 7)); -// sp.Fire(7); -// -// SpinWaitEx.SpinUntil(() => serverLog.Count == 6); -// Assert.AreEqual(new List {1, 2, 3, 5, 6, 7}, serverLog); -// -// -// sp.Advise(lifetime, i => Assert.AreNotSame(8, i, "Value {0} mustn't be received", 8)); -// cp.Fire(8); -// -// SpinWaitEx.SpinUntil(() => clientLog.Count == 6); -// Assert.AreEqual(new List {1, 2, 4, 5, 6, 8}, clientLog); -// -// //Connection is broken for now, proxy is not alive -// } -// }); -// } -// } -// } -// #endif +#if !NET35 +using System.Collections.Generic; +using System.Threading; +using JetBrains.Collections.Viewable; +using JetBrains.Lifetimes; +using JetBrains.Rd.Base; +using JetBrains.Rd.Impl; +using JetBrains.Threading; +using NUnit.Framework; +using Test.Lifetimes; + +namespace Test.RdFramework +{ + [TestFixture] + [Ignore("TODO: this test tends to hang")] + class SocketProxyTest : LifetimesTestBase + { + [Test] + public void TestSimple() + { + // using var factory = Log.UsingLogFactory(new TextWriterLogFactory(Console.Out, LoggingLevel.TRACE)); + Lifetime.Using(lifetime => + { + var proxyLifetimeDefinition = lifetime.CreateNested(); + var proxyLifetime = proxyLifetimeDefinition.Lifetime; + { + SynchronousScheduler.Instance.SetActive(lifetime); + + var (serverProtocol, _) = SocketWireIpEndpointTest.CreateServer(lifetime); + + var proxy = new SocketProxy("TestProxy", proxyLifetime, serverProtocol).With(socketProxy => + socketProxy.Start()); + Thread.Sleep(SocketWireIpEndpointTest.DefaultTimeout); + + var clientProtocol = SocketWireIpEndpointTest.CreateClient(lifetime, proxy.Port); + + var sp = NewRdSignal().Static(1); + sp.BindTopLevel(lifetime, serverProtocol, SocketWireIpEndpointTest.Top); + + var cp = NewRdSignal().Static(1); + cp.BindTopLevel(lifetime, clientProtocol, SocketWireIpEndpointTest.Top); + + var serverLog = new List(); + var clientLog = new List(); + + sp.Advise(lifetime, i => serverLog.Add(i)); + cp.Advise(lifetime, i => clientLog.Add(i)); + + //Connection is established for now + + sp.Fire(1); + + SpinWaitEx.SpinUntil(() => serverLog.Count == 1); + SpinWaitEx.SpinUntil(() => clientLog.Count == 1); + Assert.AreEqual(new List {1}, serverLog); + Assert.AreEqual(new List {1}, clientLog); + + cp.Fire(2); + + SpinWaitEx.SpinUntil(() => serverLog.Count == 2); + SpinWaitEx.SpinUntil(() => clientLog.Count == 2); + Assert.AreEqual(new List {1, 2}, serverLog); + Assert.AreEqual(new List {1, 2}, clientLog); + + proxy.StopServerToClientMessaging(); + + cp.Advise(lifetime, i => Assert.AreNotSame(3, i, "Value {0} mustn't be received", 3)); + + sp.Fire(3); + + SpinWaitEx.SpinUntil(() => serverLog.Count == 3); + Assert.AreEqual(new List {1, 2, 3}, serverLog); + + + proxy.StopClientToServerMessaging(); + + sp.Advise(lifetime, i => Assert.AreNotSame(4, i, "Value {0} mustn't be received", 4)); + + cp.Fire(4); + + SpinWaitEx.SpinUntil(() => clientLog.Count == 3); + Assert.AreEqual(new List {1, 2, 4}, clientLog); + + //Connection is broken for now + + proxy.StartServerToClientMessaging(); + + sp.Fire(5); + SpinWaitEx.SpinUntil(() => serverLog.Count == 4); + SpinWaitEx.SpinUntil(() => clientLog.Count == 4); + Assert.AreEqual(new List {1, 2, 3, 5}, serverLog); + Assert.AreEqual(new List {1, 2, 4, 5}, clientLog); + + + proxy.StartClientToServerMessaging(); + + cp.Fire(6); + SpinWaitEx.SpinUntil(() => serverLog.Count == 5); + SpinWaitEx.SpinUntil(() => clientLog.Count == 5); + Assert.AreEqual(new List {1, 2, 3, 5, 6}, serverLog); + Assert.AreEqual(new List {1, 2, 4, 5, 6}, clientLog); + + //Connection is established for now + + proxyLifetimeDefinition.Terminate(); + + + cp.Advise(lifetime, i => Assert.AreNotSame(7, i, "Value {0} mustn't be received", 7)); + sp.Fire(7); + + SpinWaitEx.SpinUntil(() => serverLog.Count == 6); + Assert.AreEqual(new List {1, 2, 3, 5, 6, 7}, serverLog); + + + sp.Advise(lifetime, i => Assert.AreNotSame(8, i, "Value {0} mustn't be received", 8)); + cp.Fire(8); + + SpinWaitEx.SpinUntil(() => clientLog.Count == 6); + Assert.AreEqual(new List {1, 2, 4, 5, 6, 8}, clientLog); + + //Connection is broken for now, proxy is not alive + } + }); + } + } +} +#endif diff --git a/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs b/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs index ed9650727..09cb457d5 100644 --- a/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs +++ b/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs @@ -24,7 +24,9 @@ internal override int GetPortOrPath() return port; } - internal override (IProtocol ServerProtocol, int portOrPath) Server(Lifetime lifetime, int port = 0) + internal override (IProtocol ServerProtocol, int portOrPath) Server(Lifetime lifetime, int port = 0) => CreateServer(lifetime, port); + + internal static (IProtocol ServerProtocol, int portOrPath) CreateServer(Lifetime lifetime, int port = 0) { var id = "TestServer"; var endPointWrapper = EndPointWrapper.CreateIpEndPoint(IPAddress.Loopback, port); @@ -33,7 +35,9 @@ internal override (IProtocol ServerProtocol, int portOrPath) Server(Lifetime lif return (protocol, server.Port!.Value); } - internal override IProtocol Client(Lifetime lifetime, int port) + internal override IProtocol Client(Lifetime lifetime, int port) => CreateClient(lifetime, port); + + internal static IProtocol CreateClient(Lifetime lifetime, int port) { var id = "TestClient"; var client = new SocketWire.Client(lifetime, SynchronousScheduler.Instance, port, id); diff --git a/rd-net/Test.RdFramework/SocketWireTestBase.cs b/rd-net/Test.RdFramework/SocketWireTestBase.cs index 51f79714f..5593de202 100644 --- a/rd-net/Test.RdFramework/SocketWireTestBase.cs +++ b/rd-net/Test.RdFramework/SocketWireTestBase.cs @@ -362,12 +362,15 @@ public void TestSocketFactory() } else { - new SocketWire.Client(lf1.Lifetime, SynchronousScheduler.Instance, factory.LocalPath!); +#if NET8_0_OR_GREATER + var connectionParams = new EndPointWrapper.UnixSocketConnectionParams { Path = factory.LocalPath }; + new SocketWire.Client(lf1.Lifetime, SynchronousScheduler.Instance, connectionParams); SpinWaitEx.SpinUntil(() => factory.Connected.Count == 1); var lf2 = new LifetimeDefinition(); - new SocketWire.Client(lf2.Lifetime, SynchronousScheduler.Instance, factory.LocalPath); + new SocketWire.Client(lf2.Lifetime, SynchronousScheduler.Instance, connectionParams); SpinWaitEx.SpinUntil(() => factory.Connected.Count == 2); +#endif } lf1.Terminate(); diff --git a/rd-net/Test.RdFramework/SocketWireUnixEndpointTest.cs b/rd-net/Test.RdFramework/SocketWireUnixEndpointTest.cs index f333cd30d..f485d4ffc 100644 --- a/rd-net/Test.RdFramework/SocketWireUnixEndpointTest.cs +++ b/rd-net/Test.RdFramework/SocketWireUnixEndpointTest.cs @@ -1,3 +1,4 @@ +#if NET8_0_OR_GREATER using System.IO; using JetBrains.Collections.Viewable; using JetBrains.Lifetimes; @@ -15,7 +16,8 @@ public class SocketWireUnixEndpointTest : SocketWireTestBase internal override (IProtocol ServerProtocol, string portOrPath) Server(Lifetime lifetime, string path = null) { var id = "TestServer"; - var endPointWrapper = EndPointWrapper.CreateUnixEndPoint(path); + var connectionsParams = new EndPointWrapper.UnixSocketConnectionParams { Path = path }; + var endPointWrapper = EndPointWrapper.CreateUnixEndPoint(connectionsParams); var server = new SocketWire.Server(lifetime, SynchronousScheduler.Instance, endPointWrapper, id); var protocol = new Protocol(id, new Serializers(), new Identities(IdKind.Server), SynchronousScheduler.Instance, server, lifetime); return (protocol, endPointWrapper.LocalPath); @@ -24,11 +26,16 @@ internal override (IProtocol ServerProtocol, string portOrPath) Server(Lifetime internal override IProtocol Client(Lifetime lifetime, string path) { var id = "TestClient"; - var client = new SocketWire.Client(lifetime, SynchronousScheduler.Instance, path, id); + var connectionsParams = new EndPointWrapper.UnixSocketConnectionParams { Path = path }; + var client = new SocketWire.Client(lifetime, SynchronousScheduler.Instance, connectionsParams, id); return new Protocol(id, new Serializers(), new Identities(IdKind.Server), SynchronousScheduler.Instance, client, lifetime); } - internal override EndPointWrapper CreateEndpointWrapper() => EndPointWrapper.CreateUnixEndPoint(); + internal override EndPointWrapper CreateEndpointWrapper() + { + var connectionsParams = new EndPointWrapper.UnixSocketConnectionParams { Path = null }; + return EndPointWrapper.CreateUnixEndPoint(connectionsParams); + } // internal IProtocol Client(Lifetime lifetime, IProtocol serverProtocol) // { @@ -44,4 +51,5 @@ internal override (IProtocol ServerProtocol, IProtocol ClientProtocol) CreateSer var clientProtocol = Client(lifetime, path); return (serverProtocol, clientProtocol); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/rd-net/Test.RdFramework/Test.RdFramework.csproj b/rd-net/Test.RdFramework/Test.RdFramework.csproj index cf51b4579..ec9fe3b2a 100644 --- a/rd-net/Test.RdFramework/Test.RdFramework.csproj +++ b/rd-net/Test.RdFramework/Test.RdFramework.csproj @@ -1,7 +1,7 @@  - net8.0 + netcoreapp3.1;net8.0 net472;$(TargetFrameworks);net35 Full diff --git a/rd-net/Test.Reflection.App/Test.Reflection.App.csproj b/rd-net/Test.Reflection.App/Test.Reflection.App.csproj index d29a43d50..ba201a824 100644 --- a/rd-net/Test.Reflection.App/Test.Reflection.App.csproj +++ b/rd-net/Test.Reflection.App/Test.Reflection.App.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net472;net8.0 false LatestMajor disable diff --git a/rd-net/global.json b/rd-net/global.json new file mode 100644 index 000000000..cf2164e4e --- /dev/null +++ b/rd-net/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.100", + "rollForward": "major" + } +} \ No newline at end of file From 1a2f0b0658aae670c41dad5329b03ad4dc7c47a6 Mon Sep 17 00:00:00 2001 From: Vladimir Rudnev Date: Tue, 7 Jan 2025 19:40:44 +0200 Subject: [PATCH 5/6] Fix compilation after rebase on fresh master --- rd-net/RdFramework.Reflection/ReflectionRdActivator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs b/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs index b23d7b414..62900d616 100644 --- a/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs +++ b/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs @@ -451,7 +451,7 @@ public static string GetTypeName(Type type) { var rpcInterface = ReflectionSerializerVerifier.GetRpcInterface(type.GetTypeInfo()); if (rpcInterface != null) - return rpcInterface.AssemblyQualifiedName; + return rpcInterface.AssemblyQualifiedName!; } return typename!; From b4afd702208d1e3d1d663bbfa36fbbae3723ad3a Mon Sep 17 00:00:00 2001 From: Vladimir Rudnev Date: Wed, 8 Jan 2025 12:51:24 +0200 Subject: [PATCH 6/6] fix windows compilation --- rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs b/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs index 09cb457d5..d135855e9 100644 --- a/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs +++ b/rd-net/Test.RdFramework/SocketWireIpEndpointTest.cs @@ -1,3 +1,4 @@ +#if !NET35 using System; using System.Net; using System.Net.Sockets; @@ -142,4 +143,5 @@ public void TestPacketLoss(bool isClientToServer) // Thread.Sleep(TimeSpan.FromSeconds(50)); // }); // } -} \ No newline at end of file +} +#endif \ No newline at end of file