diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c57371d3..2726a126b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,28 @@ Release Notes ==== +# 02-17-2024 +DotNext 5.0.2 +* Fixed XML docs + +DotNext.Metaprogramming 5.0.2 +* Fixed [223](https://github.com/dotnet/dotNext/issues/223) + +DotNext.Unsafe 5.0.2 +* Updated dependencies + +DotNext.Threading 5.0.2 +* Added correct validation for maximum possible timeout for all `WaitAsync` methods + +DotNext.IO 5.0.2 +* Updated dependencies + +DotNext.Net.Cluster 5.0.2 +* Prevent indexing of WAL files on Windows + +DotNext.AspNetCore.Cluster 5.0.2 +* Updated dependencies + # 01-23-2024 DotNext 5.0.1 * Smallish performance improvements of dynamic buffers diff --git a/README.md b/README.md index b596fdf44..e11bc6c5d 100644 --- a/README.md +++ b/README.md @@ -44,30 +44,27 @@ All these things are implemented in 100% managed code on top of existing .NET AP * [NuGet Packages](https://www.nuget.org/profiles/rvsakno) # What's new -Release Date: 01-23-2024 +Release Date: 02-17-2024 -DotNext 5.0.1 -* Smallish performance improvements of dynamic buffers +DotNext 5.0.2 +* Fixed XML docs -DotNext.Metaprogramming 5.0.1 -* Updated dependencies +DotNext.Metaprogramming 5.0.2 +* Fixed [223](https://github.com/dotnet/dotNext/issues/223) -DotNext.Unsafe 5.0.1 +DotNext.Unsafe 5.0.2 * Updated dependencies -DotNext.Threading 5.0.1 -* Updated dependencies +DotNext.Threading 5.0.2 +* Added correct validation for maximum possible timeout for all `WaitAsync` methods -DotNext.IO 5.0.1 -* Improved performance of `FileWriter` and `FileBufferingWriter` classes by utilizing Scatter/Gather IO -* Reduced memory allocations required by async methods of `FileWriter` and `FileBufferingWriter` classes +DotNext.IO 5.0.2 * Updated dependencies -DotNext.Net.Cluster 5.0.1 -* Improved IO performance of Persistent WAL due to related improvements in DotNext.IO library -* Updated dependencies +DotNext.Net.Cluster 5.0.2 +* Prevent indexing of WAL files on Windows -DotNext.AspNetCore.Cluster 5.0.1 +DotNext.AspNetCore.Cluster 5.0.2 * Updated dependencies Changelog for previous versions located [here](./CHANGELOG.md). diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 8ce0e4022..4b1b87523 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -19,7 +19,7 @@ - + @@ -27,14 +27,14 @@ - + - - + + \ No newline at end of file diff --git a/src/DotNext.IO/DotNext.IO.csproj b/src/DotNext.IO/DotNext.IO.csproj index 719897c8e..3eebc2074 100644 --- a/src/DotNext.IO/DotNext.IO.csproj +++ b/src/DotNext.IO/DotNext.IO.csproj @@ -11,7 +11,7 @@ .NET Foundation and Contributors .NEXT Family of Libraries - 5.0.1 + 5.0.2 DotNext.IO MIT diff --git a/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj b/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj index 9cbc9292f..e9e1ad8e0 100644 --- a/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj +++ b/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj @@ -8,7 +8,7 @@ true false nullablePublicOnly - 5.0.1 + 5.0.2 .NET Foundation .NEXT Family of Libraries diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/AsyncResultExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/AsyncResultExpression.cs index 730bc4471..0a1ffb1bb 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/AsyncResultExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/AsyncResultExpression.cs @@ -60,6 +60,10 @@ public AsyncResultExpression(bool valueTask) /// public override Type Type => taskType; + // indicates that AsyncResult cannot throw exception so it can be inlined + internal bool IsSimpleResult + => AsyncResult is ConstantExpression or ParameterExpression or DefaultExpression; + /// /// Translates this expression into predefined set of expressions /// using Lowering technique. @@ -103,4 +107,7 @@ protected override AsyncResultExpression VisitChildren(ExpressionVisitor visitor var expression = visitor.Visit(AsyncResult); return ReferenceEquals(expression, AsyncResult) ? this : new(expression, taskType); } + + internal AsyncResultExpression Update(Expression asyncResult) + => new(asyncResult, taskType); } \ No newline at end of file diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/CollectionAccessExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/CollectionAccessExpression.cs index 90ac36a39..86f4787a3 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/CollectionAccessExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/CollectionAccessExpression.cs @@ -138,11 +138,11 @@ public override Expression Reduce() if (indexer is null) result = ArrayAccess(temp ?? Collection, Index); else if (count is null) - result = MakeIndex(temp ?? Collection, indexer, new[] { Index.Reduce() }); + result = MakeIndex(temp ?? Collection, indexer, [Index.Reduce()]); else - result = MakeIndex(temp ?? Collection, indexer, new[] { MakeIndex(temp ?? Collection, count, Index) }); + result = MakeIndex(temp ?? Collection, indexer, [MakeIndex(temp ?? Collection, count, Index)]); - return temp is null ? result : Block(Type, new[] { temp }, Assign(temp, Collection), result); + return temp is null ? result : Block(Type, [temp], Assign(temp, Collection), result); } /// diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs b/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs index 172c2fcb5..9ad3e4eed 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs @@ -1206,7 +1206,7 @@ internal static Expression AddEpilogue(this Expression expression, bool inferTyp [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(Activator))] public static MethodCallExpression New(this Expression type, params Expression[] args) { - var activate = typeof(Activator).GetMethod(nameof(Activator.CreateInstance), new[] { typeof(Type), typeof(object[]) }); + var activate = typeof(Activator).GetMethod(nameof(Activator.CreateInstance), [typeof(Type), typeof(object[])]); Debug.Assert(activate is not null); return Expression.Call(activate, type, Expression.NewArrayInit(typeof(object), args)); } @@ -1456,9 +1456,9 @@ public static Expression AsOptional(this Expression expression) public static Expression AsResult(this Expression expression) { var exception = Expression.Parameter(typeof(Exception)); - var ctor = typeof(Result<>).MakeGenericType(expression.Type).GetConstructor(new[] { expression.Type }); + var ctor = typeof(Result<>).MakeGenericType(expression.Type).GetConstructor([expression.Type]); Debug.Assert(ctor?.DeclaringType is not null); - var fallbackCtor = ctor.DeclaringType.GetConstructor(new[] { typeof(Exception) }); + var fallbackCtor = ctor.DeclaringType.GetConstructor([typeof(Exception)]); Debug.Assert(fallbackCtor is not null); return Expression.TryCatch( Expression.New(ctor, expression), diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/InterpolationExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/InterpolationExpression.cs index 76d983426..aa8f1b3f8 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/InterpolationExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/InterpolationExpression.cs @@ -105,19 +105,19 @@ private Expression MakePlainString() case 0: return Constant(Format); case 1: - var formatMethod = typeof(string).GetMethod(nameof(string.Format), new[] { typeof(IFormatProvider), typeof(string), typeof(object) }); + var formatMethod = typeof(string).GetMethod(nameof(string.Format), [typeof(IFormatProvider), typeof(string), typeof(object)]); Debug.Assert(formatMethod is not null); return Call(formatMethod, FormatProvider, Constant(Format), arguments[0]); case 2: - formatMethod = typeof(string).GetMethod(nameof(string.Format), new[] { typeof(IFormatProvider), typeof(string), typeof(object), typeof(object) }); + formatMethod = typeof(string).GetMethod(nameof(string.Format), [typeof(IFormatProvider), typeof(string), typeof(object), typeof(object)]); Debug.Assert(formatMethod is not null); return Call(formatMethod, FormatProvider, Constant(Format), FormatProvider, arguments[0], arguments[1]); case 3: - formatMethod = typeof(string).GetMethod(nameof(string.Format), new[] { typeof(IFormatProvider), typeof(string), typeof(object), typeof(object), typeof(object) }); + formatMethod = typeof(string).GetMethod(nameof(string.Format), [typeof(IFormatProvider), typeof(string), typeof(object), typeof(object), typeof(object)]); Debug.Assert(formatMethod is not null); return Call(formatMethod, FormatProvider, Constant(Format), FormatProvider, arguments[0], arguments[1], arguments[2]); default: - formatMethod = typeof(string).GetMethod(nameof(string.Format), new[] { typeof(IFormatProvider), typeof(string), typeof(object[]) }); + formatMethod = typeof(string).GetMethod(nameof(string.Format), [typeof(IFormatProvider), typeof(string), typeof(object[])]); Debug.Assert(formatMethod is not null); return Call(formatMethod, FormatProvider, Constant(Format), FormatProvider, NewArrayInit(typeof(object), arguments)); } diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/ItemIndexExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/ItemIndexExpression.cs index 0aafdb835..1369ac13c 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/ItemIndexExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/ItemIndexExpression.cs @@ -74,7 +74,7 @@ internal static Expression GetOffset(Expression index, Expression count) /// Translated expression. public override Expression Reduce() { - ConstructorInfo? ctor = typeof(Index).GetConstructor(new[] { typeof(int), typeof(bool) }); + ConstructorInfo? ctor = typeof(Index).GetConstructor([typeof(int), typeof(bool)]); Debug.Assert(ctor is not null); return New(ctor, conversionRequired ? Convert(Value, typeof(int)) : Value, Constant(IsFromEnd)); } diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/LockExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/LockExpression.cs index 36252f1fb..e360d689e 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/LockExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/LockExpression.cs @@ -94,9 +94,9 @@ public Expression Body [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(Monitor))] public override Expression Reduce() { - var monitorEnter = typeof(Monitor).GetMethod(nameof(Monitor.Enter), new[] { typeof(object) }); + var monitorEnter = typeof(Monitor).GetMethod(nameof(Monitor.Enter), [typeof(object)]); Debug.Assert(monitorEnter is not null); - var monitorExit = typeof(Monitor).GetMethod(nameof(Monitor.Exit), new[] { typeof(object) }); + var monitorExit = typeof(Monitor).GetMethod(nameof(Monitor.Exit), [typeof(object)]); Debug.Assert(monitorExit is not null); var body = TryFinally(Body, Call(monitorExit, SyncRoot)); return assignment is null ? diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs index 8122eb1a7..37b2245a3 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs @@ -70,7 +70,7 @@ private static Expression ToExpression(DynamicMetaObject arg, out BindingRestric } } - return Expression.Call(typeof(ExpressionBuilder), nameof(ExpressionBuilder.Const), new[] { arg.Expression.Type }, arg.Expression); + return Expression.Call(typeof(ExpressionBuilder), nameof(ExpressionBuilder.Const), [arg.Expression.Type], arg.Expression); } private static IReadOnlyList ToExpressions(DynamicMetaObject[] args, out BindingRestrictions restrictions) diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/MutationExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/MutationExpression.cs index b71612d15..0d602e79b 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/MutationExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/MutationExpression.cs @@ -95,13 +95,13 @@ public override Expression Reduce() if (Bindings.Count > 0) { var tempVar = Parameter(Type, "copy"); - ICollection statements = new List(Bindings.Count + 2) { Assign(tempVar, result) }; + List statements = new(Bindings.Count + 2) { Assign(tempVar, result) }; foreach (var binding in Bindings) statements.Add(Assign(MakeMemberAccess(tempVar, binding.Member), binding.Expression)); statements.Add(tempVar); - result = Block(tempVar.Type, new[] { tempVar }, statements); + result = Block(tempVar.Type, [tempVar], statements); } return result; diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/NullCoalescingAssignmentExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/NullCoalescingAssignmentExpression.cs index c35fe0eca..b0faa95be 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/NullCoalescingAssignmentExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/NullCoalescingAssignmentExpression.cs @@ -84,7 +84,7 @@ public override Expression Reduce() return Block( Left.Type, - new[] { localVar }, + [localVar], Assign(localVar, Left), Assign(Left, Build(localVar, Right)), localVar); diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/RangeExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/RangeExpression.cs index 65a7f8600..a44c20af7 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/RangeExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/RangeExpression.cs @@ -56,7 +56,7 @@ internal static Expression GetOffsetAndLength(Expression range, Expression lengt /// Translated expression. public override Expression Reduce() { - ConstructorInfo? ctor = typeof(Range).GetConstructor(new[] { typeof(Index), typeof(Index) }); + ConstructorInfo? ctor = typeof(Range).GetConstructor([typeof(Index), typeof(Index)]); Debug.Assert(ctor is not null); return New(ctor, Start.Reduce(), End.Reduce()); } diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/RefAnyValExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/RefAnyValExpression.cs index 7fa48cab0..b03c4e279 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/RefAnyValExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/RefAnyValExpression.cs @@ -42,5 +42,5 @@ public RefAnyValExpression(ParameterExpression typedRef, Type referenceType) /// using Lowering technique. /// /// Translated expression. - public override Expression Reduce() => Call(typeof(Intrinsics), nameof(Intrinsics.AsRef), new[] { ReferenceType }, TypedReferenceVar); + public override Expression Reduce() => Call(typeof(Intrinsics), nameof(Intrinsics.AsRef), [ReferenceType], TypedReferenceVar); } \ No newline at end of file diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/SliceExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/SliceExpression.cs index bb2fee5fb..68abb4b28 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/SliceExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/SliceExpression.cs @@ -99,7 +99,7 @@ private static IEnumerable GetSliceMethods(Type collection) [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(RuntimeHelpers))] private static MethodCallExpression SubArray(Expression array, Expression range) { - MethodInfo? subArray = typeof(RuntimeHelpers).GetMethod(nameof(RuntimeHelpers.GetSubArray), 1, new[] { Type.MakeGenericMethodParameter(0).MakeArrayType(), typeof(Range) }); + MethodInfo? subArray = typeof(RuntimeHelpers).GetMethod(nameof(RuntimeHelpers.GetSubArray), 1, [Type.MakeGenericMethodParameter(0).MakeArrayType(), typeof(Range)]); Debug.Assert(subArray is not null); subArray = subArray.MakeGenericMethod(array.Type.GetElementType()!); return Call(subArray, array, range.Reduce()); @@ -108,7 +108,7 @@ private static MethodCallExpression SubArray(Expression array, Expression range) private static BlockExpression SubCollection(Expression collection, MethodInfo slice, PropertyInfo count, Expression range) { var offsetAndLengthCall = RangeExpression.GetOffsetAndLength(range, Property(collection, count), out var offsetAndLength, out var offsetField, out var lengthField); - return Block(new[] { offsetAndLength }, Assign(offsetAndLength, offsetAndLengthCall), Call(collection, slice, offsetField, lengthField)); + return Block([offsetAndLength], Assign(offsetAndLength, offsetAndLengthCall), Call(collection, slice, offsetField, lengthField)); } /// @@ -127,7 +127,7 @@ public override Expression Reduce() else result = SubCollection(temp ?? Collection, slice, count, Range); - return temp is null ? result : Block(Type, new[] { temp }, Assign(temp, Collection), result); + return temp is null ? result : Block(Type, [temp], Assign(temp, Collection), result); } /// diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/WriteLineExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/WriteLineExpression.cs index b8a4a37e3..49dfd35b7 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/WriteLineExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/WriteLineExpression.cs @@ -60,7 +60,7 @@ private WriteLineExpression(Expression value, Kind kind) [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(TextWriter))] private static MethodCallExpression WriteLineTo(MemberExpression stream, Expression value) { - MethodInfo? writeLineMethod = typeof(TextWriter).GetMethod(nameof(TextWriter.WriteLine), new[] { value.Type }); + MethodInfo? writeLineMethod = typeof(TextWriter).GetMethod(nameof(TextWriter.WriteLine), [value.Type]); // WriteLine method will always be resolved here because Type.DefaultBinder // chooses TextWriter.WriteLine(object) if there is no exact match @@ -91,7 +91,7 @@ private MethodCallExpression WriteLineToError() [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(System.Diagnostics.Debug))] private MethodCallExpression WriteLineToDebug() { - var writeLineMethod = typeof(System.Diagnostics.Debug).GetMethod(nameof(System.Diagnostics.Debug.WriteLine), new[] { typeof(object) }); + var writeLineMethod = typeof(System.Diagnostics.Debug).GetMethod(nameof(System.Diagnostics.Debug.WriteLine), [typeof(object)]); System.Diagnostics.Debug.Assert(writeLineMethod is not null); return Call(writeLineMethod, value.Type.IsValueType ? Convert(value, typeof(object)) : value); } diff --git a/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs b/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs index 4ce80f4a9..1ceeabeb6 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs @@ -193,7 +193,7 @@ public MatchBuilder Case(string memberName, Expression memberValue, FuncThe action to be executed if object matches to the pattern. /// this builder. public MatchBuilder Case(string memberName1, Expression memberValue1, string memberName2, Expression memberValue2, Func body) - => Case(StructuralPattern(new[] { (memberName1, memberValue1), (memberName2, memberValue2) }), value => body(Expression.PropertyOrField(value, memberName1), Expression.PropertyOrField(value, memberName2))); + => Case(StructuralPattern([(memberName1, memberValue1), (memberName2, memberValue2)]), value => body(Expression.PropertyOrField(value, memberName1), Expression.PropertyOrField(value, memberName2))); /// /// Defines pattern matching based on structural matching. @@ -207,7 +207,7 @@ public MatchBuilder Case(string memberName1, Expression memberValue1, string mem /// The action to be executed if object matches to the pattern. /// this builder. public MatchBuilder Case(string memberName1, Expression memberValue1, string memberName2, Expression memberValue2, string memberName3, Expression memberValue3, Func body) - => Case(StructuralPattern(new[] { (memberName1, memberValue1), (memberName2, memberValue2), (memberName3, memberValue3) }), value => body(Expression.PropertyOrField(value, memberName1), Expression.PropertyOrField(value, memberName2), Expression.PropertyOrField(value, memberName3))); + => Case(StructuralPattern([(memberName1, memberValue1), (memberName2, memberValue2), (memberName3, memberValue3)]), value => body(Expression.PropertyOrField(value, memberName1), Expression.PropertyOrField(value, memberName2), Expression.PropertyOrField(value, memberName3))); private static (string, Expression) GetMemberPattern(object @this, string memberName, Type memberType, Func valueProvider) { @@ -545,7 +545,7 @@ internal MatchByTwoMembersStatement(MatchBuilder builder, string memberName1, Ex private protected override MatchBuilder Build(MatchBuilder builder, Action scope) { - var pattern = StructuralPattern(new[] { (memberName1, memberValue1), (memberName2, memberValue2) }); + var pattern = StructuralPattern([(memberName1, memberValue1), (memberName2, memberValue2)]); return builder.MatchByCondition(pattern, new CaseStatementBuilder(this, memberName1, memberName2, scope)); } } @@ -591,7 +591,7 @@ internal MatchByThreeMembersStatement(MatchBuilder builder, string memberName1, private protected override MatchBuilder Build(MatchBuilder builder, Action scope) { - var pattern = StructuralPattern(new[] { (memberName1, memberValue1), (memberName2, memberValue2), (memberName3, memberValue3) }); + var pattern = StructuralPattern([(memberName1, memberValue1), (memberName2, memberValue2), (memberName3, memberValue3)]); return builder.MatchByCondition(pattern, new CaseStatementBuilder(this, memberName1, memberName2, memberName3, scope)); } } diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs index 7af7fe79e..85e3c8106 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs @@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.CompilerServices; -using static System.Diagnostics.Debug; using static System.Linq.Enumerable; namespace DotNext.Runtime.CompilerServices; @@ -61,6 +60,12 @@ internal AsyncStateMachineBuilder(Type taskType, IReadOnlyList parameter.GetUserData().Set(ParameterPositionSlot, position); @@ -74,7 +79,7 @@ orderby position ascending internal IEnumerable Closures => Variables.Keys.Where(ClosureAnalyzer.IsClosure); private ParameterExpression NewStateSlot(Type type) - => NewStateSlot(() => Expression.Variable(type)); + => NewStateSlot(new Func(Expression.Variable).Bind(type)); private ParameterExpression NewStateSlot(Func factory) { @@ -88,7 +93,7 @@ protected override Expression VisitBlock(BlockExpression node) { if (node.Type == typeof(void)) { - Statement.Rewrite(ref node); + node = Statement.Rewrite(node); node.Variables.ForEach(variable => Variables.Add(variable, null)); node = node.Update(Empty(), node.Expressions); return context.Rewrite(node, base.VisitBlock); @@ -149,8 +154,7 @@ protected override Expression VisitLambda(Expression node) { // inner lambda may have closures, we must handle this accordingly var analyzer = new ClosureAnalyzer(Variables); - var lambda = analyzer.Visit(node) as LambdaExpression; - Debug.Assert(lambda is not null); + var lambda = (LambdaExpression)analyzer.Visit(node); return analyzer.Closures.Count > 0 ? new ClosureExpression(lambda, analyzer.Closures) : lambda; } @@ -163,15 +167,11 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression node) protected override Expression VisitSwitch(SwitchExpression node) { - if (node.Type == typeof(void)) - { - Statement.Rewrite(ref node); - return context.Rewrite(node, base.VisitSwitch); - } - else - { + if (node.Type != typeof(void)) throw new NotSupportedException(ExceptionMessages.VoidSwitchExpected); - } + + node = Statement.Rewrite(node); + return context.Rewrite(node, base.VisitSwitch); } protected override Expression VisitGoto(GotoExpression node) @@ -231,7 +231,7 @@ private Expression VisitAwait(AwaitExpression node) return node.Reduce(awaiterSlot, stateId, transition.Successful ?? throw new InvalidOperationException(), AsyncMethodEnd, prologue); } - private Expression VisitAsyncResult(AsyncResultExpression expr) + private AsyncResultExpression VisitAsyncResult(AsyncResultExpression expr) { if (context.IsInFinally) throw new InvalidOperationException(ExceptionMessages.LeavingFinallyClause); @@ -240,8 +240,16 @@ private Expression VisitAsyncResult(AsyncResultExpression expr) var prologue = context.CurrentStatement.PrologueCodeInserter(); expr = (AsyncResultExpression)base.VisitExtension(expr); + if (Task.HasResult && expr.IsSimpleResult is false) + { + ResultVariable ??= Expression.Parameter(Task.ResultType); + prologue(Expression.Assign(ResultVariable, expr.AsyncResult)); + expr = expr.Update(ResultVariable); + } + foreach (var finalization in context.CreateJumpPrologue(AsyncMethodEnd.Goto(), this)) prologue(finalization); + return expr; } @@ -249,20 +257,25 @@ protected override Expression VisitExtension(Expression node) { switch (node) { - case StatePlaceholderExpression placeholder: - return placeholder; + case StatePlaceholderExpression: + break; case AsyncResultExpression result: - return VisitAsyncResult(result); + node = VisitAsyncResult(result); + break; case AwaitExpression await: - return context.Rewrite(await, VisitAwait); + node = context.Rewrite(await, VisitAwait); + break; case RecoverFromExceptionExpression recovery: Variables.Add(recovery.Receiver, null); - return recovery; - case StateMachineExpression sme: - return sme; + break; + case StateMachineExpression: + break; default: - return context.Rewrite(node, base.VisitExtension); + node = context.Rewrite(node, base.VisitExtension); + break; } + + return node; } private static bool IsAssignment(BinaryExpression binary) => binary.NodeType is ExpressionType.Assign or @@ -371,26 +384,26 @@ private static IndexExpression UpdateArguments(IndexExpression node, IReadOnlyCo => node.Update(node.Object!, arguments); protected override Expression VisitIndex(IndexExpression node) - => context.Rewrite(node, n => RewriteCallable(n, n.Arguments.ToArray(), base.VisitIndex, UpdateArguments)); + => context.Rewrite(node, n => RewriteCallable(n, [.. n.Arguments], base.VisitIndex, UpdateArguments)); private static NewExpression UpdateArguments(NewExpression node, IReadOnlyCollection arguments) => node.Update(arguments); protected override Expression VisitNew(NewExpression node) - => context.Rewrite(node, n => RewriteCallable(n, n.Arguments.ToArray(), base.VisitNew, UpdateArguments)); + => context.Rewrite(node, n => RewriteCallable(n, [.. n.Arguments], base.VisitNew, UpdateArguments)); private static NewArrayExpression UpdateArguments(NewArrayExpression node, IReadOnlyCollection arguments) => node.Update(arguments); protected override Expression VisitNewArray(NewArrayExpression node) - => context.Rewrite(node, n => RewriteCallable(n, n.Expressions.ToArray(), base.VisitNewArray, UpdateArguments)); + => context.Rewrite(node, n => RewriteCallable(n, [.. n.Expressions], base.VisitNewArray, UpdateArguments)); protected override Expression VisitLoop(LoopExpression node) { if (node.Type != typeof(void)) throw new NotSupportedException(ExceptionMessages.VoidLoopExpected); - Statement.Rewrite(ref node); + node = Statement.Rewrite(node); return context.Rewrite(node, base.VisitLoop); } @@ -457,7 +470,7 @@ private static MemberExpression GetStateField(ParameterExpression stateMachine) private Expression Build(LambdaExpression stateMachineMethod) { - Assert(stateMachine is not null); + Debug.Assert(stateMachine is not null); var stateVariable = Expression.Variable(GetStateField(stateMachine).Type); var parameters = methodBuilder.Parameters; ICollection newBody = new LinkedList(); @@ -495,7 +508,7 @@ private Expression Build(LambdaExpression stateMachineMethod) var startMethod = stateMachine.Type.GetMethod(nameof(AsyncStateMachine.Start)); Debug.Assert(startMethod is not null); newBody.Add(methodBuilder.Task.AdjustTaskType(Expression.Call(startMethod, stateMachineMethod, stateVariable))); - return Expression.Lambda(Expression.Block(new[] { stateVariable }, newBody), true, parameters); + return Expression.Lambda(Expression.Block([stateVariable], newBody), true, parameters); } private sealed class StateMachineBuilder @@ -550,7 +563,7 @@ private static MemberExpression[] CreateStateHolderType(Type returnType, bool us slots = builder.Build(sm.Build, out _); } - Assert(sm.StateMachine is not null); + Debug.Assert(sm.StateMachine is not null); stateMachine = sm.StateMachine; return slots; } @@ -583,7 +596,7 @@ protected override Expression VisitParameter(ParameterExpression node) protected override Expression VisitExtension(Expression node) { - Assert(stateMachine is not null); + Debug.Assert(stateMachine is not null); return node switch { StatePlaceholderExpression placeholder => placeholder.Reduce(), @@ -605,6 +618,13 @@ internal Expression Build(Expression body, bool tailCall, bool usePoo // replace all special expressions body = Visit(body); + if (methodBuilder.ResultVariable is { } resultVar) + { + body = body is BlockExpression block + ? block.Update(block.Variables.Append(resultVar), block.Expressions) + : Expression.Block(body.Type, [resultVar], body); + } + // now we have state machine method, wrap it into lambda return Build(BuildStateMachine(body, stateMachine, tailCall)); } diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs index 7c1ccb0c8..809a71ee7 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs @@ -13,9 +13,11 @@ namespace DotNext.Runtime.CompilerServices; /// Represents a builder of the lambda expression /// that can be compiled to the renderer of the interpolated string. /// +/// The total number of characters in known at compile-time. +/// The number of placeholders. [InterpolatedStringHandler] [StructLayout(LayoutKind.Auto)] -public struct InterpolatedStringTemplateBuilder +public struct InterpolatedStringTemplateBuilder(int literalLength, int formattedCount) { [StructLayout(LayoutKind.Auto)] private readonly struct Segment @@ -95,20 +97,7 @@ internal void WriteTo(scoped ref int position, scoped ref BufferWriterSlim } } - private readonly int literalLength, formattedCount; - private List? segments; - - /// - /// Initializes a new builder. - /// - /// The total number of characters in known at compile-time. - /// The number of placeholders. - public InterpolatedStringTemplateBuilder(int literalLength, int formattedCount) - { - segments = new(formattedCount); - this.literalLength = literalLength; - this.formattedCount = formattedCount; - } + private List? segments = new(formattedCount); [DebuggerBrowsable(DebuggerBrowsableState.Never)] private List Segments => segments ??= []; @@ -207,7 +196,7 @@ public readonly LambdaExpression Build() // try-finally block to dispose the writer expr = Expression.Block(statements); expr = Expression.TryFinally(expr, Expression.Call(writerLocal, nameof(BufferWriterSlim.Dispose), [])); - expr = Expression.Block(new[] { preallocatedBufferLocal, writerLocal, handlerLocal }, expr); + expr = Expression.Block([preallocatedBufferLocal, writerLocal, handlerLocal], expr); return Expression.Lambda( expr, diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/MoveNextExpression.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/MoveNextExpression.cs index 852a9b570..2e43ceb98 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/MoveNextExpression.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/MoveNextExpression.cs @@ -31,7 +31,7 @@ internal override Expression Reduce(ParameterExpression stateMachine) { const BindingFlags PublicInstanceFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; var genericParam = Type.MakeGenericMethodParameter(0).MakeByRefType(); - var moveNext = stateMachine.Type.GetMethod(nameof(AsyncStateMachine.MoveNext), 1, PublicInstanceFlags, null, new[] { genericParam, typeof(uint) }, null)!.MakeGenericMethod(awaiter.Type); + var moveNext = stateMachine.Type.GetMethod(nameof(AsyncStateMachine.MoveNext), 1, PublicInstanceFlags, null, [genericParam, typeof(uint)], null)!.MakeGenericMethod(awaiter.Type); return stateMachine.Call(moveNext, awaiter, StateId); } } \ No newline at end of file diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/Statement.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/Statement.cs index 3067c0d75..34fd1ceff 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/Statement.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/Statement.cs @@ -40,13 +40,13 @@ internal void Insert(Expression expr) internal readonly Expression Content; internal Statement(Expression expression) - : this(expression, Enumerable.Empty(), Enumerable.Empty()) + : this(expression, [], []) { } private Statement(Expression expression, IEnumerable prologue, IEnumerable epilogue) { - Content = expression ?? Empty(); + Content = expression; if (expression is Statement stmt) { InsertIntoHead(prologue, this.prologue = stmt.prologue); @@ -57,19 +57,19 @@ private Statement(Expression expression, IEnumerable prologue, IEnum this.prologue = new LinkedList(prologue); this.epilogue = new LinkedList(epilogue); } - } - private static void InsertIntoHead(IEnumerable source, LinkedList destination) - { - if (destination.First is null) + static void InsertIntoHead(IEnumerable source, LinkedList destination) { - destination.AddAll(source); - } - else - { - var first = destination.First; - foreach (var expr in source) - destination.AddBefore(first, expr); + if (destination.First is null) + { + destination.AddAll(source); + } + else + { + var first = destination.First; + foreach (var expr in source) + destination.AddBefore(first, expr); + } } } @@ -78,43 +78,36 @@ private static void InsertIntoHead(IEnumerable source, LinkedList loop = loop.Update(loop.BreakLabel, loop.ContinueLabel, Wrap(loop.Body)); + internal static LoopExpression Rewrite(LoopExpression loop) + => loop.Update(loop.BreakLabel, loop.ContinueLabel, Wrap(loop.Body)); - internal static void Rewrite(ref BlockExpression block) - => block = block.Update(block.Variables, block.Expressions.Select(Wrap)!); + internal static BlockExpression Rewrite(BlockExpression block) + => block.Update(block.Variables, block.Expressions.Select(Wrap)!); - internal static void Rewrite(ref SwitchExpression @switch) - => @switch = @switch.Update(@switch.SwitchValue, @switch.Cases.Select(c => c.Update(c.TestValues, Wrap(c.Body))), Wrap(@switch.DefaultBody)); + internal static SwitchExpression Rewrite(SwitchExpression @switch) + => @switch.Update(@switch.SwitchValue, @switch.Cases.Select(c => c.Update(c.TestValues, Wrap(c.Body))), Wrap(@switch.DefaultBody)); private static CodeInsertionPoint CaptureRewritePoint(LinkedList codeBlock) - { - if (codeBlock.First is null) - return new CodeInsertionPoint(codeBlock); - - Debug.Assert(codeBlock.Last is not null); - return new CodeInsertionPoint(codeBlock.Last); - } + => codeBlock.Last is { } last ? new(last) : new(codeBlock); internal DotNext.CodeInsertionPoint PrologueCodeInserter() => CaptureRewritePoint(prologue).Insert; diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/VisitorContext.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/VisitorContext.cs index 7b08b9620..005a4e685 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/VisitorContext.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/VisitorContext.cs @@ -90,7 +90,8 @@ private void ContainsAwait() foreach (var attr in attributes) { if (ReferenceEquals(ExpressionAttributes.Get(CurrentStatement), attr)) - return; + break; + attr.ContainsAwait = true; } } diff --git a/src/DotNext.Tests/IO/Hashing/FNV1aTests.cs b/src/DotNext.Tests/IO/Hashing/FNV1aTests.cs index ab9c00c77..d14f9869c 100644 --- a/src/DotNext.Tests/IO/Hashing/FNV1aTests.cs +++ b/src/DotNext.Tests/IO/Hashing/FNV1aTests.cs @@ -37,7 +37,7 @@ private static void HashTest(FNV1a algor [Theory] [InlineData(false)] [InlineData(true)] - public static void Hash128(bool salted) => HashTest(new FNV1a64(salted)); + public static void Hash128(bool salted) => HashTest(new FNV1a128(salted)); [Fact] public static void HashList() diff --git a/src/DotNext.Tests/Metaprogramming/RegressionIssue17.cs b/src/DotNext.Tests/Metaprogramming/RegressionIssue17.cs index e4c2f94c9..5456cee9e 100644 --- a/src/DotNext.Tests/Metaprogramming/RegressionIssue17.cs +++ b/src/DotNext.Tests/Metaprogramming/RegressionIssue17.cs @@ -40,13 +40,8 @@ private static Expression>> GetTestExpression(bo }); } - public class TestClass + public class TestClass(string testString) { - public TestClass(string testString) - { - TestString = testString; - } - - public string TestString { get; set; } + public string TestString { get; set; } = testString; } } \ No newline at end of file diff --git a/src/DotNext.Tests/Metaprogramming/RegressionIssue223.cs b/src/DotNext.Tests/Metaprogramming/RegressionIssue223.cs new file mode 100644 index 000000000..a2690d99e --- /dev/null +++ b/src/DotNext.Tests/Metaprogramming/RegressionIssue223.cs @@ -0,0 +1,41 @@ +using System.Linq.Expressions; +using System.Reflection; +using DotNext.Linq.Expressions; + +namespace DotNext.Metaprogramming; + +using static Linq.Expressions.ExpressionBuilder; +using static Metaprogramming.CodeGenerator; + +public sealed class RegressionIssue223 : Test +{ + [Fact] + public static async Task ThrowOnReturn() + { + var lambda = AsyncLambda>>(_ => + { + Try(() => + { + var methodInfo = new Func>(Throw).Method; + var methodResult = Expression.Call(null, methodInfo); + + Return(methodResult.Await()); + }) + .Catch(typeof(Exception), _ => + { + CallStatic(typeof(Console), nameof(Console.WriteLine), Expression.Constant("Exception caught")); + }) + .End(); + }); + + var action = lambda.Compile(); + + Equal(0, await action()); + + static async Task Throw() + { + await Task.Yield(); + throw new InvalidOperationException("Exception was not caught"); + } + } +} \ No newline at end of file diff --git a/src/DotNext.Tests/Threading/SchedulerTests.cs b/src/DotNext.Tests/Threading/SchedulerTests.cs index abb99f94a..89e42263e 100644 --- a/src/DotNext.Tests/Threading/SchedulerTests.cs +++ b/src/DotNext.Tests/Threading/SchedulerTests.cs @@ -51,4 +51,10 @@ public static void ScheduleCanceled() True(Scheduler.ScheduleAsync(static (args, token) => ValueTask.CompletedTask, 42, DefaultTimeout, new(true)).Task.IsCanceled); True(Scheduler.ScheduleAsync(static (args, token) => ValueTask.FromResult(42), 42, DefaultTimeout, new(true)).Task.IsCanceled); } + + [Fact] + public static void TooLargeTimeout() + { + Throws(static () => Scheduler.ScheduleAsync(static (args, token) => ValueTask.FromResult(args), 42, TimeSpan.FromMilliseconds(Timeout.MaxTimeoutParameterTicks + 1L))); + } } \ No newline at end of file diff --git a/src/DotNext.Threading/DotNext.Threading.csproj b/src/DotNext.Threading/DotNext.Threading.csproj index 14826210d..8794d3a67 100644 --- a/src/DotNext.Threading/DotNext.Threading.csproj +++ b/src/DotNext.Threading/DotNext.Threading.csproj @@ -7,7 +7,7 @@ true true nullablePublicOnly - 5.0.1 + 5.0.2 .NET Foundation and Contributors .NEXT Family of Libraries diff --git a/src/DotNext.Threading/Threading/AsyncAutoResetEvent.cs b/src/DotNext.Threading/Threading/AsyncAutoResetEvent.cs index 89773589f..08d5e89c2 100644 --- a/src/DotNext.Threading/Threading/AsyncAutoResetEvent.cs +++ b/src/DotNext.Threading/Threading/AsyncAutoResetEvent.cs @@ -66,7 +66,7 @@ private void OnCompleted(DefaultWaitNode node) /// /// Indicates whether this event is set. /// - public bool IsSet => Volatile.Read(ref manager.Value); + public bool IsSet => Volatile.Read(in manager.Value); /// /// Sets the state of this event to non signaled, causing consumers to wait asynchronously. diff --git a/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs b/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs index 87ac6a958..7a77e84b5 100644 --- a/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs +++ b/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs @@ -252,7 +252,7 @@ internal ValueTask SignalAndWaitAsync(out bool completedSynchronously, Tim { case Timeout.InfiniteTicks: goto default; - case < 0L: + case < 0L or > Timeout.MaxTimeoutParameterTicks: task = ValueTask.FromException(new ArgumentOutOfRangeException(nameof(timeout))); break; case 0L: diff --git a/src/DotNext.Threading/Threading/AsyncExchanger.cs b/src/DotNext.Threading/Threading/AsyncExchanger.cs index 983771a76..a9294da03 100644 --- a/src/DotNext.Threading/Threading/AsyncExchanger.cs +++ b/src/DotNext.Threading/Threading/AsyncExchanger.cs @@ -111,7 +111,7 @@ public ValueTask ExchangeAsync(T value, TimeSpan timeout, CancellationToken t { case Timeout.InfiniteTicks: goto default; - case < 0L: + case < 0L or > Timeout.MaxTimeoutParameterTicks: result = ValueTask.FromException(new ArgumentOutOfRangeException(nameof(timeout))); break; case 0L: diff --git a/src/DotNext.Threading/Threading/AsyncTrigger.cs b/src/DotNext.Threading/Threading/AsyncTrigger.cs index 31f2e7c4c..581a3333e 100644 --- a/src/DotNext.Threading/Threading/AsyncTrigger.cs +++ b/src/DotNext.Threading/Threading/AsyncTrigger.cs @@ -151,7 +151,7 @@ public ValueTask SignalAndWaitAsync(bool resumeAll, bool throwOnEmptyQueue { case Timeout.InfiniteTicks: goto default; - case < 0L: + case < 0L or > Timeout.MaxTimeoutParameterTicks: task = ValueTask.FromException(new ArgumentOutOfRangeException(nameof(timeout))); break; case 0L: diff --git a/src/DotNext.Threading/Threading/QueuedSynchronizer.cs b/src/DotNext.Threading/Threading/QueuedSynchronizer.cs index 2faaec3a3..edd2d9b93 100644 --- a/src/DotNext.Threading/Threading/QueuedSynchronizer.cs +++ b/src/DotNext.Threading/Threading/QueuedSynchronizer.cs @@ -176,7 +176,7 @@ private protected ValueTask AcquireAsync(ref Valu { case Timeout.InfiniteTicks: goto default; - case < 0L: + case < 0L or > Timeout.MaxTimeoutParameterTicks: task = ValueTask.FromException(new ArgumentOutOfRangeException("timeout")); break; case 0L: // attempt to acquire synchronously @@ -248,7 +248,7 @@ private protected ValueTask TryAcquireAsync { case Timeout.InfiniteTicks: goto default; - case < 0L: + case < 0L or > Timeout.MaxTimeoutParameterTicks: task = ValueTask.FromException(new ArgumentOutOfRangeException("timeout")); break; case 0L: // attempt to acquire synchronously @@ -834,7 +834,7 @@ protected ValueTask TryAcquireAsync(TContext context, TimeSpan timeout, Ca { case Timeout.InfiniteTicks: goto default; - case < 0L: + case < 0L or > Timeout.MaxTimeoutParameterTicks: task = ValueTask.FromException(new ArgumentOutOfRangeException(nameof(timeout))); break; case 0L: @@ -896,7 +896,7 @@ protected ValueTask AcquireAsync(TContext context, TimeSpan timeout, Cancellatio { case Timeout.InfiniteTicks: goto default; - case < 0L: + case < 0L or > Timeout.MaxTimeoutParameterTicks: task = ValueTask.FromException(new ArgumentOutOfRangeException(nameof(timeout))); break; case 0L: diff --git a/src/DotNext.Threading/Threading/Scheduler.cs b/src/DotNext.Threading/Threading/Scheduler.cs index 7061bb199..3af44921f 100644 --- a/src/DotNext.Threading/Threading/Scheduler.cs +++ b/src/DotNext.Threading/Threading/Scheduler.cs @@ -48,7 +48,7 @@ public static DelayedTask ScheduleAsync(Func throw new ArgumentOutOfRangeException(nameof(delay)), + < 0L and not Timeout.InfiniteTicks or > Timeout.MaxTimeoutParameterTicks => throw new ArgumentOutOfRangeException(nameof(delay)), 0L => new ImmediateTask(callback, args, token), _ => DelayedTaskStateMachine.Start(callback, args, delay, token), }; diff --git a/src/DotNext.Threading/Threading/Tasks/ManualResetCompletionSource.cs b/src/DotNext.Threading/Threading/Tasks/ManualResetCompletionSource.cs index 0f3ebf20c..8a0105d98 100644 --- a/src/DotNext.Threading/Threading/Tasks/ManualResetCompletionSource.cs +++ b/src/DotNext.Threading/Threading/Tasks/ManualResetCompletionSource.cs @@ -272,7 +272,7 @@ private protected void OnCompleted(Action continuation, object? state, private protected short? Activate(TimeSpan timeout, CancellationToken token) { - if (timeout.Ticks is < 0L and not Timeout.InfiniteTicks) + if (timeout.Ticks is < 0L and not Timeout.InfiniteTicks or > Timeout.MaxTimeoutParameterTicks) throw new ArgumentOutOfRangeException(nameof(timeout)); // The task can be created for the completed (but not yet consumed) source. diff --git a/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.cs b/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.cs index 0922f79fa..e28027ab4 100644 --- a/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.cs +++ b/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.cs @@ -211,7 +211,7 @@ public ValueTask WaitToReadAsync(TimeSpan timeout, CancellationToken token { case Timeout.InfiniteTicks: goto default; - case < 0L: + case < 0L or > Timeout.MaxTimeoutParameterTicks: task = ValueTask.FromException(new ArgumentOutOfRangeException(nameof(timeout))); break; case 0L: diff --git a/src/DotNext.Unsafe/DotNext.Unsafe.csproj b/src/DotNext.Unsafe/DotNext.Unsafe.csproj index c85dd7f51..291a3ed08 100644 --- a/src/DotNext.Unsafe/DotNext.Unsafe.csproj +++ b/src/DotNext.Unsafe/DotNext.Unsafe.csproj @@ -7,7 +7,7 @@ enable true true - 5.0.1 + 5.0.2 nullablePublicOnly .NET Foundation and Contributors diff --git a/src/DotNext/DotNext.csproj b/src/DotNext/DotNext.csproj index d3345bacd..7218a2535 100644 --- a/src/DotNext/DotNext.csproj +++ b/src/DotNext/DotNext.csproj @@ -11,7 +11,7 @@ .NET Foundation and Contributors .NEXT Family of Libraries - 5.0.1 + 5.0.2 DotNext MIT diff --git a/src/DotNext/Predicate.cs b/src/DotNext/Predicate.cs index 60a9b8f67..5033ad9e3 100644 --- a/src/DotNext/Predicate.cs +++ b/src/DotNext/Predicate.cs @@ -1,6 +1,4 @@ -using Debug = System.Diagnostics.Debug; - -namespace DotNext; +namespace DotNext; /// /// Provides extension methods for type and diff --git a/src/DotNext/Threading/Timeout.cs b/src/DotNext/Threading/Timeout.cs index e97db9add..6851b586a 100644 --- a/src/DotNext/Threading/Timeout.cs +++ b/src/DotNext/Threading/Timeout.cs @@ -15,6 +15,12 @@ public readonly struct Timeout /// public const long InfiniteTicks = System.Threading.Timeout.Infinite * TimeSpan.TicksPerMillisecond; + /// + /// Represents maximum possible timeout value, in ticks, that can be passed to + /// some methods such as or . + /// + public const long MaxTimeoutParameterTicks = int.MaxValue * TimeSpan.TicksPerMillisecond; + private readonly Timestamp created; // IsEmpty means infinite timeout private readonly TimeSpan timeout; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Internal.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Internal.cs index ef0921617..489039e2c 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Internal.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Internal.cs @@ -218,17 +218,9 @@ public void Format(Span output) } } - private sealed class VersionedFileReader : FileReader + private sealed class VersionedFileReader(SafeFileHandle handle, long fileOffset, int bufferSize, MemoryAllocator allocator, ulong version) : FileReader(handle, fileOffset, bufferSize, allocator) { - private long version; - - internal VersionedFileReader(SafeFileHandle handle, long fileOffset, int bufferSize, MemoryAllocator allocator, long version) - : base(handle, fileOffset, bufferSize, allocator) - { - this.version = version; - } - - internal void VerifyVersion(long expected) + internal void VerifyVersion(ulong expected) { if (version != expected) Reset(); @@ -251,7 +243,7 @@ internal abstract class ConcurrentStorageAccess : Disposable private VersionedFileReader?[] readers; // This field is used to control 'freshness' of the read buffers - private long version; // volatile + private ulong version; // volatile private protected ConcurrentStorageAccess(string fileName, int fileOffset, int bufferSize, MemoryAllocator allocator, int readersCount, WriteMode writeMode, long initialSize) { @@ -273,14 +265,18 @@ private protected ConcurrentStorageAccess(string fileName, int fileOffset, int b Handle = File.OpenHandle(fileName, fileMode, FileAccess.ReadWrite, FileShare.Read, options, initialSize); + if (fileMode is FileMode.CreateNew) + { + File.SetAttributes(Handle, FileAttributes.NotContentIndexed); + } + this.fileOffset = fileOffset; writer = new(Handle, fileOffset, bufferSize, allocator); readers = new VersionedFileReader[readersCount]; this.allocator = allocator; FileName = fileName; - version = long.MinValue; - if (readersCount is 1) + if (readers.Length is 1) readers[0] = new(Handle, fileOffset, bufferSize, allocator, version); autoFlush = writeMode is WriteMode.AutoFlush; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs index 6b972bdaa..c325fb669 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs @@ -7,7 +7,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft; using Buffers; -using Threading; using BoxedClusterMemberId = Runtime.BoxedValue; using IntegrityException = IO.Log.IntegrityException; @@ -68,9 +67,19 @@ private NodeState(string fileName, MemoryAllocator allocator, bool integri { handle = File.OpenHandle(fileName, FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.WriteThrough, Capacity); buffer.Span.Clear(); + + FileAttributes attributes; if (integrityCheck) + { + attributes = FileAttributes.NotContentIndexed | FileAttributes.IntegrityStream; WriteInt64LittleEndian(Checksum, Hash(Data)); + } + else + { + attributes = FileAttributes.NotContentIndexed; + } + File.SetAttributes(handle, attributes); RandomAccess.Write(handle, buffer.Span, 0L); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.SessionManagement.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.SessionManagement.cs index 8af635741..764a90533 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.SessionManagement.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.SessionManagement.cs @@ -31,7 +31,7 @@ private sealed class FastSessionIdPool : SessionIdPool internal override int Take() { int sessionId; - ulong current, newValue = Volatile.Read(ref control); + ulong current, newValue = Volatile.Read(in control); do { sessionId = BitOperations.TrailingZeroCount(current = newValue);