io_splice 4.2.0 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +0 -1
- data/README +1 -3
- data/Rakefile +0 -37
- data/examples/splice-cp.rb +3 -11
- data/ext/io_splice/extconf.rb +2 -0
- data/ext/io_splice/io_splice_ext.c +51 -37
- data/io_splice.gemspec +0 -1
- data/lib/io/splice.rb +21 -3
- data/test/test_io_splice.rb +0 -124
- metadata +2 -32
- data/test/test_copy_stream.rb +0 -340
- data/test/test_rack_file_compat.rb +0 -31
- data/test/test_tcp_splice.rb +0 -66
data/.gitignore
CHANGED
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
data/README
CHANGED
@@ -14,11 +14,9 @@ buffer.
|
|
14
14
|
arbitrary file descriptors (assuming kernel support), not just
|
15
15
|
file-to-socket (or file-to-anything in newer Linux).
|
16
16
|
|
17
|
-
* Thread-safe blocking operations under Ruby 1.9
|
17
|
+
* Thread-safe blocking operations under Ruby 1.9+, releases GVL
|
18
18
|
if blocking operations are used.
|
19
19
|
|
20
|
-
* Almost drop-in replacement for IO.copy_stream: IO::Splice.copy_stream
|
21
|
-
|
22
20
|
* Safely usable with non-blocking I/O frameworks (unlike IO.copy_stream)
|
23
21
|
when combined with the IO::Splice::F_NONBLOCK flag.
|
24
22
|
|
data/Rakefile
CHANGED
@@ -31,40 +31,3 @@ task :publish_news do
|
|
31
31
|
rf.login
|
32
32
|
rf.post_news('qrp', subject, body)
|
33
33
|
end
|
34
|
-
|
35
|
-
desc "post to RAA"
|
36
|
-
task :raa_update do
|
37
|
-
require 'net/http'
|
38
|
-
require 'net/netrc'
|
39
|
-
rc = Net::Netrc.locate('io_splice-raa') or abort "~/.netrc not found"
|
40
|
-
password = rc.password
|
41
|
-
|
42
|
-
s = Gem::Specification.load('io_splice.gemspec')
|
43
|
-
desc = [ s.description.strip ]
|
44
|
-
desc << ""
|
45
|
-
desc << "* #{s.email}"
|
46
|
-
desc << "* #{git_url}"
|
47
|
-
desc << "* #{cgit_url}"
|
48
|
-
desc = desc.join("\n")
|
49
|
-
uri = URI.parse('http://raa.ruby-lang.org/regist.rhtml')
|
50
|
-
form = {
|
51
|
-
:name => s.name,
|
52
|
-
:short_description => s.summary,
|
53
|
-
:version => s.version.to_s,
|
54
|
-
:status => 'stable',
|
55
|
-
:owner => s.authors.first,
|
56
|
-
:email => s.email,
|
57
|
-
:category_major => 'Library',
|
58
|
-
:category_minor => 'System',
|
59
|
-
:url => s.homepage,
|
60
|
-
:download => 'http://rubyforge.org/frs/?group_id=5626',
|
61
|
-
:license => "LGPL",
|
62
|
-
:description_style => 'Plain',
|
63
|
-
:description => desc,
|
64
|
-
:pass => password,
|
65
|
-
:submit => 'Update',
|
66
|
-
}
|
67
|
-
res = Net::HTTP.post_form(uri, form)
|
68
|
-
p res
|
69
|
-
puts res.body
|
70
|
-
end
|
data/examples/splice-cp.rb
CHANGED
@@ -1,13 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# -*- encoding: binary -*-
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# is never copied into userspace.
|
7
|
-
|
8
|
-
require 'io/splice'
|
9
|
-
|
10
|
-
usage = "#$0 SOURCE DEST"
|
11
|
-
source = ARGV.shift or abort usage
|
12
|
-
dest = ARGV.shift or abort usage
|
13
|
-
IO::Splice.copy_stream(source, dest)
|
3
|
+
# This example is no longer valid, IO.copy_stream is faster in Ruby 2.2+
|
4
|
+
# since it uses sendfile directly, which now allows direct file-to-file
|
5
|
+
# copying (on Linux only) with fewer syscalls than splice.
|
data/ext/io_splice/extconf.rb
CHANGED
@@ -3,7 +3,9 @@ $CPPFLAGS << ' -D_GNU_SOURCE=1'
|
|
3
3
|
|
4
4
|
have_func('splice', %w(fcntl.h)) or abort 'splice(2) not defined'
|
5
5
|
have_func('tee', %w(fcntl.h)) or abort 'tee(2) not defined'
|
6
|
+
have_func('pipe2', %w(fcntl.h unistd.h))
|
6
7
|
have_func('rb_thread_blocking_region')
|
8
|
+
have_func('rb_thread_call_without_gvl')
|
7
9
|
have_macro('F_GETPIPE_SZ', %w(fcntl.h))
|
8
10
|
have_macro('F_SETPIPE_SZ', %w(fcntl.h))
|
9
11
|
|
@@ -79,23 +79,32 @@ static int check_fileno(VALUE io)
|
|
79
79
|
errno = saved_errno;
|
80
80
|
return fd;
|
81
81
|
}
|
82
|
-
|
82
|
+
|
83
|
+
#if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && defined(HAVE_RUBY_THREAD_H)
|
84
|
+
/* Ruby 2.0+ */
|
85
|
+
# include <ruby/thread.h>
|
86
|
+
# define WITHOUT_GVL(fn,a,ubf,b) \
|
87
|
+
rb_thread_call_without_gvl((fn),(a),(ubf),(b))
|
88
|
+
#elif defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
89
|
+
typedef VALUE (*my_blocking_fn_t)(void*);
|
90
|
+
# define WITHOUT_GVL(fn,a,ubf,b) \
|
91
|
+
rb_thread_blocking_region((my_blocking_fn_t)(fn),(a),(ubf),(b))
|
92
|
+
|
93
|
+
#else /* Ruby 1.8 */
|
83
94
|
/* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
|
84
95
|
# include <rubysig.h>
|
85
96
|
# define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
|
86
97
|
typedef void rb_unblock_function_t(void *);
|
87
|
-
typedef
|
88
|
-
static
|
89
|
-
|
90
|
-
rb_blocking_function_t *fn, void *data1,
|
91
|
-
rb_unblock_function_t *ubf, void *data2)
|
98
|
+
typedef void * rb_blocking_function_t(void *);
|
99
|
+
static void * WITHOUT_GVL(rb_blocking_function_t *func, void *data1,
|
100
|
+
rb_unblock_function_t *ubf, void *data2)
|
92
101
|
{
|
93
|
-
|
102
|
+
void *rv;
|
94
103
|
|
95
104
|
assert(RUBY_UBF_IO == ubf && "RUBY_UBF_IO required for emulation");
|
96
105
|
|
97
106
|
TRAP_BEG;
|
98
|
-
rv =
|
107
|
+
rv = func(data1);
|
99
108
|
TRAP_END;
|
100
109
|
|
101
110
|
return rv;
|
@@ -108,17 +117,11 @@ rb_thread_blocking_region(
|
|
108
117
|
#ifndef RSTRING_LEN
|
109
118
|
# define RSTRING_LEN(s) (RSTRING(s)->len)
|
110
119
|
#endif
|
111
|
-
#ifndef RARRAY_PTR
|
112
|
-
# define RARRAY_PTR(s) (RARRAY(s)->ptr)
|
113
|
-
#endif
|
114
120
|
#ifndef RARRAY_LEN
|
115
121
|
# define RARRAY_LEN(s) (RARRAY(s)->len)
|
116
122
|
#endif
|
117
123
|
|
118
|
-
|
119
|
-
{
|
120
|
-
return rb_thread_blocking_region(fn, data, RUBY_UBF_IO, 0);
|
121
|
-
}
|
124
|
+
#define io_run(fn,data) WITHOUT_GVL((fn),(data),RUBY_UBF_IO,0)
|
122
125
|
|
123
126
|
struct splice_args {
|
124
127
|
int fd_in;
|
@@ -129,14 +132,14 @@ struct splice_args {
|
|
129
132
|
unsigned flags;
|
130
133
|
};
|
131
134
|
|
132
|
-
static
|
135
|
+
static void * nogvl_splice(void *ptr)
|
133
136
|
{
|
134
137
|
struct splice_args *a = ptr;
|
135
138
|
|
136
139
|
if (a->len > MAX_AT_ONCE)
|
137
140
|
a->len = MAX_AT_ONCE;
|
138
141
|
|
139
|
-
return (
|
142
|
+
return (void *)splice(a->fd_in, a->off_in, a->fd_out, a->off_out,
|
140
143
|
a->len, a->flags);
|
141
144
|
}
|
142
145
|
|
@@ -210,7 +213,6 @@ static ssize_t do_splice(int argc, VALUE *argv, unsigned dflags)
|
|
210
213
|
* * IO::Splice::F_MOVE
|
211
214
|
* * IO::Splice::F_NONBLOCK
|
212
215
|
* * IO::Splice::F_MORE
|
213
|
-
* * IO::Splice::WAITALL
|
214
216
|
*
|
215
217
|
* Returns the number of bytes spliced.
|
216
218
|
* Raises EOFError when +io_in+ has reached end of file.
|
@@ -278,14 +280,14 @@ struct tee_args {
|
|
278
280
|
};
|
279
281
|
|
280
282
|
/* runs without GVL */
|
281
|
-
static
|
283
|
+
static void * nogvl_tee(void *ptr)
|
282
284
|
{
|
283
285
|
struct tee_args *a = ptr;
|
284
286
|
|
285
287
|
if (a->len > MAX_AT_ONCE)
|
286
288
|
a->len = MAX_AT_ONCE;
|
287
289
|
|
288
|
-
return (
|
290
|
+
return (void *)tee(a->fd_in, a->fd_out, a->len, a->flags);
|
289
291
|
}
|
290
292
|
|
291
293
|
static ssize_t do_tee(int argc, VALUE *argv, unsigned dflags)
|
@@ -344,7 +346,6 @@ static ssize_t do_tee(int argc, VALUE *argv, unsigned dflags)
|
|
344
346
|
*
|
345
347
|
* +flags+ may be zero (the default) or a combination of:
|
346
348
|
* * IO::Splice::F_NONBLOCK
|
347
|
-
* * IO::Splice::WAITALL
|
348
349
|
*
|
349
350
|
* Other IO::Splice flags are currently unimplemented or have no effect.
|
350
351
|
*
|
@@ -406,30 +407,28 @@ struct vmsplice_args {
|
|
406
407
|
unsigned flags;
|
407
408
|
};
|
408
409
|
|
409
|
-
static
|
410
|
+
static void * nogvl_vmsplice(void *ptr)
|
410
411
|
{
|
411
412
|
struct vmsplice_args *a = ptr;
|
412
413
|
|
413
|
-
return (
|
414
|
+
return (void *)vmsplice(a->fd, a->iov, a->nr_segs, a->flags);
|
414
415
|
}
|
415
416
|
|
416
417
|
/* this can't be a function since we use alloca() */
|
417
418
|
#define ARY2IOVEC(iov,iovcnt,expect,ary) \
|
418
419
|
do { \
|
419
|
-
VALUE *cur; \
|
420
420
|
struct iovec *tmp; \
|
421
|
-
long
|
422
|
-
|
423
|
-
|
424
|
-
if (n > IOV_MAX) \
|
421
|
+
unsigned long i; \
|
422
|
+
iovcnt = (unsigned long)RARRAY_LEN(ary); \
|
423
|
+
if (iovcnt > IOV_MAX) \
|
425
424
|
rb_raise(rb_eArgError, "array is larger than IOV_MAX"); \
|
426
|
-
iov = tmp = alloca(sizeof(struct iovec) *
|
425
|
+
iov = tmp = alloca(sizeof(struct iovec) * iovcnt); \
|
427
426
|
expect = 0; \
|
428
|
-
|
429
|
-
|
430
|
-
Check_Type(
|
431
|
-
tmp->iov_base = RSTRING_PTR(
|
432
|
-
tmp->iov_len = RSTRING_LEN(
|
427
|
+
for (i = 0; i < iovcnt; tmp++, i++) { \
|
428
|
+
VALUE cur = rb_ary_entry(ary, (long)i); \
|
429
|
+
Check_Type(cur, T_STRING); \
|
430
|
+
tmp->iov_base = RSTRING_PTR(cur); \
|
431
|
+
tmp->iov_len = RSTRING_LEN(cur); \
|
433
432
|
expect += tmp->iov_len; \
|
434
433
|
} \
|
435
434
|
} while (0)
|
@@ -619,8 +618,14 @@ static VALUE set_pipe_size(VALUE self, VALUE size)
|
|
619
618
|
|
620
619
|
static int can_mod_pipe_size(void)
|
621
620
|
{
|
621
|
+
/*
|
622
|
+
* pipe2 appeared in Linux 2.6.27, F_*PIPE_SZ appeared in 2.6.35,
|
623
|
+
* thus not having pipe2 automatically disqualifies us from having
|
624
|
+
* F_*PIPE_SZ support
|
625
|
+
*/
|
626
|
+
#ifdef HAVE_PIPE2
|
622
627
|
int fds[2];
|
623
|
-
int rc =
|
628
|
+
int rc = pipe2(fds, O_CLOEXEC);
|
624
629
|
|
625
630
|
if (rc == 0) {
|
626
631
|
rc = fcntl(fds[0], F_GETPIPE_SZ);
|
@@ -629,13 +634,22 @@ static int can_mod_pipe_size(void)
|
|
629
634
|
(void)close(fds[0]);
|
630
635
|
(void)close(fds[1]);
|
631
636
|
} else {
|
632
|
-
/*
|
637
|
+
/*
|
638
|
+
* weird error, but don't raise during init, this could be
|
639
|
+
* ENOSYS, even..
|
640
|
+
*/
|
633
641
|
rc = 0;
|
634
642
|
}
|
635
643
|
errno = 0;
|
636
644
|
return rc;
|
645
|
+
#else /* ! HAVE_PIPE2 */
|
646
|
+
return 0;
|
647
|
+
#endif /* ! HAVE_PIPE2 */
|
637
648
|
}
|
638
649
|
|
650
|
+
#define NODOC_CONST(klass,name,value) \
|
651
|
+
rb_define_const((klass),(name),(value))
|
652
|
+
|
639
653
|
void Init_io_splice_ext(void)
|
640
654
|
{
|
641
655
|
VALUE mSplice = rb_define_module_under(rb_cIO, "Splice");
|
@@ -696,7 +710,7 @@ void Init_io_splice_ext(void)
|
|
696
710
|
*
|
697
711
|
* IO.vmsplice always defaults to this behavior.
|
698
712
|
*/
|
699
|
-
|
713
|
+
NODOC_CONST(mSplice, "WAITALL", UINT2NUM(WAITALL));
|
700
714
|
|
701
715
|
/*
|
702
716
|
* The maximum size of an atomic write to a pipe
|
data/io_splice.gemspec
CHANGED
@@ -21,7 +21,6 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.rubyforge_project = %q{qrp}
|
22
22
|
s.test_files = Dir['test/test_*.rb']
|
23
23
|
s.add_development_dependency('wrongdoc', '~> 1.5')
|
24
|
-
s.add_development_dependency('rack', '~> 1.2')
|
25
24
|
|
26
25
|
# s.licenses = %w(LGPL) # accessor not compatible with older RubyGems
|
27
26
|
end
|
data/lib/io/splice.rb
CHANGED
@@ -3,6 +3,14 @@ require 'io_splice_ext'
|
|
3
3
|
require 'io/wait'
|
4
4
|
|
5
5
|
module IO::Splice
|
6
|
+
@warned = false
|
7
|
+
|
8
|
+
def self.__deprecated
|
9
|
+
return if @warned
|
10
|
+
warn("IO::Splice.{copy_stream,full} are deprecated " \
|
11
|
+
"and to be removed in io_splice 5.x")
|
12
|
+
@warned = true
|
13
|
+
end
|
6
14
|
|
7
15
|
# The maximum default capacity of the pipe in bytes.
|
8
16
|
# Under stock Linux, this is 65536 bytes as of 2.6.11, and 4096 before
|
@@ -36,10 +44,19 @@ module IO::Splice
|
|
36
44
|
# Otherwise the copy will be until EOF is reached on the +src+.
|
37
45
|
# +src+ and +dst+ must be IO objects or respond to +to_io+
|
38
46
|
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
47
|
+
# Unlike IO.copy_stream, this does not take into account
|
48
|
+
# userspace I/O buffers nor IO-like objects with no underlying
|
49
|
+
# file descriptor (e.g. StringIO).
|
50
|
+
#
|
51
|
+
# This is unsafe for socket-to-socket copies unless there is an
|
52
|
+
# active (blocking) reader on the other end.
|
53
|
+
#
|
54
|
+
# This method is deprecated and will be removed in a future, as it is
|
55
|
+
# potentially unsafe for socket-to-socket operations and difficult-to-use.
|
56
|
+
# IO.copy_stream on Linux 2.6.33 and later allows using sendfile for
|
57
|
+
# file-to-file copies, so this offers no advantage.
|
42
58
|
def self.copy_stream(src, dst, len = nil, src_offset = nil)
|
59
|
+
__deprecated
|
43
60
|
close = []
|
44
61
|
need_open?(src) and close << (src = File.open(src))
|
45
62
|
need_open?(dst) and close << (dst = File.open(dst, "w"))
|
@@ -90,6 +107,7 @@ module IO::Splice
|
|
90
107
|
#
|
91
108
|
# This method is safe for splicing a pipe +src+ into any type of +dst+ IO.
|
92
109
|
def self.full(src, dst, len, src_offset)
|
110
|
+
__deprecated
|
93
111
|
IO.splice(src, src_offset, dst, nil, len, F_MOVE | WAITALL)
|
94
112
|
end
|
95
113
|
|
data/test/test_io_splice.rb
CHANGED
@@ -332,130 +332,6 @@ class Test_IO_Splice < Test::Unit::TestCase
|
|
332
332
|
assert IO::Splice::PIPE_CAPA >= IO::Splice::PIPE_BUF
|
333
333
|
end
|
334
334
|
|
335
|
-
def test_splice_copy_stream_file_to_file_small
|
336
|
-
a, b = Tempfile.new('a'), Tempfile.new('b')
|
337
|
-
a.syswrite 'hello world'
|
338
|
-
a.sysseek(0)
|
339
|
-
IO::Splice.copy_stream(a, b)
|
340
|
-
b.rewind
|
341
|
-
assert_equal 'hello world', b.read
|
342
|
-
end
|
343
|
-
|
344
|
-
def test_splice_copy_stream_file_to_file_big
|
345
|
-
buf = ('ab' * IO::Splice::PIPE_CAPA) + 'hi'
|
346
|
-
a, b = Tempfile.new('a'), Tempfile.new('b')
|
347
|
-
a.syswrite buf
|
348
|
-
a.sysseek(0)
|
349
|
-
IO::Splice.copy_stream(a, b)
|
350
|
-
b.rewind
|
351
|
-
assert_equal buf, b.read
|
352
|
-
end
|
353
|
-
|
354
|
-
def test_splice_copy_stream_file_to_file_big_partial
|
355
|
-
nr = IO::Splice::PIPE_CAPA
|
356
|
-
buf = ('ab' * nr) + 'hi'
|
357
|
-
a, b = Tempfile.new('a'), Tempfile.new('b')
|
358
|
-
a.syswrite buf
|
359
|
-
a.sysseek(0)
|
360
|
-
assert_equal nr, IO::Splice.copy_stream(a, b, nr)
|
361
|
-
b.rewind
|
362
|
-
assert_equal('ab' * (nr/2), b.read)
|
363
|
-
end
|
364
|
-
|
365
|
-
def test_splice_copy_stream_file_to_file_len
|
366
|
-
a, b = Tempfile.new('a'), Tempfile.new('b')
|
367
|
-
a.syswrite 'hello world'
|
368
|
-
a.sysseek(0)
|
369
|
-
IO::Splice.copy_stream(a, b, 5)
|
370
|
-
b.rewind
|
371
|
-
assert_equal 'hello', b.read
|
372
|
-
end
|
373
|
-
|
374
|
-
def test_splice_copy_stream_pipe_to_file_len
|
375
|
-
a = Tempfile.new('a')
|
376
|
-
r, w = IO.pipe
|
377
|
-
w.syswrite 'hello world'
|
378
|
-
IO::Splice.copy_stream(r, a, 5)
|
379
|
-
a.rewind
|
380
|
-
assert_equal 'hello', a.read
|
381
|
-
end
|
382
|
-
|
383
|
-
def test_splice_copy_stream_paths
|
384
|
-
a = Tempfile.new('a')
|
385
|
-
b = Tempfile.new('a')
|
386
|
-
a.syswrite('hello world')
|
387
|
-
IO::Splice.copy_stream(a.path, b.path, 5)
|
388
|
-
assert_equal 'hello', b.read
|
389
|
-
end
|
390
|
-
|
391
|
-
def test_splice_copy_stream_src_offset
|
392
|
-
a = Tempfile.new('a')
|
393
|
-
b = Tempfile.new('a')
|
394
|
-
a.syswrite('hello world')
|
395
|
-
IO::Splice.copy_stream(a.path, b.path, 5, 6)
|
396
|
-
assert_equal 'world', b.read
|
397
|
-
end
|
398
|
-
|
399
|
-
def test_splice_copy_stream_src_offset_unchanged
|
400
|
-
a = Tempfile.new('a')
|
401
|
-
b = Tempfile.new('a')
|
402
|
-
a.syswrite('hello world')
|
403
|
-
assert_equal 0, a.sysseek(0, IO::SEEK_SET)
|
404
|
-
IO::Splice.copy_stream(a, b.path, 5, 6)
|
405
|
-
assert_equal 'world', b.read
|
406
|
-
assert_equal 0, a.sysseek(0, IO::SEEK_CUR)
|
407
|
-
end
|
408
|
-
|
409
|
-
def test_copy_stream_nonblock_src
|
410
|
-
server = TCPServer.new('127.0.0.1', 0)
|
411
|
-
port = server.addr[1]
|
412
|
-
rp, wp = IO.pipe
|
413
|
-
rs = TCPSocket.new('127.0.0.1', port)
|
414
|
-
rs.nonblock = true
|
415
|
-
nr = 0
|
416
|
-
assert_raises(Timeout::Error) do
|
417
|
-
timeout(0.05) { nr += IO::Splice.copy_stream(rs, wp, 5) }
|
418
|
-
end
|
419
|
-
assert_equal 0, nr
|
420
|
-
rs.close
|
421
|
-
server.close
|
422
|
-
end if mri?
|
423
|
-
|
424
|
-
def test_copy_stream_nonblock_dst
|
425
|
-
server = TCPServer.new('127.0.0.1', 0)
|
426
|
-
port = server.addr[1]
|
427
|
-
rp, wp = IO.pipe
|
428
|
-
rs = TCPSocket.new('127.0.0.1', port)
|
429
|
-
rs.nonblock = true
|
430
|
-
client = server.accept
|
431
|
-
buf = ' ' * IO::Splice::PIPE_CAPA
|
432
|
-
nr = 0
|
433
|
-
assert_raises(Timeout::Error) do
|
434
|
-
loop do
|
435
|
-
begin
|
436
|
-
wp.write_nonblock(buf)
|
437
|
-
rescue Errno::EAGAIN
|
438
|
-
end
|
439
|
-
timeout(0.05) do
|
440
|
-
nr += IO::Splice.copy_stream(rp, rs, IO::Splice::PIPE_CAPA)
|
441
|
-
end
|
442
|
-
end
|
443
|
-
end
|
444
|
-
assert_equal nr, client.read(nr).size
|
445
|
-
rs.close
|
446
|
-
server.close
|
447
|
-
end if mri?
|
448
|
-
|
449
|
-
def test_copy_stream_eof
|
450
|
-
r, w = IO.pipe
|
451
|
-
w.syswrite 'hello world'
|
452
|
-
w.close
|
453
|
-
a = Tempfile.new('a')
|
454
|
-
assert_equal 11, IO::Splice.copy_stream(r, a)
|
455
|
-
a.rewind
|
456
|
-
assert_equal 'hello world', a.read
|
457
|
-
end
|
458
|
-
|
459
335
|
def test_pipe_size
|
460
336
|
r, w = IO.pipe
|
461
337
|
assert_kind_of Integer, r.pipe_size
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: !binary |-
|
3
3
|
aW9fc3BsaWNl
|
4
4
|
version: !ruby/object:Gem::Version
|
5
|
-
version: 4.
|
5
|
+
version: 4.3.0
|
6
6
|
prerelease:
|
7
7
|
platform: ruby
|
8
8
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2014-02-15 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: !binary |-
|
@@ -34,27 +34,6 @@ dependencies:
|
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: !binary |-
|
36
36
|
MS41
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: !binary |-
|
39
|
-
cmFjaw==
|
40
|
-
requirement: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - !binary |-
|
44
|
-
fj4=
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: !binary |-
|
47
|
-
MS4y
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
none: false
|
52
|
-
requirements:
|
53
|
-
- - !binary |-
|
54
|
-
fj4=
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version: !binary |-
|
57
|
-
MS4y
|
58
37
|
description: ! 'The splice family of Linux system calls can transfer data between
|
59
38
|
file
|
60
39
|
|
@@ -103,12 +82,9 @@ files:
|
|
103
82
|
- local.mk.sample
|
104
83
|
- pkg.mk
|
105
84
|
- setup.rb
|
106
|
-
- test/test_copy_stream.rb
|
107
85
|
- test/test_io_splice.rb
|
108
86
|
- test/test_io_splice_eintr.rb
|
109
87
|
- test/test_io_splice_in_full.rb
|
110
|
-
- test/test_rack_file_compat.rb
|
111
|
-
- test/test_tcp_splice.rb
|
112
88
|
homepage: http://bogomips.org/ruby_io_splice/
|
113
89
|
licenses: []
|
114
90
|
post_install_message:
|
@@ -139,15 +115,9 @@ signing_key:
|
|
139
115
|
specification_version: 3
|
140
116
|
summary: zero-copy pipe I/O for Linux and Ruby
|
141
117
|
test_files:
|
142
|
-
- !binary |-
|
143
|
-
dGVzdC90ZXN0X3JhY2tfZmlsZV9jb21wYXQucmI=
|
144
118
|
- !binary |-
|
145
119
|
dGVzdC90ZXN0X2lvX3NwbGljZV9pbl9mdWxsLnJi
|
146
|
-
- !binary |-
|
147
|
-
dGVzdC90ZXN0X3RjcF9zcGxpY2UucmI=
|
148
120
|
- !binary |-
|
149
121
|
dGVzdC90ZXN0X2lvX3NwbGljZS5yYg==
|
150
|
-
- !binary |-
|
151
|
-
dGVzdC90ZXN0X2NvcHlfc3RyZWFtLnJi
|
152
122
|
- !binary |-
|
153
123
|
dGVzdC90ZXN0X2lvX3NwbGljZV9laW50ci5yYg==
|
data/test/test_copy_stream.rb
DELETED
@@ -1,340 +0,0 @@
|
|
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
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# -*- encoding: binary -*-
|
2
|
-
require "rack"
|
3
|
-
require "test/unit"
|
4
|
-
require "socket"
|
5
|
-
require "io/splice"
|
6
|
-
|
7
|
-
class TestRackFileCompat < Test::Unit::TestCase
|
8
|
-
def setup
|
9
|
-
@app = Rack::File.new(File.dirname(__FILE__))
|
10
|
-
@req = Rack::MockRequest.new(@app)
|
11
|
-
@base_file = File.basename(__FILE__)
|
12
|
-
@r, @w = UNIXSocket.pair
|
13
|
-
end
|
14
|
-
|
15
|
-
def teardown
|
16
|
-
[ @r, @w ].each { |io| io.closed? or io.close }
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_get_rack_file
|
20
|
-
env = Rack::MockRequest.env_for "http://example.com/#@base_file"
|
21
|
-
status, headers, body = @app.call(env)
|
22
|
-
assert_equal 200, status.to_i
|
23
|
-
headers.each { |k,v|
|
24
|
-
assert_instance_of String, k.to_str
|
25
|
-
assert_instance_of String, v.to_str
|
26
|
-
}
|
27
|
-
thr = Thread.new { @r.read(File.size(__FILE__)) }
|
28
|
-
assert_equal File.size(__FILE__), IO::Splice.copy_stream(body, @w)
|
29
|
-
assert_equal File.read(__FILE__), thr.value
|
30
|
-
end
|
31
|
-
end if IO.respond_to?(:copy_stream)
|
data/test/test_tcp_splice.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require 'io/wait'
|
3
|
-
require 'io/splice'
|
4
|
-
require 'io/nonblock'
|
5
|
-
require "test/unit"
|
6
|
-
|
7
|
-
class TestTCPCopyStream < Test::Unit::TestCase
|
8
|
-
def setup
|
9
|
-
host = ENV["TEST_HOST"] || "127.0.0.1"
|
10
|
-
@srv = TCPServer.new(host, 0)
|
11
|
-
@port = @srv.addr[1]
|
12
|
-
@client = TCPSocket.new(host, @port)
|
13
|
-
@client.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
14
|
-
@accept = @srv.accept
|
15
|
-
@accept.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
16
|
-
@client.sync = @accept.sync = true
|
17
|
-
@r, @w = IO.pipe
|
18
|
-
end
|
19
|
-
|
20
|
-
def teardown
|
21
|
-
@srv.close
|
22
|
-
[ @client, @accept, @r, @w ].each { |io| io.close unless io.closed? }
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_client_to_server_eof
|
26
|
-
nr = 2000
|
27
|
-
buf = '0123456789abcdef' * 1024
|
28
|
-
expect = buf.size * nr
|
29
|
-
thr = Thread.new do
|
30
|
-
nr.times { @client.write(buf) }
|
31
|
-
@client.close
|
32
|
-
end
|
33
|
-
sleep 1 # wait for rcvbuf to fill up
|
34
|
-
bytes = IO::Splice.copy_stream(@accept, "/dev/null")
|
35
|
-
assert_equal expect, bytes
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_client_to_server_expect
|
39
|
-
nr = 2000
|
40
|
-
buf = '0123456789abcdef' * 1024
|
41
|
-
expect = buf.size * nr
|
42
|
-
thr = Thread.new do
|
43
|
-
nr.times { @client.write(buf) }
|
44
|
-
end
|
45
|
-
sleep 1 # wait for rcvbuf to fill up
|
46
|
-
bytes = IO::Splice.copy_stream(@accept, "/dev/null", expect)
|
47
|
-
assert_equal expect, bytes
|
48
|
-
end
|
49
|
-
|
50
|
-
def test_mega_splice
|
51
|
-
nr = 2000
|
52
|
-
buf = '0123456789abcdef' * 1024
|
53
|
-
expect = buf.size * nr
|
54
|
-
thr = Thread.new do
|
55
|
-
nr.times { @client.write(buf) }
|
56
|
-
@client.close
|
57
|
-
end
|
58
|
-
size_t_max = if (1 << 30).kind_of?(Bignum)
|
59
|
-
0xffffffff
|
60
|
-
else
|
61
|
-
0xffffffffffffffff
|
62
|
-
end
|
63
|
-
bytes = IO::Splice.copy_stream(@accept, "/dev/null", size_t_max)
|
64
|
-
assert_equal expect, bytes
|
65
|
-
end
|
66
|
-
end
|