Skip to content

Commit d6a3ba5

Browse files
committed
Release 5.25.1
1 parent ba6d9f9 commit d6a3ba5

24 files changed

+320
-200
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
Release Notes
22
====
33

4+
# 09-15-2025
5+
<a href="https://www.nuget.org/packages/dotnext/5.25.0">DotNext 5.25.0</a>
6+
* Added `CatchException` extension method to capture the exception produced by `await` operator instead of raising it at the call site
7+
* Various performance improvements
8+
9+
<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.25.0">DotNext.Metaprogramming 5.25.0</a>
10+
* Fixed mutability modifiers for properties of `UnmanagedMemory<T>` type
11+
12+
<a href="https://www.nuget.org/packages/dotnext.unsafe/5.25.0">DotNext.Unsafe 5.25.0</a>
13+
* Updated dependencies
14+
15+
<a href="https://www.nuget.org/packages/dotnext.threading/5.25.0">DotNext.Threading 5.25.0</a>
16+
* Added optional hard concurrency limit for async lock primitives
17+
* Rewritten the internal engine for async lock primitives to decrease the lock contention and increase the response time
18+
* Async lock primitive no longer produce lock contention time to improve the response time
19+
20+
<a href="https://www.nuget.org/packages/dotnext.io/5.25.0">DotNext.IO 5.25.0</a>
21+
* Updated dependencies
22+
23+
<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.25.0">DotNext.Net.Cluster 5.25.0</a>
24+
* Updated dependencies
25+
26+
<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.25.0">DotNext.AspNetCore.Cluster 5.25.0</a>
27+
* Updated dependencies
28+
429
# 08-23-2025
530
<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.24.1">DotNext.Net.Cluster 5.24.1</a>
631
* Fixed stream ID inflation of the multiplexing protocol client if underlying TCP connection is unstable

