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.
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v3.1.0.GIT
4
+ DEF_VER=v4.0.0.GIT
5
5
 
6
6
  LF='
7
7
  '
@@ -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 NUM2INT(io);
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 long do_splice(int argc, VALUE *argv, unsigned dflags)
137
+ static ssize_t do_splice(int argc, VALUE *argv, unsigned dflags)
131
138
  {
132
- off_t i, o;
133
- VALUE fd_in, off_in, fd_out, off_out, len, flags;
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
- &fd_in, &off_in, &fd_out, &off_out, &len, &flags);
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.fd_in = my_fileno(fd_in);
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
- return (long)io_run(nogvl_splice, &a);
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(fd_in, off_in, fd_out, off_out, len) => integer
152
- * IO.splice(fd_in, off_in, fd_out, off_out, len, flags) => integer
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 +fd_in+ or +fd_out+
155
- * MUST be a pipe. +fd_in+ and +fd_out+ may BOTH be pipes as of
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, IO::Splice::F_NONBLOCK, IO::Splice::F_MORE
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 +fd_in+ has reached end of file.
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
- long n = do_splice(argc, argv, 0);
229
+ ssize_t n = do_splice(argc, argv, 0);
194
230
 
195
231
  if (n == 0)
196
232
  rb_eof_error();
197
- if (n < 0)
233
+ if (n == -1)
198
234
  rb_sys_fail("splice");
199
- return LONG2NUM(n);
235
+ return SSIZET2NUM(n);
200
236
  }
201
237
 
202
238
  /*
203
239
  * call-seq:
204
- * IO.trysplice(fd_in, off_in, fd_out, off_out, len) => integer
205
- * IO.trysplice(fd_in, off_in, fd_out, off_out, len, flags) => integer
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
- long n = do_splice(argc, argv, SPLICE_F_NONBLOCK);
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 < 0) {
257
+ if (n == -1) {
222
258
  if (errno == EAGAIN)
223
259
  return sym_EAGAIN;
224
260
  rb_sys_fail("splice");
225
261
  }
226
- return LONG2NUM(n);
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 long do_tee(int argc, VALUE *argv, unsigned dflags)
280
+ static ssize_t do_tee(int argc, VALUE *argv, unsigned dflags)
245
281
  {
246
- VALUE fd_in, fd_out, len, flags;
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", &fd_in, &fd_out, &len, &flags);
250
- a.fd_in = my_fileno(fd_in);
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 (long)nb_io_run(nogvl_tee, &a, a.flags);
322
+ return total;
256
323
  }
257
324
 
258
325
  /*
259
326
  * call-seq:
260
- * IO.tee(fd_in, fd_out, len) => integer
261
- * IO.tee(fd_in, fd_out, len, flags) => integer
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 +fd_in+ to +fd_out+. +fd_in+
264
- * and +fd_out+ must both refer to pipe descriptors. +fd_in+ and +fd_out+
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 IO::Splice::F_NONBLOCK
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 +fd_in+ is closed and emptied.
272
- * Raises Errno::EAGAIN when +fd_in+ is empty and/or +fd_out+ is full
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
- long n = do_tee(argc, argv, 0);
353
+ ssize_t n = do_tee(argc, argv, 0);
284
354
 
285
355
  if (n == 0)
286
356
  rb_eof_error();
287
- if (n < 0)
357
+ if (n == -1)
288
358
  rb_sys_fail("tee");
289
359
 
290
- return LONG2NUM(n);
360
+ return SSIZET2NUM(n);
291
361
  }
292
362
 
293
363
  /*
294
364
  * call-seq:
295
- * IO.trytee(fd_in, fd_out, len) => integer
296
- * IO.trytee(fd_in, fd_out, len, flags) => integer
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
- long n = do_tee(argc, argv, SPLICE_F_NONBLOCK);
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 < 0) {
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 LONG2NUM(n);
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(fd, string_array) => integer
389
- * IO.vmsplice(fd, string_array, flags) => integer
390
- * IO.vmsplice(fd, string) => integer
391
- * IO.vmsplice(fd, string, flags) => integer
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 fd.
394
- * +fd+ must be the writable end of a pipe.
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
- long rv = 0;
481
+ ssize_t rv = 0;
412
482
  ssize_t left;
413
483
  struct vmsplice_args a;
414
- VALUE fd, data, flags;
484
+ VALUE io, data, flags;
415
485
 
416
- rb_scan_args(argc, argv, "21", &fd, &data, &flags);
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(fd);
506
+ a.fd = my_fileno(io);
437
507
  a.flags = NIL_P(flags) ? 0 : NUM2UINT(flags);
438
508
 
439
509
  for (;;) {
440
- long n = (long)nb_io_run(nogvl_vmsplice, &a, a.flags);
510
+ ssize_t n = (ssize_t)io_run(nogvl_vmsplice, &a);
441
511
 
442
- if (n < 0) {
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 if (rb_io_wait_writable(a.fd))
447
- continue;
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 LONG2NUM(rv);
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
@@ -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, select_args) until len == 0
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, select_args)
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, select_args)
58
- len -= full(r, dst, n, nil, select_args)
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, select_args)
63
- rv += full(r, dst, n, nil, select_args)
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
- # The +_select_args+ parameter is reserved for internal use and
80
- # may be removed in future versions. Do not write code that
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
- # The +_select_args+ parameter is reserved for internal use and
98
- # may be removed in future versions. Do not write code that
99
- # depends on +_select_args+.
100
- def self.partial(src, dst, len, src_offset,
101
- _select_args = selectable(src, dst))
102
- begin
103
- rv = IO.trysplice(src, src_offset, dst, nil, len, F_MOVE)
104
- end while rv == :EAGAIN and IO.select(*_select_args)
105
- rv
106
- end
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
@@ -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: 3
4
+ hash: 63
5
5
  prerelease:
6
6
  segments:
7
- - 3
8
- - 1
7
+ - 4
9
8
  - 0
10
- version: 3.1.0
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-01 00:00:00 Z
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.7.2
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