/home/travis/build/MoarVM/MoarVM/src/io/io.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* Delegatory functions that assert we have a capable handle, then delegate |
4 | | * through the IO table to the correct operation. */ |
5 | | |
6 | 14.2k | static MVMOSHandle * verify_is_handle(MVMThreadContext *tc, MVMObject *oshandle, const char *op) { |
7 | 14.2k | if (REPR(oshandle)->ID != MVM_REPR_ID_MVMOSHandle) |
8 | 0 | MVM_exception_throw_adhoc(tc, "%s requires an object with REPR MVMOSHandle (got %s with REPR %s)", op, MVM_6model_get_debug_name(tc, oshandle), REPR(oshandle)->name); |
9 | 14.2k | if (!IS_CONCRETE(oshandle)) |
10 | 0 | MVM_exception_throw_adhoc(tc, "%s requires a concrete MVMOSHandle, but got a type object", op); |
11 | 14.2k | return (MVMOSHandle *)oshandle; |
12 | 14.2k | } |
13 | | |
14 | 14.2k | static uv_mutex_t * acquire_mutex(MVMThreadContext *tc, MVMOSHandle *handle) { |
15 | 14.2k | uv_mutex_t *mutex = handle->body.mutex; |
16 | 14.2k | MVM_gc_mark_thread_blocked(tc); |
17 | 14.2k | uv_mutex_lock(mutex); |
18 | 14.2k | MVM_gc_mark_thread_unblocked(tc); |
19 | 14.2k | MVM_tc_set_ex_release_mutex(tc, mutex); |
20 | 14.2k | return mutex; |
21 | 14.2k | } |
22 | | |
23 | 14.2k | static void release_mutex(MVMThreadContext *tc, uv_mutex_t *mutex) { |
24 | 14.2k | uv_mutex_unlock(mutex); |
25 | 14.2k | MVM_tc_clear_ex_release_mutex(tc); |
26 | 14.2k | } |
27 | | |
28 | 212 | MVMint64 MVM_io_close(MVMThreadContext *tc, MVMObject *oshandle) { |
29 | 212 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "close"); |
30 | 212 | if (handle->body.ops->closable) { |
31 | 212 | MVMint64 ret; |
32 | 212 | MVMROOT(tc, handle, { |
33 | 212 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
34 | 212 | ret = handle->body.ops->closable->close(tc, handle); |
35 | 212 | release_mutex(tc, mutex); |
36 | 212 | }); |
37 | 212 | return ret; |
38 | 212 | } |
39 | 212 | else |
40 | 0 | MVM_exception_throw_adhoc(tc, "Cannot close this kind of handle"); |
41 | 212 | } |
42 | | |
43 | 1 | MVMint64 MVM_io_is_tty(MVMThreadContext *tc, MVMObject *oshandle) { |
44 | 1 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "istty"); |
45 | 1 | /* We need the extra check on is_tty because it is NULL for pipes. */ |
46 | 1 | if (handle->body.ops->introspection && handle->body.ops->introspection->is_tty) { |
47 | 1 | MVMint64 ret; |
48 | 1 | MVMROOT(tc, handle, { |
49 | 1 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
50 | 1 | ret = handle->body.ops->introspection->is_tty(tc, handle); |
51 | 1 | release_mutex(tc, mutex); |
52 | 1 | }); |
53 | 1 | return ret; |
54 | 1 | } |
55 | 0 | else { |
56 | 0 | return 0; |
57 | 0 | } |
58 | 1 | } |
59 | | |
60 | 0 | MVMint64 MVM_io_fileno(MVMThreadContext *tc, MVMObject *oshandle) { |
61 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "get native descriptor"); |
62 | 0 | if (handle->body.ops->introspection) { |
63 | 0 | MVMint64 ret; |
64 | 0 | MVMROOT(tc, handle, { |
65 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
66 | 0 | ret = handle->body.ops->introspection->native_descriptor(tc, handle); |
67 | 0 | release_mutex(tc, mutex); |
68 | 0 | }); |
69 | 0 | return ret; |
70 | 0 | } |
71 | 0 | else { |
72 | 0 | return -1; |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | 6 | void MVM_io_seek(MVMThreadContext *tc, MVMObject *oshandle, MVMint64 offset, MVMint64 flag) { |
77 | 6 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "seek"); |
78 | 6 | if (handle->body.ops->seekable) { |
79 | 6 | MVMROOT(tc, handle, { |
80 | 6 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
81 | 6 | handle->body.ops->seekable->seek(tc, handle, offset, flag); |
82 | 6 | release_mutex(tc, mutex); |
83 | 6 | }); |
84 | 6 | } |
85 | 6 | else |
86 | 0 | MVM_exception_throw_adhoc(tc, "Cannot seek this kind of handle"); |
87 | 6 | } |
88 | | |
89 | 9 | MVMint64 MVM_io_tell(MVMThreadContext *tc, MVMObject *oshandle) { |
90 | 9 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "tell"); |
91 | 9 | if (handle->body.ops->seekable) { |
92 | 9 | MVMint64 result; |
93 | 9 | MVMROOT(tc, handle, { |
94 | 9 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
95 | 9 | result = handle->body.ops->seekable->tell(tc, handle); |
96 | 9 | release_mutex(tc, mutex); |
97 | 9 | }); |
98 | 9 | return result; |
99 | 9 | } |
100 | 9 | else |
101 | 0 | MVM_exception_throw_adhoc(tc, "Cannot tell this kind of handle"); |
102 | 9 | } |
103 | | |
104 | 387 | void MVM_io_read_bytes(MVMThreadContext *tc, MVMObject *oshandle, MVMObject *result, MVMint64 length) { |
105 | 387 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "read bytes"); |
106 | 387 | MVMint64 bytes_read; |
107 | 387 | char *buf; |
108 | 387 | |
109 | 387 | /* Ensure the target is in the correct form. */ |
110 | 387 | if (!IS_CONCRETE(result) || REPR(result)->ID != MVM_REPR_ID_VMArray) |
111 | 0 | MVM_exception_throw_adhoc(tc, "read_fhb requires a native array to write to"); |
112 | 387 | if (((MVMArrayREPRData *)STABLE(result)->REPR_data)->slot_type != MVM_ARRAY_U8 |
113 | 2 | && ((MVMArrayREPRData *)STABLE(result)->REPR_data)->slot_type != MVM_ARRAY_I8) |
114 | 0 | MVM_exception_throw_adhoc(tc, "read_fhb requires a native array of uint8 or int8"); |
115 | 387 | |
116 | 387 | if (length < 1) |
117 | 0 | MVM_exception_throw_adhoc(tc, "Out of range: attempted to read %"PRId64" bytes from filehandle", length); |
118 | 387 | |
119 | 387 | if (handle->body.ops->sync_readable) { |
120 | 387 | MVMROOT2(tc, handle, result, { |
121 | 387 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
122 | 387 | bytes_read = handle->body.ops->sync_readable->read_bytes(tc, handle, &buf, length); |
123 | 387 | release_mutex(tc, mutex); |
124 | 387 | }); |
125 | 387 | } |
126 | 387 | else |
127 | 0 | MVM_exception_throw_adhoc(tc, "Cannot read characters from this kind of handle"); |
128 | 387 | |
129 | 387 | /* Stash the data in the VMArray. */ |
130 | 387 | ((MVMArray *)result)->body.slots.i8 = (MVMint8 *)buf; |
131 | 387 | ((MVMArray *)result)->body.start = 0; |
132 | 387 | ((MVMArray *)result)->body.ssize = bytes_read; |
133 | 387 | ((MVMArray *)result)->body.elems = bytes_read; |
134 | 387 | } |
135 | | |
136 | 12.9k | void MVM_io_write_bytes(MVMThreadContext *tc, MVMObject *oshandle, MVMObject *buffer) { |
137 | 12.9k | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "write bytes"); |
138 | 12.9k | char *output; |
139 | 12.9k | MVMint64 output_size; |
140 | 12.9k | |
141 | 12.9k | /* Ensure the target is in the correct form. */ |
142 | 12.9k | if (!IS_CONCRETE(buffer) || REPR(buffer)->ID != MVM_REPR_ID_VMArray) |
143 | 0 | MVM_exception_throw_adhoc(tc, "write_fhb requires a native array to read from"); |
144 | 12.9k | if (((MVMArrayREPRData *)STABLE(buffer)->REPR_data)->slot_type != MVM_ARRAY_U8 |
145 | 0 | && ((MVMArrayREPRData *)STABLE(buffer)->REPR_data)->slot_type != MVM_ARRAY_I8) |
146 | 0 | MVM_exception_throw_adhoc(tc, "write_fhb requires a native array of uint8 or int8"); |
147 | 12.9k | |
148 | 12.9k | output = (char *)(((MVMArray *)buffer)->body.slots.i8 + ((MVMArray *)buffer)->body.start); |
149 | 12.9k | output_size = ((MVMArray *)buffer)->body.elems; |
150 | 12.9k | |
151 | 12.9k | if (handle->body.ops->sync_writable) { |
152 | 12.9k | MVMROOT(tc, handle, { |
153 | 12.9k | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
154 | 12.9k | handle->body.ops->sync_writable->write_bytes(tc, handle, output, output_size); |
155 | 12.9k | release_mutex(tc, mutex); |
156 | 12.9k | }); |
157 | 12.9k | } |
158 | 12.9k | else |
159 | 0 | MVM_exception_throw_adhoc(tc, "Cannot write bytes to this kind of handle"); |
160 | 12.9k | } |
161 | | |
162 | | void MVM_io_write_bytes_c(MVMThreadContext *tc, MVMObject *oshandle, char *output, |
163 | 316 | MVMuint64 output_size) { |
164 | 316 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "write bytes"); |
165 | 316 | if (handle->body.ops->sync_writable) { |
166 | 316 | MVMROOT(tc, handle, { |
167 | 316 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
168 | 316 | handle->body.ops->sync_writable->write_bytes(tc, handle, output, output_size); |
169 | 316 | release_mutex(tc, mutex); |
170 | 316 | }); |
171 | 316 | } |
172 | 316 | else |
173 | 0 | MVM_exception_throw_adhoc(tc, "Cannot write bytes to this kind of handle"); |
174 | 316 | } |
175 | | |
176 | | MVMObject * MVM_io_read_bytes_async(MVMThreadContext *tc, MVMObject *oshandle, MVMObject *queue, |
177 | 0 | MVMObject *schedulee, MVMObject *buf_type, MVMObject *async_type) { |
178 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "read bytes asynchronously"); |
179 | 0 | if (handle->body.ops->async_readable) { |
180 | 0 | MVMObject *result; |
181 | 0 | MVMROOT5(tc, queue, schedulee, buf_type, async_type, handle, { |
182 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
183 | 0 | result = (MVMObject *)handle->body.ops->async_readable->read_bytes(tc, |
184 | 0 | handle, queue, schedulee, buf_type, async_type); |
185 | 0 | release_mutex(tc, mutex); |
186 | 0 | }); |
187 | 0 | return result; |
188 | 0 | } |
189 | 0 | else |
190 | 0 | MVM_exception_throw_adhoc(tc, "Cannot read bytes asynchronously from this kind of handle"); |
191 | 0 | } |
192 | | |
193 | | MVMObject * MVM_io_write_bytes_async(MVMThreadContext *tc, MVMObject *oshandle, MVMObject *queue, |
194 | 0 | MVMObject *schedulee, MVMObject *buffer, MVMObject *async_type) { |
195 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "write buffer asynchronously"); |
196 | 0 | if (buffer == NULL) |
197 | 0 | MVM_exception_throw_adhoc(tc, "Failed to write to filehandle: NULL buffer given"); |
198 | 0 | if (handle->body.ops->async_writable) { |
199 | 0 | MVMObject *result; |
200 | 0 | MVMROOT5(tc, queue, schedulee, buffer, async_type, handle, { |
201 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
202 | 0 | result = (MVMObject *)handle->body.ops->async_writable->write_bytes(tc, |
203 | 0 | handle, queue, schedulee, buffer, async_type); |
204 | 0 | release_mutex(tc, mutex); |
205 | 0 | }); |
206 | 0 | return result; |
207 | 0 | } |
208 | 0 | else |
209 | 0 | MVM_exception_throw_adhoc(tc, "Cannot write bytes asynchronously to this kind of handle"); |
210 | 0 | } |
211 | | |
212 | | MVMObject * MVM_io_write_bytes_to_async(MVMThreadContext *tc, MVMObject *oshandle, MVMObject *queue, |
213 | | MVMObject *schedulee, MVMObject *buffer, MVMObject *async_type, |
214 | 0 | MVMString *host, MVMint64 port) { |
215 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "write buffer asynchronously to destination"); |
216 | 0 | if (buffer == NULL) |
217 | 0 | MVM_exception_throw_adhoc(tc, "Failed to write to filehandle: NULL buffer given"); |
218 | 0 | if (handle->body.ops->async_writable_to) { |
219 | 0 | MVMObject *result; |
220 | 0 | MVMROOT6(tc, host, queue, schedulee, buffer, async_type, handle, { |
221 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
222 | 0 | result = (MVMObject *)handle->body.ops->async_writable_to->write_bytes_to(tc, |
223 | 0 | handle, queue, schedulee, buffer, async_type, host, port); |
224 | 0 | release_mutex(tc, mutex); |
225 | 0 | }); |
226 | 0 | return result; |
227 | 0 | } |
228 | 0 | else |
229 | 0 | MVM_exception_throw_adhoc(tc, "Cannot write bytes to a destination asynchronously to this kind of handle"); |
230 | 0 | } |
231 | | |
232 | 16 | MVMint64 MVM_io_eof(MVMThreadContext *tc, MVMObject *oshandle) { |
233 | 16 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "eof"); |
234 | 16 | if (handle->body.ops->sync_readable) { |
235 | 16 | MVMint64 result; |
236 | 16 | MVMROOT(tc, handle, { |
237 | 16 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
238 | 16 | result = handle->body.ops->sync_readable->eof(tc, handle); |
239 | 16 | release_mutex(tc, mutex); |
240 | 16 | }); |
241 | 16 | return result; |
242 | 16 | } |
243 | 16 | else |
244 | 0 | MVM_exception_throw_adhoc(tc, "Cannot eof this kind of handle"); |
245 | 16 | } |
246 | | |
247 | 0 | MVMint64 MVM_io_lock(MVMThreadContext *tc, MVMObject *oshandle, MVMint64 flag) { |
248 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "lock"); |
249 | 0 | if (handle->body.ops->lockable) { |
250 | 0 | MVMint64 result; |
251 | 0 | MVMROOT(tc, handle, { |
252 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
253 | 0 | result = handle->body.ops->lockable->lock(tc, handle, flag); |
254 | 0 | release_mutex(tc, mutex); |
255 | 0 | }); |
256 | 0 | return result; |
257 | 0 | } |
258 | 0 | else |
259 | 0 | MVM_exception_throw_adhoc(tc, "Cannot lock this kind of handle"); |
260 | 0 | } |
261 | | |
262 | 0 | void MVM_io_unlock(MVMThreadContext *tc, MVMObject *oshandle) { |
263 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "unlock"); |
264 | 0 | if (handle->body.ops->lockable) { |
265 | 0 | MVMROOT(tc, handle, { |
266 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
267 | 0 | handle->body.ops->lockable->unlock(tc, handle); |
268 | 0 | release_mutex(tc, mutex); |
269 | 0 | }); |
270 | 0 | } |
271 | 0 | else |
272 | 0 | MVM_exception_throw_adhoc(tc, "Cannot unlock this kind of handle"); |
273 | 0 | } |
274 | | |
275 | 288 | void MVM_io_flush(MVMThreadContext *tc, MVMObject *oshandle, MVMint32 sync) { |
276 | 288 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "flush"); |
277 | 288 | if (handle->body.ops->sync_writable) { |
278 | 288 | MVMROOT(tc, handle, { |
279 | 288 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
280 | 288 | handle->body.ops->sync_writable->flush(tc, handle, sync); |
281 | 288 | release_mutex(tc, mutex); |
282 | 288 | }); |
283 | 288 | } |
284 | 288 | else |
285 | 0 | MVM_exception_throw_adhoc(tc, "Cannot flush this kind of handle"); |
286 | 288 | } |
287 | | |
288 | 0 | void MVM_io_truncate(MVMThreadContext *tc, MVMObject *oshandle, MVMint64 offset) { |
289 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "truncate"); |
290 | 0 | if (handle->body.ops->sync_writable) { |
291 | 0 | MVMROOT(tc, handle, { |
292 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
293 | 0 | handle->body.ops->sync_writable->truncate(tc, handle, offset); |
294 | 0 | release_mutex(tc, mutex); |
295 | 0 | }); |
296 | 0 | } |
297 | 0 | else |
298 | 0 | MVM_exception_throw_adhoc(tc, "Cannot truncate this kind of handle"); |
299 | 0 | } |
300 | | |
301 | 0 | void MVM_io_connect(MVMThreadContext *tc, MVMObject *oshandle, MVMString *host, MVMint64 port) { |
302 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "connect"); |
303 | 0 | if (handle->body.ops->sockety) { |
304 | 0 | MVMROOT2(tc, host, handle, { |
305 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
306 | 0 | handle->body.ops->sockety->connect(tc, handle, host, port); |
307 | 0 | release_mutex(tc, mutex); |
308 | 0 | }); |
309 | 0 | } |
310 | 0 | else |
311 | 0 | MVM_exception_throw_adhoc(tc, "Cannot connect this kind of handle"); |
312 | 0 | } |
313 | | |
314 | 0 | void MVM_io_bind(MVMThreadContext *tc, MVMObject *oshandle, MVMString *host, MVMint64 port, MVMint32 backlog) { |
315 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "bind"); |
316 | 0 | if (handle->body.ops->sockety) { |
317 | 0 | MVMROOT2(tc, host, handle, { |
318 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
319 | 0 | handle->body.ops->sockety->bind(tc, handle, host, port, backlog); |
320 | 0 | release_mutex(tc, mutex); |
321 | 0 | }); |
322 | 0 | } |
323 | 0 | else |
324 | 0 | MVM_exception_throw_adhoc(tc, "Cannot bind this kind of handle"); |
325 | 0 | } |
326 | | |
327 | 0 | MVMint64 MVM_io_getport(MVMThreadContext *tc, MVMObject *oshandle) { |
328 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "getport"); |
329 | 0 | if (handle->body.ops->sockety) { |
330 | 0 | MVMint64 result; |
331 | 0 | MVMROOT(tc, handle, { |
332 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
333 | 0 | result = handle->body.ops->sockety->getport(tc, handle); |
334 | 0 | release_mutex(tc, mutex); |
335 | 0 | }); |
336 | 0 | return result; |
337 | 0 | } |
338 | 0 | else |
339 | 0 | MVM_exception_throw_adhoc(tc, "Cannot getport for this kind of handle"); |
340 | 0 | } |
341 | | |
342 | 0 | MVMObject * MVM_io_accept(MVMThreadContext *tc, MVMObject *oshandle) { |
343 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "accept"); |
344 | 0 | if (handle->body.ops->sockety) { |
345 | 0 | MVMObject *result; |
346 | 0 | MVMROOT(tc, handle, { |
347 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
348 | 0 | result = handle->body.ops->sockety->accept(tc, handle); |
349 | 0 | release_mutex(tc, mutex); |
350 | 0 | }); |
351 | 0 | return result; |
352 | 0 | } |
353 | 0 | else |
354 | 0 | MVM_exception_throw_adhoc(tc, "Cannot accept this kind of handle"); |
355 | 0 | } |
356 | | |
357 | 0 | void MVM_io_set_buffer_size(MVMThreadContext *tc, MVMObject *oshandle, MVMint64 size) { |
358 | 0 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "set buffer size"); |
359 | 0 | if (handle->body.ops->set_buffer_size) { |
360 | 0 | MVMROOT(tc, handle, { |
361 | 0 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
362 | 0 | handle->body.ops->set_buffer_size(tc, handle, size); |
363 | 0 | release_mutex(tc, mutex); |
364 | 0 | }); |
365 | 0 | } |
366 | 0 | else |
367 | 0 | MVM_exception_throw_adhoc(tc, "Cannot set buffer size on this kind of handle"); |
368 | 0 | } |
369 | | |
370 | 14 | MVMObject * MVM_io_get_async_task_handle(MVMThreadContext *tc, MVMObject *oshandle) { |
371 | 14 | MVMOSHandle *handle = verify_is_handle(tc, oshandle, "get async task handle"); |
372 | 14 | if (handle->body.ops->get_async_task_handle) { |
373 | 14 | MVMObject *ath; |
374 | 14 | MVMROOT(tc, handle, { |
375 | 14 | uv_mutex_t *mutex = acquire_mutex(tc, handle); |
376 | 14 | ath = handle->body.ops->get_async_task_handle(tc, handle); |
377 | 14 | release_mutex(tc, mutex); |
378 | 14 | }); |
379 | 14 | return ath; |
380 | 14 | } |
381 | 14 | else |
382 | 0 | MVM_exception_throw_adhoc(tc, "Cannot get async task handle from this kind of handle"); |
383 | 14 | } |
384 | | |
385 | 144 | void MVM_io_flush_standard_handles(MVMThreadContext *tc) { |
386 | 144 | MVM_io_flush(tc, tc->instance->stdout_handle, 0); |
387 | 144 | MVM_io_flush(tc, tc->instance->stderr_handle, 0); |
388 | 144 | } |