blob: 8bea083bc2c2fb0cb41c4c9a4a2594b314222679 [file] [log] [blame]
Jan Tattermusche81adf32016-04-04 15:59:50 -07001#region Copyright notice and license
2
3// Copyright 2016, Google Inc.
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32#endregion
33
34using System;
35using System.Collections.Generic;
36using System.Diagnostics;
37using System.IO;
38using System.Linq;
39using System.Text.RegularExpressions;
40using System.Threading;
41using System.Threading.Tasks;
42using Google.Protobuf;
43using Grpc.Core;
44using Grpc.Core.Utils;
45using Grpc.Testing;
46
47namespace Grpc.IntegrationTesting
48{
49 public interface IInterarrivalTimer
50 {
51 void WaitForNext();
52
53 Task WaitForNextAsync();
54 }
55
56 /// <summary>
57 /// Interarrival timer that doesn't wait at all.
58 /// </summary>
59 public class ClosedLoopInterarrivalTimer : IInterarrivalTimer
60 {
61 public ClosedLoopInterarrivalTimer()
62 {
63 }
64
65 public void WaitForNext()
66 {
67 // NOP
68 }
69
70 public Task WaitForNextAsync()
71 {
Jan Tattermusch5fe5eba2016-10-21 10:54:21 +020072 return TaskUtils.CompletedTask;
Jan Tattermusche81adf32016-04-04 15:59:50 -070073 }
74 }
75
76 /// <summary>
77 /// Interarrival timer that generates Poisson process load.
78 /// </summary>
79 public class PoissonInterarrivalTimer : IInterarrivalTimer
80 {
Jan Tattermusche81adf32016-04-04 15:59:50 -070081 readonly ExponentialDistribution exponentialDistribution;
82 DateTime? lastEventTime;
83
84 public PoissonInterarrivalTimer(double offeredLoad)
85 {
86 this.exponentialDistribution = new ExponentialDistribution(new Random(), offeredLoad);
87 this.lastEventTime = DateTime.UtcNow;
88 }
89
90 public void WaitForNext()
91 {
92 var waitDuration = GetNextWaitDuration();
93 int millisTimeout = (int) Math.Round(waitDuration.TotalMilliseconds);
94 if (millisTimeout > 0)
95 {
96 // TODO(jtattermusch): probably only works well for a relatively low interarrival rate
97 Thread.Sleep(millisTimeout);
98 }
99 }
100
101 public async Task WaitForNextAsync()
102 {
103 var waitDuration = GetNextWaitDuration();
104 int millisTimeout = (int) Math.Round(waitDuration.TotalMilliseconds);
105 if (millisTimeout > 0)
106 {
107 // TODO(jtattermusch): probably only works well for a relatively low interarrival rate
108 await Task.Delay(millisTimeout);
109 }
110 }
111
112 private TimeSpan GetNextWaitDuration()
113 {
114 if (!lastEventTime.HasValue)
115 {
116 this.lastEventTime = DateTime.Now;
117 }
118
119 var origLastEventTime = this.lastEventTime.Value;
Jan Tattermuscheff9cf02016-04-05 10:37:35 -0700120 this.lastEventTime = origLastEventTime + TimeSpan.FromSeconds(exponentialDistribution.Next());
Jan Tattermusche81adf32016-04-04 15:59:50 -0700121 return this.lastEventTime.Value - origLastEventTime;
122 }
123
124 /// <summary>
125 /// Exp generator.
126 /// </summary>
127 private class ExponentialDistribution
128 {
129 readonly Random random;
130 readonly double lambda;
131 readonly double lambdaReciprocal;
132
133 public ExponentialDistribution(Random random, double lambda)
134 {
135 this.random = random;
136 this.lambda = lambda;
137 this.lambdaReciprocal = 1.0 / lambda;
138 }
139
140 public double Next()
141 {
142 double uniform = random.NextDouble();
143 // Use 1.0-uni above to avoid NaN if uni is 0
144 return lambdaReciprocal * (-Math.Log(1.0 - uniform));
145 }
146 }
147 }
148}