1 #region Copyright notice and license 2 3 // Copyright 2016 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.Diagnostics; 22 using System.IO; 23 using System.Linq; 24 using System.Text.RegularExpressions; 25 using System.Threading; 26 using System.Threading.Tasks; 27 using Google.Protobuf; 28 using Grpc.Core; 29 using Grpc.Core.Utils; 30 using Grpc.Testing; 31 32 namespace Grpc.IntegrationTesting 33 { 34 public interface IInterarrivalTimer 35 { WaitForNext()36 void WaitForNext(); 37 WaitForNextAsync()38 Task WaitForNextAsync(); 39 } 40 41 /// <summary> 42 /// Interarrival timer that doesn't wait at all. 43 /// </summary> 44 public class ClosedLoopInterarrivalTimer : IInterarrivalTimer 45 { ClosedLoopInterarrivalTimer()46 public ClosedLoopInterarrivalTimer() 47 { 48 } 49 WaitForNext()50 public void WaitForNext() 51 { 52 // NOP 53 } 54 WaitForNextAsync()55 public Task WaitForNextAsync() 56 { 57 return TaskUtils.CompletedTask; 58 } 59 } 60 61 /// <summary> 62 /// Interarrival timer that generates Poisson process load. 63 /// </summary> 64 public class PoissonInterarrivalTimer : IInterarrivalTimer 65 { 66 readonly ExponentialDistribution exponentialDistribution; 67 DateTime? lastEventTime; 68 PoissonInterarrivalTimer(double offeredLoad)69 public PoissonInterarrivalTimer(double offeredLoad) 70 { 71 this.exponentialDistribution = new ExponentialDistribution(new Random(), offeredLoad); 72 this.lastEventTime = DateTime.UtcNow; 73 } 74 WaitForNext()75 public void WaitForNext() 76 { 77 var waitDuration = GetNextWaitDuration(); 78 int millisTimeout = (int) Math.Round(waitDuration.TotalMilliseconds); 79 if (millisTimeout > 0) 80 { 81 // TODO(jtattermusch): probably only works well for a relatively low interarrival rate 82 Thread.Sleep(millisTimeout); 83 } 84 } 85 WaitForNextAsync()86 public async Task WaitForNextAsync() 87 { 88 var waitDuration = GetNextWaitDuration(); 89 int millisTimeout = (int) Math.Round(waitDuration.TotalMilliseconds); 90 if (millisTimeout > 0) 91 { 92 // TODO(jtattermusch): probably only works well for a relatively low interarrival rate 93 await Task.Delay(millisTimeout); 94 } 95 } 96 GetNextWaitDuration()97 private TimeSpan GetNextWaitDuration() 98 { 99 if (!lastEventTime.HasValue) 100 { 101 this.lastEventTime = DateTime.Now; 102 } 103 104 var origLastEventTime = this.lastEventTime.Value; 105 this.lastEventTime = origLastEventTime + TimeSpan.FromSeconds(exponentialDistribution.Next()); 106 return this.lastEventTime.Value - origLastEventTime; 107 } 108 109 /// <summary> 110 /// Exp generator. 111 /// </summary> 112 private class ExponentialDistribution 113 { 114 readonly Random random; 115 readonly double lambda; 116 readonly double lambdaReciprocal; 117 ExponentialDistribution(Random random, double lambda)118 public ExponentialDistribution(Random random, double lambda) 119 { 120 this.random = random; 121 this.lambda = lambda; 122 this.lambdaReciprocal = 1.0 / lambda; 123 } 124 Next()125 public double Next() 126 { 127 double uniform = random.NextDouble(); 128 // Use 1.0-uni above to avoid NaN if uni is 0 129 return lambdaReciprocal * (-Math.Log(1.0 - uniform)); 130 } 131 } 132 } 133 } 134