sleepy_penguin.git  about / heads / tags
Linux I/O events for Ruby
blob c9fe61856effc15ea032a5531d82323f7f8290b2 4386 bytes (raw)
$ git show HEAD:ext/sleepy_penguin/splice.c	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
 
#include "sleepy_penguin.h"
#include "sp_copy.h"
#ifdef HAVE_SPLICE
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/uio.h>
#include <unistd.h>

static VALUE sym_EAGAIN;

#ifndef F_LINUX_SPECIFIC_BASE
#  define F_LINUX_SPECIFIC_BASE 1024
#endif

#ifndef F_GETPIPE_SZ
#  define F_SETPIPE_SZ    (F_LINUX_SPECIFIC_BASE + 7)
#  define F_GETPIPE_SZ    (F_LINUX_SPECIFIC_BASE + 8)
#endif

static int check_fileno(VALUE io)
{
	int saved_errno = errno;
	int fd = rb_sp_fileno(io);
	errno = saved_errno;
	return fd;
}

static void *nogvl_splice(void *ptr)
{
	struct copy_args *a = ptr;

	return (void *)splice(a->fd_in, a->off_in, a->fd_out, a->off_out,
	                     a->len, a->flags);
}

/* :nodoc: */
static VALUE my_splice(VALUE mod, VALUE io_in, VALUE off_in,
			VALUE io_out, VALUE off_out,
			VALUE len, VALUE flags)
{
	off_t i = 0, o = 0;
	struct copy_args a;
	ssize_t bytes;

	a.off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i);
	a.off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o);
	a.len = NUM2SIZET(len);
	a.flags = NUM2UINT(flags);

	for (;;) {
		a.fd_in = check_fileno(io_in);
		a.fd_out = check_fileno(io_out);
		bytes = (ssize_t)IO_RUN(nogvl_splice, &a);
		if (bytes == 0) return Qnil;
		if (bytes < 0) {
			switch (errno) {
			case EINTR: continue;
			case EAGAIN: return sym_EAGAIN;
			default: rb_sys_fail("splice");
			}
		}
		return SSIZET2NUM(bytes);
	}
}

struct tee_args {
	int fd_in;
	int fd_out;
	size_t len;
	unsigned flags;
};

/* runs without GVL */
static void *nogvl_tee(void *ptr)
{
	struct tee_args *a = ptr;

	return (void *)tee(a->fd_in, a->fd_out, a->len, a->flags);
}

/* :nodoc: */
static VALUE my_tee(VALUE mod, VALUE io_in, VALUE io_out,
			VALUE len, VALUE flags)
{
	struct tee_args a;
	ssize_t bytes;

	a.len = (size_t)NUM2SIZET(len);
	a.flags = NUM2UINT(flags);

	for (;;) {
		a.fd_in = check_fileno(io_in);
		a.fd_out = check_fileno(io_out);
		bytes = (ssize_t)IO_RUN(nogvl_tee, &a);
		if (bytes == 0) return Qnil;
		if (bytes < 0) {
			switch (errno) {
			case EINTR: continue;
			case EAGAIN: return sym_EAGAIN;
			default: rb_sys_fail("tee");
			}
		}
		return SSIZET2NUM(bytes);
	}
}

void sleepy_penguin_init_splice(void)
{
	VALUE mod = rb_define_module("SleepyPenguin");
	rb_define_singleton_method(mod, "__splice", my_splice, 6);
	rb_define_singleton_method(mod, "__tee", my_tee, 4);

	/*
	 * Attempt to move pages instead of copying.  This is only a hint
	 * and support for it was removed in Linux 2.6.21.  It will be
         * re-added for FUSE filesystems only in Linux 2.6.35.
	 */
	rb_define_const(mod, "F_MOVE", UINT2NUM(SPLICE_F_MOVE));

	/*
	 * Do not block on pipe I/O.  This flag only affects the pipe(s)
	 * being spliced from/to and has no effect on the non-pipe
	 * descriptor (which requires non-blocking operation to be set
	 * explicitly).
	 *
	 * The non-blocking flag (O_NONBLOCK) on the pipe descriptors
	 * themselves are ignored by this family of functions, and
	 * using this flag is the only way to get non-blocking operation
	 * out of them.
	 *
	 * It is highly recommended this flag be set
	 * whenever splicing from a socket into a pipe unless there is
	 * another (native) thread or process doing a blocking read on that
	 * pipe.  Otherwise it is possible to block a single-threaded process
	 * if the socket buffers are larger than the pipe buffers.
	 */
	rb_define_const(mod, "F_NONBLOCK", UINT2NUM(SPLICE_F_NONBLOCK));

	/*
	 * Indicate that there may be more data coming into the outbound
	 * descriptor.  This can allow the kernel to avoid sending partial
	 * frames from sockets.  Currently only used with splice.
	 */
	rb_define_const(mod, "F_MORE", UINT2NUM(SPLICE_F_MORE));

	/*
	 * fcntl() command constant used to return the size of a pipe.
	 * This constant is only defined when running Linux 2.6.35
	 * or later.
	 *
	 *	require 'fcntl'
	 *	r, w = IO.pipe
	 *	r.fcntl(SleepyPenguin::F_GETPIPE_SZ) => Integer
	 */
	rb_define_const(mod, "F_GETPIPE_SZ", UINT2NUM(F_GETPIPE_SZ));

	/*
	 * fcntl() command constant used to set the size of a pipe.
	 * This constant is only defined when running Linux 2.6.35
	 * or later.
	 *
	 * call-seq:
	 *
	 *	require 'fcntl'
	 *	r, w = IO.pipe
	 *	r.fcntl(SleepyPenguin::F_SETPIPE_SZ, 131072)
	 */
	rb_define_const(mod, "F_SETPIPE_SZ", UINT2NUM(F_SETPIPE_SZ));

	sym_EAGAIN = ID2SYM(rb_intern("EAGAIN"));
}
#endif

git clone https://yhbt.net/sleepy_penguin.git