| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* Implementation of sb_wait_fh_readable and sb_wait_fd_readable, used by the |
|
2
|
|
|
|
|
|
|
* $sb->append_console_line(timeout => $t) feature. |
|
3
|
|
|
|
|
|
|
* |
|
4
|
|
|
|
|
|
|
* This is basically a cross-platform version of select(). It waits for a handle to become |
|
5
|
|
|
|
|
|
|
* readable up to a caller-specified timeout. The caller can then most likely perform a read |
|
6
|
|
|
|
|
|
|
* without blocking. On every OS but Win32, this is as easy as a select() call. On Win32, |
|
7
|
|
|
|
|
|
|
* the goal is in fact *impossible* for arbitrary user-supplied handles; the asynchronous |
|
8
|
|
|
|
|
|
|
* "overlapped i/o" offered by Win32 requires a handle to be initially created with that |
|
9
|
|
|
|
|
|
|
* feature enabled. Lacking the overlapped flag, checking a handle for readability has to be |
|
10
|
|
|
|
|
|
|
* implemented differently for each type of handle. |
|
11
|
|
|
|
|
|
|
* - Sockets : use select() |
|
12
|
|
|
|
|
|
|
* - Real Files: assume it doesn't block and return true |
|
13
|
|
|
|
|
|
|
* - Pipes : poll in a loop with PeekNamedPipe |
|
14
|
|
|
|
|
|
|
* - Console : PeekConsoleInput to look for key events |
|
15
|
|
|
|
|
|
|
* I'm ignoring serial ports for now since I don't have an easy way to test. |
|
16
|
|
|
|
|
|
|
*/ |
|
17
|
|
|
|
|
|
|
|
|
18
|
34
|
|
|
|
|
|
static struct timeval* sb_timeout_sv_to_timeval(pTHX_ SV *timeout_sv, struct timeval *tv_out) { |
|
19
|
34
|
50
|
|
|
|
|
if (timeout_sv && SvOK(timeout_sv)) { |
|
|
|
50
|
|
|
|
|
|
|
20
|
34
|
|
|
|
|
|
NV timeout = SvNV(timeout_sv); |
|
21
|
34
|
50
|
|
|
|
|
if (timeout < 0) |
|
22
|
0
|
|
|
|
|
|
croak("timeout must be >= 0"); |
|
23
|
|
|
|
|
|
|
|
|
24
|
34
|
|
|
|
|
|
tv_out->tv_sec = (long) timeout; |
|
25
|
34
|
|
|
|
|
|
tv_out->tv_usec = (long) ((timeout - (NV) tv_out->tv_sec) * 1000000.0); |
|
26
|
34
|
50
|
|
|
|
|
if (tv_out->tv_usec < 0) |
|
27
|
0
|
|
|
|
|
|
tv_out->tv_usec = 0; |
|
28
|
34
|
50
|
|
|
|
|
if (tv_out->tv_usec >= 1000000) { |
|
29
|
0
|
|
|
|
|
|
tv_out->tv_sec += tv_out->tv_usec / 1000000; |
|
30
|
0
|
|
|
|
|
|
tv_out->tv_usec %= 1000000; |
|
31
|
|
|
|
|
|
|
} |
|
32
|
34
|
|
|
|
|
|
return tv_out; |
|
33
|
|
|
|
|
|
|
} |
|
34
|
0
|
|
|
|
|
|
return NULL; |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
#ifdef WIN32 |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
static DWORD sb_win32_timeout_sv_to_ms(pTHX_ SV *timeout_sv) { |
|
40
|
|
|
|
|
|
|
DWORD wait_ms= INFINITE; |
|
41
|
|
|
|
|
|
|
/* The value INFINITE is 0xFFFFFFFF, so the timeout must be less than that constant. */ |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
if (timeout_sv && SvOK(timeout_sv)) { |
|
44
|
|
|
|
|
|
|
NV timeout = SvNV(timeout_sv); |
|
45
|
|
|
|
|
|
|
if (timeout < 0) |
|
46
|
|
|
|
|
|
|
croak("timeout must be >= 0"); |
|
47
|
|
|
|
|
|
|
if (timeout > (NV)(0xFFFFFFFD * 0.001)) |
|
48
|
|
|
|
|
|
|
wait_ms = 0xFFFFFFFD; |
|
49
|
|
|
|
|
|
|
else |
|
50
|
|
|
|
|
|
|
wait_ms = (DWORD) (timeout * 1000.0 + 0.5); |
|
51
|
|
|
|
|
|
|
} |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
return wait_ms; |
|
54
|
|
|
|
|
|
|
} |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
/* For console input handles, they appear ready when *any* event is pending. The next ReadFile |
|
57
|
|
|
|
|
|
|
* will discard any non-character events and block until it receives a character event. |
|
58
|
|
|
|
|
|
|
* Note that an edge case of codepage 65001 (UTF-8) is that if one keypress generates multiple |
|
59
|
|
|
|
|
|
|
* bytes and you only read one byte, the rest of the bytes are readable without blocking but |
|
60
|
|
|
|
|
|
|
* there is literally no way to discover that status. This will end up waiting for the |
|
61
|
|
|
|
|
|
|
* following keypress before returning true. |
|
62
|
|
|
|
|
|
|
*/ |
|
63
|
|
|
|
|
|
|
static bool |
|
64
|
|
|
|
|
|
|
sb_wait_win32_console_readable(HANDLE hdl, DWORD wait_ms) { |
|
65
|
|
|
|
|
|
|
while (1) { |
|
66
|
|
|
|
|
|
|
INPUT_RECORD in_rec[16]; |
|
67
|
|
|
|
|
|
|
DWORD i, nread; |
|
68
|
|
|
|
|
|
|
DWORD ready; |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
ready = WaitForSingleObject(hdl, wait_ms); |
|
71
|
|
|
|
|
|
|
if (ready == WAIT_TIMEOUT) |
|
72
|
|
|
|
|
|
|
return false; |
|
73
|
|
|
|
|
|
|
if (ready != WAIT_OBJECT_0) |
|
74
|
|
|
|
|
|
|
croak_with_syserror("WaitForSingleObject failed", GetLastError()); |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
/* After the first blocking wait, drain any non-character events and then |
|
77
|
|
|
|
|
|
|
* return, rather than trying to calculate how much time is left. |
|
78
|
|
|
|
|
|
|
* The caller should always expect the possibility of an early return. |
|
79
|
|
|
|
|
|
|
*/ |
|
80
|
|
|
|
|
|
|
if (wait_ms != INFINITE) |
|
81
|
|
|
|
|
|
|
wait_ms = 0; |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
/* Inspect pending console events until we find a real |
|
84
|
|
|
|
|
|
|
* character-producing key event. Discard non-character events so we |
|
85
|
|
|
|
|
|
|
* don't wake forever on the same unread record. |
|
86
|
|
|
|
|
|
|
*/ |
|
87
|
|
|
|
|
|
|
if (PeekConsoleInputW(hdl, in_rec, sizeof(in_rec)/sizeof(*in_rec), &nread)) { |
|
88
|
|
|
|
|
|
|
for (i = 0; i < nread; i++) { |
|
89
|
|
|
|
|
|
|
if (in_rec[i].EventType == KEY_EVENT |
|
90
|
|
|
|
|
|
|
&& in_rec[i].Event.KeyEvent.bKeyDown |
|
91
|
|
|
|
|
|
|
&& in_rec[i].Event.KeyEvent.uChar.UnicodeChar != 0 |
|
92
|
|
|
|
|
|
|
) |
|
93
|
|
|
|
|
|
|
break; |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
secret_buffer_wipe((char*) in_rec, sizeof(in_rec)); |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
/* discard the non-char events */ |
|
98
|
|
|
|
|
|
|
if (i > 0) { |
|
99
|
|
|
|
|
|
|
DWORD nread2; |
|
100
|
|
|
|
|
|
|
if (!ReadConsoleInputW(hdl, in_rec, i, &nread2)) |
|
101
|
|
|
|
|
|
|
croak_with_syserror("ReadConsoleInput failed", GetLastError()); |
|
102
|
|
|
|
|
|
|
secret_buffer_wipe((char*) in_rec, sizeof(in_rec)); |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
if (i == nread) |
|
105
|
|
|
|
|
|
|
continue; |
|
106
|
|
|
|
|
|
|
} |
|
107
|
|
|
|
|
|
|
else { |
|
108
|
|
|
|
|
|
|
SetLastError(0); |
|
109
|
|
|
|
|
|
|
} |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
return true; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
/* There doesn't seem to be any better way to do this than polling at short intervals |
|
116
|
|
|
|
|
|
|
* until PeekNamedPipe gets some data. This is terrible but I'm done wasting time on it. |
|
117
|
|
|
|
|
|
|
*/ |
|
118
|
|
|
|
|
|
|
static bool sb_wait_win32_pipe_readable(HANDLE hdl, DWORD wait_ms) { |
|
119
|
|
|
|
|
|
|
DWORD start_tick = 0; |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
if (wait_ms != INFINITE) { |
|
122
|
|
|
|
|
|
|
/* Don't allow huge values of wait_ms lest it wrap while we were sleeping. |
|
123
|
|
|
|
|
|
|
* The API contract is that we wait *up to* than this number, anyway. */ |
|
124
|
|
|
|
|
|
|
if (wait_ms > 0xFFFF0000) |
|
125
|
|
|
|
|
|
|
wait_ms= 0xFFFF0000; |
|
126
|
|
|
|
|
|
|
start_tick = GetTickCount(); |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
while (1) { |
|
130
|
|
|
|
|
|
|
DWORD avail = 0; |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
if (!PeekNamedPipe(hdl, NULL, 0, NULL, &avail, NULL)) { |
|
133
|
|
|
|
|
|
|
DWORD err = GetLastError(); |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
/* Broken or disconnected pipe: a subsequent read should complete |
|
136
|
|
|
|
|
|
|
* immediately with EOF/failure rather than block. |
|
137
|
|
|
|
|
|
|
*/ |
|
138
|
|
|
|
|
|
|
if (err == ERROR_BROKEN_PIPE || err == ERROR_PIPE_NOT_CONNECTED) |
|
139
|
|
|
|
|
|
|
return true; |
|
140
|
|
|
|
|
|
|
croak_with_syserror("PeekNamedPipe failed", err); |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
if (avail > 0) |
|
144
|
|
|
|
|
|
|
return true; |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
if (wait_ms == 0) |
|
147
|
|
|
|
|
|
|
return false; |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
if (wait_ms == INFINITE) { |
|
150
|
|
|
|
|
|
|
Sleep(5); |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
else { |
|
153
|
|
|
|
|
|
|
DWORD elapsed = GetTickCount() - start_tick; |
|
154
|
|
|
|
|
|
|
DWORD sleep_ms; |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
if (elapsed >= wait_ms) |
|
157
|
|
|
|
|
|
|
return false; |
|
158
|
|
|
|
|
|
|
sleep_ms = wait_ms - elapsed; |
|
159
|
|
|
|
|
|
|
Sleep(sleep_ms > 5 ? 5 : sleep_ms); |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
/* Given a Win32 HANDLE, dispatch to the function that can wait for |
|
165
|
|
|
|
|
|
|
* readability for that type of handle. |
|
166
|
|
|
|
|
|
|
*/ |
|
167
|
|
|
|
|
|
|
bool sb_wait_win32_handle_readable(pTHX_ HANDLE hdl, SV *timeout_sv) { |
|
168
|
|
|
|
|
|
|
DWORD ftype; |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
SetLastError(0); |
|
171
|
|
|
|
|
|
|
ftype = GetFileType(hdl); |
|
172
|
|
|
|
|
|
|
if (ftype == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR) |
|
173
|
|
|
|
|
|
|
croak_with_syserror("GetFileType failed", GetLastError()); |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
switch (ftype) { |
|
176
|
|
|
|
|
|
|
case FILE_TYPE_DISK: |
|
177
|
|
|
|
|
|
|
/* Regular files are effectively always readable for our purposes. */ |
|
178
|
|
|
|
|
|
|
return true; |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
case FILE_TYPE_CHAR: { |
|
181
|
|
|
|
|
|
|
DWORD console_mode; |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
/* Only console handles are supported here. Other character devices are not. */ |
|
184
|
|
|
|
|
|
|
if (!GetConsoleMode(hdl, &console_mode)) { |
|
185
|
|
|
|
|
|
|
SetLastError(0); |
|
186
|
|
|
|
|
|
|
croak("timeout is not supported on this type of Win32 character handle"); |
|
187
|
|
|
|
|
|
|
} |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
return sb_wait_win32_console_readable(hdl, sb_win32_timeout_sv_to_ms(aTHX_ timeout_sv)); |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
case FILE_TYPE_PIPE: { |
|
193
|
|
|
|
|
|
|
DWORD avail= 0; |
|
194
|
|
|
|
|
|
|
/* both named pipes and winsock SOCKETs are reported as TYPE_PIPE */ |
|
195
|
|
|
|
|
|
|
if (PeekNamedPipe(hdl, NULL, 0, NULL, &avail, NULL)) { |
|
196
|
|
|
|
|
|
|
if (avail > 0) |
|
197
|
|
|
|
|
|
|
return true; |
|
198
|
|
|
|
|
|
|
return sb_wait_win32_pipe_readable(hdl, sb_win32_timeout_sv_to_ms(aTHX_ timeout_sv)); |
|
199
|
|
|
|
|
|
|
} |
|
200
|
|
|
|
|
|
|
else if (GetLastError() != ERROR_INVALID_FUNCTION) |
|
201
|
|
|
|
|
|
|
croak_with_syserror("PeekNamedPipe failed", GetLastError()); |
|
202
|
|
|
|
|
|
|
/* else fall through because it wasn't really a pipe */ |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
default: |
|
205
|
|
|
|
|
|
|
croak("timeout is not supported on this type of Win32 handle"); |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
#endif |
|
209
|
|
|
|
|
|
|
|
|
210
|
34
|
|
|
|
|
|
static bool sb_wait_fd_readable(pTHX_ int fd, SV *timeout_sv) { |
|
211
|
|
|
|
|
|
|
#ifdef WIN32 |
|
212
|
|
|
|
|
|
|
/* Win32 getsockopt() and select() use SOCKET pointers, but Perl has defined |
|
213
|
|
|
|
|
|
|
* macros so that we use the regular POSIX style API, and can share code below. |
|
214
|
|
|
|
|
|
|
*/ |
|
215
|
|
|
|
|
|
|
int val, len = sizeof(val); |
|
216
|
|
|
|
|
|
|
int save_errno= errno; |
|
217
|
|
|
|
|
|
|
int ret= getsockopt(fd, SOL_SOCKET, SO_TYPE, (char*)&val, &len); |
|
218
|
|
|
|
|
|
|
errno= save_errno; |
|
219
|
|
|
|
|
|
|
if (ret < 0) { |
|
220
|
|
|
|
|
|
|
/* Not a socket, so need to use something other than 'select' */ |
|
221
|
|
|
|
|
|
|
HANDLE hFile = (HANDLE)_get_osfhandle(fd); |
|
222
|
|
|
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) |
|
223
|
|
|
|
|
|
|
croak("Handle has no system file descriptor"); |
|
224
|
|
|
|
|
|
|
return sb_wait_win32_handle_readable(aTHX_ hFile, timeout_sv); |
|
225
|
|
|
|
|
|
|
} else |
|
226
|
|
|
|
|
|
|
#endif |
|
227
|
|
|
|
|
|
|
{ |
|
228
|
|
|
|
|
|
|
fd_set rfds; |
|
229
|
|
|
|
|
|
|
int r; |
|
230
|
|
|
|
|
|
|
struct timeval tv; |
|
231
|
|
|
|
|
|
|
|
|
232
|
578
|
100
|
|
|
|
|
FD_ZERO(&rfds); |
|
233
|
34
|
|
|
|
|
|
FD_SET(fd, &rfds); |
|
234
|
|
|
|
|
|
|
|
|
235
|
34
|
|
|
|
|
|
r = select(fd + 1, &rfds, NULL, NULL, sb_timeout_sv_to_timeval(aTHX_ timeout_sv, &tv)); |
|
236
|
34
|
50
|
|
|
|
|
if (r < 0 && errno != EINTR) |
|
|
|
0
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
|
croak_with_syserror("select failed", errno); |
|
238
|
34
|
|
|
|
|
|
return (r > 0); |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
|
|
242
|
34
|
|
|
|
|
|
static bool sb_wait_fh_readable(pTHX_ PerlIO *fh, SV *timeout_sv) { |
|
243
|
|
|
|
|
|
|
int fd; |
|
244
|
34
|
50
|
|
|
|
|
if (PerlIO_get_cnt(fh) > 0) |
|
245
|
0
|
|
|
|
|
|
return true; |
|
246
|
34
|
50
|
|
|
|
|
if ((fd= PerlIO_fileno(fh)) < 0) |
|
247
|
0
|
|
|
|
|
|
croak("Handle has no system file descriptor (fileno)"); |
|
248
|
34
|
|
|
|
|
|
return sb_wait_fd_readable(aTHX_ fd, timeout_sv); |
|
249
|
|
|
|
|
|
|
} |