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 +30 -5
- data/LICENSE +3 -3
- data/README +10 -0
- data/ext/io_splice/io_splice_ext.c +136 -62
- data/lib/io/splice.rb +2 -2
- data/local.mk.sample +3 -52
- data/test/test_io_splice.rb +152 -5
- metadata +2 -2
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)
|
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.
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
#
|
14
|
-
# define
|
15
|
-
#
|
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
|
-
|
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 =
|
144
|
-
.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)
|
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 =
|
198
|
-
.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
|
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
|
-
|
311
|
+
ARY2IOVEC(a.iov, a.nr_segs, left, data);
|
312
|
+
a.fd = my_fileno(fd);
|
313
|
+
a.flags = NUM2UINT(flags);
|
255
314
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
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
|
-
|
272
|
-
|
273
|
-
|
336
|
+
rv += n;
|
337
|
+
left -= n;
|
338
|
+
if (left == 0)
|
339
|
+
break;
|
340
|
+
advance_vmsplice_args(&a, n);
|
341
|
+
}
|
274
342
|
|
275
|
-
return LONG2NUM(
|
343
|
+
return LONG2NUM(rv);
|
276
344
|
}
|
277
345
|
|
278
346
|
void Init_io_splice_ext(void)
|
279
347
|
{
|
280
|
-
VALUE
|
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(
|
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)
|
295
|
-
* spliced from/to and has no effect on the non-pipe
|
296
|
-
* (which requires non-blocking operation to be set
|
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(
|
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(
|
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(
|
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(
|
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
|
-
|
6
|
+
module Splice
|
7
7
|
|
8
8
|
# the version of IO::Splice, currently 0.1.0
|
9
|
-
VERSION = '
|
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
|
-
|
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
|
-
|
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
|
-
$@
|
data/test/test_io_splice.rb
CHANGED
@@ -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(
|
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(
|
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(
|
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(
|
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(
|
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:
|
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-
|
12
|
+
date: 2010-05-27 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|