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