• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 
3 // Copyright 2019 The gRPC Authors
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #endregion
18 
19 using System;
20 using System.Collections.Generic;
21 using System.Linq;
22 using System.Threading;
23 using System.Threading.Tasks;
24 using Grpc.Core;
25 using Grpc.Core.Internal;
26 using Grpc.Core.Utils;
27 using Grpc.Testing;
28 using NUnit.Framework;
29 
30 namespace Grpc.IntegrationTesting
31 {
32     /// <summary>
33     /// Runs interop tests in-process.
34     /// </summary>
35     public class UnobservedTaskExceptionTest
36     {
37         const string Host = "localhost";
38         Server server;
39         Channel channel;
40         TestService.TestServiceClient client;
41 
42         [OneTimeSetUp]
Init()43         public void Init()
44         {
45             // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
46             server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
47             {
48                 Services = { TestService.BindService(new TestServiceImpl()) },
49                 Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
50             };
51             server.Start();
52 
53             int port = server.Ports.Single().BoundPort;
54             channel = new Channel(Host, port, ChannelCredentials.Insecure);
55             client = new TestService.TestServiceClient(channel);
56         }
57 
58         [OneTimeTearDown]
Cleanup()59         public void Cleanup()
60         {
61             channel.ShutdownAsync().Wait();
62             server.ShutdownAsync().Wait();
63         }
64 
65         [Test]
NoUnobservedTaskExceptionForAbandonedStreamingResponse()66         public async Task NoUnobservedTaskExceptionForAbandonedStreamingResponse()
67         {
68             // Verify that https://github.com/grpc/grpc/issues/17458 has been fixed.
69             // Create a streaming response call, then cancel it without reading all the responses
70             // and check that no unobserved task exceptions have been thrown.
71 
72             var unobservedTaskExceptionCounter = new AtomicCounter();
73 
74             TaskScheduler.UnobservedTaskException += (sender, e) => {
75                 unobservedTaskExceptionCounter.Increment();
76                 Console.WriteLine("Detected unobserved task exception: " + e.Exception);
77             };
78 
79             var bodySizes = new List<int> { 10, 10, 10, 10, 10 };
80             var request = new StreamingOutputCallRequest {
81                 ResponseParameters = { bodySizes.Select((size) => new ResponseParameters { Size = size }) }
82             };
83 
84             for (int i = 0; i < 50; i++)
85             {
86                 Console.WriteLine($"Starting iteration {i}");
87                 using (var call = client.StreamingOutputCall(request))
88                 {
89                     // Intentionally only read the first response (we know there's more)
90                     // The call will be cancelled as soon as we leave the "using" statement.
91                     var firstResponse = await call.ResponseStream.MoveNext();
92                 }
93                 // Make it more likely to trigger the "Unobserved task exception" warning
94                 GC.Collect();
95             }
96 
97             Assert.AreEqual(0, unobservedTaskExceptionCounter.Count);
98         }
99     }
100 }
101