sleepy_penguin 3.4.1 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.document +1 -0
- data/.olddoc.yml +3 -4
- data/GIT-VERSION-GEN +1 -1
- data/LICENSE +3 -3
- data/README +7 -4
- data/TODO +1 -0
- data/ext/sleepy_penguin/cfr.c +62 -0
- data/ext/sleepy_penguin/epoll.c +34 -24
- data/ext/sleepy_penguin/eventfd.c +6 -5
- data/ext/sleepy_penguin/extconf.rb +6 -0
- data/ext/sleepy_penguin/init.c +83 -12
- data/ext/sleepy_penguin/inotify.c +48 -36
- data/ext/sleepy_penguin/kqueue.c +22 -21
- data/ext/sleepy_penguin/sendfile.c +120 -0
- data/ext/sleepy_penguin/sleepy_penguin.h +15 -28
- data/ext/sleepy_penguin/sp_copy.h +33 -0
- data/ext/sleepy_penguin/splice.c +174 -0
- data/ext/sleepy_penguin/timerfd.c +1 -5
- data/ext/sleepy_penguin/util.c +12 -0
- data/lib/sleepy_penguin.rb +28 -0
- data/lib/sleepy_penguin/cfr.rb +29 -0
- data/lib/sleepy_penguin/epoll.rb +13 -10
- data/lib/sleepy_penguin/kqueue.rb +6 -6
- data/lib/sleepy_penguin/sp.rb +1 -1
- data/lib/sleepy_penguin/splice.rb +125 -0
- data/pkg.mk +5 -12
- data/sleepy_penguin.gemspec +13 -15
- data/test/helper.rb +2 -7
- data/test/test_cfr.rb +35 -0
- data/test/test_constants.rb +2 -4
- data/test/test_epoll.rb +35 -6
- data/test/test_epoll_gc.rb +2 -5
- data/test/test_epoll_io.rb +3 -6
- data/test/test_epoll_optimizations.rb +2 -2
- data/test/test_eventfd.rb +2 -5
- data/test/test_inotify.rb +2 -4
- data/test/test_kqueue.rb +35 -7
- data/test/test_kqueue_io.rb +2 -5
- data/test/test_pipesize.rb +22 -0
- data/test/test_sendfile.rb +26 -0
- data/test/test_splice.rb +250 -0
- data/test/test_splice_eintr.rb +31 -0
- data/test/test_timerfd.rb +2 -5
- metadata +27 -34
- data/lib/sleepy_penguin/epoll/io.rb +0 -28
- data/lib/sleepy_penguin/kqueue/io.rb +0 -30
@@ -0,0 +1,174 @@
|
|
1
|
+
#include "sleepy_penguin.h"
|
2
|
+
#include "sp_copy.h"
|
3
|
+
#ifdef HAVE_SPLICE
|
4
|
+
#include <errno.h>
|
5
|
+
#include <fcntl.h>
|
6
|
+
#include <assert.h>
|
7
|
+
#include <sys/uio.h>
|
8
|
+
#include <unistd.h>
|
9
|
+
|
10
|
+
static VALUE sym_EAGAIN;
|
11
|
+
|
12
|
+
#ifndef F_LINUX_SPECIFIC_BASE
|
13
|
+
# define F_LINUX_SPECIFIC_BASE 1024
|
14
|
+
#endif
|
15
|
+
|
16
|
+
#ifndef F_GETPIPE_SZ
|
17
|
+
# define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
|
18
|
+
# define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
|
19
|
+
#endif
|
20
|
+
|
21
|
+
static int check_fileno(VALUE io)
|
22
|
+
{
|
23
|
+
int saved_errno = errno;
|
24
|
+
int fd = rb_sp_fileno(io);
|
25
|
+
errno = saved_errno;
|
26
|
+
return fd;
|
27
|
+
}
|
28
|
+
|
29
|
+
static void *nogvl_splice(void *ptr)
|
30
|
+
{
|
31
|
+
struct copy_args *a = ptr;
|
32
|
+
|
33
|
+
return (void *)splice(a->fd_in, a->off_in, a->fd_out, a->off_out,
|
34
|
+
a->len, a->flags);
|
35
|
+
}
|
36
|
+
|
37
|
+
/* :nodoc: */
|
38
|
+
static VALUE my_splice(VALUE mod, VALUE io_in, VALUE off_in,
|
39
|
+
VALUE io_out, VALUE off_out,
|
40
|
+
VALUE len, VALUE flags)
|
41
|
+
{
|
42
|
+
off_t i = 0, o = 0;
|
43
|
+
struct copy_args a;
|
44
|
+
ssize_t bytes;
|
45
|
+
|
46
|
+
a.off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i);
|
47
|
+
a.off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o);
|
48
|
+
a.len = NUM2SIZET(len);
|
49
|
+
a.flags = NUM2UINT(flags);
|
50
|
+
|
51
|
+
for (;;) {
|
52
|
+
a.fd_in = check_fileno(io_in);
|
53
|
+
a.fd_out = check_fileno(io_out);
|
54
|
+
bytes = (ssize_t)IO_RUN(nogvl_splice, &a);
|
55
|
+
if (bytes == 0) return Qnil;
|
56
|
+
if (bytes < 0) {
|
57
|
+
switch (errno) {
|
58
|
+
case EINTR: continue;
|
59
|
+
case EAGAIN: return sym_EAGAIN;
|
60
|
+
default: rb_sys_fail("splice");
|
61
|
+
}
|
62
|
+
}
|
63
|
+
return SSIZET2NUM(bytes);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
struct tee_args {
|
68
|
+
int fd_in;
|
69
|
+
int fd_out;
|
70
|
+
size_t len;
|
71
|
+
unsigned flags;
|
72
|
+
};
|
73
|
+
|
74
|
+
/* runs without GVL */
|
75
|
+
static void *nogvl_tee(void *ptr)
|
76
|
+
{
|
77
|
+
struct tee_args *a = ptr;
|
78
|
+
|
79
|
+
return (void *)tee(a->fd_in, a->fd_out, a->len, a->flags);
|
80
|
+
}
|
81
|
+
|
82
|
+
/* :nodoc: */
|
83
|
+
static VALUE my_tee(VALUE mod, VALUE io_in, VALUE io_out,
|
84
|
+
VALUE len, VALUE flags)
|
85
|
+
{
|
86
|
+
struct tee_args a;
|
87
|
+
ssize_t bytes;
|
88
|
+
|
89
|
+
a.len = (size_t)NUM2SIZET(len);
|
90
|
+
a.flags = NUM2UINT(flags);
|
91
|
+
|
92
|
+
for (;;) {
|
93
|
+
a.fd_in = check_fileno(io_in);
|
94
|
+
a.fd_out = check_fileno(io_out);
|
95
|
+
bytes = (ssize_t)IO_RUN(nogvl_tee, &a);
|
96
|
+
if (bytes == 0) return Qnil;
|
97
|
+
if (bytes < 0) {
|
98
|
+
switch (errno) {
|
99
|
+
case EINTR: continue;
|
100
|
+
case EAGAIN: return sym_EAGAIN;
|
101
|
+
default: rb_sys_fail("tee");
|
102
|
+
}
|
103
|
+
}
|
104
|
+
return SSIZET2NUM(bytes);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
void sleepy_penguin_init_splice(void)
|
109
|
+
{
|
110
|
+
VALUE mod = rb_define_module("SleepyPenguin");
|
111
|
+
rb_define_singleton_method(mod, "__splice", my_splice, 6);
|
112
|
+
rb_define_singleton_method(mod, "__tee", my_tee, 4);
|
113
|
+
|
114
|
+
/*
|
115
|
+
* Attempt to move pages instead of copying. This is only a hint
|
116
|
+
* and support for it was removed in Linux 2.6.21. It will be
|
117
|
+
* re-added for FUSE filesystems only in Linux 2.6.35.
|
118
|
+
*/
|
119
|
+
rb_define_const(mod, "F_MOVE", UINT2NUM(SPLICE_F_MOVE));
|
120
|
+
|
121
|
+
/*
|
122
|
+
* Do not block on pipe I/O. This flag only affects the pipe(s)
|
123
|
+
* being spliced from/to and has no effect on the non-pipe
|
124
|
+
* descriptor (which requires non-blocking operation to be set
|
125
|
+
* explicitly).
|
126
|
+
*
|
127
|
+
* The non-blocking flag (O_NONBLOCK) on the pipe descriptors
|
128
|
+
* themselves are ignored by this family of functions, and
|
129
|
+
* using this flag is the only way to get non-blocking operation
|
130
|
+
* out of them.
|
131
|
+
*
|
132
|
+
* It is highly recommended this flag be set
|
133
|
+
* (or SleepyPenguin.trysplice used)
|
134
|
+
* whenever splicing from a socket into a pipe unless there is
|
135
|
+
* another (native) thread or process doing a blocking read on that
|
136
|
+
* pipe. Otherwise it is possible to block a single-threaded process
|
137
|
+
* if the socket buffers are larger than the pipe buffers.
|
138
|
+
*/
|
139
|
+
rb_define_const(mod, "F_NONBLOCK", UINT2NUM(SPLICE_F_NONBLOCK));
|
140
|
+
|
141
|
+
/*
|
142
|
+
* Indicate that there may be more data coming into the outbound
|
143
|
+
* descriptor. This can allow the kernel to avoid sending partial
|
144
|
+
* frames from sockets. Currently only used with splice.
|
145
|
+
*/
|
146
|
+
rb_define_const(mod, "F_MORE", UINT2NUM(SPLICE_F_MORE));
|
147
|
+
|
148
|
+
/*
|
149
|
+
* fcntl() command constant used to return the size of a pipe.
|
150
|
+
* This constant is only defined when running Linux 2.6.35
|
151
|
+
* or later.
|
152
|
+
*
|
153
|
+
* require 'fcntl'
|
154
|
+
* r, w = IO.pipe
|
155
|
+
* r.fcntl(SleepyPenguin::F_GETPIPE_SZ) => Integer
|
156
|
+
*/
|
157
|
+
rb_define_const(mod, "F_GETPIPE_SZ", UINT2NUM(F_GETPIPE_SZ));
|
158
|
+
|
159
|
+
/*
|
160
|
+
* fcntl() command constant used to set the size of a pipe.
|
161
|
+
* This constant is only defined when running Linux 2.6.35
|
162
|
+
* or later.
|
163
|
+
*
|
164
|
+
* call-seq:
|
165
|
+
*
|
166
|
+
* require 'fcntl'
|
167
|
+
* r, w = IO.pipe
|
168
|
+
* r.fcntl(SleepyPenguin::F_SETPIPE_SZ, 131072)
|
169
|
+
*/
|
170
|
+
rb_define_const(mod, "F_SETPIPE_SZ", UINT2NUM(F_SETPIPE_SZ));
|
171
|
+
|
172
|
+
sym_EAGAIN = ID2SYM(rb_intern("EAGAIN"));
|
173
|
+
}
|
174
|
+
#endif
|
@@ -31,10 +31,8 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
|
|
31
31
|
|
32
32
|
fd = timerfd_create(clockid, flags);
|
33
33
|
if (fd < 0) {
|
34
|
-
if (errno
|
35
|
-
rb_gc();
|
34
|
+
if (rb_sp_gc_for_fd(errno))
|
36
35
|
fd = timerfd_create(clockid, flags);
|
37
|
-
}
|
38
36
|
if (fd < 0)
|
39
37
|
rb_sys_fail("timerfd_create");
|
40
38
|
}
|
@@ -122,8 +120,6 @@ static VALUE expirations(int argc, VALUE *argv, VALUE self)
|
|
122
120
|
rb_scan_args(argc, argv, "01", &nonblock);
|
123
121
|
if (RTEST(nonblock))
|
124
122
|
rb_sp_set_nonblock(fd);
|
125
|
-
else
|
126
|
-
blocking_io_prepare(fd);
|
127
123
|
retry:
|
128
124
|
r = (ssize_t)rb_sp_fd_region(tfd_read, &buf, fd);
|
129
125
|
if (r < 0) {
|
data/ext/sleepy_penguin/util.c
CHANGED
@@ -118,6 +118,9 @@ int rb_sp_fileno(VALUE io)
|
|
118
118
|
{
|
119
119
|
rb_io_t *fptr;
|
120
120
|
|
121
|
+
if (RB_TYPE_P(io, T_FIXNUM))
|
122
|
+
return FIX2INT(io);
|
123
|
+
|
121
124
|
io = rb_io_get_io(io);
|
122
125
|
GetOpenFile(io, fptr);
|
123
126
|
return FPTR_TO_FD(fptr);
|
@@ -153,3 +156,12 @@ int rb_sp_wait(rb_sp_waitfn waiter, VALUE obj, int *fd)
|
|
153
156
|
*fd = rb_sp_fileno(obj);
|
154
157
|
return rc;
|
155
158
|
}
|
159
|
+
|
160
|
+
int rb_sp_gc_for_fd(int err)
|
161
|
+
{
|
162
|
+
if (err == EMFILE || err == ENFILE || err == ENOMEM) {
|
163
|
+
rb_gc();
|
164
|
+
return 1;
|
165
|
+
}
|
166
|
+
return 0;
|
167
|
+
}
|
data/lib/sleepy_penguin.rb
CHANGED
@@ -15,3 +15,31 @@ if defined?(SleepyPenguin::Inotify) &&
|
|
15
15
|
# :startdoc
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
module SleepyPenguin
|
20
|
+
require_relative 'sleepy_penguin/splice' if respond_to?(:__splice)
|
21
|
+
require_relative 'sleepy_penguin/cfr' if respond_to?(:__cfr)
|
22
|
+
require_relative 'sleepy_penguin/epoll' if const_defined?(:Epoll)
|
23
|
+
require_relative 'sleepy_penguin/kqueue' if const_defined?(:Kqueue)
|
24
|
+
|
25
|
+
# Copies +len+ bytes from +src+ to +dst+, where +src+ refers to
|
26
|
+
# an open, mmap(2)-able File and +dst+ refers to a Socket.
|
27
|
+
# An optional +offset+ keyword may be specified for the +src+ File.
|
28
|
+
# Using +offset+ will not adjust the offset of the underlying file
|
29
|
+
# handle itself; in other words: this allows concurrent threads to
|
30
|
+
# use linux_sendfile to write data from one open file to multiple
|
31
|
+
# sockets.
|
32
|
+
#
|
33
|
+
# Returns the number of bytes written on success, or :wait_writable
|
34
|
+
# if the +dst+ Socket is non-blocking and the operation would block.
|
35
|
+
# A return value of zero bytes indicates EOF is reached on the +src+
|
36
|
+
# file.
|
37
|
+
#
|
38
|
+
# Newer OSes may be more flexible in whether or not +dst+ or +src+
|
39
|
+
# is a regular file or socket, respectively.
|
40
|
+
#
|
41
|
+
# This method was added in sleepy_penguin 3.5.0.
|
42
|
+
def self.linux_sendfile(dst, src, len, offset: nil)
|
43
|
+
__lsf(dst, src, offset, len)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SleepyPenguin
|
2
|
+
|
3
|
+
# call-seq:
|
4
|
+
# SleepyPenguin.copy_file_range(src, dst, len[, keywords]) => # Integer
|
5
|
+
#
|
6
|
+
# Performs and in-kernel copy of +len+ bytes from +src+ to +dst+,
|
7
|
+
# where +src+ and +dst+ are regular files on the same filesystem.
|
8
|
+
# Returns the number of bytes copied, which may be less than
|
9
|
+
# requested.
|
10
|
+
#
|
11
|
+
# +flags+ is currently unused, but may be specified in the future.
|
12
|
+
#
|
13
|
+
# Keywords:
|
14
|
+
#
|
15
|
+
# :off_in and :off_out if non-nil may be used to specify an Integer
|
16
|
+
# offset for each respective descriptor. If specified, the file
|
17
|
+
# offsets of each file description will not be moved, providing
|
18
|
+
# pread(2)/pwrite(2)-like semantics.
|
19
|
+
#
|
20
|
+
# See copy_file_range(2) manpage for full documentation:
|
21
|
+
# http://man7.org/linux/man-pages/man2/copy_file_range.2.html
|
22
|
+
#
|
23
|
+
# This method only works in Linux 4.5+ with sleepy_penguin 3.5.0+,
|
24
|
+
# and may require up-to-date kernel headers for non-x86/x86-64 systems.
|
25
|
+
def self.copy_file_range(io_in, io_out, len, flags = 0,
|
26
|
+
off_in: nil, off_out: nil)
|
27
|
+
__cfr(io_in, off_in, io_out, off_out, len, flags)
|
28
|
+
end
|
29
|
+
end
|
data/lib/sleepy_penguin/epoll.rb
CHANGED
@@ -45,13 +45,16 @@ class SleepyPenguin::Epoll
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
# Calls epoll_wait(2) and yields Integer +events+ and IO objects watched
|
48
|
+
# Calls epoll_wait(2) and yields Integer +events+ and +IO+ objects watched
|
49
49
|
# for. +maxevents+ is the maximum number of events to process at once,
|
50
50
|
# lower numbers may prevent starvation when used by epoll_wait in multiple
|
51
51
|
# threads. Larger +maxevents+ reduces syscall overhead for
|
52
52
|
# single-threaded applications. +maxevents+ defaults to 64 events.
|
53
53
|
# +timeout+ is specified in milliseconds, +nil+
|
54
54
|
# (the default) meaning it will block and wait indefinitely.
|
55
|
+
#
|
56
|
+
# As of sleepy_penguin 3.5.0+, it is possible to nest
|
57
|
+
# #wait calls within the same thread.
|
55
58
|
def wait(maxevents = 64, timeout = nil)
|
56
59
|
# snapshot the marks so we do can sit this thread on epoll_wait while other
|
57
60
|
# threads may call epoll_ctl. People say RCU is a poor man's GC, but our
|
@@ -62,7 +65,7 @@ class SleepyPenguin::Epoll
|
|
62
65
|
end
|
63
66
|
|
64
67
|
# we keep a snapshot of @marks around in case another thread closes
|
65
|
-
# the
|
68
|
+
# the io while it is being transferred to userspace. We release mtx
|
66
69
|
# so another thread may add events to us while we're sleeping.
|
67
70
|
@io.epoll_wait(maxevents, timeout) { |events, io| yield(events, io) }
|
68
71
|
ensure
|
@@ -87,7 +90,7 @@ class SleepyPenguin::Epoll
|
|
87
90
|
# call-seq:
|
88
91
|
# ep.del(io) -> 0
|
89
92
|
#
|
90
|
-
# Disables an IO object from being watched.
|
93
|
+
# Disables an +IO+ object from being watched.
|
91
94
|
def del(io)
|
92
95
|
fd = io.to_io.fileno
|
93
96
|
@mtx.synchronize do
|
@@ -125,7 +128,7 @@ class SleepyPenguin::Epoll
|
|
125
128
|
# call-seq:
|
126
129
|
# epoll.mod(io, flags) -> 0
|
127
130
|
#
|
128
|
-
# Changes the watch for an existing IO object based on +events+.
|
131
|
+
# Changes the watch for an existing +IO+ object based on +events+.
|
129
132
|
# Returns zero on success, will raise SystemError on failure.
|
130
133
|
def mod(io, events)
|
131
134
|
events = __event_flags(events)
|
@@ -166,7 +169,7 @@ class SleepyPenguin::Epoll
|
|
166
169
|
begin
|
167
170
|
@io.epoll_ctl(CTL_MOD, io, events)
|
168
171
|
rescue Errno::ENOENT
|
169
|
-
warn "epoll event cache failed (mod -> add)"
|
172
|
+
warn "epoll event cache failed (mod -> add)\n"
|
170
173
|
@io.epoll_ctl(CTL_ADD, io, events)
|
171
174
|
@marks[fd] = io
|
172
175
|
end
|
@@ -174,7 +177,7 @@ class SleepyPenguin::Epoll
|
|
174
177
|
begin
|
175
178
|
@io.epoll_ctl(CTL_ADD, io, events)
|
176
179
|
rescue Errno::EEXIST
|
177
|
-
warn "epoll event cache failed (add -> mod)"
|
180
|
+
warn "epoll event cache failed (add -> mod)\n"
|
178
181
|
@io.epoll_ctl(CTL_MOD, io, events)
|
179
182
|
end
|
180
183
|
@marks[fd] = io
|
@@ -214,8 +217,8 @@ class SleepyPenguin::Epoll
|
|
214
217
|
# call-seq:
|
215
218
|
# ep.io_for(io) -> object
|
216
219
|
#
|
217
|
-
# Returns the given IO object currently being watched for. Different
|
218
|
-
# IO objects may internally refer to the same process file descriptor.
|
220
|
+
# Returns the given +IO+ object currently being watched for. Different
|
221
|
+
# +IO+ objects may internally refer to the same process file descriptor.
|
219
222
|
# Mostly used for debugging.
|
220
223
|
def io_for(io)
|
221
224
|
fd = __fileno(io)
|
@@ -244,9 +247,9 @@ class SleepyPenguin::Epoll
|
|
244
247
|
# call-seq:
|
245
248
|
# epoll.include?(io) -> true or false
|
246
249
|
#
|
247
|
-
# Returns whether or not a given IO is watched and prevented from being
|
250
|
+
# Returns whether or not a given +IO+ is watched and prevented from being
|
248
251
|
# garbage-collected by the current Epoll object. This may include
|
249
|
-
# closed IO objects.
|
252
|
+
# closed +IO+ objects.
|
250
253
|
def include?(io)
|
251
254
|
fd = __fileno(io)
|
252
255
|
@mtx.synchronize do
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'thread'
|
2
|
+
require_relative 'kevent'
|
2
3
|
|
3
|
-
# The high-level Kqueue interface. This provides fork-safety
|
4
|
-
#
|
4
|
+
# The high-level Kqueue interface. This provides fork-safety; as
|
5
|
+
# underlying kqueue descriptors are closed by the OS upon fork.
|
5
6
|
# This also provides memory protection from bugs due to not storing an
|
6
7
|
# external reference to an object, but still requires the user to store
|
7
8
|
# their own object references.
|
@@ -33,10 +34,6 @@ class SleepyPenguin::Kqueue
|
|
33
34
|
|
34
35
|
def __kq_check # :nodoc:
|
35
36
|
return if @pid == $$ || @io.closed?
|
36
|
-
unless @io.respond_to?(:autoclose=)
|
37
|
-
raise RuntimeError,
|
38
|
-
"Kqueue is not safe to use without IO#autoclose=, upgrade to Ruby 1.9+"
|
39
|
-
end
|
40
37
|
|
41
38
|
# kqueue has (strange) close-on-fork behavior
|
42
39
|
objects = @copies.values
|
@@ -55,6 +52,9 @@ class SleepyPenguin::Kqueue
|
|
55
52
|
# Ruby GC, otherwise ObjectSpace._id2ref may return invalid objects.
|
56
53
|
# Unlike the low-level Kqueue::IO#kevent, the block given yields only
|
57
54
|
# a single Kevent struct, not a 6-element array.
|
55
|
+
#
|
56
|
+
# As of sleepy_penguin 3.5.0+, it is possible to nest #kevent
|
57
|
+
# calls within the same thread.
|
58
58
|
def kevent(changelist = nil, *args)
|
59
59
|
@mtx.synchronize { __kq_check }
|
60
60
|
if changelist
|
data/lib/sleepy_penguin/sp.rb
CHANGED
@@ -0,0 +1,125 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
module SleepyPenguin
|
4
|
+
# call-seq:
|
5
|
+
# SleepyPenguin.splice(io_in, io_out, len[, flags [, keywords]) => Integer
|
6
|
+
#
|
7
|
+
# Splice +len+ bytes from/to a pipe. Either +io_in+ or +io_out+
|
8
|
+
# MUST be a pipe. +io_in+ and +io_out+ may BOTH be pipes as of
|
9
|
+
# Linux 2.6.31 or later.
|
10
|
+
#
|
11
|
+
# +flags+ defaults to zero if unspecified.
|
12
|
+
# It may be an Integer bitmask, a Symbol, or Array of Symbols
|
13
|
+
#
|
14
|
+
# The integer bitmask may any combination of:
|
15
|
+
#
|
16
|
+
# * SleepyPenguin::F_MOVE - attempt to move pages instead of copying
|
17
|
+
#
|
18
|
+
# * SleepyPenguin::F_NONBLOCK - do not block on pipe I/O (only)
|
19
|
+
#
|
20
|
+
# * SleepyPenguin::F_MORE - indicates more data will be sent soon
|
21
|
+
#
|
22
|
+
# Symbols may be used as well to specify a single flag:
|
23
|
+
#
|
24
|
+
# * :move - corresponds to F_MOVE
|
25
|
+
# * :nonblock - corresponds to F_NONBLOCK
|
26
|
+
# * :more - corresponds to F_MORE
|
27
|
+
#
|
28
|
+
# Or, an array of any combination of the above symbols.
|
29
|
+
#
|
30
|
+
# Keywords:
|
31
|
+
#
|
32
|
+
# :off_in and :off_out if non-nil may be used to
|
33
|
+
# specify an offset for the respective non-pipe file descriptor.
|
34
|
+
#
|
35
|
+
# :exception defaults to +true+. Setting it to +false+
|
36
|
+
# will return :EAGAIN symbol instead of raising Errno::EAGAIN.
|
37
|
+
# This will also return +nil+ instead of raising EOFError
|
38
|
+
# when +io_in+ is at the end.
|
39
|
+
#
|
40
|
+
# Raises EOFError when +io_in+ has reached end of file.
|
41
|
+
# Raises Errno::EAGAIN if the SleepyPenguin::F_NONBLOCK flag is set
|
42
|
+
# and the pipe has no data to read from or space to write to. May
|
43
|
+
# also raise Errno::EAGAIN if the non-pipe descriptor has no data
|
44
|
+
# to read from or space to write to.
|
45
|
+
#
|
46
|
+
# As splice never exposes buffers to userspace, it will not take
|
47
|
+
# into account userspace buffering done by Ruby or stdio. It is
|
48
|
+
# also not subject to encoding/decoding filters under Ruby 1.9+.
|
49
|
+
#
|
50
|
+
# Consider using `exception: false` if +io_out+ is a pipe or if you
|
51
|
+
# are using non-blocking I/O on both descriptors as it avoids the
|
52
|
+
# cost of raising common Errno::EAGAIN exceptions.
|
53
|
+
#
|
54
|
+
# See manpage for full documentation:
|
55
|
+
# http://man7.org/linux/man-pages/man2/splice.2.html
|
56
|
+
#
|
57
|
+
# Support for this exists in sleepy_penguin 3.5.0+
|
58
|
+
def self.splice(io_in, io_out, len, flags = 0,
|
59
|
+
off_in: nil, off_out: nil, exception: true)
|
60
|
+
flags = __map_splice_flags(flags)
|
61
|
+
ret = __splice(io_in, off_in, io_out, off_out, len, flags)
|
62
|
+
exception ? __map_exc(ret) : ret
|
63
|
+
end
|
64
|
+
|
65
|
+
# call-seq:
|
66
|
+
# SleepyPenguin.tee(io_in, io_out, len[, flags[, keywords]) => Integer
|
67
|
+
#
|
68
|
+
# Copies up to +len+ bytes of data from +io_in+ to +io_out+. +io_in+
|
69
|
+
# and +io_out+ must both refer to pipe descriptors. +io_in+ and +io_out+
|
70
|
+
# may not be endpoints of the same pipe.
|
71
|
+
#
|
72
|
+
# +flags+ may be zero (the default) or a combination of:
|
73
|
+
# * SleepyPenguin::F_NONBLOCK
|
74
|
+
#
|
75
|
+
# As a shortcut, the `:nonblock` symbol may be used instead.
|
76
|
+
#
|
77
|
+
# Other splice-related flags are currently unimplemented in the
|
78
|
+
# kernel and have no effect.
|
79
|
+
#
|
80
|
+
# Returns the number of bytes duplicated if successful.
|
81
|
+
# Raises EOFError when +io_in+ is closed and emptied.
|
82
|
+
# Raises Errno::EAGAIN when +io_in+ is empty and/or +io_out+ is full
|
83
|
+
# and +flags+ specifies non-blocking operation
|
84
|
+
#
|
85
|
+
# Keywords:
|
86
|
+
#
|
87
|
+
# :exception defaults to +true+. Setting it to +false+
|
88
|
+
# will return :EAGAIN symbol instead of raising Errno::EAGAIN.
|
89
|
+
# This will also return +nil+ instead of raising EOFError
|
90
|
+
# when +io_in+ is at the end.
|
91
|
+
#
|
92
|
+
# Consider using `exception: false` if +io_out+ is a pipe or if you
|
93
|
+
# are using non-blocking I/O on both descriptors as it avoids the
|
94
|
+
# cost of raising common Errno::EAGAIN exceptions.
|
95
|
+
#
|
96
|
+
# See manpage for full documentation:
|
97
|
+
# http://man7.org/linux/man-pages/man2/tee.2.html
|
98
|
+
#
|
99
|
+
# Support for this exists in sleepy_penguin 3.5.0+
|
100
|
+
def self.tee(io_in, io_out, len, flags = 0, exception: true)
|
101
|
+
flags = __map_splice_flags(flags)
|
102
|
+
ret = __tee(io_in, io_out, len, flags)
|
103
|
+
exception ? __map_exc(ret) : ret
|
104
|
+
end if respond_to?(:__tee)
|
105
|
+
|
106
|
+
@__splice_f_map = { # :nodoc:
|
107
|
+
:nonblock => F_NONBLOCK,
|
108
|
+
:more => F_MORE,
|
109
|
+
:move => F_MOVE
|
110
|
+
}
|
111
|
+
|
112
|
+
def self.__map_splice_flags(flags) # :nodoc:
|
113
|
+
onef = @__splice_f_map[flags] and return onef
|
114
|
+
flags.respond_to?(:inject) ?
|
115
|
+
flags.inject(0) { |fl, sym| fl |= @__splice_f_map[sym] } : flags
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.__map_exc(ret) # :nodoc:
|
119
|
+
case ret
|
120
|
+
when :EAGAIN then raise Errno::EAGAIN, 'Resource temporarily unavailable'
|
121
|
+
when nil then raise EOFError, 'end of file reached'
|
122
|
+
end
|
123
|
+
ret
|
124
|
+
end
|
125
|
+
end
|