UnobservedTaskExceptionTest.cs 3.6 KB

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