kgio RubyGem user+dev discussion/patches/pulls/bugs/help
 help / color / Atom feed
74576e857c7da40dc499264969841140c6b25edd blob 6304 bytes (raw)

  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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
 
/*
 * We use a very basic strategy to use TCP_CORK semantics optimally
 * in most TCP servers:  On corked sockets, we will uncork on recv()
 * if there was a previous send().  Otherwise we do not fiddle
 * with TCP_CORK at all.
 *
 * Under Linux, we can rely on TCP_CORK being inherited in an
 * accept()-ed client socket so we can avoid syscalls for each
 * accept()-ed client if we know the accept() socket corks.
 *
 * This module does NOTHING for client TCP sockets, we only deal
 * with accept()-ed sockets right now.
 */

#include "kgio.h"
#include "my_fileno.h"
#include <netinet/tcp.h>

/*
 * As of FreeBSD 4.5, TCP_NOPUSH == TCP_CORK
 * ref: http://dotat.at/writing/nopush.html
 * We won't care for older FreeBSD since nobody runs Ruby on them...
 */
#ifdef TCP_CORK
#  define KGIO_NOPUSH TCP_CORK
#elif defined(TCP_NOPUSH)
#  define KGIO_NOPUSH TCP_NOPUSH
#endif

#ifdef KGIO_NOPUSH
static ID id_autopush_state;
static int enabled = 1;

enum autopush_state {
	AUTOPUSH_STATE_ACCEPTOR_IGNORE = -1,
	AUTOPUSH_STATE_IGNORE = 0,
	AUTOPUSH_STATE_WRITER = 1,
	AUTOPUSH_STATE_WRITTEN = 2,
	AUTOPUSH_STATE_ACCEPTOR = 3
};

#if defined(R_CAST) && \
    defined(HAVE_TYPE_STRUCT_RFILE) && \
    defined(HAVE_TYPE_STRUCT_ROBJECT) && \
    ((SIZEOF_STRUCT_RFILE + SIZEOF_INT) <= (SIZEOF_STRUCT_ROBJECT))

struct AutopushSocket {
	struct RFile rfile;
	enum autopush_state autopush_state;
};

static enum autopush_state state_get(VALUE io)
{
	return ((struct AutopushSocket *)(io))->autopush_state;
}

static void state_set(VALUE io, enum autopush_state state)
{
	((struct AutopushSocket *)(io))->autopush_state = state;
}
#else
static enum autopush_state state_get(VALUE io)
{
	VALUE val;

	if (rb_ivar_defined(io, id_autopush_state) == Qfalse)
		return AUTOPUSH_STATE_IGNORE;
	val = rb_ivar_get(io, id_autopush_state);

	return (enum autopush_state)NUM2INT(val);
}

static void state_set(VALUE io, enum autopush_state state)
{
	rb_ivar_set(io, id_autopush_state, INT2NUM(state));
}
#endif /* IVAR fallback */

static enum autopush_state detect_acceptor_state(VALUE io);
static void push_pending_data(VALUE io);

/*
 * call-seq:
 *	Kgio.autopush? -> true or false
 *
 * Returns whether or not autopush is enabled.
 *
 * Only available on systems with TCP_CORK (Linux) or
 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
 */
static VALUE s_get_autopush(VALUE self)
{
	return enabled ? Qtrue : Qfalse;
}

/*
 * call-seq:
 *	Kgio.autopush = true
 *	Kgio.autopush = false
 *
 * Enables or disables autopush for sockets created with kgio_accept
 * and kgio_tryaccept methods.  Autopush relies on TCP_CORK/TCP_NOPUSH
 * being enabled on the listen socket.
 *
 * Only available on systems with TCP_CORK (Linux) or
 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
 */
static VALUE s_set_autopush(VALUE self, VALUE val)
{
	enabled = RTEST(val);

	return val;
}

/*
 * call-seq:
 *
 *	io.kgio_autopush?  -> true or false
 *
 * Returns the current autopush state of the Kgio::SocketMethods-enabled
 * socket.
 *
 * Only available on systems with TCP_CORK (Linux) or
 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
 */
static VALUE autopush_get(VALUE io)
{
	return state_get(io) <= 0 ? Qfalse : Qtrue;
}

