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