• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 // Copyright 2015 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.Runtime.InteropServices;
18 using System.Threading;
19 
20 using Grpc.Core.Utils;
21 
22 namespace Grpc.Core.Internal
23 {
24     /// <summary>
25     /// gpr_timespec from grpc/support/time.h
26     /// </summary>
27     [StructLayout(LayoutKind.Sequential)]
28     internal struct Timespec : IEquatable<Timespec>
29     {
30         /// <summary>
31         /// Indicates whether this instance and a specified object are equal.
32         /// </summary>
EqualsGrpc.Core.Internal.Timespec33         public override bool Equals(object obj)
34         {
35             return obj is Timespec && Equals((Timespec)obj);
36         }
37 
38         /// <summary>
39         /// Returns the hash code for this instance.
40         /// </summary>
GetHashCodeGrpc.Core.Internal.Timespec41         public override int GetHashCode()
42         {
43             unchecked
44             {
45                 const int Prime = 373587911;
46                 int i = (int)clock_type;
47                 i = (i * Prime) ^ tv_nsec;
48                 i = (i * Prime) ^ tv_nsec.GetHashCode();
49                 return i;
50             }
51         }
52 
53         /// <summary>
54         /// Returns the full type name of this instance.
55         /// </summary>
ToStringGrpc.Core.Internal.Timespec56         public override string ToString()
57         {
58             return typeof(Timespec).FullName;
59         }
60 
61         /// <summary>
62         /// Indicates whether this instance and a specified object are equal.
63         /// </summary>
EqualsGrpc.Core.Internal.Timespec64         public bool Equals(Timespec other)
65         {
66             return this.clock_type == other.clock_type
67                 && this.tv_nsec == other.tv_nsec
68                 && this.tv_sec == other.tv_sec;
69         }
70 
71         const long NanosPerSecond = 1000 * 1000 * 1000;
72         const long NanosPerTick = 100;
73         const long TicksPerSecond = NanosPerSecond / NanosPerTick;
74 
75         static readonly NativeMethods Native = NativeMethods.Get();
76         static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
77 
TimespecGrpc.Core.Internal.Timespec78         public Timespec(long tv_sec, int tv_nsec) : this(tv_sec, tv_nsec, ClockType.Realtime)
79         {
80         }
81 
TimespecGrpc.Core.Internal.Timespec82         public Timespec(long tv_sec, int tv_nsec, ClockType clock_type)
83         {
84             this.tv_sec = tv_sec;
85             this.tv_nsec = tv_nsec;
86             this.clock_type = clock_type;
87         }
88 
89         private long tv_sec;
90         private int tv_nsec;
91         private ClockType clock_type;
92 
93         /// <summary>
94         /// Timespec a long time in the future.
95         /// </summary>
96         public static Timespec InfFuture
97         {
98             get
99             {
100                 return new Timespec(long.MaxValue, 0, ClockType.Realtime);
101             }
102         }
103 
104         /// <summary>
105         /// Timespec a long time in the past.
106         /// </summary>
107         public static Timespec InfPast
108         {
109             get
110             {
111                 return new Timespec(long.MinValue, 0, ClockType.Realtime);
112             }
113         }
114 
115         /// <summary>
116         /// Return Timespec representing the current time.
117         /// </summary>
118         public static Timespec Now
119         {
120             get
121             {
122                 return Native.gprsharp_now(ClockType.Realtime);
123             }
124         }
125 
126         /// <summary>
127         /// Seconds since unix epoch.
128         /// </summary>
129         public long TimevalSeconds
130         {
131             get
132             {
133                 return tv_sec;
134             }
135         }
136 
137         /// <summary>
138         /// The nanoseconds part of timeval.
139         /// </summary>
140         public int TimevalNanos
141         {
142             get
143             {
144                 return tv_nsec;
145             }
146         }
147 
148         /// <summary>
149         /// Converts the timespec to desired clock type.
150         /// </summary>
ToClockTypeGrpc.Core.Internal.Timespec151         public Timespec ToClockType(ClockType targetClock)
152         {
153             return Native.gprsharp_convert_clock_type(this, targetClock);
154         }
155 
156         /// <summary>
157         /// Converts Timespec to DateTime.
158         /// Timespec needs to be of type GPRClockType.Realtime and needs to represent a legal value.
159         /// DateTime has lower resolution (100ns), so rounding can occurs.
160         /// Value are always rounded up to the nearest DateTime value in the future.
161         ///
162         /// For Timespec.InfFuture or if timespec is after the largest representable DateTime, DateTime.MaxValue is returned.
163         /// For Timespec.InfPast or if timespec is before the lowest representable DateTime, DateTime.MinValue is returned.
164         ///
165         /// Unless DateTime.MaxValue or DateTime.MinValue is returned, the resulting DateTime is always in UTC
166         /// (DateTimeKind.Utc)
167         /// </summary>
ToDateTimeGrpc.Core.Internal.Timespec168         public DateTime ToDateTime()
169         {
170             GrpcPreconditions.CheckState(tv_nsec >= 0 && tv_nsec < NanosPerSecond);
171             GrpcPreconditions.CheckState(clock_type == ClockType.Realtime);
172 
173             // fast path for InfFuture
174             if (this.Equals(InfFuture))
175             {
176                 return DateTime.MaxValue;
177             }
178 
179             // fast path for InfPast
180             if (this.Equals(InfPast))
181             {
182                 return DateTime.MinValue;
183             }
184 
185             try
186             {
187                 // convert nanos to ticks, round up to the nearest tick
188                 long ticksFromNanos = tv_nsec / NanosPerTick + ((tv_nsec % NanosPerTick != 0) ? 1 : 0);
189                 long ticksTotal = checked(tv_sec * TicksPerSecond + ticksFromNanos);
190                 return UnixEpoch.AddTicks(ticksTotal);
191             }
192             catch (OverflowException)
193             {
194                 // ticks out of long range
195                 return tv_sec > 0 ? DateTime.MaxValue : DateTime.MinValue;
196             }
197             catch (ArgumentOutOfRangeException)
198             {
199                 // resulting date time would be larger than MaxValue
200                 return tv_sec > 0 ? DateTime.MaxValue : DateTime.MinValue;
201             }
202         }
203 
204         /// <summary>
205         /// Creates DateTime to Timespec.
206         /// DateTime has to be in UTC (DateTimeKind.Utc) unless it's DateTime.MaxValue or DateTime.MinValue.
207         /// For DateTime.MaxValue of date time after the largest representable Timespec, Timespec.InfFuture is returned.
208         /// For DateTime.MinValue of date time before the lowest representable Timespec, Timespec.InfPast is returned.
209         /// </summary>
210         /// <returns>The date time.</returns>
211         /// <param name="dateTime">Date time.</param>
FromDateTimeGrpc.Core.Internal.Timespec212         public static Timespec FromDateTime(DateTime dateTime)
213         {
214             if (dateTime == DateTime.MaxValue)
215             {
216                 return Timespec.InfFuture;
217             }
218 
219             if (dateTime == DateTime.MinValue)
220             {
221                 return Timespec.InfPast;
222             }
223 
224             GrpcPreconditions.CheckArgument(dateTime.Kind == DateTimeKind.Utc, "dateTime needs of kind DateTimeKind.Utc or be equal to DateTime.MaxValue or DateTime.MinValue.");
225 
226             try
227             {
228                 TimeSpan timeSpan = dateTime - UnixEpoch;
229                 long ticks = timeSpan.Ticks;
230 
231                 long seconds = ticks / TicksPerSecond;
232                 int nanos = (int)((ticks % TicksPerSecond) * NanosPerTick);
233                 if (nanos < 0)
234                 {
235                     // correct the result based on C# modulo semantics for negative dividend
236                     seconds--;
237                     nanos += (int)NanosPerSecond;
238                 }
239                 return new Timespec(seconds, nanos);
240             }
241             catch (ArgumentOutOfRangeException)
242             {
243                 return dateTime > UnixEpoch ? Timespec.InfFuture : Timespec.InfPast;
244             }
245         }
246 
247         /// <summary>
248         /// Gets current timestamp using <c>GPRClockType.Precise</c>.
249         /// Only available internally because core needs to be compiled with
250         /// GRPC_TIMERS_RDTSC support for this to use RDTSC.
251         /// </summary>
252         internal static Timespec PreciseNow
253         {
254             get
255             {
256                 return Native.gprsharp_now(ClockType.Precise);
257             }
258         }
259 
260         // for tests only
261         internal static int NativeSize
262         {
263             get
264             {
265                 return Native.gprsharp_sizeof_timespec();
266             }
267         }
268 
269         // for tests only
270         internal static Timespec NativeInfFuture
271         {
272             get
273             {
274                 return Native.gprsharp_inf_future(ClockType.Realtime);
275             }
276         }
277 
278         // for tests only
279         public static Timespec NativeInfPast
280         {
281             get
282             {
283                 return Native.gprsharp_inf_past(ClockType.Realtime);
284             }
285         }
286     }
287 }
288