kgio 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/GIT-VERSION-FILE CHANGED
@@ -1 +1 @@
1
- GIT_VERSION = 2.1.1
1
+ GIT_VERSION = 2.2.0
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v2.1.1.GIT
4
+ DEF_VER=v2.2.0.GIT
5
5
 
6
6
  LF='
7
7
  '
data/GNUmakefile CHANGED
@@ -1,153 +1,10 @@
1
- # use GNU Make to run tests in parallel, and without depending on RubyGems
2
1
  all::
3
- RUBY = ruby
4
- RAKE = rake
5
- RSYNC = rsync
6
-
7
- GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
8
- @./GIT-VERSION-GEN
9
- -include GIT-VERSION-FILE
10
- -include local.mk
11
- ifeq ($(DLEXT),) # "so" for Linux
12
- DLEXT := $(shell $(RUBY) -rrbconfig -e 'puts Config::CONFIG["DLEXT"]')
13
- endif
14
- ifeq ($(RUBY_VERSION),)
15
- RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
16
- endif
17
-
18
- install:
19
- $(prep_setup_rb)
20
- $(RM) -r .install-tmp
21
- mkdir .install-tmp
22
- $(RUBY) setup.rb all
23
- $(RM) $^
24
- $(RM) -r .install-tmp
25
- $(prep_setup_rb)
26
-
27
- setup_rb_files := .config InstalledFiles
28
- prep_setup_rb := @-$(RM) $(setup_rb_files);$(MAKE) -C $(ext) clean
29
-
30
- clean:
31
- -$(MAKE) -C ext/kgio clean
32
- $(RM) $(setup_rb_files) ext/kgio/Makefile
33
-
34
- pkg_extra := GIT-VERSION-FILE NEWS ChangeLog LATEST
35
- ChangeLog: GIT-VERSION-FILE .wrongdoc.yml
36
- wrongdoc prepare
37
-
38
- .manifest: ChangeLog
39
- (git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \
40
- LC_ALL=C sort > $@+
41
- cmp $@+ $@ || mv $@+ $@
42
- $(RM) $@+
43
-
44
- doc: .document .wrongdoc.yml
45
- find lib ext -type f -name '*.rbc' -exec rm -f '{}' ';'
46
- $(RM) -r doc
47
- wrongdoc all
48
- install -m644 COPYING doc/COPYING
49
- install -m644 $(shell grep '^[A-Z]' .document) doc/
50
-
51
- ifneq ($(VERSION),)
2
+ RSYNC_DEST := bogomips.org:/srv/bogomips/kgio
52
3
  rfproject := rainbows
53
4
  rfpackage := kgio
54
- pkggem := pkg/$(rfpackage)-$(VERSION).gem
55
- pkgtgz := pkg/$(rfpackage)-$(VERSION).tgz
56
- release_notes := release_notes-$(VERSION)
57
- release_changes := release_changes-$(VERSION)
58
-
59
- release-notes: $(release_notes)
60
- release-changes: $(release_changes)
61
- $(release_changes):
62
- wrongdoc release_changes > $@+
63
- $(VISUAL) $@+ && test -s $@+ && mv $@+ $@
64
- $(release_notes):
65
- wrongdoc release_notes > $@+
66
- $(VISUAL) $@+ && test -s $@+ && mv $@+ $@
67
-
68
- # ensures we're actually on the tagged $(VERSION), only used for release
69
- verify:
70
- test x"$(shell umask)" = x0022
71
- git rev-parse --verify refs/tags/v$(VERSION)^{}
72
- git diff-index --quiet HEAD^0
73
- test `git rev-parse --verify HEAD^0` = \
74
- `git rev-parse --verify refs/tags/v$(VERSION)^{}`
75
-
76
- fix-perms:
77
- -git ls-tree -r HEAD | awk '/^100644 / {print $$NF}' | xargs chmod 644
78
- -git ls-tree -r HEAD | awk '/^100755 / {print $$NF}' | xargs chmod 755
79
-
80
- gem: $(pkggem)
81
-
82
- install-gem: $(pkggem)
83
- gem install $(CURDIR)/$<
84
-
85
- $(pkggem): manifest fix-perms
86
- gem build $(rfpackage).gemspec
87
- mkdir -p pkg
88
- mv $(@F) $@
89
-
90
- $(pkgtgz): distdir = $(basename $@)
91
- $(pkgtgz): HEAD = v$(VERSION)
92
- $(pkgtgz): manifest fix-perms
93
- @test -n "$(distdir)"
94
- $(RM) -r $(distdir)
95
- mkdir -p $(distdir)
96
- tar cf - `cat .manifest` | (cd $(distdir) && tar xf -)
97
- cd pkg && tar cf - $(basename $(@F)) | gzip -9 > $(@F)+
98
- mv $@+ $@
99
-
100
- package: $(pkgtgz) $(pkggem)
101
-
102
- test-release: verify package $(release_notes) $(release_changes)
103
- release: verify package $(release_notes) $(release_changes)
104
- # make tgz release on RubyForge
105
- rubyforge add_release -f -n $(release_notes) -a $(release_changes) \
106
- $(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
107
- # push gem to RubyGems.org
108
- gem push $(pkggem)
109
- # in case of gem downloads from RubyForge releases page
110
- -rubyforge add_file \
111
- $(rfproject) $(rfpackage) $(VERSION) $(pkggem)
5
+ include pkg.mk
6
+ ifneq ($(VERSION),)
7
+ release::
112
8
  $(RAKE) raa_update VERSION=$(VERSION)
113
9
  $(RAKE) publish_news VERSION=$(VERSION)
114
- else
115
- gem install-gem: GIT-VERSION-FILE
116
- $(MAKE) $@ VERSION=$(GIT_VERSION)
117
10
  endif
118
-
119
- ext := ext/kgio/kgio_ext.$(DLEXT)
120
- ext/kgio/Makefile: ext/kgio/extconf.rb
121
- cd $(@D) && $(RUBY) extconf.rb
122
-
123
- $(ext): $(wildcard ext/kgio/*.[ch] ext/kgio/*/*.h) ext/kgio/Makefile
124
- $(MAKE) -C $(@D)
125
-
126
- all:: test
127
-
128
- build: $(ext)
129
- test_units := $(wildcard test/test_*.rb)
130
- test: test-unit
131
- test-unit: $(test_units)
132
- $(test_units): build
133
- $(RUBY) -I lib:ext/kgio $@
134
-
135
- # this requires GNU coreutils variants
136
- publish_doc:
137
- -git set-file-times
138
- $(MAKE) doc
139
- find doc/images -type f | \
140
- TZ=UTC xargs touch -d '1970-01-01 00:00:00' doc/rdoc.css
141
- $(MAKE) doc_gz
142
- chmod 644 $$(find doc -type f)
143
- $(RSYNC) -av doc/ bogomips.org:/srv/bogomips/kgio/
144
- git ls-files | xargs touch
145
-
146
- # Create gzip variants of the same timestamp as the original so nginx
147
- # "gzip_static on" can serve the gzipped versions directly.
148
- doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
149
- doc_gz:
150
- for i in $(docs); do \
151
- gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
152
-
153
- .PHONY: .FORCE-GIT-VERSION-FILE doc manifest test $(test_units)
data/LATEST ADDED
@@ -0,0 +1,25 @@
1
+ === kgio 2.2.0 - kinder, gentler I/O for the Internets / 2011-02-04 03:07 UTC
2
+
3
+ * sockets accept()ed by a TCP_NOPUSH/TCP_CORK listener
4
+ automatically flush on kgio_*read calls if there is pending
5
+ data. "Kgio.autopush = false" disables this globally,
6
+ and Kgio::Socket also get "kgio_autopush=" to enable/disable
7
+ on a per-object individual basis.
8
+
9
+ * ECONNRESET exceptions get empty backtraces for kgio_*read.
10
+ There's nothing a programmer can do about these, so there's
11
+ no point in going through the expensive backtrace generation
12
+ process.
13
+
14
+ * Kgio.try* singleton methods added for working with non-Kgio
15
+ enhanced objects. No more needing to use Object#extend
16
+ and blowing away your method cache to make existing I/O
17
+ objects kinder and gentler.
18
+
19
+ * IPv6 support should be complete, systems without a native
20
+ getaddrinfo(3) are now unsupported (and will remain so
21
+ unless somebody complains).
22
+
23
+ There should be no other backwards-incompatible changes other
24
+ than requiring getaddrinfo(3) and friends for IPv6 support.
25
+
data/NEWS CHANGED
@@ -1,4 +1,44 @@
1
- === 2.0.0 / 2010-11-19 01:18 UTC
1
+ === kgio 2.2.0 - kinder, gentler I/O for the Internets / 2011-02-04 03:07 UTC
2
+
3
+ * sockets accept()ed by a TCP_NOPUSH/TCP_CORK listener
4
+ automatically flush on kgio_*read calls if there is pending
5
+ data. "Kgio.autopush = false" disables this globally,
6
+ and Kgio::Socket also get "kgio_autopush=" to enable/disable
7
+ on a per-object individual basis.
8
+
9
+ * ECONNRESET exceptions get empty backtraces for kgio_*read.
10
+ There's nothing a programmer can do about these, so there's
11
+ no point in going through the expensive backtrace generation
12
+ process.
13
+
14
+ * Kgio.try* singleton methods added for working with non-Kgio
15
+ enhanced objects. No more needing to use Object#extend
16
+ and blowing away your method cache to make existing I/O
17
+ objects kinder and gentler.
18
+
19
+ * IPv6 support should be complete, systems without a native
20
+ getaddrinfo(3) are now unsupported (and will remain so
21
+ unless somebody complains).
22
+
23
+ There should be no other backwards-incompatible changes other
24
+ than requiring getaddrinfo(3) and friends for IPv6 support.
25
+
26
+ === kgio 2.1.1 - one small Rubinius fix / 2010-12-26 02:08 UTC
27
+
28
+ We now avoid errno side-effects in kgio_wait_*able methods.
29
+ This affects Rubinius, but may affect other Ruby platforms
30
+ (particularly those that use stdio) as well.
31
+
32
+ === kgio 2.1.0 - accept improvements and fixes / 2010-12-26 01:07 UTC
33
+
34
+ kgio_accept and kgio_tryaccept now take an optional argument
35
+ to override the default Kgio::Socket class that is returned.
36
+
37
+ These methods also fall back to using regular accept() if
38
+ kgio was built on a system with accept4() and later run on
39
+ a system without accept4().
40
+
41
+ === kgio 2.0.0 - major internal API changes / 2010-11-19 01:18 UTC
2
42
 
3
43
  (no code changes from 2.0.0pre1)
4
44
 
@@ -19,7 +59,7 @@
19
59
  add default kgio_wait_*able methods
20
60
  switch entirely to kgio_wait_*able methods
21
61
 
22
- === 2.0.0pre1 / 2010-11-18 23:16 UTC
62
+ === kgio 2.0.0pre1 - major internal API changes / 2010-11-18 23:16 UTC
23
63
 
24
64
  This release should make Kgio easier and more consistent
25
65
  to use across a variety of libraries/applications.
@@ -38,20 +78,20 @@
38
78
  add default kgio_wait_*able methods
39
79
  switch entirely to kgio_wait_*able methods
40
80
 
41
- === 1.3.1 / 2010-10-08 22:20 UTC
81
+ === kgio 1.3.1 - fix zero-length reads / 2010-10-08 22:20 UTC
42
82
 
43
83
  kgio_read and kgio_tryread will now return an empty string when
44
84
  a length of zero is specified instead of nil (which would signal
45
85
  an EOF). This emulates the behavior of IO#read, IO#readpartial,
46
86
  IO#sysread, IO#read_nonblock in core Ruby for consistency.
47
87
 
48
- === 1.3.0 / 2010-10-08 03:03 UTC
88
+ === kgio 1.3.0 - bug and usability fixes / 2010-10-08 03:03 UTC
49
89
 
50
90
  * make Kgio::WaitWritable and Kgio::WaitReadable symbols
51
91
  * trywrite: fix stupid off-by-one error causing corrupt writes
52
92
  on retries
53
93
 
54
- === 1.2.1 / 2010-10-07 07:20 UTC
94
+ === kgio 1.2.1 - doc and *BSD workarounds / 2010-10-07 07:20 UTC
55
95
 
56
96
  This fixes our accept4() wrapper which did not work as expected
57
97
  on some *BSD-based systems due to fcntl(fd, F_GETFL) returning
@@ -60,7 +100,7 @@
60
100
 
61
101
  Also some RDoc fixes.
62
102
 
63
- === 1.2.0 / 2010-10-05 23:14 UTC
103
+ === kgio 1.2.0 - cleanups and minor improvements / 2010-10-05 23:14 UTC
64
104
 
65
105
  The C extension is now split into several files for
66
106
  ease-of-maintenance.
@@ -69,17 +109,17 @@
69
109
  Errno::EPIPE, Errno::ECONNRESET) are now less expensive as they
70
110
  are generated without backtraces.
71
111
 
72
- === 1.1.0 / 2010-09-29 01:17 UTC
112
+ === kgio 1.1.0 - flexible accept methods / 2010-09-29 01:17 UTC
73
113
 
74
114
  * alternate classes may now be returned by accept/tryaccept
75
115
  by setting Kgio.accept_class=
76
116
 
77
- === 1.0.1 / 2010-09-28 03:00 UTC
117
+ === kgio 1.0.1 - compatibility fixes / 2010-09-28 03:00 UTC
78
118
 
79
119
  * add compatibility for ancient Rubies (1.8.6)
80
120
  * linux: fix accept4() support for newer Linux
81
121
 
82
- === 1.0.0 / 2010-09-28 00:29 UTC
122
+ === kgio 1.0.0 - initial release / 2010-09-28 00:29 UTC
83
123
 
84
124
  A kinder, gentler I/O library for Ruby
85
125
 
data/README CHANGED
@@ -45,13 +45,13 @@ You may also install it via RubyGems.org:
45
45
  You can get the latest source via git from the following locations
46
46
  (these versions may not be stable):
47
47
 
48
- git://git.bogomips.org/kgio.git
48
+ git://bogomips.org/kgio.git
49
49
  git://repo.or.cz/kgio.git (mirror)
50
50
 
51
51
  You may browse the code from the web and download the latest snapshot
52
52
  tarballs here:
53
53
 
54
- * http://git.bogomips.org/cgit/kgio.git (cgit)
54
+ * http://bogomips.org/kgio.git (cgit)
55
55
  * http://repo.or.cz/w/kgio.git (gitweb)
56
56
 
57
57
  See the HACKING guide on how to contribute and build prerelease gems
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: binary -*-
2
- cgit_url = "http://git.bogomips.org/cgit/kgio.git"
3
- git_url = 'git://git.bogomips.org/kgio.git'
2
+ cgit_url = "http://bogomips.org/kgio.git"
3
+ git_url = 'git://bogomips.org/kgio.git'
4
4
 
5
5
  desc "post news article to rubyforge"
6
6
  task :publish_news do
data/ext/kgio/accept.c CHANGED
@@ -1,5 +1,5 @@
1
1
  #include "kgio.h"
2
- #include "missing/accept4.h"
2
+ #include "missing_accept4.h"
3
3
  #include "sock_for_fd.h"
4
4
 
5
5
  static VALUE localhost;
@@ -133,14 +133,21 @@ static VALUE acceptor(int argc, const VALUE *argv)
133
133
  rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
134
134
  }