/*
 * call-seq:
 *
 *	io.kgio_autopush = true
 *	io.kgio_autopush = false
 *
 * Enables or disables autopush on any given Kgio::SocketMethods-capable
 * IO object.  This does NOT enable or disable TCP_NOPUSH/TCP_CORK right
 * away, that must be done with IO.setsockopt
 *
 * Only available on systems with TCP_CORK (Linux) or
 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
 */
static VALUE autopush_set(VALUE io, VALUE vbool)
{
	if (RTEST(vbool))
		state_set(io, AUTOPUSH_STATE_WRITER);
	else
		state_set(io, AUTOPUSH_STATE_IGNORE);
	return vbool;
}

void init_kgio_autopush(void)
{
	VALUE mKgio = rb_define_module("Kgio");
	VALUE tmp;

	rb_define_singleton_method(mKgio, "autopush?", s_get_autopush, 0);
	rb_define_singleton_method(mKgio, "autopush=", s_set_autopush, 1);

	tmp = rb_define_module_under(mKgio, "SocketMethods");
	rb_define_method(tmp, "kgio_autopush=", autopush_set, 1);
	rb_define_method(tmp, "kgio_autopush?", autopush_get, 0);

	id_autopush_state = rb_intern("@kgio_autopush_state");
}

/*
 * called after a successful write, just mark that we've put something
 * in the skb and will need to uncork on the next write.
 */
void kgio_autopush_send(VALUE io)
{
	if (state_get(io) == AUTOPUSH_STATE_WRITER)
		state_set(io, AUTOPUSH_STATE_WRITTEN);
}

/* called on successful accept() */
void kgio_autopush_accept(VALUE accept_io, VALUE client_io)
{
	enum autopush_state acceptor_state;

	if (!enabled)
		return;
	acceptor_state = state_get(accept_io);
	if (acceptor_state == AUTOPUSH_STATE_IGNORE)
		acceptor_state = detect_acceptor_state(accept_io);
	if (acceptor_state == AUTOPUSH_STATE_ACCEPTOR)
		state_set(client_io, AUTOPUSH_STATE_WRITER);
	else
		state_set(client_io, AUTOPUSH_STATE_IGNORE);
}

void kgio_autopush_recv(VALUE io)
{
	if (enabled && (state_get(io) == AUTOPUSH_STATE_WRITTEN)) {
		push_pending_data(io);
		state_set(io, AUTOPUSH_STATE_WRITER);
	}
}

static enum autopush_state detect_acceptor_state(VALUE io)
{
	int corked = 0;
	int fd = my_fileno(io);
	socklen_t optlen = sizeof(int);
	enum autopush_state state;

	if (getsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &corked, &optlen) != 0) {
		if (errno != EOPNOTSUPP)
			rb_sys_fail("getsockopt(TCP_CORK/TCP_NOPUSH)");
		errno = 0;
		state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
	} else if (corked) {
		state = AUTOPUSH_STATE_ACCEPTOR;
	} else {
		state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
	}
	state_set(io, state);

	return state;
}

/*
 * checks to see if we've written anything since the last recv()
 * If we have, uncork the socket and immediately recork it.
 */
static void push_pending_data(VALUE io)
{
	int optval = 0;
	const socklen_t optlen = sizeof(int);
	const int fd = my_fileno(io);

	if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
		rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 0)");
	/* immediately recork */
	optval = 1;
	if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
		rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 1)");
}
#else /* !KGIO_NOPUSH */
void kgio_autopush_recv(VALUE io){}
void kgio_autopush_send(VALUE io){}
void init_kgio_autopush(void)
{
}
#endif /* ! KGIO_NOPUSH */
debug log:

solving 74576e8 ...
found 74576e8 in https://yhbt.net/kgio.git

kgio RubyGem user+dev discussion/patches/pulls/bugs/help

Archives are clonable:
	git clone --mirror https://yhbt.net/kgio-public
	git clone --mirror http://ou63pmih66umazou.onion/kgio-public

Example config snippet for mirrors

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.lang.ruby.kgio
	nntp://ou63pmih66umazou.onion/inbox.comp.lang.ruby.kgio

 note: .onion URLs require Tor: https://www.torproject.org/

AGPL code for this site: git clone https://public-inbox.org/public-inbox.git