@@ -23,6 +23,7 @@ import (
23
23
"fmt"
24
24
"io"
25
25
"strings"
26
+ "sync"
26
27
"testing"
27
28
"time"
28
29
@@ -32,6 +33,7 @@ import (
32
33
"google.golang.org/grpc/codes"
33
34
"google.golang.org/grpc/connectivity"
34
35
"google.golang.org/grpc/credentials/insecure"
36
+ "google.golang.org/grpc/internal"
35
37
"google.golang.org/grpc/internal/balancer/stub"
36
38
"google.golang.org/grpc/internal/channelz"
37
39
"google.golang.org/grpc/internal/grpctest"
@@ -132,11 +134,11 @@ func (s) TestChannelIdleness_Disabled_NoActivity(t *testing.T) {
132
134
if err != nil {
133
135
t .Fatalf ("grpc.Dial() failed: %v" , err )
134
136
}
135
- t . Cleanup ( func () { cc .Close () } )
137
+ defer cc .Close ()
136
138
137
139
// Start a test backend and push an address update via the resolver.
138
140
backend := stubserver .StartTestService (t , nil )
139
- t . Cleanup ( backend .Stop )
141
+ defer backend .Stop ( )
140
142
r .UpdateState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
141
143
142
144
// Verify that the ClientConn moves to READY.
@@ -178,12 +180,12 @@ func (s) TestChannelIdleness_Enabled_NoActivity(t *testing.T) {
178
180
if err != nil {
179
181
t .Fatalf ("grpc.Dial() failed: %v" , err )
180
182
}
181
- t . Cleanup ( func () { cc .Close () } )
183
+ defer cc .Close ()
182
184
183
185
// Start a test backend and push an address update via the resolver.
184
186
lis := testutils .NewListenerWrapper (t , nil )
185
187
backend := stubserver .StartTestService (t , & stubserver.StubServer {Listener : lis })
186
- t . Cleanup ( backend .Stop )
188
+ defer backend .Stop ( )
187
189
r .UpdateState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
188
190
189
191
// Verify that the ClientConn moves to READY.
@@ -266,11 +268,10 @@ func (s) TestChannelIdleness_Enabled_OngoingCall(t *testing.T) {
266
268
if err != nil {
267
269
t .Fatalf ("grpc.Dial() failed: %v" , err )
268
270
}
269
- t . Cleanup ( func () { cc .Close () } )
271
+ defer cc .Close ()
270
272
271
- // Start a test backend which keeps a unary RPC call active by blocking on a
272
- // channel that is closed by the test later on. Also push an address update
273
- // via the resolver.
273
+ // Start a test backend that keeps the RPC call active by blocking
274
+ // on a channel that is closed by the test later on.
274
275
blockCh := make (chan struct {})
275
276
backend := & stubserver.StubServer {
276
277
EmptyCallF : func (ctx context.Context , in * testpb.Empty ) (* testpb.Empty , error ) {
@@ -285,16 +286,19 @@ func (s) TestChannelIdleness_Enabled_OngoingCall(t *testing.T) {
285
286
if err := backend .StartServer (); err != nil {
286
287
t .Fatalf ("Failed to start backend: %v" , err )
287
288
}
288
- t .Cleanup (backend .Stop )
289
+ defer backend .Stop ()
290
+
291
+ // Push an address update containing the address of the above
292
+ // backend via the manual resolver.
289
293
r .UpdateState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
290
294
291
295
// Verify that the ClientConn moves to READY.
292
296
ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
293
297
defer cancel ()
294
298
testutils .AwaitState (ctx , t , cc , connectivity .Ready )
295
299
296
- // Spawn a goroutine which checks expected state transitions and idleness
297
- // channelz trace events .
300
+ // Spawn a goroutine to check for expected behavior while a blocking
301
+ // RPC all is made from the main test goroutine .
298
302
errCh := make (chan error , 1 )
299
303
go func () {
300
304
defer close (blockCh )
@@ -353,11 +357,11 @@ func (s) TestChannelIdleness_Enabled_ActiveSinceLastCheck(t *testing.T) {
353
357
if err != nil {
354
358
t .Fatalf ("grpc.Dial() failed: %v" , err )
355
359
}
356
- t . Cleanup ( func () { cc .Close () } )
360
+ defer cc .Close ()
357
361
358
362
// Start a test backend and push an address update via the resolver.
359
363
backend := stubserver .StartTestService (t , nil )
360
- t . Cleanup ( backend .Stop )
364
+ defer backend .Stop ( )
361
365
r .UpdateState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
362
366
363
367
// Verify that the ClientConn moves to READY.
@@ -408,7 +412,7 @@ func (s) TestChannelIdleness_Enabled_ExitIdleOnRPC(t *testing.T) {
408
412
// restarted when exiting idle, it will push the same address to grpc again.
409
413
r := manual .NewBuilderWithScheme ("whatever" )
410
414
backend := stubserver .StartTestService (t , nil )
411
- t . Cleanup ( backend .Stop )
415
+ defer backend .Stop ( )
412
416
r .InitialState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
413
417
414
418
// Create a ClientConn with a short idle_timeout.
@@ -422,7 +426,7 @@ func (s) TestChannelIdleness_Enabled_ExitIdleOnRPC(t *testing.T) {
422
426
if err != nil {
423
427
t .Fatalf ("grpc.Dial() failed: %v" , err )
424
428
}
425
- t . Cleanup ( func () { cc .Close () } )
429
+ defer cc .Close ()
426
430
427
431
// Verify that the ClientConn moves to READY.
428
432
ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
@@ -473,7 +477,7 @@ func (s) TestChannelIdleness_Enabled_IdleTimeoutRacesWithRPCs(t *testing.T) {
473
477
// restarted when exiting idle, it will push the same address to grpc again.
474
478
r := manual .NewBuilderWithScheme ("whatever" )
475
479
backend := stubserver .StartTestService (t , nil )
476
- t . Cleanup ( backend .Stop )
480
+ defer backend .Stop ( )
477
481
r .InitialState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
478
482
479
483
// Create a ClientConn with a short idle_timeout.
@@ -487,7 +491,7 @@ func (s) TestChannelIdleness_Enabled_IdleTimeoutRacesWithRPCs(t *testing.T) {
487
491
if err != nil {
488
492
t .Fatalf ("grpc.Dial() failed: %v" , err )
489
493
}
490
- t . Cleanup ( func () { cc .Close () } )
494
+ defer cc .Close ()
491
495
492
496
// Verify that the ClientConn moves to READY.
493
497
ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
@@ -516,7 +520,7 @@ func (s) TestChannelIdleness_Connect(t *testing.T) {
516
520
// restarted when exiting idle, it will push the same address to grpc again.
517
521
r := manual .NewBuilderWithScheme ("whatever" )
518
522
backend := stubserver .StartTestService (t , nil )
519
- t . Cleanup ( backend .Stop )
523
+ defer backend .Stop ( )
520
524
r .InitialState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
521
525
522
526
// Create a ClientConn with a short idle_timeout.
@@ -530,7 +534,7 @@ func (s) TestChannelIdleness_Connect(t *testing.T) {
530
534
if err != nil {
531
535
t .Fatalf ("grpc.Dial() failed: %v" , err )
532
536
}
533
- t . Cleanup ( func () { cc .Close () } )
537
+ defer cc .Close ()
534
538
535
539
// Verify that the ClientConn moves to IDLE.
536
540
ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
@@ -544,3 +548,77 @@ func (s) TestChannelIdleness_Connect(t *testing.T) {
544
548
// Verify that the ClientConn moves back to READY.
545
549
testutils .AwaitState (ctx , t , cc , connectivity .Ready )
546
550
}
551
+
552
+ // runFunc runs f repeatedly until the context expires.
553
+ func runFunc (ctx context.Context , f func ()) {
554
+ for {
555
+ select {
556
+ case <- ctx .Done ():
557
+ return
558
+ case <- time .After (10 * time .Millisecond ):
559
+ f ()
560
+ }
561
+ }
562
+ }
563
+
564
+ // Tests the scenario where there are concurrent calls to exit and enter idle
565
+ // mode on the ClientConn. Verifies that there is no race under this scenario.
566
+ func (s ) TestChannelIdleness_RaceBetweenEnterAndExitIdleMode (t * testing.T ) {
567
+ // Start a test backend and set the bootstrap state of the resolver to
568
+ // include this address. This will ensure that when the resolver is
569
+ // restarted when exiting idle, it will push the same address to grpc again.
570
+ r := manual .NewBuilderWithScheme ("whatever" )
571
+ backend := stubserver .StartTestService (t , nil )
572
+ defer backend .Stop ()
573
+ r .InitialState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
574
+
575
+ // Create a ClientConn with a long idle_timeout. We will explicitly trigger
576
+ // entering and exiting IDLE mode from the test.
577
+ dopts := []grpc.DialOption {
578
+ grpc .WithTransportCredentials (insecure .NewCredentials ()),
579
+ grpc .WithResolvers (r ),
580
+ grpc .WithIdleTimeout (30 * time .Minute ),
581
+ grpc .WithDefaultServiceConfig (`{"loadBalancingConfig": [{"pick_first":{}}]}` ),
582
+ }
583
+ cc , err := grpc .Dial (r .Scheme ()+ ":///test.server" , dopts ... )
584
+ if err != nil {
585
+ t .Fatalf ("grpc.Dial() failed: %v" , err )
586
+ }
587
+ defer cc .Close ()
588
+
589
+ enterIdle := internal .EnterIdleModeForTesting .(func (* grpc.ClientConn ) error )
590
+ enterIdleFunc := func () {
591
+ if err := enterIdle (cc ); err != nil {
592
+ t .Errorf ("Failed to enter idle mode: %v" , err )
593
+ }
594
+ }
595
+ exitIdle := internal .ExitIdleModeForTesting .(func (* grpc.ClientConn ) error )
596
+ exitIdleFunc := func () {
597
+ if err := exitIdle (cc ); err != nil {
598
+ t .Errorf ("Failed to exit idle mode: %v" , err )
599
+ }
600
+ }
601
+ // Spawn goroutines that call methods on the ClientConn to enter and exit
602
+ // idle mode concurrently for one second.
603
+ ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Second )
604
+ defer cancel ()
605
+ var wg sync.WaitGroup
606
+ wg .Add (4 )
607
+ go func () {
608
+ runFunc (ctx , enterIdleFunc )
609
+ wg .Done ()
610
+ }()
611
+ go func () {
612
+ runFunc (ctx , enterIdleFunc )
613
+ wg .Done ()
614
+ }()
615
+ go func () {
616
+ runFunc (ctx , exitIdleFunc )
617
+ wg .Done ()
618
+ }()
619
+ go func () {
620
+ runFunc (ctx , exitIdleFunc )
621
+ wg .Done ()
622
+ }()
623
+ wg .Wait ()
624
+ }
0 commit comments