135
135
 
136
+ #if defined(__linux__)
137
+ # define post_accept kgio_autopush_accept
138
+ #else
139
+ # define post_accept(a,b) for(;0;)
140
+ #endif
141
+
136
142
  static VALUE
137
- my_accept(VALUE io, VALUE klass,
143
+ my_accept(VALUE accept_io, VALUE klass,
138
144
  struct sockaddr *addr, socklen_t *addrlen, int nonblock)
139
145
  {
140
146
  int client;
147
+ VALUE client_io;
141
148
  struct accept_args a;
142
149
 
143
- a.fd = my_fileno(io);
150
+ a.fd = my_fileno(accept_io);
144
151
  a.addr = addr;
145
152
  a.addrlen = addrlen;
146
153
  retry:
@@ -175,20 +182,55 @@ retry:
175
182
  rb_sys_fail("accept");
176
183
  }
177
184
  }
178
- return sock_for_fd(klass, client);
185
+ client_io = sock_for_fd(klass, client);
186
+ post_accept(accept_io, client_io);
187
+ return client_io;
179
188
  }
180
189
 
181
- static void in_addr_set(VALUE io, struct sockaddr_in *addr)
190
+ static VALUE in_addr_set(VALUE io, struct sockaddr_storage *addr, socklen_t len)
191
+ {
192
+ VALUE host;
193
+ int host_len, rc;
194
+ char *host_ptr;
195
+
196
+ switch (addr->ss_family) {
197
+ case AF_INET:
198
+ host_len = (long)INET_ADDRSTRLEN;
199
+ break;
200
+ case AF_INET6:
201
+ host_len = (long)INET6_ADDRSTRLEN;
202
+ break;
203
+ default:
204
+ rb_raise(rb_eRuntimeError, "unsupported address family");
205
+ }
206
+ host = rb_str_new(NULL, host_len);
207
+ host_ptr = RSTRING_PTR(host);
208
+ rc = getnameinfo((struct sockaddr *)addr, len,
209
+ host_ptr, host_len, NULL, 0, NI_NUMERICHOST);
210
+ if (rc != 0)
211
+ rb_raise(rb_eRuntimeError, "getnameinfo: %s", gai_strerror(rc));
212
+ rb_str_set_len(host, strlen(host_ptr));
213
+ return rb_ivar_set(io, iv_kgio_addr, host);
214
+ }
215
+
216
+ /*
217
+ * call-seq:
218
+ *
219
+ * io.kgio_addr! => refreshes the given sock address
220
+ */
221
+ static VALUE addr_bang(VALUE io)
182
222
  {
183
- VALUE host = rb_str_new(0, INET_ADDRSTRLEN);
184
- socklen_t addrlen = (socklen_t)INET_ADDRSTRLEN;
185
- const char *name;
186
-
187
- name = inet_ntop(AF_INET, &addr->sin_addr, RSTRING_PTR(host), addrlen);
188
- if (name == NULL)
189
- rb_sys_fail("inet_ntop");
190
- rb_str_set_len(host, strlen(name));
191
- rb_ivar_set(io, iv_kgio_addr, host);
223
+ int fd = my_fileno(io);
224
+ struct sockaddr_storage addr;
225
+ socklen_t len = sizeof(struct sockaddr_storage);
226
+
227
+ if (getpeername(fd, (struct sockaddr *)&addr, &len) != 0)
228
+ rb_sys_fail("getpeername");
229
+
230
+ if (addr.ss_family == AF_UNIX)
231
+ return rb_ivar_set(io, iv_kgio_addr, localhost);
232
+
233
+ return in_addr_set(io, &addr, len);
192
234
  }