README.md

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,31 +44,30 @@ All these things are implemented in 100% managed code on top of existing .NET AP
4444
* [NuGet Packages](https://www.nuget.org/profiles/rvsakno)
4545

4646
# What's new
47-
Release Date: 08-19-2025
47+
Release Date: 09-15-2025
4848

49-
<a href="https://www.nuget.org/packages/dotnext/5.24.0">DotNext 5.24.0</a>
50-
* Merged [258](https://github.com/dotnet/dotNext/pull/258)
51-
* Added `CopyTo` extension method overload for [ReadOnlySequence&lt;T&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.readonlysequence-1) data type that returns the position within the sequence
52-
* Fixed correctness of atomic read/write operations exposed by `Atomic` static class for **double** data type on 32-bit platforms
53-
* `LockAcquisition` static methods are no longer extension methods to avoid ambiguity (see [267](https://github.com/dotnet/dotNext/discussions/267))
49+
<a href="https://www.nuget.org/packages/dotnext/5.25.0">DotNext 5.25.0</a>
50+
* Added `CatchException` extension method to capture the exception produced by `await` operator instead of raising it at the call site
51+
* Various performance improvements
5452

55-
<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.24.0">DotNext.Metaprogramming 5.24.0</a>
56-
* Updated dependencies
53+
<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.25.0">DotNext.Metaprogramming 5.25.0</a>
54+
* Fixed mutability modifiers for properties of `UnmanagedMemory<T>` type
5755

58-
<a href="https://www.nuget.org/packages/dotnext.unsafe/5.24.0">DotNext.Unsafe 5.24.0</a>
59-
* Added custom marshallers for `IUnmanagedMemory<T>` interface and `Pointer<T>` data type that are compatible with PInvoke source generator
56+
<a href="https://www.nuget.org/packages/dotnext.unsafe/5.25.0">DotNext.Unsafe 5.25.0</a>
57+
* Updated dependencies
6058

61-
<a href="https://www.nuget.org/packages/dotnext.threading/5.24.0">DotNext.Threading 5.24.0</a>
62-
* `AsyncLockAcquisition` static methods are no longer extension methods to avoid ambiguity (see [267](https://github.com/dotnet/dotNext/discussions/267))
63-
* Lock contention reported by all async lock primitives is now an up-down counter rather than regular counter
59+
<a href="https://www.nuget.org/packages/dotnext.threading/5.25.0">DotNext.Threading 5.25.0</a>
60+
* Added optional hard concurrency limit for async lock primitives
61+
* Rewritten the internal engine for async lock primitives to decrease the lock contention and increase the response time
62+
* Async lock primitive no longer produce lock contention time to improve the response time
6463

65-
<a href="https://www.nuget.org/packages/dotnext.io/5.24.0">DotNext.IO 5.24.0</a>
66-
* Improved behavioral compatibility with [Pipe](https://learn.microsoft.com/en-us/dotnet/api/system.io.pipelines.pipe) class by extension methods exposed by `PipeExtensions` class
64+
<a href="https://www.nuget.org/packages/dotnext.io/5.25.0">DotNext.IO 5.25.0</a>
65+
* Updated dependencies
6766

68-
<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.24.1">DotNext.Net.Cluster 5.24.1</a>
69-
* Added `DotNext.Net.Multiplexing` namespace that exposes simple unencrypted multiplexing protocol implementation on top of TCP. The multiplexed channel is exposed as [IDuplexPipe](https://learn.microsoft.com/en-us/dotnet/api/system.io.pipelines.iduplexpipe). The main purpose of this implementation is the efficient communication between nodes within the cluster inside the trusted LAN
67+
<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.25.0">DotNext.Net.Cluster 5.25.0</a>
68+
* Updated dependencies
7069

71-
<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.24.1">DotNext.AspNetCore.Cluster 5.24.1</a>
70+
<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.25.0">DotNext.AspNetCore.Cluster 5.25.0</a>
7271
* Updated dependencies
7372

7473
<a href="https://www.nuget.org/packages/dotnext.maintenanceservices/0.6.0">DotNext.MaintenanceServices 0.6.0</a>

src/DotNext.Tests/DelegateHelpersTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ public static void TryInvokeFunc()
149149
static MethodInfo GetMethod(int argCount)
150150
{
151151
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly;
152-
return Single(typeof(Func).GetMethods(flags), candidate => candidate.Name == nameof(Func.TryInvoke) && candidate.GetParameters().Length == argCount + 1);
152+
return Single(typeof(Func).GetMethods(flags),
153+
candidate => candidate.Name is nameof(Func.TryInvoke) && candidate.GetParameters().Length == argCount + 1);
153154
}
154155

155156
var successValue = Expression.Constant(42, typeof(int));
@@ -611,7 +612,7 @@ static MethodInfo GetMethod(int argCount)
611612
{
612613
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly;
613614
return Single(typeof(DelegateHelpers).GetMethods(flags),
614-
candidate => candidate.Name == nameof(DelegateHelpers.TryInvoke) && candidate.GetParameters().Length == argCount + 1);
615+
candidate => candidate.Name is nameof(DelegateHelpers.TryInvoke) && candidate.GetParameters().Length == argCount + 1);
615616
}
616617

617618
var successValue = Expression.Empty();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace DotNext.Threading;
2+
3+
public sealed class QueuedSynchronizerTests : Test
4+
{
5+
[Fact]
6+
public static async Task ThrowOnAcquisitionAsync()
7+
{
8+
await using var synchronizer = new MySynchronizer();
9+
await ThrowsAsync<ArithmeticException>(synchronizer.ThrowAsync().AsTask);
10+
False(synchronizer.TryAcquire());
11+
}
12+
13+
private sealed class MySynchronizer : QueuedSynchronizer<bool>
14+
{
15+
public ValueTask ThrowAsync(CancellationToken token = default)
16+
=> AcquireAsync(context: false, token);
17+
18+
public bool TryAcquire() => TryAcquire(context: false);
19+
20+
protected override bool CanAcquire(bool context) => context;
21+
22+
protected override Exception GetAcquisitionException(bool canAcquire)
23+
=> canAcquire ? null : new ArithmeticException();
24+
}
25+
}

src/DotNext.Tests/Threading/Tasks/ConversionTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ public static async Task SuspendException()
5050
await Task.FromException(new Exception()).SuspendException(Predicate.Constant<Exception>(true)).ConfigureAwait(true);
5151
await ValueTask.FromException(new Exception()).SuspendException(Predicate.Constant<Exception>(true)).ConfigureAwait(true);
5252

53-
var result = await Task.FromException<int>(new ArithmeticException()).SuspendException().ConfigureAwait(true);
53+
var result = await Task.FromException<int>(new ArithmeticException()).CatchException().ConfigureAwait(true);
5454
False(result.IsSuccessful);
5555
IsType<ArithmeticException>(result.Error);
5656

57-
result = await ValueTask.FromException<int>(new ArithmeticException()).SuspendException().ConfigureAwait(true);
57+
result = await ValueTask.FromException<int>(new ArithmeticException()).CatchException().ConfigureAwait(true);
5858
False(result.IsSuccessful);
5959
IsType<ArithmeticException>(result.Error);
6060
}
@@ -80,11 +80,11 @@ public static async Task SuspendException2()
8080
[Fact]
8181
public static async Task ConvertExceptionToError()
8282
{
83-
var result = await Task.FromException<int>(new Exception()).SuspendException(ToError).ConfigureAwait(true);
83+
var result = await Task.FromException<int>(new Exception()).CatchException(ToError).ConfigureAwait(true);
8484
False(result.IsSuccessful);
8585
Equal(EnvironmentVariableTarget.Machine, result.Error);
8686

87-
result = await ValueTask.FromException<int>(new Exception()).SuspendException(ToError).ConfigureAwait(true);
87+
result = await ValueTask.FromException<int>(new Exception()).CatchException(ToError).ConfigureAwait(true);
8888
False(result.IsSuccessful);
8989
Equal(EnvironmentVariableTarget.Machine, result.Error);
9090

src/DotNext.Tests/Threading/Tasks/GenericValueTaskCompletionSourceTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public static async Task CompleteWithToken(bool runContinuationsAsynchronously)
6868
var source = new ValueTaskCompletionSource<int>(runContinuationsAsynchronously);
6969
var completionToken = source.Reset();
7070
var task = source.CreateTask(InfiniteTimeSpan, default);
71-
False(source.TrySetResult(short.MaxValue, 42));
71+
False(source.TrySetResult(completionData: null, short.MaxValue, 42));
7272
False(task.IsCompleted);
7373
True(source.TrySetResult(completionToken, 42));
7474
Equal(42, await task);

src/DotNext.Tests/Threading/Tasks/ValueTaskCompletionSourceTests.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public static async Task CompleteWithToken(bool runContinuationsAsynchronously)
6161
var source = new ValueTaskCompletionSource(runContinuationsAsynchronously);
6262
var completionToken = source.Reset();
6363
var task = source.CreateTask(InfiniteTimeSpan, default);
64-
False(source.TrySetResult(short.MaxValue));
64+
False(source.TrySetResult(completionData: null, short.MaxValue));
6565
False(task.IsCompleted);
6666
True(source.TrySetResult(completionToken));
6767
await task;
@@ -167,4 +167,29 @@ public static async Task CanceledToken()
167167
var task = source.CreateTask(InfiniteTimeSpan, new(true)).AsTask();
168168
await ThrowsAsync<OperationCanceledException>(Func.Constant(task));
169169
}
170+
171+
[Fact]
172+
public static async Task LazyCompletion()
173+
{
174+
var source = new TestCompletionSource();
175+
var task = source.CreateTask(InfiniteTimeSpan, CancellationToken.None).AsTask();
176+
177+
True(source.TrySetResult(string.Empty, completionToken: null, e: null, out var resumable));
178+
True(resumable);
179+
Same(string.Empty, source.CompletionData);
180+
False(task.IsCompleted);
181+
182+
source.NotifyConsumer();
183+
await task;
184+
}
185+
186+
private sealed class TestCompletionSource : ValueTaskCompletionSource
187+
{
188+
public new bool TrySetResult(object completionData, short? completionToken, Exception e, out bool resumable)
189+
=> base.TrySetResult(completionData, completionToken, e, out resumable);
190+
191+
public new object CompletionData => base.CompletionData;
192+
193+
public new void NotifyConsumer() => base.NotifyConsumer();
194+
}
170195
}

src/DotNext.Threading/DotNext.Threading.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<ImplicitUsings>true</ImplicitUsings>
88
<IsAotCompatible>true</IsAotCompatible>
99
<Features>nullablePublicOnly</Features>
10-
<VersionPrefix>5.25.0</VersionPrefix>
10+
<VersionPrefix>5.25.1</VersionPrefix>
1111
<VersionSuffix></VersionSuffix>
1212
<Authors>.NET Foundation and Contributors</Authors>
1313
<Product>.NEXT Family of Libraries</Product>

src/DotNext.Threading/Threading/AsyncAutoResetEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public bool Set()
9393
}
9494
}
9595

96-
suspendedCaller?.Resume();
96+
suspendedCaller?.NotifyConsumer();
9797
}
9898

9999
return result;

src/DotNext.Threading/Threading/AsyncCorrelationSource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public bool Pulse(TKey eventId, in Result<TValue> value, out object? userData)
7777
userData = node.UserData;
7878
result = node.TrySetResult(Sentinel.Instance, completionToken, in value, out var resumable);
7979
if (resumable)
80-
node.Resume();
80+
node.NotifyConsumer();
8181
}
8282
else
8383
{

0 commit comments

Comments
 (0)