io_splice 4.2.0 → 4.3.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/.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
|