1
1
using System ;
2
2
using System . Runtime . CompilerServices ;
3
+ using System . Threading ;
3
4
using System . Threading . Tasks ;
4
5
using Grpc . Core ;
5
6
@@ -10,11 +11,42 @@ internal sealed class MetadataContext
10
11
internal MetadataContext ( object ? state ) => State = state ;
11
12
12
13
internal object ? State { get ; }
13
- private Metadata ? _headers , _trailers ;
14
+ private Metadata ? _trailers ;
15
+ private object ? _headersTaskOrSource ;
16
+
14
17
internal Metadata Headers
15
18
{
16
- get => _headers ?? Throw ( "Headers are not yet available" ) ;
19
+ get
20
+ {
21
+ var pending = GetHeadersTask ( false ) ;
22
+ return pending is object && pending . RanToCompletion ( )
23
+ ? pending . Result
24
+ : Throw ( "Headers are not yet available" ) ;
25
+ }
26
+ }
27
+
28
+ internal Task < Metadata > ? GetHeadersTask ( bool createIfMissing )
29
+ {
30
+ return _headersTaskOrSource switch
31
+ {
32
+ Task < Metadata > task => task ,
33
+ TaskCompletionSource < Metadata > tcs => tcs . Task ,
34
+ _ => createIfMissing ? InterlockedCreateSource ( ) : null ,
35
+ } ;
36
+
37
+ Task < Metadata > InterlockedCreateSource ( )
38
+ {
39
+ var newTcs = new TaskCompletionSource < Metadata > ( ) ;
40
+ var existing = Interlocked . CompareExchange ( ref _headersTaskOrSource , newTcs , null ) ;
41
+ return existing switch
42
+ {
43
+ Task < Metadata > task => task ,
44
+ TaskCompletionSource < Metadata > tcs => tcs . Task ,
45
+ _ => newTcs . Task ,
46
+ } ;
47
+ }
17
48
}
49
+
18
50
internal Metadata Trailers
19
51
{
20
52
get => _trailers ?? Throw ( "Trailers are not yet available" ) ;
@@ -27,7 +59,8 @@ internal Metadata Trailers
27
59
internal MetadataContext Reset ( )
28
60
{
29
61
Status = Status . DefaultSuccess ;
30
- _headers = _trailers = null ;
62
+ _trailers = null ;
63
+ _headersTaskOrSource = null ;
31
64
return this ;
32
65
}
33
66
@@ -57,24 +90,34 @@ internal void SetTrailers<T>(T call, Func<T, Status> getStatus, Func<T, Metadata
57
90
58
91
internal ValueTask SetHeadersAsync ( Task < Metadata > headers )
59
92
{
93
+ var tcs = Interlocked . CompareExchange ( ref _headersTaskOrSource , headers , null ) as TaskCompletionSource < Metadata > ;
60
94
if ( headers . RanToCompletion ( ) )
61
95
{
62
- _headers = headers . Result ;
96
+ // headers are sync; update TCS if one
97
+ tcs ? . TrySetResult ( headers . Result ) ;
63
98
return default ;
64
99
}
65
100
else
66
101
{
67
- return Awaited ( this , headers ) ;
102
+ // headers are async (or faulted); pay the piper
103
+ return Awaited ( this , tcs , headers ) ;
68
104
}
69
- static async ValueTask Awaited ( MetadataContext context , Task < Metadata > headers )
105
+
106
+ static async ValueTask Awaited ( MetadataContext context , TaskCompletionSource < Metadata > ? tcs , Task < Metadata > headers )
70
107
{
71
108
try
72
109
{
73
- context . _headers = await headers . ConfigureAwait ( false ) ;
110
+ tcs ? . TrySetResult ( await headers . ConfigureAwait ( false ) ) ;
74
111
}
75
112
catch ( RpcException fault )
76
113
{
77
114
context . SetTrailers ( fault ) ;
115
+ tcs ? . TrySetException ( fault ) ;
116
+ throw ;
117
+ }
118
+ catch ( Exception ex )
119
+ {
120
+ tcs ? . TrySetException ( ex ) ;
78
121
throw ;
79
122
}
80
123
}
0 commit comments