1 | /*=========================================================================*\ |
2 | * Timeout management functions |
3 | * LuaSocket toolkit |
4 | \*=========================================================================*/ |
5 | #include <stdio.h> |
6 | #include <limits.h> |
7 | #include <float.h> |
8 | |
9 | #include "lua.h" |
10 | #include "lauxlib.h" |
11 | #include "compat.h" |
12 | |
13 | #include "auxiliar.h" |
14 | #include "timeout.h" |
15 | |
16 | #ifdef _WIN32 |
17 | #include <windows.h> |
18 | #else |
19 | #include <time.h> |
20 | #include <sys/time.h> |
21 | #endif |
22 | |
23 | /* min and max macros */ |
24 | #ifndef MIN |
25 | #define MIN(x, y) ((x) < (y) ? x : y) |
26 | #endif |
27 | #ifndef MAX |
28 | #define MAX(x, y) ((x) > (y) ? x : y) |
29 | #endif |
30 | |
31 | /*=========================================================================*\ |
32 | * Internal function prototypes |
33 | \*=========================================================================*/ |
34 | static int timeout_lua_gettime(lua_State *L); |
35 | static int timeout_lua_sleep(lua_State *L); |
36 | |
37 | static luaL_Reg func[] = { |
38 | { "gettime" , timeout_lua_gettime }, |
39 | { "sleep" , timeout_lua_sleep }, |
40 | { NULL, NULL } |
41 | }; |
42 | |
43 | /*=========================================================================*\ |
44 | * Exported functions. |
45 | \*=========================================================================*/ |
46 | /*-------------------------------------------------------------------------*\ |
47 | * Initialize structure |
48 | \*-------------------------------------------------------------------------*/ |
49 | void timeout_init(p_timeout tm, double block, double total) { |
50 | tm->block = block; |
51 | tm->total = total; |
52 | } |
53 | |
54 | /*-------------------------------------------------------------------------*\ |
55 | * Determines how much time we have left for the next system call, |
56 | * if the previous call was successful |
57 | * Input |
58 | * tm: timeout control structure |
59 | * Returns |
60 | * the number of ms left or -1 if there is no time limit |
61 | \*-------------------------------------------------------------------------*/ |
62 | double timeout_get(p_timeout tm) { |
63 | if (tm->block < 0.0 && tm->total < 0.0) { |
64 | return -1; |
65 | } else if (tm->block < 0.0) { |
66 | double t = tm->total - timeout_gettime() + tm->start; |
67 | return MAX(t, 0.0); |
68 | } else if (tm->total < 0.0) { |
69 | return tm->block; |
70 | } else { |
71 | double t = tm->total - timeout_gettime() + tm->start; |
72 | return MIN(tm->block, MAX(t, 0.0)); |
73 | } |
74 | } |
75 | |
76 | /*-------------------------------------------------------------------------*\ |
77 | * Returns time since start of operation |
78 | * Input |
79 | * tm: timeout control structure |
80 | * Returns |
81 | * start field of structure |
82 | \*-------------------------------------------------------------------------*/ |
83 | double timeout_getstart(p_timeout tm) { |
84 | return tm->start; |
85 | } |
86 | |
87 | /*-------------------------------------------------------------------------*\ |
88 | * Determines how much time we have left for the next system call, |
89 | * if the previous call was a failure |
90 | * Input |
91 | * tm: timeout control structure |
92 | * Returns |
93 | * the number of ms left or -1 if there is no time limit |
94 | \*-------------------------------------------------------------------------*/ |
95 | double timeout_getretry(p_timeout tm) { |
96 | if (tm->block < 0.0 && tm->total < 0.0) { |
97 | return -1; |
98 | } else if (tm->block < 0.0) { |
99 | double t = tm->total - timeout_gettime() + tm->start; |
100 | return MAX(t, 0.0); |
101 | } else if (tm->total < 0.0) { |
102 | double t = tm->block - timeout_gettime() + tm->start; |
103 | return MAX(t, 0.0); |
104 | } else { |
105 | double t = tm->total - timeout_gettime() + tm->start; |
106 | return MIN(tm->block, MAX(t, 0.0)); |
107 | } |
108 | } |
109 | |
110 | /*-------------------------------------------------------------------------*\ |
111 | * Marks the operation start time in structure |
112 | * Input |
113 | * tm: timeout control structure |
114 | \*-------------------------------------------------------------------------*/ |
115 | p_timeout timeout_markstart(p_timeout tm) { |
116 | tm->start = timeout_gettime(); |
117 | return tm; |
118 | } |
119 | |
120 | /*-------------------------------------------------------------------------*\ |
121 | * Gets time in s, relative to January 1, 1970 (UTC) |
122 | * Returns |
123 | * time in s. |
124 | \*-------------------------------------------------------------------------*/ |
125 | #ifdef _WIN32 |
126 | double timeout_gettime(void) { |
127 | FILETIME ft; |
128 | double t; |
129 | GetSystemTimeAsFileTime(&ft); |
130 | /* Windows file time (time since January 1, 1601 (UTC)) */ |
131 | t = ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7); |
132 | /* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */ |
133 | return (t - 11644473600.0); |
134 | } |
135 | #else |
136 | double timeout_gettime(void) { |
137 | struct timeval v; |
138 | gettimeofday(&v, (struct timezone *) NULL); |
139 | /* Unix Epoch time (time since January 1, 1970 (UTC)) */ |
140 | return v.tv_sec + v.tv_usec/1.0e6; |
141 | } |
142 | #endif |
143 | |
144 | /*-------------------------------------------------------------------------*\ |
145 | * Initializes module |
146 | \*-------------------------------------------------------------------------*/ |
147 | int timeout_open(lua_State *L) { |
148 | luaL_setfuncs(L, func, 0); |
149 | return 0; |
150 | } |
151 | |
152 | /*-------------------------------------------------------------------------*\ |
153 | * Sets timeout values for IO operations |
154 | * Lua Input: base, time [, mode] |
155 | * time: time out value in seconds |
156 | * mode: "b" for block timeout, "t" for total timeout. (default: b) |
157 | \*-------------------------------------------------------------------------*/ |
158 | int timeout_meth_settimeout(lua_State *L, p_timeout tm) { |
159 | double t = luaL_optnumber(L, 2, -1); |
160 | const char *mode = luaL_optstring(L, 3, "b" ); |
161 | switch (*mode) { |
162 | case 'b': |
163 | tm->block = t; |
164 | break; |
165 | case 'r': case 't': |
166 | tm->total = t; |
167 | break; |
168 | default: |
169 | luaL_argcheck(L, 0, 3, "invalid timeout mode" ); |
170 | break; |
171 | } |
172 | lua_pushnumber(L, 1); |
173 | return 1; |
174 | } |
175 | |
176 | /*-------------------------------------------------------------------------*\ |
177 | * Gets timeout values for IO operations |
178 | * Lua Output: block, total |
179 | \*-------------------------------------------------------------------------*/ |
180 | int timeout_meth_gettimeout(lua_State *L, p_timeout tm) { |
181 | lua_pushnumber(L, tm->block); |
182 | lua_pushnumber(L, tm->total); |
183 | return 2; |
184 | } |
185 | |
186 | /*=========================================================================*\ |
187 | * Test support functions |
188 | \*=========================================================================*/ |
189 | /*-------------------------------------------------------------------------*\ |
190 | * Returns the time the system has been up, in secconds. |
191 | \*-------------------------------------------------------------------------*/ |
192 | static int timeout_lua_gettime(lua_State *L) |
193 | { |
194 | lua_pushnumber(L, timeout_gettime()); |
195 | return 1; |
196 | } |
197 | |
198 | /*-------------------------------------------------------------------------*\ |
199 | * Sleep for n seconds. |
200 | \*-------------------------------------------------------------------------*/ |
201 | #ifdef _WIN32 |
202 | int timeout_lua_sleep(lua_State *L) |
203 | { |
204 | double n = luaL_checknumber(L, 1); |
205 | if (n < 0.0) n = 0.0; |
206 | if (n < DBL_MAX/1000.0) n *= 1000.0; |
207 | if (n > INT_MAX) n = INT_MAX; |
208 | Sleep((int)n); |
209 | return 0; |
210 | } |
211 | #else |
212 | int timeout_lua_sleep(lua_State *L) |
213 | { |
214 | double n = luaL_checknumber(L, 1); |
215 | struct timespec t, r; |
216 | if (n < 0.0) n = 0.0; |
217 | if (n > INT_MAX) n = INT_MAX; |
218 | t.tv_sec = (int) n; |
219 | n -= t.tv_sec; |
220 | t.tv_nsec = (int) (n * 1000000000); |
221 | if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999; |
222 | while (nanosleep(&t, &r) != 0) { |
223 | t.tv_sec = r.tv_sec; |
224 | t.tv_nsec = r.tv_nsec; |
225 | } |
226 | return 0; |
227 | } |
228 | #endif |
229 | |