io_splice 0.1.0 → 1.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/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