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.
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...
24 # define KGIO_NOPUSH TCP_CORK
25 #elif defined(TCP_NOPUSH)
26 # define KGIO_NOPUSH TCP_NOPUSH
30 static ID id_autopush_state
;
31 static int enabled
= 1;
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
{
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
;
61 static enum autopush_state
state_get(VALUE io
)
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
);
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
;
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
);
117 * io.kgio_autopush? -> true or false
119 * Returns the current autopush state of the Kgio::SocketMethods-enabled
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
;
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
);
147 socklen_t len
= sizeof(val
);
150 state_set(io
, AUTOPUSH_STATE_WRITER
);
152 state_set(io
, AUTOPUSH_STATE_IGNORE
);
156 void init_kgio_autopush(void)
158 VALUE mKgio
= rb_define_module("Kgio");
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
;
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
);
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
)
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)");
216 state
= AUTOPUSH_STATE_ACCEPTOR_IGNORE
;
218 state
= AUTOPUSH_STATE_ACCEPTOR
;
220 state
= AUTOPUSH_STATE_ACCEPTOR_IGNORE
;
222 state_set(io
, 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
)
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 */
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 */