make timed kgio_wait_* implementation safer
[kgio.git] / ext / kgio / autopush.c
blob252ecddd299422c9be33a2f5ebcd0c0f77a8f801
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 <netinet/tcp.h>
19 * As of FreeBSD 4.5, TCP_NOPUSH == TCP_CORK
20 * ref: http://dotat.at/writing/nopush.html
21 * We won't care for older FreeBSD since nobody runs Ruby on them...
23 #ifdef TCP_CORK
24 # define KGIO_NOPUSH TCP_CORK
25 #elif defined(TCP_NOPUSH)
26 # define KGIO_NOPUSH TCP_NOPUSH
27 #endif
29 #ifdef KGIO_NOPUSH
30 static ID id_autopush_state;
31 static int enabled = 1;
33 enum autopush_state {
34 AUTOPUSH_STATE_ACCEPTOR_IGNORE = -1,
35 AUTOPUSH_STATE_IGNORE = 0,
36 AUTOPUSH_STATE_WRITER = 1,
37 AUTOPUSH_STATE_WRITTEN = 2,
38 AUTOPUSH_STATE_ACCEPTOR = 3
41 #if defined(R_CAST) && \
42 defined(HAVE_TYPE_STRUCT_RFILE) && \
43 defined(HAVE_TYPE_STRUCT_ROBJECT) && \
44 ((SIZEOF_STRUCT_RFILE + SIZEOF_INT) <= (SIZEOF_STRUCT_ROBJECT))
46 struct AutopushSocket {
47 struct RFile rfile;
48 enum autopush_state autopush_state;
51 static enum autopush_state state_get(VALUE io)
53 return ((struct AutopushSocket *)(io))->autopush_state;
56 static void state_set(VALUE io, enum autopush_state state)
58 ((struct AutopushSocket *)(io))->autopush_state = state;
60 #else
61 static enum autopush_state state_get(VALUE io)
63 VALUE val;
65 if (rb_ivar_defined(io, id_autopush_state) == Qfalse)
66 return AUTOPUSH_STATE_IGNORE;
67 val = rb_ivar_get(io, id_autopush_state);
69 return (enum autopush_state)NUM2INT(val);
72 static void state_set(VALUE io, enum autopush_state state)
74 rb_ivar_set(io, id_autopush_state, INT2NUM(state));
76 #endif /* IVAR fallback */
78 static enum autopush_state detect_acceptor_state(VALUE io);
79 static void push_pending_data(VALUE io);
82 * call-seq:
83 * Kgio.autopush? -> true or false
85 * Returns whether or not autopush is enabled.
87 * Only available on systems with TCP_CORK (Linux) or
88 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
90 static VALUE s_get_autopush(VALUE self)
92 return enabled ? Qtrue : Qfalse;
96 * call-seq:
97 * Kgio.autopush = true
98 * Kgio.autopush = false
100 * Enables or disables autopush for sockets created with kgio_accept
101 * and kgio_tryaccept methods. Autopush relies on TCP_CORK/TCP_NOPUSH
102 * being enabled on the listen socket.
104 * Only available on systems with TCP_CORK (Linux) or
105 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
107 static VALUE s_set_autopush(VALUE self, VALUE val)
109 enabled = RTEST(val);
111 return val;
115 * call-seq:
117 * io.kgio_autopush? -> true or false
119 * Returns the current autopush state of the Kgio::SocketMethods-enabled
120 * socket.
122 * Only available on systems with TCP_CORK (Linux) or
123 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
125 static VALUE autopush_get(VALUE io)
127 return state_get(io) <= 0 ? Qfalse : Qtrue;
131 * call-seq:
133 * io.kgio_autopush = true
134 * io.kgio_autopush = false
136 * Enables or disables autopush on any given Kgio::SocketMethods-capable
137 * IO object. This does NOT enable or disable TCP_NOPUSH/TCP_CORK right
138 * away, that must be done with IO.setsockopt
140 * Only available on systems with TCP_CORK (Linux) or
141 * TCP_NOPUSH (FreeBSD, and maybe other *BSDs).
143 static VALUE autopush_set(VALUE io, VALUE vbool)
145 int fd = my_fileno(io);
146 int val;
147 socklen_t len = sizeof(val);
149 if (RTEST(vbool))
150 state_set(io, AUTOPUSH_STATE_WRITER);
151 else
152 state_set(io, AUTOPUSH_STATE_IGNORE);
153 return vbool;
156 void init_kgio_autopush(void)
158 VALUE mKgio = rb_define_module("Kgio");
159 VALUE tmp;
161 rb_define_singleton_method(mKgio, "autopush?", s_get_autopush, 0);
162 rb_define_singleton_method(mKgio, "autopush=", s_set_autopush, 1);
164 tmp = rb_define_module_under(mKgio, "SocketMethods");
165 rb_define_method(tmp, "kgio_autopush=", autopush_set, 1);
166 rb_define_method(tmp, "kgio_autopush?", autopush_get, 0);
168 id_autopush_state = rb_intern("@kgio_autopush_state");
172 * called after a successful write, just mark that we've put something
173 * in the skb and will need to uncork on the next write.
175 void kgio_autopush_send(VALUE io)
177 if (state_get(io) == AUTOPUSH_STATE_WRITER)
178 state_set(io, AUTOPUSH_STATE_WRITTEN);
181 /* called on successful accept() */
182 void kgio_autopush_accept(VALUE accept_io, VALUE client_io)
184 enum autopush_state acceptor_state;
186 if (!enabled)
187 return;
188 acceptor_state = state_get(accept_io);
189 if (acceptor_state == AUTOPUSH_STATE_IGNORE)
190 acceptor_state = detect_acceptor_state(accept_io);
191 if (acceptor_state == AUTOPUSH_STATE_ACCEPTOR)
192 state_set(client_io, AUTOPUSH_STATE_WRITER);
193 else
194 state_set(client_io, AUTOPUSH_STATE_IGNORE);
197 void kgio_autopush_recv(VALUE io)
199 if (enabled && (state_get(io) == AUTOPUSH_STATE_WRITTEN)) {
200 push_pending_data(io);
201 state_set(io, AUTOPUSH_STATE_WRITER);
205 static enum autopush_state detect_acceptor_state(VALUE io)
207 int corked = 0;
208 int fd = my_fileno(io);
209 socklen_t optlen = sizeof(int);
210 enum autopush_state state;
212 if (getsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &corked, &optlen) != 0) {
213 if (errno != EOPNOTSUPP)
214 rb_sys_fail("getsockopt(TCP_CORK/TCP_NOPUSH)");
215 errno = 0;
216 state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
217 } else if (corked) {
218 state = AUTOPUSH_STATE_ACCEPTOR;
219 } else {
220 state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
222 state_set(io, state);
224 return state;
228 * checks to see if we've written anything since the last recv()
229 * If we have, uncork the socket and immediately recork it.
231 static void push_pending_data(VALUE io)
233 int optval = 0;
234 const socklen_t optlen = sizeof(int);
235 const int fd = my_fileno(io);
237 if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
238 rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 0)");
239 /* immediately recork */
240 optval = 1;
241 if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
242 rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 1)");
244 #else /* !KGIO_NOPUSH */
245 void kgio_autopush_recv(VALUE io){}
246 void init_kgio_autopush(void)
249 #endif /* ! KGIO_NOPUSH */