kgio 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 */