io_splice 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/GNUmakefile CHANGED
@@ -3,6 +3,7 @@ all::
3
3
  RUBY = ruby
4
4
  RAKE = rake
5
5
  GIT_URL = git://git.bogomips.org/ruby_io_splice.git
6
+ RSYNC = rsync
6
7
 
7
8
  GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
8
9
  @./GIT-VERSION-GEN
@@ -40,7 +41,7 @@ manifest: $(pkg_extra)
40
41
 
41
42
  .manifest:
42
43
  (git ls-files && \
43
- for i in $@ $(pkg_extra) $(man1_paths); \
44
+ for i in $@ $(pkg_extra); \
44
45
  do echo $$i; done) | LC_ALL=C sort > $@+
45
46
  cmp $@+ $@ || mv $@+ $@
46
47
  $(RM) $@+
@@ -67,10 +68,10 @@ cgit_atom := http://git.bogomips.org/cgit/ruby_io_splice.git/atom/?h=master
67
68
  atom = <link rel="alternate" title="Atom feed" href="$(1)" \
68
69
  type="application/atom+xml"/>
69
70
 
70
- # using rdoc 2.4.1+
71
+ # using rdoc 2.5.x + workaround patch here:
72
+ # rubyforge.org/tracker/index.php?func=detail&aid=28230&group_id=627&atid=2472
71
73
  doc: .document NEWS ChangeLog
72
- for i in $(man1_bins); do > $$i; done
73
- rdoc -Na -t "$(shell sed -ne '1s/^= //p' README)"
74
+ rdoc -a -t "$(shell sed -ne '1s/^= //p' README)"
74
75
  install -m644 COPYING doc/COPYING
75
76
  install -m644 $(shell grep '^[A-Z]' .document) doc/
76
77
  cd doc && for i in $(base_bins); do \
@@ -85,7 +86,31 @@ doc: .document NEWS ChangeLog
85
86
  doc/NEWS.html doc/README.html
86
87
  $(RAKE) -s news_atom > doc/NEWS.atom.xml
87
88
  cd doc && ln README.html tmp && mv tmp index.html
88
- $(RM) $(man1_bins)
89
+
90
+ latest: NEWS
91
+ @awk 'BEGIN{RS="=== ";ORS=""}NR==2{sub(/\n$$/,"");print RS""$$0 }' $<
92
+
93
+ # publishes docs to http://bogomips.org/ruby_io_splice/,
94
+ publish_doc:
95
+ -git set-file-times
96
+ $(RM) -r doc ChangeLog NEWS
97
+ $(MAKE) doc LOG_VERSION=$(shell git tag -l | tail -1)
98
+ awk 'BEGIN{RS="=== ";ORS=""}NR==2{sub(/\n$$/,"");print RS""$$0 }' \
99
+ NEWS > doc/LATEST
100
+ find doc/images doc/js -type f | \
101
+ TZ=UTC xargs touch -d '1970-01-01 00:00:01' doc/rdoc.css
102
+ $(MAKE) doc_gz
103
+ chmod 644 $$(find doc -type f)
104
+ $(RSYNC) -av doc/ bogomips.org:/srv/bogomips/ruby_io_splice/
105
+ git ls-files | xargs touch
106
+
107
+ # Create gzip variants of the same timestamp as the original so nginx
108
+ # "gzip_static on" can serve the gzipped versions directly.
109
+ doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
110
+ doc_gz:
111
+ touch doc/NEWS.atom.xml -d "$$(awk 'NR==1{print $$4,$$5,$$6}' NEWS)"
112
+ for i in $(docs); do \
113
+ gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
89
114
 
90
115
  ifneq ($(VERSION),)
91
116
  rfproject := qrp
data/LICENSE CHANGED
@@ -1,16 +1,16 @@
1
1
  io_splice is copyrighted Free Software by all contributors, see logs in
2
2
  revision control for names and email addresses of all of them.
3
3
 
