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\*=========================================================================*/
34static int timeout_lua_gettime(lua_State *L);
35static int timeout_lua_sleep(lua_State *L);
36
37static 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\*-------------------------------------------------------------------------*/
49void 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\*-------------------------------------------------------------------------*/
62double 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\*-------------------------------------------------------------------------*/
83double 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\*-------------------------------------------------------------------------*/
95double 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\*-------------------------------------------------------------------------*/
115p_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
126double 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
136double 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\*-------------------------------------------------------------------------*/
147int 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\*-------------------------------------------------------------------------*/
158int 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\*-------------------------------------------------------------------------*/
180int 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\*-------------------------------------------------------------------------*/
192static 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
202int 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
212int 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