Skip to content

Commit 7bbf43a

Browse files
authored
Fix possible deadlock while asynchronously reading values from reader (#6202)
Fixes #6190
1 parent 8f97112 commit 7bbf43a

File tree

1 file changed

+22
-6
lines changed

1 file changed

+22
-6
lines changed

src/Npgsql/Internal/Converters/AsyncHelpers.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,17 @@ public abstract class CompletionSource
3737

3838
public sealed class CompletionSource<T> : CompletionSource
3939
{
40-
AsyncValueTaskMethodBuilder<T> _amb = AsyncValueTaskMethodBuilder<T>.Create();
40+
AsyncValueTaskMethodBuilder<T> _amb;
4141

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

4452
public void SetResult(T value)
4553
=> _amb.SetResult(value);
@@ -50,9 +58,17 @@ public override void SetException(Exception exception)
5058

5159
public sealed class PoolingCompletionSource<T> : CompletionSource
5260
{
53-
PoolingAsyncValueTaskMethodBuilder<T> _amb = PoolingAsyncValueTaskMethodBuilder<T>.Create();
61+
PoolingAsyncValueTaskMethodBuilder<T> _amb;
5462

55-
public ValueTask<T> Task => _amb.Task;
63+
public ValueTask<T> Task { get; }
64+
65+
public PoolingCompletionSource()
66+
{
67+
_amb = PoolingAsyncValueTaskMethodBuilder<T>.Create();
68+
// PoolingAsyncValueTaskMethodBuilder's Task and SetResult aren't thread safe in regard to each other
69+
// Which is why we access it prematurely
70+
Task = _amb.Task;
71+
}
5672

5773
public void SetResult(T value)
5874
=> _amb.SetResult(value);
@@ -90,7 +106,7 @@ public CompletionSourceContinuation(object handle, delegate*<Task, CompletionSou
90106
if (task.IsCompletedSuccessfully)
91107
return new(new T?(task.Result));
92108

93-
// Otherwise we do one additional allocation, this allow us to share state machine codegen for all Ts.
109+
// Otherwise we do one additional allocation, this allows us to share state machine codegen for all Ts.
94110
var source = new PoolingCompletionSource<T?>();
95111
OnCompletedWithSource(task.AsTask(), source, new(instance, &UnboxAndComplete));
96112
return source.Task;
@@ -111,7 +127,7 @@ public static unsafe ValueTask<T> ReadAsObjectAsyncAsT<T>(this PgConverter<T> in
111127
if (task.IsCompletedSuccessfully)
112128
return new((T)task.Result);
113129

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

0 commit comments

Comments
 (0)