io_splice 3.1.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/GIT-VERSION-GEN +1 -1
- data/ext/io_splice/io_splice_ext.c +173 -83
- data/lib/io/splice.rb +27 -35
- data/lib/io/splice/mri_18.rb +34 -0
- data/test/test_copy_stream.rb +340 -0
- data/test/test_io_splice.rb +6 -3
- data/test/test_io_splice_eintr.rb +35 -0
- data/test/test_io_splice_in_full.rb +39 -0
- metadata +14 -6
data/GIT-VERSION-GEN
CHANGED
@@ -13,6 +13,7 @@
|
|
13
13
|
#include <sys/utsname.h>
|
14
14
|
|
15
15
|
static VALUE sym_EAGAIN;
|
16
|
+
#define WAITALL 0x4000000
|
16
17
|
|
17
18
|
#ifndef F_LINUX_SPECIFIC_BASE
|
18
19
|
# define F_LINUX_SPECIFIC_BASE 1024
|
@@ -37,13 +38,26 @@ static VALUE sym_EAGAIN;
|
|
37
38
|
# endif
|
38
39
|
#endif
|
39
40
|
|
41
|
+
#ifndef SSIZET2NUM
|
42
|
+
# define SSIZET2NUM(x) LONG2NUM(x)
|
43
|
+
#endif
|
44
|
+
#ifndef NUM2SSIZET
|
45
|
+
# define NUM2SSIZET(x) NUM2LONG(x)
|
46
|
+
#endif
|
47
|
+
#ifndef SIZET2NUM
|
48
|
+
# define SIZET2NUM(x) ULONG2NUM(x)
|
49
|
+
#endif
|
50
|
+
#ifndef NUM2SIZET
|
51
|
+
# define NUM2SIZET(x) NUM2ULONG(x)
|
52
|
+
#endif
|
53
|
+
|
40
54
|
static int my_fileno(VALUE io)
|
41
55
|
{
|
42
56
|
rb_io_t *fptr;
|
43
57
|
|
44
58
|
for (;;) {
|
45
59
|
switch (TYPE(io)) {
|
46
|
-
case T_FIXNUM: return
|
60
|
+
case T_FIXNUM: return FIX2INT(io);
|
47
61
|
case T_FILE: {
|
48
62
|
GetOpenFile(io, fptr);
|
49
63
|
return FPTR_TO_FD(fptr);
|
@@ -54,6 +68,14 @@ static int my_fileno(VALUE io)
|
|
54
68
|
}
|
55
69
|
}
|
56
70
|
}
|
71
|
+
|
72
|
+
static int check_fileno(VALUE io)
|
73
|
+
{
|
74
|
+
int saved_errno = errno;
|
75
|
+
int fd = my_fileno(io);
|
76
|
+
errno = saved_errno;
|
77
|
+
return fd;
|
78
|
+
}
|
57
79
|
#ifndef HAVE_RB_THREAD_BLOCKING_REGION
|
58
80
|
/* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
|
59
81
|
# include <rubysig.h>
|
@@ -95,21 +117,6 @@ static VALUE io_run(rb_blocking_function_t *fn, void *data)
|
|
95
117
|
return rb_thread_blocking_region(fn, data, RUBY_UBF_IO, 0);
|
96
118
|
}
|
97
119
|
|
98
|
-
/*
|
99
|
-
* Releases GVL only iff blocking I/O is used.
|
100
|
-
* Only use this if all file descriptors in data are pipes.
|
101
|
-
* We'll trust programmers who use non-blocking I/O explicitly to
|
102
|
-
* want the fastest possible performance without resorting to threads,
|
103
|
-
* so releasing and them immediately reacquiring the GVL would be
|
104
|
-
* a waste of time.
|
105
|
-
*/
|
106
|
-
static VALUE nb_io_run(rb_blocking_function_t *fn, void *data, unsigned flags)
|
107
|
-
{
|
108
|
-
if (flags & SPLICE_F_NONBLOCK)
|
109
|
-
return fn(data);
|
110
|
-
return io_run(fn, data);
|
111
|
-
}
|
112
|
-
|
113
120
|
struct splice_args {
|
114
121
|
int fd_in;
|
115
122
|
off_t *off_in;
|
@@ -127,32 +134,65 @@ static VALUE nogvl_splice(void *ptr)
|
|
127
134
|
a->len, a->flags);
|
128
135
|
}
|
129
136
|
|
130
|
-
static
|
137
|
+
static ssize_t do_splice(int argc, VALUE *argv, unsigned dflags)
|
131
138
|
{
|
132
|
-
off_t i, o;
|
133
|
-
VALUE
|
139
|
+
off_t i = 0, o = 0;
|
140
|
+
VALUE io_in, off_in, io_out, off_out, len, flags;
|
134
141
|
struct splice_args a;
|
142
|
+
ssize_t bytes;
|
143
|
+
ssize_t total = 0;
|
144
|
+
unsigned waitall;
|
135
145
|
|
136
146
|
rb_scan_args(argc, argv, "51",
|
137
|
-
&
|
147
|
+
&io_in, &off_in, &io_out, &off_out, &len, &flags);
|
138
148
|
|
139
149
|
a.off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i);
|
140
150
|
a.off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o);
|
141
|
-
a.
|
142
|
-
a.fd_out = my_fileno(fd_out);
|
143
|
-
a.len = (size_t)NUM2ULONG(len);
|
151
|
+
a.len = NUM2SIZET(len);
|
144
152
|
a.flags = NIL_P(flags) ? dflags : NUM2UINT(flags) | dflags;
|
153
|
+
waitall = a.flags & WAITALL;
|
154
|
+
if (waitall)
|
155
|
+
a.flags ^= WAITALL;
|
145
156
|
|
146
|
-
|
157
|
+
for (;;) {
|
158
|
+
a.fd_in = check_fileno(io_in);
|
159
|
+
a.fd_out = check_fileno(io_out);
|
160
|
+
bytes = (ssize_t)io_run(nogvl_splice, &a);
|
161
|
+
if (bytes == -1) {
|
162
|
+
if (errno == EINTR)
|
163
|
+
continue;
|
164
|
+
if (waitall && errno == EAGAIN) {
|
165
|
+
rb_io_wait_readable(check_fileno(io_in));
|
166
|
+
errno = EAGAIN;
|
167
|
+
rb_io_wait_writable(check_fileno(io_out));
|
168
|
+
continue;
|
169
|
+
}
|
170
|
+
if (total > 0)
|
171
|
+
return total;
|
172
|
+
return bytes;
|
173
|
+
} else if (bytes == 0) {
|
174
|
+
break;
|
175
|
+
} else if (waitall) {
|
176
|
+
total += bytes;
|
177
|
+
if ((a.len -= bytes) == 0)
|
178
|
+
return total;
|
179
|
+
i += bytes;
|
180
|
+
o += bytes;
|
181
|
+
} else {
|
182
|
+
return bytes;
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
return total;
|
147
187
|
}
|
148
188
|
|
149
189
|
/*
|
150
190
|
* call-seq:
|
151
|
-
* IO.splice(
|
152
|
-
* IO.splice(
|
191
|
+
* IO.splice(io_in, off_in, io_out, off_out, len) => integer
|
192
|
+
* IO.splice(io_in, off_in, io_out, off_out, len, flags) => integer
|
153
193
|
*
|
154
|
-
* Splice +len+ bytes from/to a pipe. Either +
|
155
|
-
* MUST be a pipe. +
|
194
|
+
* Splice +len+ bytes from/to a pipe. Either +io_in+ or +io_out+
|
195
|
+
* MUST be a pipe. +io_in+ and +io_out+ may BOTH be pipes as of
|
156
196
|
* Linux 2.6.31 or later.
|
157
197
|
*
|
158
198
|
* +off_in+ and +off_out+ if non-nil may be used to
|
@@ -161,22 +201,18 @@ static long do_splice(int argc, VALUE *argv, unsigned dflags)
|
|
161
201
|
* +flags+ defaults to zero if unspecified.
|
162
202
|
* +flags+ may be a bitmask of the following flags:
|
163
203
|
*
|
164
|
-
* IO::Splice::F_MOVE
|
204
|
+
* * IO::Splice::F_MOVE
|
205
|
+
* * IO::Splice::F_NONBLOCK
|
206
|
+
* * IO::Splice::F_MORE
|
207
|
+
* * IO::Splice::WAITALL
|
165
208
|
*
|
166
209
|
* Returns the number of bytes spliced.
|
167
|
-
* Raises EOFError when +
|
210
|
+
* Raises EOFError when +io_in+ has reached end of file.
|
168
211
|
* Raises Errno::EAGAIN if the IO::Splice::F_NONBLOCK flag is set
|
169
212
|
* and the pipe has no data to read from or space to write to. May
|
170
213
|
* also raise Errno::EAGAIN if the non-pipe descriptor has no data
|
171
214
|
* to read from or space to write to.
|
172
215
|
*
|
173
|
-
* rd, wr = (pipe = IO.pipe).map { |io| io.fileno }
|
174
|
-
* src_io, dst_io = File.open("/path/to/src"), File.open("/path/to/dst")
|
175
|
-
* src, dst = src_io.fileno, dst_io.fileno
|
176
|
-
*
|
177
|
-
* nr = IO.splice(src, nil, wr, nil, IO::Splice::PIPE_CAPA, 0)
|
178
|
-
* IO.splice(rd, nil, dst, nil, nr, 0)
|
179
|
-
*
|
180
216
|
* As splice never exposes buffers to userspace, it will not take
|
181
217
|
* into account userspace buffering done by Ruby or stdio. It is
|
182
218
|
* also not subject to encoding/decoding filters under Ruby 1.9.
|
@@ -190,19 +226,19 @@ static long do_splice(int argc, VALUE *argv, unsigned dflags)
|
|
190
226
|
*/
|
191
227
|
static VALUE my_splice(int argc, VALUE *argv, VALUE self)
|
192
228
|
{
|
193
|
-
|
229
|
+
ssize_t n = do_splice(argc, argv, 0);
|
194
230
|
|
195
231
|
if (n == 0)
|
196
232
|
rb_eof_error();
|
197
|
-
if (n
|
233
|
+
if (n == -1)
|
198
234
|
rb_sys_fail("splice");
|
199
|
-
return
|
235
|
+
return SSIZET2NUM(n);
|
200
236
|
}
|
201
237
|
|
202
238
|
/*
|
203
239
|
* call-seq:
|
204
|
-
* IO.trysplice(
|
205
|
-
* IO.trysplice(
|
240
|
+
* IO.trysplice(io_in, off_in, io_out, off_out, len) => integer
|
241
|
+
* IO.trysplice(io_in, off_in, io_out, off_out, len, flags) => integer
|
206
242
|
*
|
207
243
|
* Exactly like IO.splice, except +:EAGAIN+ is returned when either
|
208
244
|
* the read or write end would block instead of raising Errno::EAGAIN.
|
@@ -214,16 +250,16 @@ static VALUE my_splice(int argc, VALUE *argv, VALUE self)
|
|
214
250
|
*/
|
215
251
|
static VALUE trysplice(int argc, VALUE *argv, VALUE self)
|
216
252
|
{
|
217
|
-
|
253
|
+
ssize_t n = do_splice(argc, argv, SPLICE_F_NONBLOCK);
|
218
254
|
|
219
255
|
if (n == 0)
|
220
256
|
return Qnil;
|
221
|
-
if (n
|
257
|
+
if (n == -1) {
|
222
258
|
if (errno == EAGAIN)
|
223
259
|
return sym_EAGAIN;
|
224
260
|
rb_sys_fail("splice");
|
225
261
|
}
|
226
|
-
return
|
262
|
+
return SSIZET2NUM(n);
|
227
263
|
}
|
228
264
|
|
229
265
|
struct tee_args {
|
@@ -241,35 +277,69 @@ static VALUE nogvl_tee(void *ptr)
|
|
241
277
|
return (VALUE)tee(a->fd_in, a->fd_out, a->len, a->flags);
|
242
278
|
}
|
243
279
|
|
244
|
-
static
|
280
|
+
static ssize_t do_tee(int argc, VALUE *argv, unsigned dflags)
|
245
281
|
{
|
246
|
-
VALUE
|
282
|
+
VALUE io_in, io_out, len, flags;
|
247
283
|
struct tee_args a;
|
284
|
+
ssize_t bytes;
|
285
|
+
ssize_t total = 0;
|
286
|
+
unsigned waitall;
|
248
287
|
|
249
|
-
rb_scan_args(argc, argv, "31", &
|
250
|
-
a.
|
251
|
-
a.fd_out = my_fileno(fd_out);
|
252
|
-
a.len = (size_t)NUM2ULONG(len);
|
288
|
+
rb_scan_args(argc, argv, "31", &io_in, &io_out, &len, &flags);
|
289
|
+
a.len = (size_t)NUM2SIZET(len);
|
253
290
|
a.flags = NIL_P(flags) ? dflags : NUM2UINT(flags) | dflags;
|
291
|
+
waitall = a.flags & WAITALL;
|
292
|
+
if (waitall)
|
293
|
+
a.flags ^= WAITALL;
|
294
|
+
|
295
|
+
for (;;) {
|
296
|
+
a.fd_in = check_fileno(io_in);
|
297
|
+
a.fd_out = check_fileno(io_out);
|
298
|
+
bytes = (ssize_t)io_run(nogvl_tee, &a);
|
299
|
+
if (bytes == -1) {
|
300
|
+
if (errno == EINTR)
|
301
|
+
continue;
|
302
|
+
if (waitall && errno == EAGAIN) {
|
303
|
+
rb_io_wait_readable(check_fileno(io_in));
|
304
|
+
errno = EAGAIN;
|
305
|
+
rb_io_wait_writable(check_fileno(io_out));
|
306
|
+
continue;
|
307
|
+
}
|
308
|
+
if (total > 0)
|
309
|
+
return total;
|
310
|
+
return bytes;
|
311
|
+
} else if (bytes == 0) {
|
312
|
+
break;
|
313
|
+
} else if (waitall) {
|
314
|
+
total += bytes;
|
315
|
+
if ((a.len -= bytes) == 0)
|
316
|
+
return total;
|
317
|
+
} else {
|
318
|
+
return bytes;
|
319
|
+
}
|
320
|
+
}
|
254
321
|
|
255
|
-
return
|
322
|
+
return total;
|
256
323
|
}
|
257
324
|
|
258
325
|
/*
|
259
326
|
* call-seq:
|
260
|
-
* IO.tee(
|
261
|
-
* IO.tee(
|
327
|
+
* IO.tee(io_in, io_out, len) => integer
|
328
|
+
* IO.tee(io_in, io_out, len, flags) => integer
|
262
329
|
*
|
263
|
-
* Copies up to +len+ bytes of data from +
|
264
|
-
* and +
|
330
|
+
* Copies up to +len+ bytes of data from +io_in+ to +io_out+. +io_in+
|
331
|
+
* and +io_out+ must both refer to pipe descriptors. +io_in+ and +io_out+
|
265
332
|
* may not be endpoints of the same pipe.
|
266
333
|
*
|
267
|
-
* +flags+ may be zero (the default) or
|
334
|
+
* +flags+ may be zero (the default) or a combination of:
|
335
|
+
* * IO::Splice::F_NONBLOCK
|
336
|
+
* * IO::Splice::WAITALL
|
337
|
+
*
|
268
338
|
* Other IO::Splice flags are currently unimplemented or have no effect.
|
269
339
|
*
|
270
340
|
* Returns the number of bytes duplicated if successful.
|
271
|
-
* Raises EOFError when +
|
272
|
-
* Raises Errno::EAGAIN when +
|
341
|
+
* Raises EOFError when +io_in+ is closed and emptied.
|
342
|
+
* Raises Errno::EAGAIN when +io_in+ is empty and/or +io_out+ is full
|
273
343
|
* and +flags+ contains IO::Splice::F_NONBLOCK
|
274
344
|
*
|
275
345
|
* Consider using IO.trytee if you are using IO::Splice::F_NONBLOCK
|
@@ -280,20 +350,20 @@ static long do_tee(int argc, VALUE *argv, unsigned dflags)
|
|
280
350
|
*/
|
281
351
|
static VALUE my_tee(int argc, VALUE *argv, VALUE self)
|
282
352
|
{
|
283
|
-
|
353
|
+
ssize_t n = do_tee(argc, argv, 0);
|
284
354
|
|
285
355
|
if (n == 0)
|
286
356
|
rb_eof_error();
|
287
|
-
if (n
|
357
|
+
if (n == -1)
|
288
358
|
rb_sys_fail("tee");
|
289
359
|
|
290
|
-
return
|
360
|
+
return SSIZET2NUM(n);
|
291
361
|
}
|
292
362
|
|
293
363
|
/*
|
294
364
|
* call-seq:
|
295
|
-
* IO.trytee(
|
296
|
-
* IO.trytee(
|
365
|
+
* IO.trytee(io_in, io_out, len) => integer
|
366
|
+
* IO.trytee(io_in, io_out, len, flags) => integer
|
297
367
|
*
|
298
368
|
* Exactly like IO.tee, except +:EAGAIN+ is returned when either
|
299
369
|
* the read or write end would block instead of raising Errno::EAGAIN.
|
@@ -305,17 +375,17 @@ static VALUE my_tee(int argc, VALUE *argv, VALUE self)
|
|
305
375
|
*/
|
306
376
|
static VALUE trytee(int argc, VALUE *argv, VALUE self)
|
307
377
|
{
|
308
|
-
|
378
|
+
ssize_t n = do_tee(argc, argv, SPLICE_F_NONBLOCK);
|
309
379
|
|
310
380
|
if (n == 0)
|
311
381
|
return Qnil;
|
312
|
-
if (n
|
382
|
+
if (n == -1) {
|
313
383
|
if (errno == EAGAIN)
|
314
384
|
return sym_EAGAIN;
|
315
385
|
rb_sys_fail("tee");
|
316
386
|
}
|
317
387
|
|
318
|
-
return
|
388
|
+
return SSIZET2NUM(n);
|
319
389
|
}
|
320
390
|
|
321
391
|
struct vmsplice_args {
|
@@ -385,13 +455,13 @@ static void advance_vmsplice_args(struct vmsplice_args *a, long n)
|
|
385
455
|
|
386
456
|
/*
|
387
457
|
* call-seq:
|
388
|
-
* IO.vmsplice(
|
389
|
-
* IO.vmsplice(
|
390
|
-
* IO.vmsplice(
|
391
|
-
* IO.vmsplice(
|
458
|
+
* IO.vmsplice(io, string_array) => integer
|
459
|
+
* IO.vmsplice(io, string_array, flags) => integer
|
460
|
+
* IO.vmsplice(io, string) => integer
|
461
|
+
* IO.vmsplice(io, string, flags) => integer
|
392
462
|
*
|
393
|
-
* Transfers an array of strings into the pipe descriptor given by
|
394
|
-
* +
|
463
|
+
* Transfers an array of strings into the pipe descriptor given by io.
|
464
|
+
* +io+ must be the writable end of a pipe.
|
395
465
|
*
|
396
466
|
* This may allow the kernel to avoid data copies in some cases.
|
397
467
|
* but is (probably) of limited usefulness in Ruby. If you have
|
@@ -408,12 +478,12 @@ static void advance_vmsplice_args(struct vmsplice_args *a, long n)
|
|
408
478
|
*/
|
409
479
|
static VALUE my_vmsplice(int argc, VALUE * argv, VALUE self)
|
410
480
|
{
|
411
|
-
|
481
|
+
ssize_t rv = 0;
|
412
482
|
ssize_t left;
|
413
483
|
struct vmsplice_args a;
|
414
|
-
VALUE
|
484
|
+
VALUE io, data, flags;
|
415
485
|
|
416
|
-
rb_scan_args(argc, argv, "21", &
|
486
|
+
rb_scan_args(argc, argv, "21", &io, &data, &flags);
|
417
487
|
|
418
488
|
switch (TYPE(data)) {
|
419
489
|
case T_STRING: {
|
@@ -433,18 +503,21 @@ static VALUE my_vmsplice(int argc, VALUE * argv, VALUE self)
|
|
433
503
|
"(expected a String or Array of strings)",
|
434
504
|
rb_obj_classname(data));
|
435
505
|
}
|
436
|
-
a.fd = my_fileno(
|
506
|
+
a.fd = my_fileno(io);
|
437
507
|
a.flags = NIL_P(flags) ? 0 : NUM2UINT(flags);
|
438
508
|
|
439
509
|
for (;;) {
|
440
|
-
|
510
|
+
ssize_t n = (ssize_t)io_run(nogvl_vmsplice, &a);
|
441
511
|
|
442
|
-
if (n
|
512
|
+
if (n == -1) {
|
443
513
|
if (errno == EAGAIN) {
|
444
|
-
if (a.flags & SPLICE_F_NONBLOCK)
|
514
|
+
if (a.flags & SPLICE_F_NONBLOCK) {
|
445
515
|
rb_sys_fail("vmsplice");
|
446
|
-
else
|
447
|
-
|
516
|
+
} else {
|
517
|
+
a.fd = check_fileno(io);
|
518
|
+
if (rb_io_wait_writable(a.fd))
|
519
|
+
continue;
|
520
|
+
}
|
448
521
|
/* fall through on error */
|
449
522
|
}
|
450
523
|
/*
|
@@ -454,6 +527,10 @@ static VALUE my_vmsplice(int argc, VALUE * argv, VALUE self)
|
|
454
527
|
*/
|
455
528
|
if (rv > 0)
|
456
529
|
break;
|
530
|
+
if (errno == EINTR) {
|
531
|
+
a.fd = check_fileno(io);
|
532
|
+
continue;
|
533
|
+
}
|
457
534
|
rb_sys_fail("vmsplice");
|
458
535
|
}
|
459
536
|
|
@@ -464,7 +541,7 @@ static VALUE my_vmsplice(int argc, VALUE * argv, VALUE self)
|
|
464
541
|
advance_vmsplice_args(&a, n);
|
465
542
|
}
|
466
543
|
|
467
|
-
return
|
544
|
+
return SSIZET2NUM(rv);
|
468
545
|
}
|
469
546
|
|
470
547
|
/*
|
@@ -550,6 +627,7 @@ void Init_io_splice_ext(void)
|
|
550
627
|
* re-added for FUSE filesystems only in Linux 2.6.35.
|
551
628
|
*/
|
552
629
|
rb_define_const(mSplice, "F_MOVE", UINT2NUM(SPLICE_F_MOVE));
|
630
|
+
assert(WAITALL != SPLICE_F_MOVE && "WAITALL == F_MOVE");
|
553
631
|
|
554
632
|
/*
|
555
633
|
* Do not block on pipe I/O. This flag only affects the pipe(s)
|
@@ -563,6 +641,7 @@ void Init_io_splice_ext(void)
|
|
563
641
|
* out of them.
|
564
642
|
*/
|
565
643
|
rb_define_const(mSplice, "F_NONBLOCK", UINT2NUM(SPLICE_F_NONBLOCK));
|
644
|
+
assert(WAITALL != SPLICE_F_NONBLOCK && "WAITALL == F_NONBLOCK");
|
566
645
|
|
567
646
|
/*
|
568
647
|
* Indicate that there may be more data coming into the outbound
|
@@ -570,12 +649,23 @@ void Init_io_splice_ext(void)
|
|
570
649
|
* frames from sockets. Currently only used with splice.
|
571
650
|
*/
|
572
651
|
rb_define_const(mSplice, "F_MORE", UINT2NUM(SPLICE_F_MORE));
|
652
|
+
assert(WAITALL != SPLICE_F_MORE && "WAITALL == F_MORE");
|
573
653
|
|
574
654
|
/*
|
575
655
|
* Only usable by vmsplice. This flag probably not useful in the
|
576
656
|
* context of Ruby applications which cannot control alignment.
|
577
657
|
*/
|
578
658
|
rb_define_const(mSplice, "F_GIFT", UINT2NUM(SPLICE_F_GIFT));
|
659
|
+
assert(WAITALL != SPLICE_F_GIFT && "WAITALL == F_GIFT");
|
660
|
+
|
661
|
+
/*
|
662
|
+
* Retry until the requested transfer is complete, this will
|
663
|
+
* cause IO.splice/IO.tee to never return less than the requested
|
664
|
+
* transfer size unless an error occored.
|
665
|
+
*
|
666
|
+
* IO.vmsplice always defaults to this behavior.
|
667
|
+
*/
|
668
|
+
rb_define_const(mSplice, "WAITALL", UINT2NUM(WAITALL));
|
579
669
|
|
580
670
|
/*
|
581
671
|
* The maximum size of an atomic write to a pipe
|
data/lib/io/splice.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
require 'io_splice_ext'
|
3
|
+
require 'io/wait'
|
3
4
|
|
4
5
|
module IO::Splice
|
5
6
|
|
@@ -39,28 +40,30 @@ module IO::Splice
|
|
39
40
|
dst.kind_of?(String) and close << (dst = File.open(dst, "w"))
|
40
41
|
src, dst = src.to_io, dst.to_io
|
41
42
|
rv = len
|
42
|
-
select_args = selectable(src, dst)
|
43
43
|
|
44
44
|
if src.stat.pipe? || dst.stat.pipe?
|
45
45
|
if len
|
46
|
-
len -= full(src, dst, len, src_offset
|
46
|
+
len -= full(src, dst, len, src_offset) until len == 0
|
47
47
|
else
|
48
48
|
rv = 0
|
49
|
-
while n = partial(src, dst, PIPE_CAPA, src_offset
|
49
|
+
while n = partial(src, dst, PIPE_CAPA, src_offset)
|
50
50
|
rv += n
|
51
|
+
src_offset += n if src_offset
|
51
52
|
end
|
52
53
|
end
|
53
54
|
else
|
54
55
|
r, w = tmp = IO.pipe
|
55
56
|
close.concat(tmp)
|
56
57
|
if len
|
57
|
-
while len != 0 && n = partial(src, w, len, src_offset
|
58
|
-
|
58
|
+
while len != 0 && n = partial(src, w, len, src_offset)
|
59
|
+
src_offset += n if src_offset
|
60
|
+
len -= full(r, dst, n, nil)
|
59
61
|
end
|
60
62
|
else
|
61
63
|
rv = 0
|
62
|
-
while n = partial(src, w, PIPE_CAPA, src_offset
|
63
|
-
|
64
|
+
while n = partial(src, w, PIPE_CAPA, src_offset)
|
65
|
+
src_offset += n if src_offset
|
66
|
+
rv += full(r, dst, n, nil)
|
64
67
|
end
|
65
68
|
end
|
66
69
|
end
|
@@ -76,17 +79,8 @@ module IO::Splice
|
|
76
79
|
# This will block and wait for IO completion of +len+
|
77
80
|
# Raises +EOFError+ if end of file is reached.
|
78
81
|
# bytes. Returns the number of bytes actually spliced (always +len+)
|
79
|
-
|
80
|
-
|
81
|
-
# depends on +_select_args+.
|
82
|
-
def self.full(src, dst, len, src_offset, _select_args = selectable(src, dst))
|
83
|
-
nr = len
|
84
|
-
while nr > 0
|
85
|
-
n = partial(src, dst, nr, src_offset, _select_args) or
|
86
|
-
raise EOFError, "end of file reached"
|
87
|
-
nr -= n
|
88
|
-
end
|
89
|
-
len
|
82
|
+
def self.full(src, dst, len, src_offset)
|
83
|
+
IO.splice(src, src_offset, dst, nil, len, F_MOVE | WAITALL)
|
90
84
|
end
|
91
85
|
|
92
86
|
# splice up to +len+ bytes from +src+ to +dst+.
|
@@ -94,22 +88,20 @@ module IO::Splice
|
|
94
88
|
# may BOTH be pipes in Linux 2.6.31 or later.
|
95
89
|
# Returns the number of bytes actually spliced.
|
96
90
|
# Like IO#readpartial, this never returns Errno::EAGAIN
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
# returns an array suitable for splat-ing to IO.select for blocking I/O
|
109
|
-
def self.selectable(src, dst) # :nodoc:
|
110
|
-
rv = []
|
111
|
-
src.stat.pipe? or rv[0] = [ src ]
|
112
|
-
dst.stat.pipe? or rv[1] = [ dst ]
|
113
|
-
rv
|
91
|
+
def self.partial(src, dst, len, src_offset)
|
92
|
+
IO.splice(src, src_offset, dst, nil, len, F_MOVE)
|
93
|
+
rescue EOFError
|
94
|
+
nil
|
95
|
+
rescue Errno::EAGAIN
|
96
|
+
begin
|
97
|
+
src.to_io.wait
|
98
|
+
IO.select(nil, [dst])
|
99
|
+
rv = IO.trysplice(src, src_offset, dst, nil, len, F_MOVE)
|
100
|
+
end while rv == :EAGAIN
|
101
|
+
rv
|
114
102
|
end
|
115
103
|
end
|
104
|
+
if (! defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby") &&
|
105
|
+
RUBY_VERSION.to_f <= 1.8
|
106
|
+
require "io/splice/mri_18"
|
107
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# :enddoc:
|
2
|
+
require "io/nonblock"
|
3
|
+
module IO::Splice
|
4
|
+
class << self
|
5
|
+
remove_method :full
|
6
|
+
remove_method :partial
|
7
|
+
end
|
8
|
+
def self.full(src, dst, len, src_offset)
|
9
|
+
dst.to_io.nonblock = src.to_io.nonblock = true
|
10
|
+
spliced = 0
|
11
|
+
case n = IO.trysplice(src, src_offset, dst, nil, len, IO::Splice::F_MOVE)
|
12
|
+
when :EAGAIN
|
13
|
+
src.to_io.wait
|
14
|
+
IO.select(nil, [dst])
|
15
|
+
when Integer
|
16
|
+
spliced += n
|
17
|
+
len -= n
|
18
|
+
src_offset += n if src_offset
|
19
|
+
when nil
|
20
|
+
break
|
21
|
+
end while len > 0
|
22
|
+
spliced
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.partial(src, dst, len, src_offset)
|
26
|
+
dst.to_io.nonblock = src.to_io.nonblock = true
|
27
|
+
begin
|
28
|
+
src.to_io.wait
|
29
|
+
IO.select(nil, [dst])
|
30
|
+
rv = IO.trysplice(src, src_offset, dst, nil, len, IO::Splice::F_MOVE)
|
31
|
+
end while rv == :EAGAIN
|
32
|
+
rv
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,340 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'tmpdir'
|
3
|
+
require "fcntl"
|
4
|
+
require 'io/nonblock'
|
5
|
+
require 'socket'
|
6
|
+
require 'timeout'
|
7
|
+
require 'tempfile'
|
8
|
+
require 'io/splice'
|
9
|
+
|
10
|
+
class TestIOCopyStreamCompat < Test::Unit::TestCase
|
11
|
+
def have_nonblock?
|
12
|
+
IO.method_defined?("nonblock=")
|
13
|
+
end
|
14
|
+
|
15
|
+
def pipe(wp, rp)
|
16
|
+
re, we = nil, nil
|
17
|
+
r, w = IO.pipe
|
18
|
+
rt = Thread.new do
|
19
|
+
begin
|
20
|
+
rp.call(r)
|
21
|
+
rescue Exception
|
22
|
+
r.close
|
23
|
+
re = $!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
wt = Thread.new do
|
27
|
+
begin
|
28
|
+
wp.call(w)
|
29
|
+
rescue Exception
|
30
|
+
w.close
|
31
|
+
we = $!
|
32
|
+
end
|
33
|
+
end
|
34
|
+
flunk("timeout") unless wt.join(10) && rt.join(10)
|
35
|
+
ensure
|
36
|
+
w.close unless !w || w.closed?
|
37
|
+
r.close unless !r || r.closed?
|
38
|
+
(wt.kill; wt.join) if wt
|
39
|
+
(rt.kill; rt.join) if rt
|
40
|
+
raise we if we
|
41
|
+
raise re if re
|
42
|
+
end
|
43
|
+
|
44
|
+
def with_pipe
|
45
|
+
r, w = IO.pipe
|
46
|
+
begin
|
47
|
+
yield r, w
|
48
|
+
ensure
|
49
|
+
r.close unless r.closed?
|
50
|
+
w.close unless w.closed?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def with_read_pipe(content)
|
55
|
+
pipe(proc do |w|
|
56
|
+
w << content
|
57
|
+
w.close
|
58
|
+
end, proc do |r|
|
59
|
+
yield r
|
60
|
+
end)
|
61
|
+
end
|
62
|
+
|
63
|
+
def mkcdtmpdir
|
64
|
+
Dir.mktmpdir {|d|
|
65
|
+
Dir.chdir(d) {
|
66
|
+
yield
|
67
|
+
}
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def trapping_usr1
|
72
|
+
@usr1_rcvd = 0
|
73
|
+
trap(:USR1) { @usr1_rcvd += 1 }
|
74
|
+
yield
|
75
|
+
ensure
|
76
|
+
trap(:USR1, "DEFAULT")
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_copy_stream
|
80
|
+
mkcdtmpdir {
|
81
|
+
content = "foobar"
|
82
|
+
File.open("src", "w") {|f| f << content }
|
83
|
+
ret = IO::Splice.copy_stream("src", "dst")
|
84
|
+
assert_equal(content.bytesize, ret)
|
85
|
+
assert_equal(content, File.read("dst"))
|
86
|
+
|
87
|
+
# overwrite by smaller file.
|
88
|
+
content = "baz"
|
89
|
+
File.open("src", "w") {|f| f << content }
|
90
|
+
ret = IO::Splice.copy_stream("src", "dst")
|
91
|
+
assert_equal(content.bytesize, ret)
|
92
|
+
assert_equal(content, File.read("dst"))
|
93
|
+
|
94
|
+
ret = IO::Splice.copy_stream("src", "dst", 2)
|
95
|
+
assert_equal(2, ret)
|
96
|
+
assert_equal(content[0,2], File.read("dst"))
|
97
|
+
|
98
|
+
ret = IO::Splice.copy_stream("src", "dst", 0)
|
99
|
+
assert_equal(0, ret)
|
100
|
+
assert_equal("", File.read("dst"))
|
101
|
+
|
102
|
+
ret = IO::Splice.copy_stream("src", "dst", nil, 1)
|
103
|
+
assert_equal(content.bytesize-1, ret)
|
104
|
+
assert_equal(content[1..-1], File.read("dst"))
|
105
|
+
|
106
|
+
assert_raise(Errno::ENOENT) {
|
107
|
+
IO::Splice.copy_stream("nodir/foo", "dst")
|
108
|
+
}
|
109
|
+
|
110
|
+
assert_raise(Errno::ENOENT) {
|
111
|
+
IO::Splice.copy_stream("src", "nodir/bar")
|
112
|
+
}
|
113
|
+
|
114
|
+
pipe(proc do |w|
|
115
|
+
ret = IO::Splice.copy_stream("src", w)
|
116
|
+
assert_equal(content.bytesize, ret)
|
117
|
+
w.close
|
118
|
+
end, proc do |r|
|
119
|
+
assert_equal(content, r.read)
|
120
|
+
end)
|
121
|
+
|
122
|
+
with_pipe {|r, w|
|
123
|
+
w.close
|
124
|
+
assert_raise(IOError) { IO::Splice.copy_stream("src", w) }
|
125
|
+
}
|
126
|
+
|
127
|
+
pipe_content = "abc"
|
128
|
+
with_read_pipe(pipe_content) {|r|
|
129
|
+
ret = IO::Splice.copy_stream(r, "dst")
|
130
|
+
assert_equal(pipe_content.bytesize, ret)
|
131
|
+
assert_equal(pipe_content, File.read("dst"))
|
132
|
+
}
|
133
|
+
|
134
|
+
pipe(proc do |w|
|
135
|
+
ret = IO::Splice.copy_stream("src", w, 1, 1)
|
136
|
+
assert_equal(1, ret)
|
137
|
+
w.close
|
138
|
+
end, proc do |r|
|
139
|
+
assert_equal(content[1,1], r.read)
|
140
|
+
end)
|
141
|
+
|
142
|
+
bigcontent = "abc" * 123456
|
143
|
+
File.open("bigsrc", "w") {|f| f << bigcontent }
|
144
|
+
ret = IO::Splice.copy_stream("bigsrc", "bigdst")
|
145
|
+
assert_equal(bigcontent.bytesize, ret)
|
146
|
+
assert_equal(bigcontent, File.read("bigdst"))
|
147
|
+
|
148
|
+
File.unlink("bigdst")
|
149
|
+
ret = IO::Splice.copy_stream("bigsrc", "bigdst", nil, 100)
|
150
|
+
assert_equal(bigcontent.bytesize-100, ret)
|
151
|
+
assert_equal(bigcontent[100..-1], File.read("bigdst"))
|
152
|
+
|
153
|
+
File.unlink("bigdst")
|
154
|
+
ret = IO::Splice.copy_stream("bigsrc", "bigdst", 30000, 100)
|
155
|
+
assert_equal(30000, ret)
|
156
|
+
assert_equal(bigcontent[100, 30000], File.read("bigdst"))
|
157
|
+
|
158
|
+
File.open("bigsrc") {|f|
|
159
|
+
begin
|
160
|
+
assert_equal(0, f.pos)
|
161
|
+
ret = IO::Splice.copy_stream(f, "bigdst", nil, 10)
|
162
|
+
assert_equal(bigcontent.bytesize-10, ret)
|
163
|
+
assert_equal(bigcontent[10..-1], File.read("bigdst"))
|
164
|
+
assert_equal(0, f.pos)
|
165
|
+
ret = IO::Splice.copy_stream(f, "bigdst", 40, 30)
|
166
|
+
assert_equal(40, ret)
|
167
|
+
assert_equal(bigcontent[30, 40], File.read("bigdst"))
|
168
|
+
assert_equal(0, f.pos)
|
169
|
+
rescue NotImplementedError
|
170
|
+
#skip "pread(2) is not implemtented."
|
171
|
+
end
|
172
|
+
}
|
173
|
+
|
174
|
+
with_pipe {|r, w|
|
175
|
+
w.close
|
176
|
+
assert_raise(IOError) { IO::Splice.copy_stream("src", w) }
|
177
|
+
}
|
178
|
+
|
179
|
+
megacontent = "abc" * 1234567
|
180
|
+
File.open("megasrc", "w") {|f| f << megacontent }
|
181
|
+
|
182
|
+
if have_nonblock?
|
183
|
+
with_pipe {|r1, w1|
|
184
|
+
with_pipe {|r2, w2|
|
185
|
+
begin
|
186
|
+
r1.nonblock = true
|
187
|
+
w2.nonblock = true
|
188
|
+
rescue Errno::EBADF
|
189
|
+
skip "nonblocking IO for pipe is not implemented"
|
190
|
+
end
|
191
|
+
t1 = Thread.new { w1 << megacontent; w1.close }
|
192
|
+
t2 = Thread.new { r2.read }
|
193
|
+
ret = IO::Splice.copy_stream(r1, w2)
|
194
|
+
assert_equal(megacontent.bytesize, ret)
|
195
|
+
w2.close
|
196
|
+
t1.join
|
197
|
+
assert_equal(megacontent, t2.value)
|
198
|
+
}
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
with_pipe {|r1, w1|
|
203
|
+
with_pipe {|r2, w2|
|
204
|
+
t1 = Thread.new { w1 << megacontent; w1.close }
|
205
|
+
t2 = Thread.new { r2.read }
|
206
|
+
ret = IO::Splice.copy_stream(r1, w2)
|
207
|
+
assert_equal(megacontent.bytesize, ret)
|
208
|
+
w2.close
|
209
|
+
t1.join
|
210
|
+
assert_equal(megacontent, t2.value)
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
with_pipe {|r, w|
|
215
|
+
t = Thread.new { r.read }
|
216
|
+
ret = IO::Splice.copy_stream("megasrc", w)
|
217
|
+
assert_equal(megacontent.bytesize, ret)
|
218
|
+
w.close
|
219
|
+
assert_equal(megacontent, t.value)
|
220
|
+
}
|
221
|
+
}
|
222
|
+
end
|
223
|
+
|
224
|
+
def with_socketpair
|
225
|
+
s1, s2 = UNIXSocket.pair
|
226
|
+
begin
|
227
|
+
yield s1, s2
|
228
|
+
ensure
|
229
|
+
s1.close unless s1.closed?
|
230
|
+
s2.close unless s2.closed?
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_copy_stream_socket
|
235
|
+
mkcdtmpdir {
|
236
|
+
|
237
|
+
content = "foobar"
|
238
|
+
File.open("src", "w") {|f| f << content }
|
239
|
+
|
240
|
+
with_socketpair {|s1, s2|
|
241
|
+
ret = IO::Splice.copy_stream("src", s1)
|
242
|
+
assert_equal(content.bytesize, ret)
|
243
|
+
s1.close
|
244
|
+
assert_equal(content, s2.read)
|
245
|
+
}
|
246
|
+
|
247
|
+
bigcontent = "abc" * 123456
|
248
|
+
File.open("bigsrc", "w") {|f| f << bigcontent }
|
249
|
+
|
250
|
+
with_socketpair {|s1, s2|
|
251
|
+
t = Thread.new { s2.read }
|
252
|
+
ret = IO::Splice.copy_stream("bigsrc", s1)
|
253
|
+
assert_equal(bigcontent.bytesize, ret)
|
254
|
+
s1.close
|
255
|
+
result = t.value
|
256
|
+
assert_equal(bigcontent, result)
|
257
|
+
}
|
258
|
+
|
259
|
+
with_socketpair {|s1, s2|
|
260
|
+
t = Thread.new { s2.read }
|
261
|
+
ret = IO::Splice.copy_stream("bigsrc", s1, 10000)
|
262
|
+
assert_equal(10000, ret)
|
263
|
+
s1.close
|
264
|
+
result = t.value
|
265
|
+
assert_equal(bigcontent[0,10000], result)
|
266
|
+
}
|
267
|
+
|
268
|
+
File.open("bigsrc") {|f|
|
269
|
+
assert_equal(0, f.pos)
|
270
|
+
with_socketpair {|s1, s2|
|
271
|
+
t = Thread.new { s2.read }
|
272
|
+
ret = IO::Splice.copy_stream(f, s1, nil, 100)
|
273
|
+
assert_equal(bigcontent.bytesize-100, ret)
|
274
|
+
assert_equal(0, f.pos)
|
275
|
+
s1.close
|
276
|
+
result = t.value
|
277
|
+
assert_equal(bigcontent[100..-1], result)
|
278
|
+
}
|
279
|
+
}
|
280
|
+
|
281
|
+
File.open("bigsrc") {|f|
|
282
|
+
assert_equal(bigcontent[0,100], f.sysread(100))
|
283
|
+
assert_equal(100, f.pos)
|
284
|
+
with_socketpair {|s1, s2|
|
285
|
+
t = Thread.new { s2.read }
|
286
|
+
ret = IO::Splice.copy_stream(f, s1)
|
287
|
+
assert_equal(bigcontent.bytesize-100, ret)
|
288
|
+
assert_equal(bigcontent.length, f.sysseek(0, IO::SEEK_CUR))
|
289
|
+
s1.close
|
290
|
+
result = t.value
|
291
|
+
assert_equal(bigcontent[100..-1], result)
|
292
|
+
}
|
293
|
+
}
|
294
|
+
|
295
|
+
megacontent = "abc" * 1234567
|
296
|
+
File.open("megasrc", "w") {|f| f << megacontent }
|
297
|
+
|
298
|
+
if have_nonblock?
|
299
|
+
with_socketpair {|s1, s2|
|
300
|
+
begin
|
301
|
+
s1.nonblock = true
|
302
|
+
rescue Errno::EBADF
|
303
|
+
skip "nonblocking IO for pipe is not implemented"
|
304
|
+
end
|
305
|
+
t = Thread.new { s2.read }
|
306
|
+
ret = IO::Splice.copy_stream("megasrc", s1)
|
307
|
+
assert_equal(megacontent.bytesize, ret)
|
308
|
+
s1.close
|
309
|
+
result = t.value
|
310
|
+
assert_equal(megacontent, result)
|
311
|
+
}
|
312
|
+
with_socketpair {|s1, s2|
|
313
|
+
begin
|
314
|
+
s1.nonblock = true
|
315
|
+
rescue Errno::EBADF
|
316
|
+
skip "nonblocking IO for pipe is not implemented"
|
317
|
+
end
|
318
|
+
trapping_usr1 do
|
319
|
+
nr = 10
|
320
|
+
pid = fork do
|
321
|
+
s1.close
|
322
|
+
IO.select([s2])
|
323
|
+
Process.kill(:USR1, Process.ppid)
|
324
|
+
s2.read
|
325
|
+
end
|
326
|
+
s2.close
|
327
|
+
nr.times do
|
328
|
+
assert_equal megacontent.bytesize,
|
329
|
+
IO::Splice.copy_stream("megasrc", s1)
|
330
|
+
end
|
331
|
+
assert_equal(1, @usr1_rcvd)
|
332
|
+
s1.close
|
333
|
+
_, status = Process.waitpid2(pid)
|
334
|
+
assert status.success?, status.inspect
|
335
|
+
end
|
336
|
+
}
|
337
|
+
end
|
338
|
+
}
|
339
|
+
end
|
340
|
+
end
|
data/test/test_io_splice.rb
CHANGED
@@ -9,6 +9,10 @@ require 'io/splice'
|
|
9
9
|
|
10
10
|
class Test_IO_Splice < Test::Unit::TestCase
|
11
11
|
|
12
|
+
def self.mri?
|
13
|
+
(defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby") || !defined?(RUBY_ENGINE)
|
14
|
+
end
|
15
|
+
|
12
16
|
def test_splice
|
13
17
|
str = 'abcde'
|
14
18
|
size = 5
|
@@ -415,7 +419,7 @@ class Test_IO_Splice < Test::Unit::TestCase
|
|
415
419
|
assert_equal 0, nr
|
416
420
|
rs.close
|
417
421
|
server.close
|
418
|
-
end
|
422
|
+
end if mri?
|
419
423
|
|
420
424
|
def test_copy_stream_nonblock_dst
|
421
425
|
server = TCPServer.new('127.0.0.1', 0)
|
@@ -440,7 +444,7 @@ class Test_IO_Splice < Test::Unit::TestCase
|
|
440
444
|
assert_equal nr, client.read(nr).size
|
441
445
|
rs.close
|
442
446
|
server.close
|
443
|
-
end
|
447
|
+
end if mri?
|
444
448
|
|
445
449
|
def test_copy_stream_eof
|
446
450
|
r, w = IO.pipe
|
@@ -466,5 +470,4 @@ class Test_IO_Splice < Test::Unit::TestCase
|
|
466
470
|
assert_nothing_raised { r.pipe_size = pipe_max_size }
|
467
471
|
assert_raises(Errno::EPERM) { r.pipe_size = pipe_max_size * 2 }
|
468
472
|
end if IO.method_defined?(:pipe_size)
|
469
|
-
|
470
473
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/unit'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'socket'
|
5
|
+
require 'io/nonblock'
|
6
|
+
require 'timeout'
|
7
|
+
$-w = true
|
8
|
+
require 'io/splice'
|
9
|
+
Thread.abort_on_exception = true
|
10
|
+
|
11
|
+
class Test_IO_Splice_EINTR < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
@usr1 = 0
|
14
|
+
trap(:USR1) { @usr1 += 1 }
|
15
|
+
end
|
16
|
+
|
17
|
+
def teardown
|
18
|
+
trap(:USR1, "DEFAULT")
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_EINTR_splice_read
|
22
|
+
rd, wr = IO.pipe
|
23
|
+
tmp = Tempfile.new 'splice-read'
|
24
|
+
main = Thread.current
|
25
|
+
Thread.new do
|
26
|
+
sleep 0.01
|
27
|
+
Process.kill(:USR1, $$)
|
28
|
+
sleep 0.01
|
29
|
+
wr.write "HI"
|
30
|
+
end
|
31
|
+
nr = IO.splice rd, nil, tmp, nil, 666
|
32
|
+
assert_equal 2, nr
|
33
|
+
assert_equal 1, @usr1
|
34
|
+
end
|
35
|
+
end if defined?(RUBY_ENGINE)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/unit'
|
3
|
+
require 'tempfile'
|
4
|
+
$-w = true
|
5
|
+
require 'io/splice'
|
6
|
+
Thread.abort_on_exception = true
|
7
|
+
|
8
|
+
class Test_IO_Splice_In_Full < Test::Unit::TestCase
|
9
|
+
def test_splice_in_full
|
10
|
+
rd, wr = IO.pipe
|
11
|
+
tmp = Tempfile.new 'splice-read'
|
12
|
+
Thread.new do
|
13
|
+
111.times do
|
14
|
+
sleep 0.002
|
15
|
+
wr.write "HIHIHI"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
nr = IO.splice rd, nil, tmp, nil, 666, IO::Splice::WAITALL
|
19
|
+
assert_equal 666, nr
|
20
|
+
tmp.rewind
|
21
|
+
assert_equal "HIHIHI" * 111, tmp.read
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_tee_in_full
|
25
|
+
rd, wr = IO.pipe
|
26
|
+
a, b = IO.pipe
|
27
|
+
thr = Thread.new { a.read(666) }
|
28
|
+
Thread.new do
|
29
|
+
111.times do
|
30
|
+
sleep 0.001
|
31
|
+
wr.syswrite "HIHIHI"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
nr = IO.tee rd, b, 666, IO::Splice::WAITALL
|
35
|
+
assert_equal 666, nr
|
36
|
+
thr.join
|
37
|
+
assert_equal "HIHIHI" * 111, thr.value
|
38
|
+
end
|
39
|
+
end if defined?(RUBY_ENGINE)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: io_splice
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 63
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
|
-
-
|
8
|
-
- 1
|
7
|
+
- 4
|
9
8
|
- 0
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 4.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ruby io_splice hackers
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-05-
|
18
|
+
date: 2011-05-13 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: wrongdoc
|
@@ -48,6 +48,7 @@ extra_rdoc_files:
|
|
48
48
|
- NEWS
|
49
49
|
- ChangeLog
|
50
50
|
- lib/io/splice.rb
|
51
|
+
- lib/io/splice/mri_18.rb
|
51
52
|
- ext/io_splice/io_splice_ext.c
|
52
53
|
- LATEST
|
53
54
|
files:
|
@@ -71,10 +72,14 @@ files:
|
|
71
72
|
- ext/io_splice/io_splice_ext.c
|
72
73
|
- io_splice.gemspec
|
73
74
|
- lib/io/splice.rb
|
75
|
+
- lib/io/splice/mri_18.rb
|
74
76
|
- local.mk.sample
|
75
77
|
- pkg.mk
|
76
78
|
- setup.rb
|
79
|
+
- test/test_copy_stream.rb
|
77
80
|
- test/test_io_splice.rb
|
81
|
+
- test/test_io_splice_eintr.rb
|
82
|
+
- test/test_io_splice_in_full.rb
|
78
83
|
homepage: http://bogomips.org/ruby_io_splice/
|
79
84
|
licenses: []
|
80
85
|
|
@@ -107,9 +112,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
112
|
requirements: []
|
108
113
|
|
109
114
|
rubyforge_project: qrp
|
110
|
-
rubygems_version: 1.
|
115
|
+
rubygems_version: 1.8.1
|
111
116
|
signing_key:
|
112
117
|
specification_version: 3
|
113
118
|
summary: zero-copy pipe I/O for Linux and Ruby
|
114
119
|
test_files:
|
120
|
+
- test/test_io_splice_in_full.rb
|
115
121
|
- test/test_io_splice.rb
|
122
|
+
- test/test_copy_stream.rb
|
123
|
+
- test/test_io_splice_eintr.rb
|