193
235
 
194
236
  /*
@@ -210,13 +252,13 @@ static void in_addr_set(VALUE io, struct sockaddr_in *addr)
210
252
  */
211
253
  static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE io)
212
254
  {
213
- struct sockaddr_in addr;
214
- socklen_t addrlen = sizeof(struct sockaddr_in);
255
+ struct sockaddr_storage addr;
256
+ socklen_t addrlen = sizeof(struct sockaddr_storage);
215
257
  VALUE klass = acceptor(argc, argv);
216
258
  VALUE rv = my_accept(io, klass, (struct sockaddr *)&addr, &addrlen, 1);
217
259
 
218
260
  if (!NIL_P(rv))
219
- in_addr_set(rv, &addr);
261
+ in_addr_set(rv, &addr, addrlen);
220
262
  return rv;
221
263
  }
222
264
 
@@ -240,12 +282,12 @@ static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE io)
240
282
  */
241
283
  static VALUE tcp_accept(int argc, VALUE *argv, VALUE io)
242
284
  {
243
- struct sockaddr_in addr;
244
- socklen_t addrlen = sizeof(struct sockaddr_in);
285
+ struct sockaddr_storage addr;
286
+ socklen_t addrlen = sizeof(struct sockaddr_storage);
245
287
  VALUE klass = acceptor(argc, argv);
246
288
  VALUE rv = my_accept(io, klass, (struct sockaddr *)&addr, &addrlen, 0);
247
289
 
248
- in_addr_set(rv, &addr);
290
+ in_addr_set(rv, &addr, addrlen);
249
291
  return rv;
250
292
  }
