@@ -21,14 +21,20 @@ package grpc_test
21
21
import (
22
22
"context"
23
23
"io"
24
+ "runtime"
25
+ "sync"
24
26
"testing"
25
27
"time"
26
28
27
29
"google.golang.org/grpc"
30
+ "google.golang.org/grpc/codes"
31
+ "google.golang.org/grpc/credentials/insecure"
28
32
"google.golang.org/grpc/internal/grpcsync"
29
33
"google.golang.org/grpc/internal/stubserver"
34
+ "google.golang.org/grpc/status"
30
35
31
36
testgrpc "google.golang.org/grpc/interop/grpc_testing"
37
+ testpb "google.golang.org/grpc/interop/grpc_testing"
32
38
)
33
39
34
40
// TestServer_MaxHandlers ensures that no more than MaxConcurrentStreams server
@@ -97,3 +103,85 @@ func (s) TestServer_MaxHandlers(t *testing.T) {
97
103
t .Fatal ("Received unexpected RPC error:" , err )
98
104
}
99
105
}
106
+
107
+ // Tests the case where the stream worker goroutine option is enabled, and a
108
+ // number of RPCs are initiated around the same time that Stop() is called. This
109
+ // used to result in a write to a closed channel. This test verifies that there
110
+ // is no panic.
111
+ func (s ) TestStreamWorkers_RPCsAndStop (t * testing.T ) {
112
+ ss := stubserver .StartTestService (t , nil , grpc .NumStreamWorkers (uint32 (runtime .NumCPU ())))
113
+ // This deferred stop takes care of stopping the server when one of the
114
+ // below grpc.Dials fail, and the test exits early.
115
+ defer ss .Stop ()
116
+
117
+ ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
118
+ defer cancel ()
119
+ const numChannels = 20
120
+ const numRPCLoops = 20
121
+
122
+ // Create a bunch of clientconns and ensure that they are READY by making an
123
+ // RPC on them.
124
+ ccs := make ([]* grpc.ClientConn , numChannels )
125
+ for i := 0 ; i < numChannels ; i ++ {
126
+ var err error
127
+ ccs [i ], err = grpc .Dial (ss .Address , grpc .WithTransportCredentials (insecure .NewCredentials ()))
128
+ if err != nil {
129
+ t .Fatalf ("[iteration: %d] grpc.Dial(%s) failed: %v" , i , ss .Address , err )
130
+ }
131
+ defer ccs [i ].Close ()
132
+ client := testgrpc .NewTestServiceClient (ccs [i ])
133
+ if _ , err := client .EmptyCall (ctx , & testpb.Empty {}); err != nil {
134
+ t .Fatalf ("EmptyCall() failed: %v" , err )
135
+ }
136
+ }
137
+
138
+ // Make a bunch of concurrent RPCs on the above clientconns. These will
139
+ // eventually race with Stop(), and will start to fail.
140
+ var wg sync.WaitGroup
141
+ for i := 0 ; i < numChannels ; i ++ {
142
+ client := testgrpc .NewTestServiceClient (ccs [i ])
143
+ for j := 0 ; j < numRPCLoops ; j ++ {
144
+ wg .Add (1 )
145
+ go func (client testgrpc.TestServiceClient ) {
146
+ defer wg .Done ()
147
+ for {
148
+ _ , err := client .EmptyCall (ctx , & testpb.Empty {})
149
+ if err == nil {
150
+ continue
151
+ }
152
+ if code := status .Code (err ); code == codes .Unavailable {
153
+ // Once Stop() has been called on the server, we expect
154
+ // subsequent calls to fail with Unavailable.
155
+ return
156
+ }
157
+ t .Errorf ("EmptyCall() failed: %v" , err )
158
+ return
159
+ }
160
+ }(client )
161
+ }
162
+ }
163
+
164
+ // Call Stop() concurrently with the above RPC attempts.
165
+ ss .Stop ()
166
+ wg .Wait ()
167
+ }
168
+
169
+ // Tests the case where the stream worker goroutine option is enabled, and both
170
+ // Stop() and GracefulStop() care called. This used to result in a close of a
171
+ // closed channel. This test verifies that there is no panic.
172
+ func (s ) TestStreamWorkers_GracefulStopAndStop (t * testing.T ) {
173
+ ss := stubserver .StartTestService (t , nil , grpc .NumStreamWorkers (uint32 (runtime .NumCPU ())))
174
+ defer ss .Stop ()
175
+
176
+ if err := ss .StartClient (grpc .WithTransportCredentials (insecure .NewCredentials ())); err != nil {
177
+ t .Fatalf ("Failed to create client to stub server: %v" , err )
178
+ }
179
+ ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
180
+ defer cancel ()
181
+ client := testgrpc .NewTestServiceClient (ss .CC )
182
+ if _ , err := client .EmptyCall (ctx , & testpb.Empty {}); err != nil {
183
+ t .Fatalf ("EmptyCall() failed: %v" , err )
184
+ }
185
+
186
+ ss .S .GracefulStop ()
187
+ }
0 commit comments