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