251
293
 
@@ -402,6 +444,8 @@ void init_kgio_accept(void)
402
444
  cClientSocket = cKgio_Socket;
403
445
  mSocketMethods = rb_const_get(mKgio, rb_intern("SocketMethods"));
404
446
 
447
+ rb_define_method(mSocketMethods, "kgio_addr!", addr_bang, 0);
448
+
405
449
  rb_define_singleton_method(mKgio, "accept_cloexec?", get_cloexec, 0);
406
450
  rb_define_singleton_method(mKgio, "accept_cloexec=", set_cloexec, 1);
407
451
  rb_define_singleton_method(mKgio, "accept_nonblock?", get_nonblock, 0);
File without changes
@@ -0,0 +1,203 @@
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.
6
+ *
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.
10
+ *
11
+ * This module does NOTHING for client TCP sockets, we only deal
12
+ * with accept()-ed sockets right now.
13
+ */
14
+
15
+ #include "kgio.h"
16
+ #include <netinet/tcp.h>
17
+
18
+ /*
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...
22
+ */
23
+ #ifdef TCP_CORK
24
+ # define KGIO_NOPUSH TCP_CORK
25
+ #elif defined(TCP_NOPUSH)
26
+ # define KGIO_NOPUSH TCP_NOPUSH
27
+ #endif
28
+
29
+ #ifdef KGIO_NOPUSH
30
+ static ID id_autopush_state;
31
+ static int enabled = 1;
32
+
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
39
+ };
40
+
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))
45
+
46
+ struct AutopushSocket {
47
+ struct RFile rfile;
48
+ enum autopush_state autopush_state;
49
+ };
50
+
51
+ static enum autopush_state state_get(VALUE io)
52
+ {
53
+ return ((struct AutopushSocket *)(io))->autopush_state;
54
+ }
55
+
56
+ static void state_set(VALUE io, enum autopush_state state)
57
+ {
58
+ ((struct AutopushSocket *)(io))->autopush_state = state;
59
+ }
60
+ #else
61
+ static enum autopush_state state_get(VALUE io)
62
+ {
63
+ VALUE val;
64
+
65
+ if (rb_ivar_defined(io, id_autopush_state) == Qfalse)
66
+ return AUTOPUSH_STATE_IGNORE;
67
+ val = rb_ivar_get(io, id_autopush_state);
68
+
69
+ return (enum autopush_state)NUM2INT(val);
70
+ }
71
+
72
+ static void state_set(VALUE io, enum autopush_state state)
73
+ {
74
+ rb_ivar_set(io, id_autopush_state, INT2NUM(state));
75
+ }
76
+ #endif /* IVAR fallback */
77
+
78
+ static enum autopush_state detect_acceptor_state(VALUE io);
79
+ static void push_pending_data(VALUE io);
80
+
81
+ static VALUE s_get_autopush(VALUE self)
82
+ {
83
+ return enabled ? Qtrue : Qfalse;
84
+ }
85
+
86
+ static VALUE s_set_autopush(VALUE self, VALUE val)
87
+ {
88
+ enabled = RTEST(val);
89
+
90
+ return val;
91
+ }
92
+
93
+ static VALUE autopush_get(VALUE io)
94
+ {
95
+ return state_get(io) <= 0 ? Qfalse : Qtrue;
96
+ }
97
+
98
+ static VALUE autopush_set(VALUE io, VALUE vbool)
99
+ {
100
+ int fd = my_fileno(io);
101
+ int val;
102
+ socklen_t len = sizeof(val);
103
+
104
+ if (RTEST(vbool))
105
+ state_set(io, AUTOPUSH_STATE_WRITER);
106
+ else
107
+ state_set(io, AUTOPUSH_STATE_IGNORE);
108
+ return vbool;
109
+ }
110
+
111
+ void init_kgio_autopush(void)
112
+ {
113
+ VALUE mKgio = rb_define_module("Kgio");
114
+ VALUE tmp;
115
+
116
+ rb_define_singleton_method(mKgio, "autopush?", s_get_autopush, 0);
117
+ rb_define_singleton_method(mKgio, "autopush=", s_set_autopush, 1);
118
+
119
+ tmp = rb_define_module_under(mKgio, "SocketMethods");
120
+ rb_define_method(tmp, "kgio_autopush=", autopush_set, 1);
121
+ rb_define_method(tmp, "kgio_autopush?", autopush_get, 0);
122
+
123
+ id_autopush_state = rb_intern("@kgio_autopush_state");
124
+ }
125
+
126
+ /*
127
+ * called after a successful write, just mark that we've put something
128
+ * in the skb and will need to uncork on the next write.
129
+ */
130
+ void kgio_autopush_send(VALUE io)
131
+ {
132
+ if (state_get(io) == AUTOPUSH_STATE_WRITER)
133
+ state_set(io, AUTOPUSH_STATE_WRITTEN);
134
+ }
135
+
136
+ /* called on successful accept() */
137
+ void kgio_autopush_accept(VALUE accept_io, VALUE client_io)
138
+ {
139
+ enum autopush_state acceptor_state;
140
+
141
+ if (!enabled)
142
+ return;
143
+ acceptor_state = state_get(accept_io);
144
+ if (acceptor_state == AUTOPUSH_STATE_IGNORE)
145
+ acceptor_state = detect_acceptor_state(accept_io);
146
+ if (acceptor_state == AUTOPUSH_STATE_ACCEPTOR)
147
+ state_set(client_io, AUTOPUSH_STATE_WRITER);
148
+ else
149
+ state_set(client_io, AUTOPUSH_STATE_IGNORE);
150
+ }
151
+
152
+ void kgio_autopush_recv(VALUE io)
153
+ {
154
+ if (enabled && (state_get(io) == AUTOPUSH_STATE_WRITTEN)) {
155
+ push_pending_data(io);
156
+ state_set(io, AUTOPUSH_STATE_WRITER);
157
+ }
158
+ }
159
+
160
+ static enum autopush_state detect_acceptor_state(VALUE io)
161
+ {
162
+ int corked = 0;
163
+ int fd = my_fileno(io);
164
+ socklen_t optlen = sizeof(int);
165
+ enum autopush_state state;
166
+
167
+ if (getsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &corked, &optlen) != 0) {
168
+ if (errno != EOPNOTSUPP)
169
+ rb_sys_fail("getsockopt(TCP_CORK/TCP_NOPUSH)");
170
+ errno = 0;
171
+ state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
172
+ } else if (corked) {
173
+ state = AUTOPUSH_STATE_ACCEPTOR;
174
+ } else {
175
+ state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
176
+ }
177
+ state_set(io, state);
178
+
179
+ return state;
180
+ }
181
+
182
+ /*
183
+ * checks to see if we've written anything since the last recv()
184
+ * If we have, uncork the socket and immediately recork it.
185
+ */
186
+ static void push_pending_data(VALUE io)
187
+ {
188
+ int optval = 0;
189
+ const socklen_t optlen = sizeof(int);
190
+ const int fd = my_fileno(io);
191
+
192
+ if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
193
+ rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 0)");
194
+ /* immediately recork */
195
+ optval = 1;
196
+ if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
197
+ rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 1)");
198
+ }
199
+ #else /* !KGIO_NOPUSH */
200
+ void init_kgio_autopush(void)
201
+ {
202
+ }
203
+ #endif /* ! KGIO_NOPUSH */