reinstate the original (and dangerous) autopush in C
[kgio.git] / ext / kgio / autopush.c
blobf9b9ef20a6d8496741f0a2b806fb7e1fb81679af
1 /*
2 * We use a very basic strategy to use TCP_CORK semantics optimally
3 * in most TCP servers: On corked sockets, we will uncork on recv()
4 * if there was a previous send(). Otherwise we do not fiddle
5 * with TCP_CORK at all.
7 * Under Linux, we can rely on TCP_CORK being inherited in an
8 * accept()-ed client socket so we can avoid syscalls for each
9 * accept()-ed client if we know the accept() socket corks.
11 * This module does NOTHING for client TCP sockets, we only deal
12 * with accept()-ed sockets right now.
15 #include "kgio.h"
16 #include "my_fileno.h"
17 #include <netinet/tcp.h>
20 * As of FreeBSD 4.5, TCP_NOPUSH == TCP_CORK
21 * ref: http://dotat.at/writing/nopush.html
22 * We won't care for older FreeBSD since nobody runs Ruby on them...
24 #ifdef TCP_CORK
25 # define KGIO_NOPUSH TCP_CORK
26 #elif defined(TCP_NOPUSH)
27 # define KGIO_NOPUSH TCP_NOPUSH
28 #endif
30 #ifdef KGIO_NOPUSH
31 static ID id_autopush_state;
32 static int enabled = 1;
34 enum autopush_state {
35 AUTOPUSH_STATE_ACCEPTOR_IGNORE = -1,
36 AUTOPUSH_STATE_IGNORE = 0,
37 AUTOPUSH_STATE_WRITER = 1,
38 AUTOPUSH_STATE_WRITTEN = 2,
39 AUTOPUSH_STATE_ACCEPTOR = 3
42 #if defined(R_CAST) && \
43 defined(HAVE_TYPE_STRUCT_RFILE) && \
44 defined(HAVE_TYPE_STRUCT_ROBJECT) && \
45 ((SIZEOF_STRUCT_RFILE + SIZEOF_INT) <= (SIZEOF_STRUCT_ROBJECT))
47 struct AutopushSocket {
48 struct RFile rfile;
49 enum autopush_state autopush_state;
52 static enum autopush_state state_get(VALUE io)
54 return ((struct AutopushSocket *)(io))->autopush_state;
57 static void state_set(VALUE io, enum autopush_state state)
59 ((struct AutopushSocket *)(io))->autopush_state = state;
61 #else
62 static enum autopush_state state_get(VALUE io)
64 VALUE val;
66 if (rb_ivar_defined(io, id_autopush_state) == Qfalse)
67 return AUTOPUSH_STATE_IGNORE;
68 val = rb_ivar_get(io, id_autopush_state);
70 return (enum autopush_state)NUM2INT(val);
73 static void state_set(VALUE io, enum autopush_state state)
75 rb_ivar_set(io, id_autopush_state, INT2NUM(state));
77 #endif /* IVAR fallback */
79 static enum autopush_state detect_acceptor_state(VALUE io);
80 static void push_pending_data(VALUE io);
83 * call-seq:
84 * Kgio.autopush? -> true or false
86 * Returns whether or not autopush is enabled.
88 * Only available on systems with TCP_CORK (Linux) or
89 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
91 static VALUE s_get_autopush(VALUE self)
93 return enabled ? Qtrue : Qfalse;
97 * call-seq:
98 * Kgio.autopush = true
99 * Kgio.autopush = false
101 * Enables or disables autopush for sockets created with kgio_accept
102 * and kgio_tryaccept methods. Autopush relies on TCP_CORK/TCP_NOPUSH
103 * being enabled on the listen socket.
105 * Only available on systems with TCP_CORK (Linux) or
106 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
108 * Please do not use this (or kgio at all) in new code. Under Linux,
109 * use MSG_MORE, instead, as it requires fewer syscalls. Users of
110 * other systems are encouraged to add MSG_MORE support to their
111 * favorite OS.
113 static VALUE s_set_autopush(VALUE self, VALUE val)
115 enabled = RTEST(val);
117 return val;
121 * call-seq:
123 * io.kgio_autopush? -> true or false
125 * Returns the current autopush state of the Kgio::SocketMethods-enabled
126 * socket.
128 * Only available on systems with TCP_CORK (Linux) or
129 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
131 static VALUE autopush_get(VALUE io)
133 return state_get(io) <= 0 ? Qfalse : Qtrue;
137 * call-seq:
139 * io.kgio_autopush = true
140 * io.kgio_autopush = false
142 * Enables or disables autopush on any given Kgio::SocketMethods-capable
143 * IO object. This does NOT enable or disable TCP_NOPUSH/TCP_CORK right
144 * away, that must be done with IO.setsockopt
146 * Only available on systems with TCP_CORK (Linux) or
147 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
149 static VALUE autopush_set(VALUE io, VALUE vbool)
151 if (RTEST(vbool))
152 state_set(io, AUTOPUSH_STATE_WRITER);
153 else
154 state_set(io, AUTOPUSH_STATE_IGNORE);
155 return vbool;
158 void init_kgio_autopush(void)
160 VALUE mKgio = rb_define_module("Kgio");
161 VALUE tmp;
163 rb_define_singleton_method(mKgio, "autopush?", s_get_autopush, 0);
164 rb_define_singleton_method(mKgio, "autopush=", s_set_autopush, 1);
166 tmp = rb_define_module_under(mKgio, "SocketMethods");
167 rb_define_method(tmp, "kgio_autopush=", autopush_set, 1);
168 rb_define_method(tmp, "kgio_autopush?", autopush_get, 0);
170 id_autopush_state = rb_intern("@kgio_autopush_state");
174 * called after a successful write, just mark that we've put something
175 * in the skb and will need to uncork on the next write.
177 void kgio_autopush_send(VALUE io)
179 if (state_get(io) == AUTOPUSH_STATE_WRITER)
180 state_set(io, AUTOPUSH_STATE_WRITTEN);
183 /* called on successful accept() */
184 void kgio_autopush_accept(VALUE accept_io, VALUE client_io)
186 enum autopush_state acceptor_state;
188 if (!enabled)
189 return;
190 acceptor_state = state_get(accept_io);
191 if (acceptor_state == AUTOPUSH_STATE_IGNORE)
192 acceptor_state = detect_acceptor_state(accept_io);
193 if (acceptor_state == AUTOPUSH_STATE_ACCEPTOR)
194 state_set(client_io, AUTOPUSH_STATE_WRITER);
195 else
196 state_set(client_io, AUTOPUSH_STATE_IGNORE);
199 void kgio_autopush_recv(VALUE io)
201 if (enabled && (state_get(io) == AUTOPUSH_STATE_WRITTEN)) {
202 push_pending_data(io);
203 state_set(io, AUTOPUSH_STATE_WRITER);
207 static enum autopush_state detect_acceptor_state(VALUE io)
209 int corked = 0;
210 int fd = my_fileno(io);
211 socklen_t optlen = sizeof(int);
212 enum autopush_state state;
214 if (getsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &corked, &optlen) != 0) {
215 if (errno != EOPNOTSUPP)
216 rb_sys_fail("getsockopt(TCP_CORK/TCP_NOPUSH)");
217 errno = 0;
218 state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
219 } else if (corked) {
220 state = AUTOPUSH_STATE_ACCEPTOR;
221 } else {
222 state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
224 state_set(io, state);
226 return state;
230 * checks to see if we've written anything since the last recv()
231 * If we have, uncork the socket and immediately recork it.
233 static void push_pending_data(VALUE io)
235 int optval = 0;
236 const socklen_t optlen = sizeof(int);
237 const int fd = my_fileno(io);
239 if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
240 rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 0)");
241 /* immediately recork */
242 optval = 1;
243 if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
244 rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 1)");
246 #else /* !KGIO_NOPUSH */
247 void kgio_autopush_recv(VALUE io){}
248 void kgio_autopush_send(VALUE io){}
249 void init_kgio_autopush(void)
252 #endif /* ! KGIO_NOPUSH */