• 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
29     {
30         const long NanosPerSecond = 1000 * 1000 * 1000;
31         const long NanosPerTick = 100;
32         const long TicksPerSecond = NanosPerSecond / NanosPerTick;
33 
34         static readonly NativeMethods Native = NativeMethods.Get();
35         static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
36 
TimespecGrpc.Core.Internal.Timespec37         public Timespec(long tv_sec, int tv_nsec) : this(tv_sec, tv_nsec, ClockType.Realtime)
38         {
39         }
40 
TimespecGrpc.Core.Internal.Timespec41         public Timespec(long tv_sec, int tv_nsec, ClockType clock_type)
42         {
43             this.tv_sec = tv_sec;
44             this.tv_nsec = tv_nsec;
45             this.clock_type = clock_type;
46         }
47 
48         private long tv_sec;
49         private int tv_nsec;
50         private ClockType clock_type;
51 
52         /// <summary>
53         /// Timespec a long time in the future.
54         /// </summary>
55         public static Timespec InfFuture
56         {
57             get
58             {
59                 return new Timespec(long.MaxValue, 0, ClockType.Realtime);
60             }
61         }
62 
63         /// <summary>
64         /// Timespec a long time in the past.
65         /// </summary>
66         public static Timespec InfPast
67         {
68             get
69             {
70                 return new Timespec(long.MinValue, 0, ClockType.Realtime);
71             }
72         }
73 
74         /// <summary>
75         /// Return Timespec representing the current time.
76         /// </summary>
77         public static Timespec Now
78         {
79             get
80             {
81                 return Native.gprsharp_now(ClockType.Realtime);
82             }
83         }
84 
85         /// <summary>
86         /// Seconds since unix epoch.
87         /// </summary>
88         public long TimevalSeconds
89         {
90             get
91             {
92                 return tv_sec;
93             }
94         }
95 
96         /// <summary>
97         /// The nanoseconds part of timeval.
98         /// </summary>
99         public int TimevalNanos
100         {
101             get
102             {
103                 return tv_nsec;
104             }
105         }
106 
107         /// <summary>
108         /// Converts the timespec to desired clock type.
109         /// </summary>
ToClockTypeGrpc.Core.Internal.Timespec110         public Timespec ToClockType(ClockType targetClock)
111         {
112             return Native.gprsharp_convert_clock_type(this, targetClock);
113         }
114 
115         /// <summary>
116         /// Converts Timespec to DateTime.
117         /// Timespec needs to be of type GPRClockType.Realtime and needs to represent a legal value.
118         /// DateTime has lower resolution (100ns), so rounding can occurs.
119         /// Value are always rounded up to the nearest DateTime value in the future.
120         ///
121         /// For Timespec.InfFuture or if timespec is after the largest representable DateTime, DateTime.MaxValue is returned.
122         /// For Timespec.InfPast or if timespec is before the lowest representable DateTime, DateTime.MinValue is returned.
123         ///
124         /// Unless DateTime.MaxValue or DateTime.MinValue is returned, the resulting DateTime is always in UTC
125         /// (DateTimeKind.Utc)
126         /// </summary>
ToDateTimeGrpc.Core.Internal.Timespec127         public DateTime ToDateTime()
128         {
129             GrpcPreconditions.CheckState(tv_nsec >= 0 && tv_nsec < NanosPerSecond);
130             GrpcPreconditions.CheckState(clock_type == ClockType.Realtime);
131 
132             // fast path for InfFuture
133             if (this.Equals(InfFuture))
134             {
135                 return DateTime.MaxValue;
136             }
137 
138             // fast path for InfPast
139             if (this.Equals(InfPast))
140             {
141                 return DateTime.MinValue;
142             }
143 
144             try
145             {
146                 // convert nanos to ticks, round up to the nearest tick
147                 long ticksFromNanos = tv_nsec / NanosPerTick + ((tv_nsec % NanosPerTick != 0) ? 1 : 0);
148                 long ticksTotal = checked(tv_sec * TicksPerSecond + ticksFromNanos);
149                 return UnixEpoch.AddTicks(ticksTotal);
150             }
151             catch (OverflowException)
152             {
153                 // ticks out of long range
154                 return tv_sec > 0 ? DateTime.MaxValue : DateTime.MinValue;
155             }
156             catch (ArgumentOutOfRangeException)
157             {
158                 // resulting date time would be larger than MaxValue
159                 return tv_sec > 0 ? DateTime.MaxValue : DateTime.MinValue;
160             }
161         }
162 
163         /// <summary>
164         /// Creates DateTime to Timespec.
165         /// DateTime has to be in UTC (DateTimeKind.Utc) unless it's DateTime.MaxValue or DateTime.MinValue.
166         /// For DateTime.MaxValue of date time after the largest representable Timespec, Timespec.InfFuture is returned.
167         /// For DateTime.MinValue of date time before the lowest representable Timespec, Timespec.InfPast is returned.
168         /// </summary>
169         /// <returns>The date time.</returns>
170         /// <param name="dateTime">Date time.</param>
FromDateTimeGrpc.Core.Internal.Timespec171         public static Timespec FromDateTime(DateTime dateTime)
172         {
173             if (dateTime == DateTime.MaxValue)
174             {
175                 return Timespec.InfFuture;
176             }
177 
178             if (dateTime == DateTime.MinValue)
179             {
180                 return Timespec.InfPast;
181             }
182 
183             GrpcPreconditions.CheckArgument(dateTime.Kind == DateTimeKind.Utc, "dateTime needs of kind DateTimeKind.Utc or be equal to DateTime.MaxValue or DateTime.MinValue.");
184 
185             try
186             {
187                 TimeSpan timeSpan = dateTime - UnixEpoch;
188                 long ticks = timeSpan.Ticks;
189 
190                 long seconds = ticks / TicksPerSecond;
191                 int nanos = (int)((ticks % TicksPerSecond) * NanosPerTick);
192                 if (nanos < 0)
193                 {
194                     // correct the result based on C# modulo semantics for negative dividend
195                     seconds--;
196                     nanos += (int)NanosPerSecond;
197                 }
198                 return new Timespec(seconds, nanos);
199             }
200             catch (ArgumentOutOfRangeException)
201             {
202                 return dateTime > UnixEpoch ? Timespec.InfFuture : Timespec.InfPast;
203             }
204         }
205 
206         /// <summary>
207         /// Gets current timestamp using <c>GPRClockType.Precise</c>.
208         /// Only available internally because core needs to be compiled with
209         /// GRPC_TIMERS_RDTSC support for this to use RDTSC.
210         /// </summary>
211         internal static Timespec PreciseNow
212         {
213             get
214             {
215                 return Native.gprsharp_now(ClockType.Precise);
216             }
217         }
218 
219         // for tests only
220         internal static int NativeSize
221         {
222             get
223             {
224                 return Native.gprsharp_sizeof_timespec();
225             }
226         }
227 
228         // for tests only
229         internal static Timespec NativeInfFuture
230         {
231             get
232             {
233                 return Native.gprsharp_inf_future(ClockType.Realtime);
234             }
235         }
236 
237         // for tests only
238         public static Timespec NativeInfPast
239         {
240             get
241             {
242                 return Native.gprsharp_inf_past(ClockType.Realtime);
243             }
244         }
245     }
246 }
247