4
- You can redistribute it and/or modify it under either the terms of the GNU
4
+ You can redistribute it and/or modify it under the terms of the GNU
5
5
  Lesser General Public License as published by the Free Software Foundation,
6
6
  version 3.0 {LGPLv3}[http://www.gnu.org/licenses/lgpl-3.0.txt] (see
7
7
  link:COPYING).
8
8
 
9
- posix_mq is distributed in the hope that it will be useful, but WITHOUT
9
+ io_splice is distributed in the hope that it will be useful, but WITHOUT
10
10
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
11
  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12
12
  License for more details.
13
13
 
14
14
  You should have received a copy of the GNU Lesser General Public License
15
- along with the GNU C Library; if not, write to the Free Software
15
+ along with this library; if not, write to the Free Software
16
16
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
data/README CHANGED
@@ -85,3 +85,13 @@ don't email the git mailing list or maintainer with io_splice patches.
85
85
 
86
86
  All feedback (bug reports, user/development discussion, patches, pull
87
87
  requests) go to the mailing list: mailto:ruby.io.splice@librelist.com
88
+
89
+ == Mailing List Archives
90
+
91
+ In addition to the rsync-able archives provided by http://librelist.com/, we
92
+ are also mirrored to
93
+ {Gmane}[http://gmane.org/info.php?group=gmane.comp.lang.ruby.io-splice.general]
94
+ and maintain our own mbox mirrors downloadable via HTTP.
95
+
96
+ * nntp://news.gmane.org/gmane.comp.lang.ruby.io-splice.general
97
+ * http://bogomips.org/ruby_io_splice/archives/
@@ -10,10 +10,38 @@
10
10
  #include <limits.h>
11
11
  #include <alloca.h>
12
12
 
13
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
14
- # define RUBY_1_8_TRAP_BEG for(;0;)
15
- # define RUBY_1_8_TRAP_END for(;0;)
13
+ #if ! HAVE_RB_IO_T
14
+ # define rb_io_t OpenFile
15
+ #endif
16
+
17
+ #ifdef GetReadFile
18
+ # define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
16
19
  #else
20
+ # if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
21
+ # define FPTR_TO_FD(fptr) fileno(fptr->f)
22
+ # else
23
+ # define FPTR_TO_FD(fptr) fptr->fd
24
+ # endif
25
+ #endif
26
+
27
+ static int my_fileno(VALUE io)
28
+ {
29
+ rb_io_t *fptr;
30
+
31
+ for (;;) {
32
+ switch (TYPE(io)) {
33
+ case T_FIXNUM: return NUM2INT(io);
34
+ case T_FILE: {
35
+ GetOpenFile(io, fptr);
36
+ return FPTR_TO_FD(fptr);
37
+ }
38
+ default:
39
+ io = rb_convert_type(io, T_FILE, "IO", "to_io");
40
+ /* retry */
41
+ }
42
+ }
43
+ }
44
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
17
45
  /* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
18
46
  # include <rubysig.h>
19
47
  # define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
@@ -34,8 +62,6 @@ rb_thread_blocking_region(
34
62
 
35
63
  return rv;
36
64
  }
37
- # define RUBY_1_8_TRAP_BEG TRAP_BEG
38
- # define RUBY_1_8_TRAP_END TRAP_END
39
65
  #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
40
66
 
41
67
  #ifndef RSTRING_PTR
@@ -77,21 +103,9 @@ struct splice_args {
77
103
  static VALUE nogvl_splice(void *ptr)
78
104
  {
79
105
  struct splice_args *a = ptr;
80
- long n;
81
106
 
82
- /*
83
- * it's still possible to block because the SPLICE_F_NONBLOCK flag
84
- * only affects the pipe descriptor, not the non-pipe descriptor.
85
- * So use TRAP_BEG/TRAP_END (only) to make Ruby 1.8 happy. We also
86
- * don't want the TRAP_BEG/TRAP_END compatibility layer in 1.9,
87
- * so we use the 1.8-only versions
88
- */
89
- RUBY_1_8_TRAP_BEG;
90
- n = splice(a->fd_in, a->off_in, a->fd_out, a->off_out,
91
- a->len, a->flags);
92
- RUBY_1_8_TRAP_END;
93
-
94
- return (VALUE)n;
107
+ return (VALUE)splice(a->fd_in, a->off_in, a->fd_out, a->off_out,
108
+ a->len, a->flags);
95
109
  }
