1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 //
6 //  Class used to manage timeouts in complex system operations.
7 //
8 
9 using System.Data.Common;
10 using System.Diagnostics;
11 
12 namespace System.Data.ProviderBase
13 {
14     // Purpose:
15     //   Manages determining and tracking timeouts
16     //
17     // Intended use:
18     //   Call StartXXXXTimeout() to get a timer with the given expiration point
19     //   Get remaining time in appropriate format to pass to subsystem timeouts
20     //   Check for timeout via IsExpired for checks in managed code.
21     //   Simply abandon to GC when done.
22     internal class TimeoutTimer
23     {
24         //-------------------
25         // Fields
26         //-------------------
27         private long _timerExpire;
28         private bool _isInfiniteTimeout;
29 
30         //-------------------
31         // Timeout-setting methods
32         //-------------------
33 
34         // Get a new timer that will expire in the given number of seconds
35         //  For input, a value of zero seconds indicates infinite timeout
StartSecondsTimeout(int seconds)36         internal static TimeoutTimer StartSecondsTimeout(int seconds)
37         {
38             //--------------------
39             // Preconditions: None (seconds must conform to SetTimeoutSeconds requirements)
40 
41             //--------------------
42             // Method body
43             var timeout = new TimeoutTimer();
44             timeout.SetTimeoutSeconds(seconds);
45 
46             //---------------------
47             // Postconditions
48             Debug.Assert(timeout != null); // Need a valid timeouttimer if no error
49 
50             return timeout;
51         }
52 
53         // Get a new timer that will expire in the given number of milliseconds
54         //  No current need to support infinite milliseconds timeout
StartMillisecondsTimeout(long milliseconds)55         internal static TimeoutTimer StartMillisecondsTimeout(long milliseconds)
56         {
57             //--------------------
58             // Preconditions
59             Debug.Assert(0 <= milliseconds);
60 
61             //--------------------
62             // Method body
63             var timeout = new TimeoutTimer();
64             timeout._timerExpire = checked(ADP.TimerCurrent() + (milliseconds * TimeSpan.TicksPerMillisecond));
65             timeout._isInfiniteTimeout = false;
66 
67             //---------------------
68             // Postconditions
69             Debug.Assert(timeout != null); // Need a valid timeouttimer if no error
70 
71             return timeout;
72         }
73 
74         //-------------------
75         // Methods for changing timeout
76         //-------------------
77 
SetTimeoutSeconds(int seconds)78         internal void SetTimeoutSeconds(int seconds)
79         {
80             //--------------------
81             // Preconditions
82             Debug.Assert(0 <= seconds || InfiniteTimeout == seconds);  // no need to support negative seconds at present
83 
84             //--------------------
85             // Method body
86             if (InfiniteTimeout == seconds)
87             {
88                 _isInfiniteTimeout = true;
89             }
90             else
91             {
92                 // Stash current time + timeout
93                 _timerExpire = checked(ADP.TimerCurrent() + ADP.TimerFromSeconds(seconds));
94                 _isInfiniteTimeout = false;
95             }
96             //---------------------
97             // Postconditions:None
98         }
99 
100         //-------------------
101         // Timeout info properties
102         //-------------------
103 
104         // Indicator for infinite timeout when starting a timer
105         internal static readonly long InfiniteTimeout = 0;
106 
107         // Is this timer in an expired state?
108         internal bool IsExpired
109         {
110             get
111             {
112                 return !IsInfinite && ADP.TimerHasExpired(_timerExpire);
113             }
114         }
115 
116         // is this an infinite-timeout timer?
117         internal bool IsInfinite
118         {
119             get
120             {
121                 return _isInfiniteTimeout;
122             }
123         }
124 
125         // Special accessor for TimerExpire for use when thunking to legacy timeout methods.
126         internal long LegacyTimerExpire
127         {
128             get
129             {
130                 return (_isInfiniteTimeout) ? Int64.MaxValue : _timerExpire;
131             }
132         }
133 
134         // Returns milliseconds remaining trimmed to zero for none remaining
135         //  and long.MaxValue for infinite
136         // This method should be preferred for internal calculations that are not
137         //  yet common enough to code into the TimeoutTimer class itself.
138         internal long MillisecondsRemaining
139         {
140             get
141             {
142                 //-------------------
143                 // Preconditions: None
144 
145                 //-------------------
146                 // Method Body
147                 long milliseconds;
148                 if (_isInfiniteTimeout)
149                 {
150                     milliseconds = long.MaxValue;
151                 }
152                 else
153                 {
154                     milliseconds = ADP.TimerRemainingMilliseconds(_timerExpire);
155                     if (0 > milliseconds)
156                     {
157                         milliseconds = 0;
158                     }
159                 }
160 
161                 //--------------------
162                 // Postconditions
163                 Debug.Assert(0 <= milliseconds); // This property guarantees no negative return values
164 
165                 return milliseconds;
166             }
167         }
168     }
169 }
170 
171