/home/travis/build/MoarVM/MoarVM/src/io/syncsocket.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | #ifdef _WIN32 |
4 | | #include <winsock2.h> |
5 | | #include <ws2tcpip.h> |
6 | | #include <io.h> |
7 | | |
8 | | typedef SOCKET Socket; |
9 | | #define sa_family_t unsigned int |
10 | | #define isatty _isatty |
11 | | #else |
12 | | #include "unistd.h" |
13 | | #include <sys/socket.h> |
14 | | #include <sys/un.h> |
15 | | |
16 | | typedef int Socket; |
17 | 0 | #define closesocket close |
18 | | #endif |
19 | | |
20 | | #if defined(_MSC_VER) |
21 | | #define snprintf _snprintf |
22 | | #endif |
23 | | |
24 | | /* Assumed maximum packet size. If ever changing this to something beyond a |
25 | | * 16-bit number, then make sure to change the receive offsets in the data |
26 | | * structure below. */ |
27 | 0 | #define PACKET_SIZE 65535 |
28 | | |
29 | | /* Error handling varies between POSIX and WinSock. */ |
30 | | MVM_NO_RETURN static void throw_error(MVMThreadContext *tc, int r, char *operation) MVM_NO_RETURN_ATTRIBUTE; |
31 | | #ifdef _WIN32 |
32 | | #define MVM_IS_SOCKET_ERROR(x) ((x) == SOCKET_ERROR) |
33 | | static void throw_error(MVMThreadContext *tc, int r, char *operation) { |
34 | | int error = WSAGetLastError(); |
35 | | LPTSTR error_string = NULL; |
36 | | if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, |
37 | | NULL, error, 0, (LPTSTR)&error_string, 0, NULL) == 0) { |
38 | | /* Couldn't get error string; throw with code. */ |
39 | | MVM_exception_throw_adhoc(tc, "Could not %s: error code %d", operation, error); |
40 | | } |
41 | | MVM_exception_throw_adhoc(tc, "Could not %s: %s", operation, error_string); |
42 | | } |
43 | | #else |
44 | 0 | #define MVM_IS_SOCKET_ERROR(x) ((x) < 0) |
45 | 0 | static void throw_error(MVMThreadContext *tc, int r, char *operation) { |
46 | 0 | MVM_exception_throw_adhoc(tc, "Could not %s: %s", operation, strerror(errno)); |
47 | 0 | } |
48 | | #endif |
49 | | |
50 | | /* Data that we keep for a socket-based handle. */ |
51 | | typedef struct { |
52 | | /* The socket handle (file descriptor on POSIX, SOCKET on Windows). */ |
53 | | Socket handle; |
54 | | |
55 | | /* Buffer of the last received packet of data, and start/end pointers |
56 | | * into the data. */ |
57 | | char *last_packet; |
58 | | MVMuint16 last_packet_start; |
59 | | MVMuint16 last_packet_end; |
60 | | |
61 | | /* Did we reach EOF yet? */ |
62 | | MVMint32 eof; |
63 | | |
64 | | /* ID for instrumentation. */ |
65 | | unsigned int interval_id; |
66 | | } MVMIOSyncSocketData; |
67 | | |
68 | | /* Read a packet worth of data into the last packet buffer. */ |
69 | 0 | static void read_one_packet(MVMThreadContext *tc, MVMIOSyncSocketData *data) { |
70 | 0 | unsigned int interval_id = MVM_telemetry_interval_start(tc, "syncsocket.read_one_packet"); |
71 | 0 | int r; |
72 | 0 | data->last_packet = MVM_malloc(PACKET_SIZE); |
73 | 0 | do { |
74 | 0 | MVM_gc_mark_thread_blocked(tc); |
75 | 0 | r = recv(data->handle, data->last_packet, PACKET_SIZE, 0); |
76 | 0 | MVM_gc_mark_thread_unblocked(tc); |
77 | 0 | } while(r == -1 && errno == EINTR); |
78 | 0 | MVM_telemetry_interval_stop(tc, interval_id, "syncsocket.read_one_packet"); |
79 | 0 | if (MVM_IS_SOCKET_ERROR(r) || r == 0) { |
80 | 0 | MVM_free(data->last_packet); |
81 | 0 | data->last_packet = NULL; |
82 | 0 | if (r != 0) |
83 | 0 | throw_error(tc, r, "receive data from socket"); |
84 | 0 | } |
85 | 0 | else { |
86 | 0 | data->last_packet_start = 0; |
87 | 0 | data->last_packet_end = r; |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | 0 | MVMint64 socket_read_bytes(MVMThreadContext *tc, MVMOSHandle *h, char **buf, MVMint64 bytes) { |
92 | 0 | MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; |
93 | 0 | char *use_last_packet = NULL; |
94 | 0 | MVMuint16 use_last_packet_start, use_last_packet_end; |
95 | 0 |
|
96 | 0 | /* If at EOF, nothing more to do. */ |
97 | 0 | if (data->eof) { |
98 | 0 | *buf = NULL; |
99 | 0 | return 0; |
100 | 0 | } |
101 | 0 |
|
102 | 0 | /* See if there's anything in the packet buffer. */ |
103 | 0 | if (data->last_packet) { |
104 | 0 | MVMuint16 last_remaining = data->last_packet_end - data->last_packet_start; |
105 | 0 | if (bytes <= last_remaining) { |
106 | 0 | /* There's enough, and it's sufficient for the request. Extract it |
107 | 0 | * and return, discarding the last packet buffer if we drain it. */ |
108 | 0 | *buf = MVM_malloc(bytes); |
109 | 0 | memcpy(*buf, data->last_packet + data->last_packet_start, bytes); |
110 | 0 | if (bytes == last_remaining) { |
111 | 0 | MVM_free(data->last_packet); |
112 | 0 | data->last_packet = NULL; |
113 | 0 | } |
114 | 0 | else { |
115 | 0 | data->last_packet_start += bytes; |
116 | 0 | } |
117 | 0 | return bytes; |
118 | 0 | } |
119 | 0 | else { |
120 | 0 | /* Something, but not enough. Take the last packet for use, then |
121 | 0 | * we'll read another one. */ |
122 | 0 | use_last_packet = data->last_packet; |
123 | 0 | use_last_packet_start = data->last_packet_start; |
124 | 0 | use_last_packet_end = data->last_packet_end; |
125 | 0 | data->last_packet = NULL; |
126 | 0 | } |
127 | 0 | } |
128 | 0 |
|
129 | 0 | /* If we get here, we need to read another packet. */ |
130 | 0 | read_one_packet(tc, data); |
131 | 0 |
|
132 | 0 | /* Now assemble the result. */ |
133 | 0 | if (data->last_packet && use_last_packet) { |
134 | 0 | /* Need to assemble it from two places. */ |
135 | 0 | MVMuint32 last_available = use_last_packet_end - use_last_packet_start; |
136 | 0 | MVMuint32 available = last_available + data->last_packet_end; |
137 | 0 | if (bytes > available) |
138 | 0 | bytes = available; |
139 | 0 | *buf = MVM_malloc(bytes); |
140 | 0 | memcpy(*buf, use_last_packet + use_last_packet_start, last_available); |
141 | 0 | memcpy(*buf + last_available, data->last_packet, bytes - last_available); |
142 | 0 | if (bytes == available) { |
143 | 0 | /* We used all of the just-read packet. */ |
144 | 0 | MVM_free(data->last_packet); |
145 | 0 | data->last_packet = NULL; |
146 | 0 | } |
147 | 0 | else { |
148 | 0 | /* Still something left in the just-read packet for next time. */ |
149 | 0 | data->last_packet_start += bytes - last_available; |
150 | 0 | } |
151 | 0 | } |
152 | 0 | else if (data->last_packet) { |
153 | 0 | /* Only data from the just-read packet. */ |
154 | 0 | if (bytes >= data->last_packet_end) { |
155 | 0 | /* We need all of it, so no copying needed, just hand it back. */ |
156 | 0 | *buf = data->last_packet; |
157 | 0 | bytes = data->last_packet_end; |
158 | 0 | data->last_packet = NULL; |
159 | 0 | } |
160 | 0 | else { |
161 | 0 | /* Only need some of it. */ |
162 | 0 | *buf = MVM_malloc(bytes); |
163 | 0 | memcpy(*buf, data->last_packet, bytes); |
164 | 0 | data->last_packet_start += bytes; |
165 | 0 | } |
166 | 0 | } |
167 | 0 | else if (use_last_packet) { |
168 | 0 | /* Nothing read this time, so at the end. Drain previous packet data |
169 | 0 | * and mark EOF. */ |
170 | 0 | bytes = use_last_packet_end - use_last_packet_start; |
171 | 0 | *buf = MVM_malloc(bytes); |
172 | 0 | memcpy(*buf, use_last_packet + use_last_packet_start, bytes); |
173 | 0 | data->eof = 1; |
174 | 0 | } |
175 | 0 | else { |
176 | 0 | /* Nothing to hand back; at EOF. */ |
177 | 0 | *buf = NULL; |
178 | 0 | bytes = 0; |
179 | 0 | data->eof = 1; |
180 | 0 | } |
181 | 0 |
|
182 | 0 | return bytes; |
183 | 0 | } |
184 | | |
185 | | /* Checks if EOF has been reached on the incoming data. */ |
186 | 0 | MVMint64 socket_eof(MVMThreadContext *tc, MVMOSHandle *h) { |
187 | 0 | MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; |
188 | 0 | return data->eof; |
189 | 0 | } |
190 | | |
191 | 0 | void socket_flush(MVMThreadContext *tc, MVMOSHandle *h, MVMint32 sync) { |
192 | 0 | /* A no-op for sockets; we don't buffer. */ |
193 | 0 | } |
194 | | |
195 | 0 | void socket_truncate(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 bytes) { |
196 | 0 | MVM_exception_throw_adhoc(tc, "Cannot truncate a socket"); |
197 | 0 | } |
198 | | |
199 | | /* Writes the specified bytes to the stream. */ |
200 | 0 | MVMint64 socket_write_bytes(MVMThreadContext *tc, MVMOSHandle *h, char *buf, MVMint64 bytes) { |
201 | 0 | MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; |
202 | 0 | MVMint64 sent = 0; |
203 | 0 | unsigned int interval_id; |
204 | 0 |
|
205 | 0 | interval_id = MVM_telemetry_interval_start(tc, "syncsocket.write_bytes"); |
206 | 0 | MVM_gc_mark_thread_blocked(tc); |
207 | 0 | while (bytes > 0) { |
208 | 0 | int r; |
209 | 0 | do { |
210 | 0 | r = send(data->handle, buf, (int)bytes, 0); |
211 | 0 | } while(r == -1 && errno == EINTR); |
212 | 0 | if (MVM_IS_SOCKET_ERROR(r)) { |
213 | 0 | MVM_gc_mark_thread_unblocked(tc); |
214 | 0 | MVM_telemetry_interval_stop(tc, interval_id, "syncsocket.write_bytes"); |
215 | 0 | throw_error(tc, r, "send data to socket"); |
216 | 0 | } |
217 | 0 | sent += r; |
218 | 0 | buf += r; |
219 | 0 | bytes -= r; |
220 | 0 | } |
221 | 0 | MVM_gc_mark_thread_unblocked(tc); |
222 | 0 | MVM_telemetry_interval_annotate(bytes, interval_id, "written this many bytes"); |
223 | 0 | MVM_telemetry_interval_stop(tc, interval_id, "syncsocket.write_bytes"); |
224 | 0 | return bytes; |
225 | 0 | } |
226 | | |
227 | 0 | static MVMint64 do_close(MVMThreadContext *tc, MVMIOSyncSocketData *data) { |
228 | 0 | if (data->handle) { |
229 | 0 | closesocket(data->handle); |
230 | 0 | data->handle = 0; |
231 | 0 | } |
232 | 0 | return 0; |
233 | 0 | } |
234 | 0 | static MVMint64 close_socket(MVMThreadContext *tc, MVMOSHandle *h) { |
235 | 0 | return do_close(tc, (MVMIOSyncSocketData *)h->body.data); |
236 | 0 | } |
237 | | |
238 | 0 | static void gc_free(MVMThreadContext *tc, MVMObject *h, void *d) { |
239 | 0 | MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)d; |
240 | 0 | do_close(tc, data); |
241 | 0 | MVM_free(data); |
242 | 0 | } |
243 | | |
244 | 0 | static size_t get_struct_size_for_family(sa_family_t family) { |
245 | 0 | switch (family) { |
246 | 0 | case AF_INET6: |
247 | 0 | return sizeof(struct sockaddr_in6); |
248 | 0 | case AF_INET: |
249 | 0 | return sizeof(struct sockaddr_in); |
250 | 0 | #ifndef _WIN32 |
251 | 0 | case AF_UNIX: |
252 | 0 | return sizeof(struct sockaddr_un); |
253 | 0 | #endif |
254 | 0 | default: |
255 | 0 | return sizeof(struct sockaddr); |
256 | 0 | } |
257 | 0 | } |
258 | | |
259 | | /* This function may return any type of sockaddr e.g. sockaddr_un, sockaddr_in or sockaddr_in6 |
260 | | * It shouldn't be a problem with general code as long as the port number is kept below the int16 limit: 65536 |
261 | | * After this it defines the family which may spawn non internet sockaddr's |
262 | | * The family can be extracted by (port >> 16) & USHORT_MAX |
263 | | * |
264 | | * Currently supported families: |
265 | | * |
266 | | * AF_UNSPEC = 1 |
267 | | * Unspecified, in most cases should be equal to AF_INET or AF_INET6 |
268 | | * |
269 | | * AF_UNIX = 1 |
270 | | * Unix domain socket, will spawn a sockaddr_un which will use the given host as path |
271 | | * e.g: MVM_io_resolve_host_name(tc, "/run/moarvm.sock", 1 << 16) |
272 | | * will spawn an unix domain socket on /run/moarvm.sock |
273 | | * |
274 | | * AF_INET = 2 |
275 | | * IPv4 socket |
276 | | * |
277 | | * AF_INET6 = 10 |
278 | | * IPv6 socket |
279 | | */ |
280 | 0 | struct sockaddr * MVM_io_resolve_host_name(MVMThreadContext *tc, MVMString *host, MVMint64 port) { |
281 | 0 | char *host_cstr = MVM_string_utf8_encode_C_string(tc, host); |
282 | 0 | struct sockaddr *dest; |
283 | 0 | int error; |
284 | 0 | struct addrinfo *result; |
285 | 0 | char port_cstr[8]; |
286 | 0 | unsigned short family = (port >> 16) & USHRT_MAX; |
287 | 0 | struct addrinfo hints; |
288 | 0 |
|
289 | 0 | #ifndef _WIN32 |
290 | 0 | /* AF_UNIX = 1 */ |
291 | 0 | if (family == AF_UNIX) { |
292 | 0 | struct sockaddr_un *result_un = MVM_malloc(sizeof(struct sockaddr_un)); |
293 | 0 |
|
294 | 0 | if (strlen(host_cstr) > 107) { |
295 | 0 | MVM_free(result_un); |
296 | 0 | MVM_free(host_cstr); |
297 | 0 | MVM_exception_throw_adhoc(tc, "Socket path can only be maximal 107 characters long"); |
298 | 0 | } |
299 | 0 |
|
300 | 0 | result_un->sun_family = AF_UNIX; |
301 | 0 | strcpy(result_un->sun_path, host_cstr); |
302 | 0 | MVM_free(host_cstr); |
303 | 0 |
|
304 | 0 | return (struct sockaddr *)result_un; |
305 | 0 | } |
306 | 0 | #endif |
307 | 0 |
|
308 | 0 | hints.ai_family = family; |
309 | 0 | hints.ai_socktype = 0; |
310 | 0 | hints.ai_flags = AI_PASSIVE; |
311 | 0 | hints.ai_protocol = 0; |
312 | 0 | hints.ai_addrlen = 0; |
313 | 0 | hints.ai_addr = NULL; |
314 | 0 | hints.ai_canonname = NULL; |
315 | 0 | hints.ai_next = NULL; |
316 | 0 |
|
317 | 0 | snprintf(port_cstr, 8, "%d", (int)port); |
318 | 0 |
|
319 | 0 | MVM_gc_mark_thread_blocked(tc); |
320 | 0 | error = getaddrinfo(host_cstr, port_cstr, &hints, &result); |
321 | 0 | MVM_gc_mark_thread_unblocked(tc); |
322 | 0 | if (error == 0) { |
323 | 0 | size_t size = get_struct_size_for_family(result->ai_addr->sa_family); |
324 | 0 | MVM_free(host_cstr); |
325 | 0 | dest = MVM_malloc(size); |
326 | 0 | memcpy(dest, result->ai_addr, size); |
327 | 0 | } |
328 | 0 | else { |
329 | 0 | char *waste[] = { host_cstr, NULL }; |
330 | 0 | MVM_exception_throw_adhoc_free(tc, waste, "Failed to resolve host name '%s' with family %d. Error: '%s'", |
331 | 0 | host_cstr, family, gai_strerror(error)); |
332 | 0 | } |
333 | 0 | freeaddrinfo(result); |
334 | 0 |
|
335 | 0 | return dest; |
336 | 0 | } |
337 | | |
338 | | /* Establishes a connection. */ |
339 | 0 | static void socket_connect(MVMThreadContext *tc, MVMOSHandle *h, MVMString *host, MVMint64 port) { |
340 | 0 | MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; |
341 | 0 | unsigned int interval_id; |
342 | 0 |
|
343 | 0 | interval_id = MVM_telemetry_interval_start(tc, "syncsocket connect"); |
344 | 0 | if (!data->handle) { |
345 | 0 | struct sockaddr *dest = MVM_io_resolve_host_name(tc, host, port); |
346 | 0 | int r; |
347 | 0 |
|
348 | 0 | Socket s = socket(dest->sa_family , SOCK_STREAM , 0); |
349 | 0 | if (MVM_IS_SOCKET_ERROR(s)) { |
350 | 0 | MVM_free(dest); |
351 | 0 | MVM_telemetry_interval_stop(tc, interval_id, "syncsocket connect"); |
352 | 0 | throw_error(tc, s, "create socket"); |
353 | 0 | } |
354 | 0 |
|
355 | 0 | do { |
356 | 0 | MVM_gc_mark_thread_blocked(tc); |
357 | 0 | r = connect(s, dest, (socklen_t)get_struct_size_for_family(dest->sa_family)); |
358 | 0 | MVM_gc_mark_thread_unblocked(tc); |
359 | 0 | } while(r == -1 && errno == EINTR); |
360 | 0 | MVM_free(dest); |
361 | 0 | if (MVM_IS_SOCKET_ERROR(r)) { |
362 | 0 | MVM_telemetry_interval_stop(tc, interval_id, "syncsocket connect"); |
363 | 0 | throw_error(tc, s, "connect socket"); |
364 | 0 | } |
365 | 0 |
|
366 | 0 | data->handle = s; |
367 | 0 | } |
368 | 0 | else { |
369 | 0 | MVM_telemetry_interval_stop(tc, interval_id, "syncsocket didn't connect"); |
370 | 0 | MVM_exception_throw_adhoc(tc, "Socket is already bound or connected"); |
371 | 0 | } |
372 | 0 | } |
373 | | |
374 | 0 | static void socket_bind(MVMThreadContext *tc, MVMOSHandle *h, MVMString *host, MVMint64 port, MVMint32 backlog) { |
375 | 0 | MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; |
376 | 0 | if (!data->handle) { |
377 | 0 | struct sockaddr *dest = MVM_io_resolve_host_name(tc, host, port); |
378 | 0 | int r; |
379 | 0 |
|
380 | 0 | Socket s = socket(dest->sa_family , SOCK_STREAM , 0); |
381 | 0 | if (MVM_IS_SOCKET_ERROR(s)) { |
382 | 0 | MVM_free(dest); |
383 | 0 | throw_error(tc, s, "create socket"); |
384 | 0 | } |
385 | 0 |
|
386 | 0 | /* On POSIX, we set the SO_REUSEADDR option, which allows re-use of |
387 | 0 | * a port in TIME_WAIT state (modulo many hair details). Oringinally, |
388 | 0 | * MoarVM used libuv, which does this automatically on non-Windows. |
389 | 0 | * We have tests with bring up a server, then take it down, and then |
390 | 0 | * bring another up on the same port, and we get test failures due |
391 | 0 | * to racing to re-use the port without this. */ |
392 | 0 | #ifndef _WIN32 |
393 | 0 | { |
394 | 0 | int one = 1; |
395 | 0 | setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); |
396 | 0 | } |
397 | 0 | #endif |
398 | 0 |
|
399 | 0 | r = bind(s, dest, (socklen_t)get_struct_size_for_family(dest->sa_family)); |
400 | 0 | MVM_free(dest); |
401 | 0 | if (MVM_IS_SOCKET_ERROR(r)) |
402 | 0 | throw_error(tc, s, "bind socket"); |
403 | 0 |
|
404 | 0 | r = listen(s, (int)backlog); |
405 | 0 | if (MVM_IS_SOCKET_ERROR(r)) |
406 | 0 | throw_error(tc, s, "start listening on socket"); |
407 | 0 |
|
408 | 0 | data->handle = s; |
409 | 0 | } |
410 | 0 | else { |
411 | 0 | MVM_exception_throw_adhoc(tc, "Socket is already bound or connected"); |
412 | 0 | } |
413 | 0 | } |
414 | | |
415 | 0 | MVMint64 socket_getport(MVMThreadContext *tc, MVMOSHandle *h) { |
416 | 0 | MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; |
417 | 0 |
|
418 | 0 | struct sockaddr_storage name; |
419 | 0 | int error; |
420 | 0 | socklen_t len = sizeof(struct sockaddr_storage); |
421 | 0 | MVMint64 port = 0; |
422 | 0 |
|
423 | 0 | error = getsockname(data->handle, (struct sockaddr *) &name, &len); |
424 | 0 |
|
425 | 0 | if (error != 0) |
426 | 0 | MVM_exception_throw_adhoc(tc, "Failed to getsockname: %s", strerror(errno)); |
427 | 0 |
|
428 | 0 | switch (name.ss_family) { |
429 | 0 | case AF_INET6: |
430 | 0 | port = ntohs((*( struct sockaddr_in6 *) &name).sin6_port); |
431 | 0 | break; |
432 | 0 | case AF_INET: |
433 | 0 | port = ntohs((*( struct sockaddr_in *) &name).sin_port); |
434 | 0 | break; |
435 | 0 | } |
436 | 0 |
|
437 | 0 | return port; |
438 | 0 | } |
439 | | |
440 | 0 | static MVMint64 socket_is_tty(MVMThreadContext *tc, MVMOSHandle *h) { |
441 | 0 | MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; |
442 | 0 | return isatty(data->handle); |
443 | 0 | } |
444 | | |
445 | 0 | static MVMint64 socket_handle(MVMThreadContext *tc, MVMOSHandle *h) { |
446 | 0 | MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; |
447 | 0 | return (MVMint64)data->handle; |
448 | 0 | } |
449 | | |
450 | | static MVMObject * socket_accept(MVMThreadContext *tc, MVMOSHandle *h); |
451 | | |
452 | | /* IO ops table, populated with functions. */ |
453 | | static const MVMIOClosable closable = { close_socket }; |
454 | | static const MVMIOSyncReadable sync_readable = { socket_read_bytes, |
455 | | socket_eof }; |
456 | | static const MVMIOSyncWritable sync_writable = { socket_write_bytes, |
457 | | socket_flush, |
458 | | socket_truncate }; |
459 | | static const MVMIOSockety sockety = { socket_connect, |
460 | | socket_bind, |
461 | | socket_accept, |
462 | | socket_getport }; |
463 | | static const MVMIOIntrospection introspection = { socket_is_tty, |
464 | | socket_handle }; |
465 | | |
466 | | static const MVMIOOps op_table = { |
467 | | &closable, |
468 | | &sync_readable, |
469 | | &sync_writable, |
470 | | NULL, |
471 | | NULL, |
472 | | NULL, |
473 | | NULL, |
474 | | &sockety, |
475 | | NULL, |
476 | | NULL, |
477 | | &introspection, |
478 | | NULL, |
479 | | NULL, |
480 | | gc_free |
481 | | }; |
482 | | |
483 | 0 | static MVMObject * socket_accept(MVMThreadContext *tc, MVMOSHandle *h) { |
484 | 0 | MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; |
485 | 0 | Socket s; |
486 | 0 |
|
487 | 0 | unsigned int interval_id = MVM_telemetry_interval_start(tc, "syncsocket accept"); |
488 | 0 | do { |
489 | 0 | MVM_gc_mark_thread_blocked(tc); |
490 | 0 | s = accept(data->handle, NULL, NULL); |
491 | 0 | MVM_gc_mark_thread_unblocked(tc); |
492 | 0 | } while(s == -1 && errno == EINTR); |
493 | 0 | if (MVM_IS_SOCKET_ERROR(s)) { |
494 | 0 | MVM_telemetry_interval_stop(tc, interval_id, "syncsocket accept failed"); |
495 | 0 | throw_error(tc, s, "accept socket connection"); |
496 | 0 | } |
497 | 0 | else { |
498 | 0 | MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, |
499 | 0 | tc->instance->boot_types.BOOTIO); |
500 | 0 | MVMIOSyncSocketData * const data = MVM_calloc(1, sizeof(MVMIOSyncSocketData)); |
501 | 0 | data->handle = s; |
502 | 0 | result->body.ops = &op_table; |
503 | 0 | result->body.data = data; |
504 | 0 | MVM_telemetry_interval_stop(tc, interval_id, "syncsocket accept succeeded"); |
505 | 0 | return (MVMObject *)result; |
506 | 0 | } |
507 | 0 | } |
508 | | |
509 | 0 | MVMObject * MVM_io_socket_create(MVMThreadContext *tc, MVMint64 listen) { |
510 | 0 | MVMOSHandle * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO); |
511 | 0 | MVMIOSyncSocketData * const data = MVM_calloc(1, sizeof(MVMIOSyncSocketData)); |
512 | 0 | result->body.ops = &op_table; |
513 | 0 | result->body.data = data; |
514 | 0 | return (MVMObject *)result; |
515 | 0 | } |
516 | | |
517 | 0 | MVMString * MVM_io_get_hostname(MVMThreadContext *tc) { |
518 | 0 | char hostname[65]; |
519 | 0 | gethostname(hostname, 65); |
520 | 0 | return MVM_string_ascii_decode_nt(tc, tc->instance->VMString, hostname); |
521 | 0 | } |