96
110
 
97
111
  /*
@@ -140,13 +154,13 @@ static VALUE my_splice(VALUE self,
140
154
  struct splice_args a = {
141
155
  .off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i),
142
156
  .off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o),
143
- .fd_in = NUM2INT(fd_in),
144
- .fd_out = NUM2INT(fd_out),
157
+ .fd_in = my_fileno(fd_in),
158
+ .fd_out = my_fileno(fd_out),
145
159
  .len = (size_t)NUM2ULONG(len),
146
160
  .flags = NUM2UINT(flags),
147
161
  };
148
162
 
149
- n = (long)nb_io_run(nogvl_splice, &a, a.flags);
163
+ n = (long)rb_thread_blocking_region(nogvl_splice, &a, RUBY_UBF_IO, 0);
150
164
  if (n == 0)
151
165
  rb_eof_error();
152
166
  if (n < 0)
@@ -194,8 +208,8 @@ static VALUE my_tee(VALUE self,
194
208
  {
195
209
  long n;
196
210
  struct tee_args a = {
197
- .fd_in = NUM2INT(fd_in),
198
- .fd_out = NUM2INT(fd_out),
211
+ .fd_in = my_fileno(fd_in),
212
+ .fd_out = my_fileno(fd_out),
199
213
  .len = (size_t)NUM2ULONG(len),
200
214
  .flags = NUM2UINT(flags),
201
215
  };
@@ -223,6 +237,58 @@ static VALUE nogvl_vmsplice(void *ptr)
223
237
  return (VALUE)vmsplice(a->fd, a->iov, a->nr_segs, a->flags);
224
238
  }
225
239
 
240
+ /* this can't be a function since we use alloca() */
241
+ #define ARY2IOVEC(iov,iovcnt,expect,ary) \
242
+ do { \
243
+ VALUE *cur; \
244
+ struct iovec *tmp; \
245
+ long n; \
246
+ Check_Type(ary, T_ARRAY); \
247
+ cur = RARRAY_PTR(ary); \
248
+ n = RARRAY_LEN(ary); \
249
+ if (n > IOV_MAX) \
250
+ rb_raise(rb_eArgError, "array is larger than IOV_MAX"); \
251
+ iov = tmp = alloca(sizeof(struct iovec) * n); \
252
+ expect = 0; \
253
+ iovcnt = n; \
254
+ for (; --n >= 0; tmp++, cur++) { \
255
+ Check_Type(*cur, T_STRING); \
256
+ tmp->iov_base = RSTRING_PTR(*cur); \
257
+ tmp->iov_len = RSTRING_LEN(*cur); \
258
+ expect += tmp->iov_len; \
259
+ } \
260
+ } while (0)
261
+
262
+ static void advance_vmsplice_args(struct vmsplice_args *a, long n)
263
+ {
264
+ struct iovec *new_iov = a->iov;
265
+ int i;
266
+
267
+ /* skip over iovecs we've already written completely */
268
+ for (i = 0; i < a->nr_segs; i++, new_iov++) {
269
+ if (n == 0)
270
+ break;
271
+ /*
272
+ * partially written iov,
273
+ * modify and retry with current iovec in
274
+ * front
275
+ */
276
+ if (new_iov->iov_len > (size_t)n) {
277
+ VALUE base = (VALUE)new_iov->iov_base;
278
+
279
+ new_iov->iov_len -= n;
280
+ new_iov->iov_base = (void *)(base + n);
281
+ break;
282
+ }
283
+
284
+ n -= new_iov->iov_len;
285
+ }
286
+
287
+ /* setup to retry without the already-written iovecs */
288
+ a->nr_segs -= i;
289
+ a->iov = new_iov;
290
+ }
291
+
226
292
  /*
227
293
  * call-seq:
228
294
  * IO.vmsplice(fd, string_array, flags) => integer
@@ -238,46 +304,48 @@ static VALUE nogvl_vmsplice(void *ptr)
238
304
  */
