Skip to content

Commit 1ae2763

Browse files
committed
Fix possible deadlock while asynchronously reading values from reader (#6202)
Fixes #6190 (cherry picked from commit 7bbf43a)
1 parent 2781000 commit 1ae2763

File tree

1 file changed

+27
-7
lines changed

1 file changed

+27
-7
lines changed

src/Npgsql/Internal/Converters/AsyncHelpers.cs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,17 @@ public abstract class CompletionSource
3939

4040
public sealed class CompletionSource<T> : CompletionSource
4141
{
42-
AsyncValueTaskMethodBuilder<T> _amb = AsyncValueTaskMethodBuilder<T>.Create();
42+
AsyncValueTaskMethodBuilder<T> _amb;
4343

44-
public ValueTask<T> Task => _amb.Task;
44+
public ValueTask<T> Task { get; }
45+
46+
public CompletionSource()
47+
{
48+
_amb = AsyncValueTaskMethodBuilder<T>.Create();
49+
// AsyncValueTaskMethodBuilder's Task and SetResult aren't thread safe in regard to each other
50+
// Which is why we access it prematurely
51+
Task = _amb.Task;
52+
}
4553

4654
public void SetResult(T value)
4755
=> _amb.SetResult(value);
@@ -53,11 +61,23 @@ public override void SetException(Exception exception)
5361
public sealed class PoolingCompletionSource<T> : CompletionSource
5462
{
5563
#if NETSTANDARD
56-
AsyncValueTaskMethodBuilder<T> _amb = AsyncValueTaskMethodBuilder<T>.Create();
64+
AsyncValueTaskMethodBuilder<T> _amb;
5765
#else
58-
PoolingAsyncValueTaskMethodBuilder<T> _amb = PoolingAsyncValueTaskMethodBuilder<T>.Create();
66+
PoolingAsyncValueTaskMethodBuilder<T> _amb;
5967
#endif
60-
public ValueTask<T> Task => _amb.Task;
68+
public ValueTask<T> Task { get; }
69+
70+
public PoolingCompletionSource()
71+
{
72+
#if NETSTANDARD
73+
_amb = AsyncValueTaskMethodBuilder<T>.Create();
74+
#else
75+
_amb = PoolingAsyncValueTaskMethodBuilder<T>.Create();
76+
#endif
77+
// PoolingAsyncValueTaskMethodBuilder's Task and SetResult aren't thread safe in regard to each other
78+
// Which is why we access it prematurely
79+
Task = _amb.Task;
80+
}
6181

6282
public void SetResult(T value)
6383
=> _amb.SetResult(value);
@@ -95,7 +115,7 @@ public CompletionSourceContinuation(object handle, delegate*<Task, CompletionSou
95115
if (task.IsCompletedSuccessfully)
96116
return new(new T?(task.Result));
97117

98-
// Otherwise we do one additional allocation, this allow us to share state machine codegen for all Ts.
118+
// Otherwise we do one additional allocation, this allows us to share state machine codegen for all Ts.
99119
var source = new PoolingCompletionSource<T?>();
100120
OnCompletedWithSource(task.AsTask(), source, new(instance, &UnboxAndComplete));
101121
return source.Task;
@@ -116,7 +136,7 @@ public static unsafe ValueTask<T> ReadAsObjectAsyncAsT<T>(this PgConverter<T> in
116136
if (task.IsCompletedSuccessfully)
117137
return new((T)task.Result);
118138

119-
// Otherwise we do one additional allocation, this allow us to share state machine codegen for all Ts.
139+
// Otherwise we do one additional allocation, this allows us to share state machine codegen for all Ts.
120140
var source = new PoolingCompletionSource<T>();
121141
OnCompletedWithSource(task.AsTask(), source, new(instance, &UnboxAndComplete));
122142
return source.Task;

0 commit comments

Comments
 (0)