sleepy_penguin 3.4.1 → 3.5.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.
- 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
|