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