239
305
  static VALUE my_vmsplice(VALUE self, VALUE fd, VALUE data, VALUE flags)
240
306
  {
241
- long n;
307
+ long rv = 0;
308
+ ssize_t left;
242
309
  struct vmsplice_args a;
243
- struct iovec *tmp;
244
- VALUE *ary;
245
-
246
- switch (TYPE(data)) {
247
- case T_ARRAY:
248
- ary = RARRAY_PTR(data);
249
- a.nr_segs = RARRAY_LEN(data);
250
-
251
- if (a.nr_segs > IOV_MAX)
252
- rb_raise(rb_eArgError, "array larger than IOV_MAX");
253
310
 
254
- a.iov = tmp = alloca(sizeof(struct iovec) * a.nr_segs);
311
+ ARY2IOVEC(a.iov, a.nr_segs, left, data);
312
+ a.fd = my_fileno(fd);
313
+ a.flags = NUM2UINT(flags);
255
314
 
256
- for (n = (long)a.nr_segs; --n >= 0; tmp++, ary++) {
257
- if (TYPE(*ary) != T_STRING)
258
- rb_raise(rb_eArgError,
259
- "must be an array of strings");
260
- tmp->iov_base = RSTRING_PTR(*ary);
261
- tmp->iov_len = RSTRING_LEN(*ary);
315
+ for (;;) {
316
+ long n = (long)nb_io_run(nogvl_vmsplice, &a, a.flags);
317
+
318
+ if (n < 0) {
319
+ if (errno == EAGAIN) {
320
+ if (a.flags & SPLICE_F_NONBLOCK)
321
+ rb_sys_fail("vmsplice");
322
+ else if (rb_io_wait_writable(a.fd))
323
+ continue;
324
+ /* fall through on error */
325
+ }
326
+ /*
327
+ * unlikely to hit this case, return the
328
+ * already written bytes, we'll let the next
329
+ * write (or close) fail instead
330
+ */
331
+ if (rv > 0)
332
+ break;
333
+ rb_sys_fail("vmsplice");
262
334
  }
263
- break;
264
- default:
265
- rb_raise(rb_eArgError, "must be an array of strings");
266
- }
267
-
268
- a.fd = NUM2INT(fd);
269
- a.flags = NUM2UINT(flags);
270
335
 
271
- n = (long)nb_io_run(nogvl_vmsplice, &a, a.flags);
272
- if (n < 0)
273
- rb_sys_fail("vmsplice");
336
+ rv += n;
337
+ left -= n;
338
+ if (left == 0)
339
+ break;
340
+ advance_vmsplice_args(&a, n);
341
+ }
274
342
 
275
- return LONG2NUM(n);
343
+ return LONG2NUM(rv);
276
344
  }
277
345
 
278
346
  void Init_io_splice_ext(void)
279
347
  {
280
- VALUE cSplice = rb_define_class_under(rb_cIO, "Splice", rb_cObject);
348
+ VALUE mSplice = rb_define_module_under(rb_cIO, "Splice");
281
349
 
282
350
  rb_define_singleton_method(rb_cIO, "splice", my_splice, 6);
283
351
  rb_define_singleton_method(rb_cIO, "tee", my_tee, 4);
@@ -288,32 +356,38 @@ void Init_io_splice_ext(void)
288
356
  * and support for it was removed in Linux 2.6.21 and has not been
289
357
  * readded as of 2.6.30.
290
358
  */
291
- rb_define_const(cSplice, "F_MOVE", UINT2NUM(SPLICE_F_MOVE));
359
+ rb_define_const(mSplice, "F_MOVE", UINT2NUM(SPLICE_F_MOVE));
292
360
 
293
361
  /*
294
- * Do not block on I/O. This flag only affects the pipe(s) being
295
- * spliced from/to and has no effect on the non-pipe descriptor
296
- * (which requires non-blocking operation to be set explicitly).
362
+ * Do not block on pipe I/O. This flag only affects the pipe(s)
363
+ * being spliced from/to and has no effect on the non-pipe
364
+ * descriptor (which requires non-blocking operation to be set
365
+ * explicitly).
366
+ *
367
+ * The non-blocking flag (O_NONBLOCK) on the pipe descriptors
368
+ * themselves are ignored by this family of functions, and
369
+ * using this flag is the only way to get non-blocking operation
370
+ * out of them.
297
371
  */
298
- rb_define_const(cSplice, "F_NONBLOCK", UINT2NUM(SPLICE_F_NONBLOCK));
372
+ rb_define_const(mSplice, "F_NONBLOCK", UINT2NUM(SPLICE_F_NONBLOCK));
299
373
 
300
374
  /*
301
375
  * Indicate that there may be more data coming into the outbound
302
376
  * descriptor. This can allow the kernel to avoid sending partial
303
377
  * frames from sockets. Currently only used with splice.
304
378
  */
305
- rb_define_const(cSplice, "F_MORE", UINT2NUM(SPLICE_F_MORE));
379
+ rb_define_const(mSplice, "F_MORE", UINT2NUM(SPLICE_F_MORE));
306
380
 
307
381
  /*
308
382
  * Only usable by vmsplice. This flag probably not useful in the
309
383
  * context of Ruby applications which cannot control alignment.
310
384
  */
311
- rb_define_const(cSplice, "F_GIFT", UINT2NUM(SPLICE_F_GIFT));
385
+ rb_define_const(mSplice, "F_GIFT", UINT2NUM(SPLICE_F_GIFT));
312
386
 
313
387
  /*
314
388
  * The maximum size of an atomic write to a pipe
315
389
  * POSIX requires this to be at least 512 bytes.
316
390
  * Under Linux, this is 4096 bytes.
317
391
  */
318
- rb_define_const(cSplice, "PIPE_BUF", UINT2NUM(PIPE_BUF));
392
+ rb_define_const(mSplice, "PIPE_BUF", UINT2NUM(PIPE_BUF));
319
393
  }
data/lib/io/splice.rb CHANGED
@@ -3,10 +3,10 @@ require 'io_splice_ext'
3
3
 
4
4
  class IO
5
5
 
6
- class Splice
6
+ module Splice
7
7
 
8
8
  # the version of IO::Splice, currently 0.1.0
9
- VERSION = '0.1.0'
9
+ VERSION = '1.0.0'
10
10
 
11
11
  # The maximum capacity of the pipe in bytes.
12
12
  # Under stock Linux, this is 65536 bytes as of 2.6.11, and 4096 before
data/local.mk.sample CHANGED
@@ -1,64 +1,15 @@
1
- # this is a sample local.mk file, feel free to modify it for your needs
2
- # GNUmakefile will source local.mk in the top-level source tree
3
- # if it is present.
4
- #
5
- # This is depends on a bunch of GNU-isms from bash, touch.
6
-
7
- RSYNC = rsync
8
1
  DLEXT := so
9
- gems :=
10
2
 
11
3
  # Avoid loading rubygems to speed up tests because gmake is
12
4
  # fork+exec heavy with Ruby.
13
5
  prefix = $(HOME)
6
+
14
7
  ifeq ($(r19),)
15
8
  RUBY := $(prefix)/bin/ruby
16
- gem_paths := $(addprefix $(prefix)/lib/ruby/gems/1.8/gems/,$(gems))
9
+ RAKE := $(prefix)/bin/rake
17
10
  else
18
11
  prefix := $(prefix)/ruby-1.9
19
12
  export PATH := $(prefix)/bin:$(PATH)
20
13
  RUBY := $(prefix)/bin/ruby --disable-gems
21
- gem_paths := $(addprefix $(prefix)/lib/ruby/gems/1.9.1/gems/,$(gems))
14
+ RAKE := $(prefix)/bin/rake
22
15
  endif
23
-
24
- ifdef gem_paths
25
- sp :=
26
- sp +=
27
- export RUBYLIB := $(subst $(sp),:,$(addsuffix /lib,$(gem_paths)))
28
- endif
29
-
30
- # pipefail is THE reason to use bash (v3+) or never revisions of ksh93
31
- # SHELL := /bin/bash -e -o pipefail
32
- SHELL := /bin/ksh93 -e -o pipefail
33
-
34
- # trace execution of tests
35
- # TRACER = strace -f -o $(t_pfx).strace -s 100000
36
- TRACER = /usr/bin/time -v -o $(t_pfx).time
37
-
38
- latest: NEWS
39
- @awk 'BEGIN{RS="=== ";ORS=""}NR==2{sub(/\n$$/,"");print RS""$$0 }' $<
40
-
41
- # publishes docs to http://bogomips.org/ruby_io_splice/
42
- publish_doc:
43
- -git set-file-times
44
- $(RM) -r doc ChangeLog NEWS
45
- $(MAKE) doc LOG_VERSION=$(shell git tag -l | tail -1)
46
- $(MAKE) -s latest > doc/LATEST
47
- find doc/images doc/js -type f | \
48
- TZ=UTC xargs touch -d '1970-01-01 00:00:00' doc/rdoc.css
49
- $(MAKE) doc_gz
50
- chmod 644 $$(find doc -type f)
51
- $(RSYNC) -av doc/ dcvr:/srv/bogomips/ruby_io_splice/
52
- git ls-files | xargs touch
53
-
54
- # Create gzip variants of the same timestamp as the original so nginx
55
- # "gzip_static on" can serve the gzipped versions directly.
56
- doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
57
- doc_gz:
58
- touch doc/NEWS.atom.xml -d "$$(awk 'NR==1{print $$4,$$5,$$6}' NEWS)"
59
- for i in $(docs); do \
60
- gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
61
-
62
- # launches any of the following shells with RUBYLIB set
63
- irb sh bash ksh:
64
- $@
@@ -1,15 +1,60 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'test/unit'
3
3
  require 'tempfile'
4
+ require 'socket'
5
+ require 'io/nonblock'
6
+ $-w = true
4
7
  require 'io/splice'
5
8
 
9
+ # unused_port provides an unused port on +addr+ usable for TCP that is
10
+ # guaranteed to be unused across all unicorn builds on that system. It
11
+ # prevents race conditions by using a lock file other unicorn builds
12
+ # will see. This is required if you perform several builds in parallel
13
+ # with a continuous integration system or run tests in parallel via
14
+ # gmake. This is NOT guaranteed to be race-free if you run other
15
+ # processes that bind to random ports for testing (but the window
16
+ # for a race condition is very small).
17
+ def unused_port(addr = '127.0.0.1')
18
+ retries = 100
19
+ base = 5000
20
+ port = sock = nil
21
+ begin
22
+ begin
23
+ port = base + rand(32768 - base)
24
+ while port == 8080
25
+ port = base + rand(32768 - base)
26
+ end
27
+
28
+ sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
29
+ sock.bind(Socket.pack_sockaddr_in(port, addr))
30
+ sock.listen(5)
31
+ rescue Errno::EADDRINUSE, Errno::EACCES
32
+ sock.close rescue nil
33
+ retry if (retries -= 1) >= 0
34
+ end
35
+
36
+ # since we'll end up closing the random port we just got, there's a race
37
+ # condition could allow the random port we just chose to reselect itself
38
+ # when running tests in parallel with gmake. Create a lock file while
39
+ # we have the port here to ensure that does not happen .
40
+ lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
41
+ lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
42
+ at_exit { File.unlink(lock_path) rescue nil }
43
+ rescue Errno::EEXIST
44
+ sock.close rescue nil
45
+ retry
46
+ end
47
+ sock.close rescue nil
48
+ port
49
+ end
50
+
6
51
  class Test_IO_Splice < Test::Unit::TestCase
7
52
 
8
53
  def test_splice
9
54
  str = 'abcde'
10
55
  size = 5
11
56
  rd, wr = IO.pipe
12
- tmp = Tempfile.new(nil)
57
+ tmp = Tempfile.new('ruby_io_splice')
13
58
 
14
59
  assert_nothing_raised {
15
60
  tmp.syswrite(str)
@@ -21,12 +66,48 @@ class Test_IO_Splice < Test::Unit::TestCase
21
66
  assert_equal str, rd.sysread(size)
22
67
  end
23
68
 
69
+ def test_splice_io
70
+ str = 'abcde'
71
+ size = 5
72
+ rd, wr = IO.pipe
73
+ tmp = Tempfile.new('ruby_io_splice')
74
+
75
+ assert_nothing_raised {
76
+ tmp.syswrite(str)
77
+ tmp.sysseek(0)
78
+ }
79
+
80
+ nr = IO.splice(tmp, nil, wr, nil, size, 0)
81
+ assert_equal size, nr
82
+ assert_equal str, rd.sysread(size)
83
+ end
84
+
85
+ def test_splice_io_ish
86
+ str = 'abcde'
87
+ size = 5
88
+ rd, wr = IO.pipe
89
+ tmp = Tempfile.new('ruby_io_splice')
90
+ io_ish = [ tmp ]
91
+ def io_ish.to_io
92
+ first.to_io
93
+ end
94
+
95
+ assert_nothing_raised {
96
+ tmp.syswrite(str)
97
+ tmp.sysseek(0)
98
+ }
99
+
100
+ nr = IO.splice(io_ish, nil, wr, nil, size, 0)
101
+ assert_equal size, nr
102
+ assert_equal str, rd.sysread(size)
103
+ end
104
+
24
105
  def test_splice_in_offset
25
106
  str = 'abcde'
26
107
  off = 3
27
108
  len = 2
28
109
  rd, wr = IO.pipe
29
- tmp = Tempfile.new(nil)
110
+ tmp = Tempfile.new('ruby_io_splice')
30
111
 
31
112
  assert_nothing_raised {
32
113
  tmp.syswrite(str)
@@ -41,7 +122,7 @@ class Test_IO_Splice < Test::Unit::TestCase
41
122
  def test_splice_out_offset
42
123
  str = 'abcde'
43
124
  rd, wr = IO.pipe
44
- tmp = Tempfile.new(nil)
125
+ tmp = Tempfile.new('ruby_io_splice')
45
126
 
46
127
  assert_nothing_raised { wr.syswrite(str) }
47
128
  nr = IO.splice(rd.fileno, nil, tmp.fileno, 3, str.size, 0)
@@ -52,7 +133,7 @@ class Test_IO_Splice < Test::Unit::TestCase
52
133
 
53
134
  def test_splice_nonblock
54
135
  rd, wr = IO.pipe
55
- tmp = Tempfile.new(nil)
136
+ tmp = Tempfile.new('ruby_io_splice')
56
137
 
57
138
  assert_raises(Errno::EAGAIN) {
58
139
  IO.splice(rd.fileno, nil, tmp.fileno, 0, 5, IO::Splice::F_NONBLOCK)
@@ -61,7 +142,7 @@ class Test_IO_Splice < Test::Unit::TestCase
61
142
 
62
143
  def test_splice_eof
63
144
  rd, wr = IO.pipe
64
- tmp = Tempfile.new(nil)
145
+ tmp = Tempfile.new('ruby_io_splice')
65
146
  wr.syswrite 'abc'
66
147
  wr.close
67
148
 
@@ -72,6 +153,17 @@ class Test_IO_Splice < Test::Unit::TestCase
72
153
  }
73
154
  end
74
155
 
156
+ def test_splice_nonblock_socket
157
+ port = unused_port
158
+ server = TCPServer.new('127.0.0.1', port)
159
+ rp, wp = IO.pipe
160
+ rs = TCPSocket.new('127.0.0.1', port)
161
+ rs.nonblock = true
162
+ assert_raises(Errno::EAGAIN) { IO.splice(rs, nil, wp, nil, 1024, 0) }
163
+ rs.close
164
+ server.close
165
+ end
166
+
75
167
  def test_tee
76
168
  str = 'abcde'
77
169
  size = 5
@@ -100,6 +192,19 @@ class Test_IO_Splice < Test::Unit::TestCase
100
192
  }
101
193
  end
102
194
 
195
+ def test_tee_io
196
+ str = 'abcde'
197
+ size = 5
198
+ rda, wra = IO.pipe
199
+ rdb, wrb = IO.pipe
200
+
201
+ assert_nothing_raised { wra.syswrite(str) }
202
+ nr = IO.tee(rda, wrb, size, 0)
203
+ assert_equal 5, nr
204
+ assert_equal str, rdb.sysread(5)
205
+ assert_equal str, rda.sysread(5)
206
+ end
207
+
103
208
  def test_vmsplice_array
104
209
  data = %w(hello world how are you today)
105
210
  r, w = IO.pipe
@@ -108,6 +213,14 @@ class Test_IO_Splice < Test::Unit::TestCase
108
213
  assert_equal data.join(''), r.readpartial(16384)
109
214
  end
110
215
 
216
+ def test_vmsplice_array_io
217
+ data = %w(hello world how are you today)
218
+ r, w = IO.pipe
219
+ n = IO.vmsplice(w, data, 0)
220
+ assert_equal data.join('').size, n
221
+ assert_equal data.join(''), r.readpartial(16384)
222
+ end
223
+
111
224
  def test_vmsplice_nonblock
112
225
  data = %w(hello world how are you today)
113
226
  r, w = IO.pipe
@@ -117,6 +230,40 @@ class Test_IO_Splice < Test::Unit::TestCase
117
230
  }
118
231
  end
119
232
 
233
+ def test_vmsplice_in_full
234
+ empty = ""
235
+
236
+ # bs * count should be > PIPE_BUF
237
+ [ [ 512, 512 ], [ 131073, 3 ], [ 4098, 64 ] ].each do |(bs,count)|
238
+ rd, wr = IO.pipe
239
+ buf = File.open('/dev/urandom', 'rb') { |fp| fp.sysread(bs) }
240
+
241
+ vec = (1..count).map { buf }
242
+ pid = fork do
243
+ wr.close
244
+ tmp = []
245
+ begin
246
+ sleep 0.005
247
+ tmp << rd.readpartial(8192, buf)
248
+ rescue EOFError
249
+ break
250
+ end while true
251
+ ok = (vec.join(empty) == tmp.join(empty))
252
+ exit! ok
253
+ end
254
+ assert_nothing_raised { rd.close }
255
+ assert_equal(bs * count, IO.vmsplice(wr.fileno, vec, 0))
256
+ assert_nothing_raised { wr.close }
257
+ _, status = Process.waitpid2(pid)
258
+ assert status.success?
259
+ end
260
+ end
261
+
262
+ def test_vmsplice_nil
263
+ data = %w(hello world how are you today)
264
+ assert_raises(TypeError) { IO.vmsplice(nil, data, 0) }
265
+ end
266
+
120
267
  def test_constants
121
268
  assert IO::Splice::PIPE_BUF > 0
122
269
  %w(move nonblock more gift).each { |x|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io_splice
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - io_splice hackers
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-15 00:00:00 +00:00
12
+ date: 2010-05-27 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies: []
15
15