kgio 2.3.3 → 2.4.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.
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v2.3.3.GIT
4
+ DEF_VER=v2.4.0.GIT
5
5
 
6
6
  LF='
7
7
  '
data/HACKING CHANGED
@@ -2,10 +2,8 @@
2
2
 
3
3
  === Documentation
4
4
 
5
- We use RDoc 2.5.x with Darkfish for documentation as much as possible,
6
- if you're on Ruby 1.8 you want to install the latest "rdoc" gem. Due to
7
- the lack of RDoc-to-manpage converters we know about, we're writing
8
- manpages in Markdown and converting to troff/HTML with Pandoc.
5
+ We use the latest version of {wrongdoc}[http://bogomips.org/wrongdoc] as
6
+ much as possible.
9
7
 
10
8
  Please wrap documentation at 72 characters-per-line or less (long URLs
11
9
  are exempt) so it is comfortably readable from terminals.
@@ -22,7 +20,7 @@ respective C APIs.
22
20
  All of our C code should be compatible with all reasonably modern Unices
23
21
  and should run on compilers supported by the versions of Ruby we target.
24
22
 
25
- We will NEVER support non-Free platforms under any circumstances.
23
+ We will NEVER directly support non-Free platforms under any circumstances.
26
24
 
27
25
  Our C code follows K&R indentation style (hard tabs, tabs are always 8
28
26
  characters wide) and NOT the indentation style of Matz Ruby.
@@ -63,4 +61,3 @@ Without RubyGems (via setup.rb):
63
61
 
64
62
  It is not at all recommended to mix a RubyGems installation with an
65
63
  installation done without RubyGems, however.
66
-
@@ -16,8 +16,11 @@ static int accept4_flags = SOCK_CLOEXEC | SOCK_NONBLOCK;
16
16
 
17
17
  struct accept_args {
18
18
  int fd;
19
+ int flags;
19
20
  struct sockaddr *addr;
20
21
  socklen_t *addrlen;
22
+ VALUE accept_io;
23
+ VALUE accepted_class;
21
24
  };
22
25
 
23
26
  /*
@@ -53,15 +56,19 @@ static VALUE get_accepted(VALUE klass)
53
56
  return cClientSocket;
54
57
  }
55
58
 
59
+ /*
60
+ * accept() wrapper that'll fall back on accept() if we were built on
61
+ * a system with accept4() but run on a system without accept4()
62
+ */
56
63
  static VALUE xaccept(void *ptr)
57
64
  {
58
65
  struct accept_args *a = ptr;
59
66
  int rv;
60
67
 
61
- rv = accept_fn(a->fd, a->addr, a->addrlen, accept4_flags);
68
+ rv = accept_fn(a->fd, a->addr, a->addrlen, a->flags);
62
69
  if (rv == -1 && errno == ENOSYS && accept_fn != my_accept4) {
63
70
  accept_fn = my_accept4;
64
- rv = accept_fn(a->fd, a->addr, a->addrlen, accept4_flags);
71
+ rv = accept_fn(a->fd, a->addr, a->addrlen, a->flags);
65
72
  }
66
73
 
67
74
  return (VALUE)rv;
@@ -124,16 +131,56 @@ static int thread_accept(struct accept_args *a, int force_nonblock)
124
131
  #define set_blocking_or_block(fd) (void)rb_io_wait_readable(fd)
125
132
  #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
126
133
 
127
- static VALUE acceptor(int argc, const VALUE *argv)
134
+ static void
135
+ prepare_accept(struct accept_args *a, VALUE self, int argc, const VALUE *argv)
128
136
  {
129
- if (argc == 0)
130
- return cClientSocket; /* default, legacy behavior */
131
- else if (argc == 1)
132
- return argv[0];
137
+ a->fd = my_fileno(self);
138
+ a->accept_io = self;
139
+
140
+ switch (argc) {
141
+ case 2:
142
+ a->flags = NUM2INT(argv[1]);
143
+ a->accepted_class = NIL_P(argv[0]) ? cClientSocket : argv[0];
144
+ return;
145
+ case 0: /* default, legacy behavior */
146
+ a->flags = accept4_flags;
147
+ a->accepted_class = cClientSocket;
148
+ return;
149
+ case 1:
150
+ a->flags = accept4_flags;
151
+ a->accepted_class = NIL_P(argv[0]) ? cClientSocket : argv[0];
152
+ return;
153
+ }
133
154
 
134
155
  rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
135
156
  }
136
157
 
158
+ static VALUE in_addr_set(VALUE io, struct sockaddr_storage *addr, socklen_t len)
159
+ {
160
+ VALUE host;
161
+ int host_len, rc;
162
+ char *host_ptr;
163
+
164
+ switch (addr->ss_family) {
165
+ case AF_INET:
166
+ host_len = (long)INET_ADDRSTRLEN;
167
+ break;
168
+ case AF_INET6:
169
+ host_len = (long)INET6_ADDRSTRLEN;
170
+ break;
171
+ default:
172
+ rb_raise(rb_eRuntimeError, "unsupported address family");
173
+ }
174
+ host = rb_str_new(NULL, host_len);
175
+ host_ptr = RSTRING_PTR(host);
176
+ rc = getnameinfo((struct sockaddr *)addr, len,
177
+ host_ptr, host_len, NULL, 0, NI_NUMERICHOST);
178
+ if (rc != 0)
179
+ rb_raise(rb_eRuntimeError, "getnameinfo: %s", gai_strerror(rc));
180
+ rb_str_set_len(host, strlen(host_ptr));
181
+ return rb_ivar_set(io, iv_kgio_addr, host);
182
+ }
183
+
137
184
  #if defined(__linux__)
138
185
  # define post_accept kgio_autopush_accept
139
186
  #else
@@ -141,24 +188,21 @@ static VALUE acceptor(int argc, const VALUE *argv)
141
188
  #endif
142
189
 
143
190
  static VALUE
144
- my_accept(VALUE accept_io, VALUE klass,
145
- struct sockaddr *addr, socklen_t *addrlen, int nonblock)
191
+ my_accept(struct accept_args *a, int force_nonblock)
146
192
  {
147
- int client;
193
+ int client_fd;
148
194
  VALUE client_io;
149
- struct accept_args a;
195
+ int retried = 0;
150
196
 
151
- a.fd = my_fileno(accept_io);
152
- a.addr = addr;
153
- a.addrlen = addrlen;
154
197
  retry:
155
- client = thread_accept(&a, nonblock);
156
- if (client == -1) {
198
+ client_fd = thread_accept(a, force_nonblock);
199
+ if (client_fd == -1) {
157
200
  switch (errno) {
158
201
  case EAGAIN:
159
- if (nonblock)
202
+ if (force_nonblock)
160
203
  return Qnil;
161
- set_blocking_or_block(a.fd);
204
+ a->fd = my_fileno(a->accept_io);
205
+ set_blocking_or_block(a->fd);
162
206
  #ifdef ECONNABORTED
163
207
  case ECONNABORTED:
164
208
  #endif /* ECONNABORTED */
@@ -166,6 +210,7 @@ retry:
166
210
  case EPROTO:
167
211
  #endif /* EPROTO */
168
212
  case EINTR:
213
+ a->fd = my_fileno(a->accept_io);
169
214
  goto retry;
170
215
  case ENOMEM:
171
216
  case EMFILE:
@@ -173,47 +218,27 @@ retry:
173
218
  #ifdef ENOBUFS
174
219
  case ENOBUFS:
175
220
  #endif /* ENOBUFS */
176
- errno = 0;
177
- rb_gc();
178
- client = thread_accept(&a, nonblock);
179
- }
180
- if (client == -1) {
181
- if (errno == EINTR)
221
+ if (!retried) {
222
+ retried = 1;
223
+ errno = 0;
224
+ rb_gc();
182
225
  goto retry;
226
+ }
227
+ default:
183
228
  rb_sys_fail("accept");
184
229
  }
185
230
  }
186
- client_io = sock_for_fd(klass, client);
187
- post_accept(accept_io, client_io);
231
+ client_io = sock_for_fd(a->accepted_class, client_fd);
232
+ post_accept(a->accept_io, client_io);
233
+
234
+ if (a->addr)
235
+ in_addr_set(client_io,
236
+ (struct sockaddr_storage *)a->addr, *a->addrlen);
237
+ else
238
+ rb_ivar_set(client_io, iv_kgio_addr, localhost);
188
239
  return client_io;
189
240
  }
190
241
 
191
- static VALUE in_addr_set(VALUE io, struct sockaddr_storage *addr, socklen_t len)
192
- {
193
- VALUE host;
194
- int host_len, rc;
195
- char *host_ptr;
196
-
197
- switch (addr->ss_family) {
198
- case AF_INET:
199
- host_len = (long)INET_ADDRSTRLEN;
200
- break;
201
- case AF_INET6:
202
- host_len = (long)INET6_ADDRSTRLEN;
203
- break;
204
- default:
205
- rb_raise(rb_eRuntimeError, "unsupported address family");
206
- }
207
- host = rb_str_new(NULL, host_len);
208
- host_ptr = RSTRING_PTR(host);
209
- rc = getnameinfo((struct sockaddr *)addr, len,
210
- host_ptr, host_len, NULL, 0, NI_NUMERICHOST);
211
- if (rc != 0)
212
- rb_raise(rb_eRuntimeError, "getnameinfo: %s", gai_strerror(rc));
213
- rb_str_set_len(host, strlen(host_ptr));
214
- return rb_ivar_set(io, iv_kgio_addr, host);
215
- }
216
-
217
242
  /*
218
243
  * call-seq:
219
244
  *
@@ -239,6 +264,8 @@ static VALUE addr_bang(VALUE io)
239
264
  *
240
265
  * server = Kgio::TCPServer.new('0.0.0.0', 80)
241
266
  * server.kgio_tryaccept -> Kgio::Socket or nil
267
+ * server.kgio_tryaccept(klass = MySocket) -> MySocket or nil
268
+ * server.kgio_tryaccept(nil, flags) -> Kgio::Socket or nil
242
269
  *
243
270
  * Initiates a non-blocking accept and returns a generic Kgio::Socket
244
271
  * object with the kgio_addr attribute set to the IP address of the
@@ -246,21 +273,26 @@ static VALUE addr_bang(VALUE io)
246
273
  *
247
274
  * Returns nil on EAGAIN, and raises on other errors.
248
275
  *
249
- * An optional class argument may be specified to override the
250
- * Kgio::Socket-class return value:
276
+ * An optional +klass+ argument may be specified to override the
277
+ * Kgio::Socket-class on a successful return value.
278
+ *
279
+ * An optional +flags+ argument may also be specifed to override the
280
+ * value of +Kgio.accept_cloexec+ and +Kgio.accept_nonblock+. +flags+
281
+ * is a bitmask that may contain any combination of:
251
282
  *
252
- * server.kgio_tryaccept(MySocket) -> MySocket
283
+ * - Fcntl::FD_CLOEXEC - close-on-exec flag
284
+ * - IO::NONBLOCK - non-blocking flag
253
285
  */
254
- static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE io)
286
+ static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE self)
255
287
  {
256
288
  struct sockaddr_storage addr;
257
289
  socklen_t addrlen = sizeof(struct sockaddr_storage);
258
- VALUE klass = acceptor(argc, argv);
259
- VALUE rv = my_accept(io, klass, (struct sockaddr *)&addr, &addrlen, 1);
290
+ struct accept_args a;
260
291
 
261
- if (!NIL_P(rv))
262
- in_addr_set(rv, &addr, addrlen);
263
- return rv;
292
+ a.addr = (struct sockaddr *)&addr;
293
+ a.addrlen = &addrlen;
294
+ prepare_accept(&a, self, argc, argv);
295
+ return my_accept(&a, 1);
264
296
  }
265
297
 
266
298
  /*
@@ -268,6 +300,8 @@ static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE io)
268
300
  *
269
301
  * server = Kgio::TCPServer.new('0.0.0.0', 80)
270
302
  * server.kgio_accept -> Kgio::Socket or nil
303
+ * server.kgio_tryaccept -> Kgio::Socket or nil
304
+ * server.kgio_tryaccept(klass = MySocket) -> MySocket or nil
271
305
  *
272
306
  * Initiates a blocking accept and returns a generic Kgio::Socket
273
307
  * object with the kgio_addr attribute set to the IP address of
@@ -276,20 +310,26 @@ static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE io)
276
310
  * On Ruby implementations using native threads, this can use a blocking
277
311
  * accept(2) (or accept4(2)) system call to avoid thundering herds.
278
312
  *
279
- * An optional class argument may be specified to override the
280
- * Kgio::Socket-class return value:
313
+ * An optional +klass+ argument may be specified to override the
314
+ * Kgio::Socket-class on a successful return value.
315
+ *
316
+ * An optional +flags+ argument may also be specifed to override the
317
+ * value of +Kgio.accept_cloexec+ and +Kgio.accept_nonblock+. +flags+
318
+ * is a bitmask that may contain any combination of:
281
319
  *
282
- * server.kgio_accept(MySocket) -> MySocket
320
+ * - Fcntl::FD_CLOEXEC - close-on-exec flag
321
+ * - IO::NONBLOCK - non-blocking flag
283
322
  */
284
- static VALUE tcp_accept(int argc, VALUE *argv, VALUE io)
323
+ static VALUE tcp_accept(int argc, VALUE *argv, VALUE self)
285
324
  {
286
325
  struct sockaddr_storage addr;
287
326
  socklen_t addrlen = sizeof(struct sockaddr_storage);
288
- VALUE klass = acceptor(argc, argv);
289
- VALUE rv = my_accept(io, klass, (struct sockaddr *)&addr, &addrlen, 0);
327
+ struct accept_args a;
290
328
 
291
- in_addr_set(rv, &addr, addrlen);
292
- return rv;
329
+ a.addr = (struct sockaddr *)&addr;
330
+ a.addrlen = &addrlen;
331
+ prepare_accept(&a, self, argc, argv);
332
+ return my_accept(&a, 0);
293
333
  }
294
334
 
295
335
  /*
@@ -297,26 +337,31 @@ static VALUE tcp_accept(int argc, VALUE *argv, VALUE io)
297
337
  *
298
338
  * server = Kgio::UNIXServer.new("/path/to/unix/socket")
299
339
  * server.kgio_tryaccept -> Kgio::Socket or nil
340
+ * server.kgio_tryaccept(klass = MySocket) -> MySocket or nil
341
+ * server.kgio_tryaccept(nil, flags) -> Kgio::Socket or nil
300
342
  *
301
343
  * Initiates a non-blocking accept and returns a generic Kgio::Socket
302
344
  * object with the kgio_addr attribute set (to the value of
303
345
  * Kgio::LOCALHOST) on success.
304
346
  *
305
- * Returns nil on EAGAIN, and raises on other errors.
347
+ * An optional +klass+ argument may be specified to override the
348
+ * Kgio::Socket-class on a successful return value.
306
349
  *
307
- * An optional class argument may be specified to override the
308
- * Kgio::Socket-class return value:
350
+ * An optional +flags+ argument may also be specifed to override the
351
+ * value of +Kgio.accept_cloexec+ and +Kgio.accept_nonblock+. +flags+
352
+ * is a bitmask that may contain any combination of:
309
353
  *
310
- * server.kgio_tryaccept(MySocket) -> MySocket
354
+ * - Fcntl::FD_CLOEXEC - close-on-exec flag
355
+ * - IO::NONBLOCK - non-blocking flag
311
356
  */
312
- static VALUE unix_tryaccept(int argc, VALUE *argv, VALUE io)
357
+ static VALUE unix_tryaccept(int argc, VALUE *argv, VALUE self)
313
358
  {
314
- VALUE klass = acceptor(argc, argv);
315
- VALUE rv = my_accept(io, klass, NULL, NULL, 1);
359
+ struct accept_args a;
316
360
 
317
- if (!NIL_P(rv))
318
- rb_ivar_set(rv, iv_kgio_addr, localhost);
319
- return rv;
361
+ a.addr = NULL;
362
+ a.addrlen = NULL;
363
+ prepare_accept(&a, self, argc, argv);
364
+ return my_accept(&a, 1);
320
365
  }
321
366
 
322
367
  /*
@@ -324,6 +369,8 @@ static VALUE unix_tryaccept(int argc, VALUE *argv, VALUE io)
324
369
  *
325
370
  * server = Kgio::UNIXServer.new("/path/to/unix/socket")
326
371
  * server.kgio_accept -> Kgio::Socket or nil
372
+ * server.kgio_accept(klass = MySocket) -> MySocket or nil
373
+ * server.kgio_accept(nil, flags) -> Kgio::Socket or nil
327
374
  *
328
375
  * Initiates a blocking accept and returns a generic Kgio::Socket
329
376
  * object with the kgio_addr attribute set (to the value of
@@ -332,18 +379,24 @@ static VALUE unix_tryaccept(int argc, VALUE *argv, VALUE io)
332
379
  * On Ruby implementations using native threads, this can use a blocking
333
380
  * accept(2) (or accept4(2)) system call to avoid thundering herds.
334
381
  *
335
- * An optional class argument may be specified to override the
336
- * Kgio::Socket-class return value:
382
+ * An optional +klass+ argument may be specified to override the
383
+ * Kgio::Socket-class on a successful return value.
384
+ *
385
+ * An optional +flags+ argument may also be specifed to override the
386
+ * value of +Kgio.accept_cloexec+ and +Kgio.accept_nonblock+. +flags+
387
+ * is a bitmask that may contain any combination of:
337
388
  *
338
- * server.kgio_accept(MySocket) -> MySocket
389
+ * - Fcntl::FD_CLOEXEC - close-on-exec flag
390
+ * - IO::NONBLOCK - non-blocking flag
339
391
  */
340
- static VALUE unix_accept(int argc, VALUE *argv, VALUE io)
392
+ static VALUE unix_accept(int argc, VALUE *argv, VALUE self)
341
393
  {
342
- VALUE klass = acceptor(argc, argv);
343
- VALUE rv = my_accept(io, klass, NULL, NULL, 0);
394
+ struct accept_args a;
344
395
 
345
- rb_ivar_set(rv, iv_kgio_addr, localhost);
346
- return rv;
396
+ a.addr = NULL;
397
+ a.addrlen = NULL;
398
+ prepare_accept(&a, self, argc, argv);
399
+ return my_accept(&a, 0);
347
400
  }
348
401
 
349
402
  /*
@@ -384,7 +437,7 @@ static VALUE get_nonblock(VALUE mod)
384
437
  * TCPServer#kgio_tryaccept,
385
438
  * UNIXServer#kgio_accept,
386
439
  * and UNIXServer#kgio_tryaccept
387
- * are created with the FD_CLOEXEC file descriptor flag.
440
+ * default to being created with the FD_CLOEXEC file descriptor flag.
388
441
  *
389
442
  * This is on by default, as there is little reason to deal to enable
390
443
  * it for client sockets on a socket server.
@@ -242,6 +242,7 @@ static void push_pending_data(VALUE io)
242
242
  rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 1)");
243
243
  }
244
244
  #else /* !KGIO_NOPUSH */
245
+ void kgio_autopush_recv(VALUE io){}
245
246
  void init_kgio_autopush(void)
246
247
  {
247
248
  }
@@ -0,0 +1,62 @@
1
+ /*
2
+ * this header includes functions to support broken systems
3
+ * without clock_gettime() or CLOCK_MONOTONIC
4
+ */
5
+
6
+ #ifndef HAVE_TYPE_CLOCKID_T
7
+ typedef int clockid_t;
8
+ #endif
9
+
10
+ #ifndef HAVE_CLOCK_GETTIME
11
+ # ifndef CLOCK_REALTIME
12
+ # define CLOCK_REALTIME 0 /* whatever */
13
+ # endif
14
+ static int fake_clock_gettime(clockid_t clk_id, struct timespec *res)
15
+ {
16
+ struct timeval tv;
17
+ int r = gettimeofday(&tv, NULL);
18
+
19
+ assert(0 == r && "gettimeofday() broke!?");
20
+ res->tv_sec = tv.tv_sec;
21
+ res->tv_nsec = tv.tv_usec * 1000;
22
+
23
+ return r;
24
+ }
25
+ # define clock_gettime fake_clock_gettime
26
+ #endif /* broken systems w/o clock_gettime() */
27
+
28
+ /*
29
+ * UGH
30
+ * CLOCK_MONOTONIC is not guaranteed to be a macro, either
31
+ */
32
+ #ifndef CLOCK_MONOTONIC
33
+ # if (!defined(_POSIX_MONOTONIC_CLOCK) || !defined(HAVE_CLOCK_MONOTONIC))
34
+ # define CLOCK_MONOTONIC CLOCK_REALTIME
35
+ # endif
36
+ #endif
37
+
38
+ /*
39
+ * Availability of a monotonic clock needs to be detected at runtime
40
+ * since we could've been built on a different system than we're run
41
+ * under.
42
+ */
43
+ static clockid_t hopefully_CLOCK_MONOTONIC;
44
+
45
+ static int check_clock(void)
46
+ {
47
+ struct timespec now;
48
+
49
+ hopefully_CLOCK_MONOTONIC = CLOCK_MONOTONIC;
50
+
51
+ /* we can't check this reliably at compile time */
52
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0)
53
+ return 1;
54
+
55
+ if (clock_gettime(CLOCK_REALTIME, &now) == 0) {
56
+ hopefully_CLOCK_MONOTONIC = CLOCK_REALTIME;
57
+ rb_warn("CLOCK_MONOTONIC not available, "
58
+ "falling back to CLOCK_REALTIME");
59
+ return 2;
60
+ }
61
+ return -1;
62
+ }
@@ -69,6 +69,7 @@ static VALUE tcp_connect(VALUE klass, VALUE ip, VALUE port, int io_wait)
69
69
  rc = snprintf(ipport, sizeof(ipport), "%u", uport);
70
70
  if (rc >= (int)sizeof(ipport) || rc <= 0)
71
71
  rb_raise(rb_eArgError, "invalid TCP port: %u", uport);
72
+ memset(&hints, 0, sizeof(hints));
72
73
  hints.ai_family = AF_UNSPEC;
73
74
  hints.ai_socktype = SOCK_STREAM;
74
75
  hints.ai_protocol = IPPROTO_TCP;
@@ -1,7 +1,12 @@
1
1
  require 'mkmf'
2
2
  $CPPFLAGS << ' -D_GNU_SOURCE'
3
3
  $CPPFLAGS << ' -DPOSIX_C_SOURCE=1'
4
-
4
+ $CPPFLAGS += '-D_POSIX_C_SOURCE=200112L'
5
+ unless have_macro('CLOCK_MONOTONIC', 'time.h')
6
+ have_func('CLOCK_MONOTONIC', 'time.h')
7
+ end
8
+ have_type('clockid_t', 'time.h')
9
+ have_library('rt', 'clock_gettime', 'time.h')
5
10
  have_func("poll", "poll.h")
6
11
  have_func("getaddrinfo", %w(sys/types.h sys/socket.h netdb.h)) or
7
12
  abort "getaddrinfo required"
@@ -30,5 +35,4 @@ have_func('rb_thread_blocking_region')
30
35
  have_func('rb_thread_io_blocking_region')
31
36
  have_func('rb_str_set_len')
32
37
 
33
- dir_config('kgio')
34
38
  create_makefile('kgio_ext')
@@ -1,6 +1,9 @@
1
1
  #include "kgio.h"
2
2
  #if defined(USE_KGIO_POLL)
3
+ #include <time.h>
4
+ #include "broken_system_compat.h"
3
5
  #include <poll.h>
6
+ #include <errno.h>
4
7
  #ifdef HAVE_RUBY_ST_H
5
8
  # include <ruby/st.h>
6
9
  #else
@@ -16,8 +19,43 @@ struct poll_args {
16
19
  int timeout;
17
20
  VALUE ios;
18
21
  st_table *fd_to_io;
22
+ struct timespec start;
19
23
  };
20
24
 
25
+ static int interrupted(void)
26
+ {
27
+ switch (errno) {
28
+ case EINTR:
29
+ #ifdef ERESTART
30
+ case ERESTART:
31
+ #endif
32
+ return 1;
33
+ }
34
+ return 0;
35
+ }
36
+
37
+ static int retryable(struct poll_args *a)
38
+ {
39
+ struct timespec ts;
40
+
41
+ if (a->timeout < 0)
42
+ return 1;
43
+ if (a->timeout == 0)
44
+ return 0;
45
+
46
+ clock_gettime(hopefully_CLOCK_MONOTONIC, &ts);
47
+
48
+ ts.tv_sec -= a->start.tv_sec;
49
+ ts.tv_nsec -= a->start.tv_nsec;
50
+ if (ts.tv_nsec < 0) {
51
+ ts.tv_sec--;
52
+ ts.tv_nsec += 1000000000;
53
+ }
54
+ a->timeout -= ts.tv_sec * 1000;
55
+ a->timeout -= ts.tv_nsec / 1000000;
56
+ return (a->timeout >= 0);
57
+ }
58
+
21
59
  static int num2timeout(VALUE timeout)
22
60
  {
23
61
  switch (TYPE(timeout)) {
@@ -62,6 +100,7 @@ static int io_to_pollfd_i(VALUE key, VALUE value, VALUE args)
62
100
 
63
101
  static void hash2pollfds(struct poll_args *a)
64
102
  {
103
+ a->nfds = 0;
65
104
  a->fds = xmalloc(sizeof(struct pollfd) * RHASH_SIZE(a->ios));
66
105
  a->fd_to_io = st_init_numtable();
67
106
  rb_hash_foreach(a->ios, io_to_pollfd_i, (VALUE)a);
@@ -70,6 +109,10 @@ static void hash2pollfds(struct poll_args *a)
70
109
  static VALUE nogvl_poll(void *ptr)
71
110
  {
72
111
  struct poll_args *a = ptr;
112
+
113
+ if (a->timeout > 0)
114
+ clock_gettime(hopefully_CLOCK_MONOTONIC, &a->start);
115
+
73
116
  return (VALUE)poll(a->fds, a->nfds, a->timeout);
74
117
  }
75
118
 
@@ -98,10 +141,20 @@ static VALUE do_poll(VALUE args)
98
141
  int nr;
99
142
 
100
143
  Check_Type(a->ios, T_HASH);
101
- hash2pollfds(a);
102
144
 
145
+ retry:
146
+ hash2pollfds(a);
103
147
  nr = (int)rb_thread_blocking_region(nogvl_poll, a, RUBY_UBF_IO, NULL);
104
- if (nr < 0) rb_sys_fail("poll");
148
+ if (nr < 0) {
149
+ if (interrupted()) {
150
+ if (retryable(a)) {
151
+ poll_free(args);
152
+ goto retry;
153
+ }
154
+ return Qnil;
155
+ }
156
+ rb_sys_fail("poll");
157
+ }
105
158
  if (nr == 0) return Qnil;
106
159
 
107
160
  return poll_result(nr, a);
@@ -146,7 +199,6 @@ static VALUE s_poll(int argc, VALUE *argv, VALUE self)
146
199
 
147
200
  rb_scan_args(argc, argv, "11", &a.ios, &timeout);
148
201
  a.timeout = num2timeout(timeout);
149
- a.nfds = 0;
150
202
  a.fds = NULL;
151
203
  a.fd_to_io = NULL;
152
204
 
@@ -156,6 +208,9 @@ static VALUE s_poll(int argc, VALUE *argv, VALUE self)
156
208
  void init_kgio_poll(void)
157
209
  {
158
210
  VALUE mKgio = rb_define_module("Kgio");
211
+
212
+ if (check_clock() < 0)
213
+ return;
159
214
  rb_define_singleton_method(mKgio, "poll", s_poll, -1);
160
215
 
161
216
  sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
@@ -68,6 +68,7 @@ static void prepare_read(struct io_args *a, int argc, VALUE *argv, VALUE io)
68
68
  a->buf = rb_str_new(NULL, a->len);
69
69
  } else {
70
70
  StringValue(a->buf);
71
+ rb_str_modify(a->buf);
71
72
  rb_str_resize(a->buf, a->len);
72
73
  }
73
74
  a->ptr = RSTRING_PTR(a->buf);
@@ -76,14 +77,17 @@ static void prepare_read(struct io_args *a, int argc, VALUE *argv, VALUE io)
76
77
  static int read_check(struct io_args *a, long n, const char *msg, int io_wait)
77
78
  {
78
79
  if (n == -1) {
79
- if (errno == EINTR)
80
+ if (errno == EINTR) {
81
+ a->fd = my_fileno(a->io);
80
82
  return -1;
83
+ }
81
84
  rb_str_set_len(a->buf, 0);
82
85
  if (errno == EAGAIN) {
83
86
  if (io_wait) {
84
87
  (void)kgio_call_wait_readable(a->io);
85
88
 
86
89
  /* buf may be modified in other thread/fiber */
90
+ rb_str_modify(a->buf);
87
91
  rb_str_resize(a->buf, a->len);
88
92
  a->ptr = RSTRING_PTR(a->buf);
89
93
  return -1;
@@ -307,8 +311,10 @@ static int write_check(struct io_args *a, long n, const char *msg, int io_wait)
307
311
  done:
308
312
  a->buf = Qnil;
309
313
  } else if (n == -1) {
310
- if (errno == EINTR)
314
+ if (errno == EINTR) {
315
+ a->fd = my_fileno(a->io);
311
316
  return -1;
317
+ }
312
318
  if (errno == EAGAIN) {
313
319
  long written = RSTRING_LEN(a->buf) - a->len;
314
320
 
@@ -370,7 +376,7 @@ static VALUE kgio_write(VALUE io, VALUE str)
370
376
  /*
371
377
  * call-seq:
372
378
  *
373
- * io.kgio_trywrite(str) -> nil or :wait_writable
379
+ * io.kgio_trywrite(str) -> nil, String or :wait_writable
374
380
  *
375
381
  * Returns nil if the write was completed in full.
376
382
  *
@@ -451,7 +457,7 @@ static VALUE s_tryread(int argc, VALUE *argv, VALUE mod)
451
457
  /*
452
458
  * call-seq:
453
459
  *
454
- * Kgio.trywrite(io, str) -> nil or :wait_writable
460
+ * Kgio.trywrite(io, str) -> nil, String or :wait_writable
455
461
  *
456
462
  * Returns nil if the write was completed in full.
457
463
  *
@@ -15,13 +15,12 @@ Gem::Specification.new do |s|
15
15
  s.extra_rdoc_files = extra_rdoc_files(manifest)
16
16
  s.files = manifest
17
17
  s.rdoc_options = rdoc_options
18
- s.require_paths = %w(lib ext)
19
18
  s.rubyforge_project = %q{rainbows}
20
19
  s.summary = summary
21
20
  s.test_files = Dir['test/test_*.rb']
22
21
  s.extensions = %w(ext/kgio/extconf.rb)
23
22
 
24
- s.add_development_dependency('wrongdoc', '~> 1.4')
23
+ s.add_development_dependency('wrongdoc', '~> 1.5')
25
24
  s.add_development_dependency('strace_me', '~> 1.0')
26
25
 
27
26
  # s.license = %w(LGPL) # disabled for compatibility with older RubyGems
@@ -30,6 +30,24 @@ module LibReadWriteTest
30
30
  assert_equal "", buf
31
31
  end
32
32
 
33
+ def test_read_shared
34
+ a = "." * 0x1000
35
+ b = a.dup
36
+ @wr.syswrite "a"
37
+ assert_equal "a", @rd.kgio_read(0x1000, a)
38
+ assert_equal "a", a
39
+ assert_equal "." * 0x1000, b
40
+ end
41
+
42
+ def test_read_shared_2
43
+ a = "." * 0x1000
44
+ b = a.dup
45
+ @wr.syswrite "a"
46
+ assert_equal "a", @rd.kgio_read(0x1000, b)
47
+ assert_equal "a", b
48
+ assert_equal "." * 0x1000, a
49
+ end
50
+
33
51
  def test_tryread_zero
34
52
  assert_equal "", @rd.kgio_tryread(0)
35
53
  buf = "foo"
@@ -37,6 +55,24 @@ module LibReadWriteTest
37
55
  assert_equal "", buf
38
56
  end
39
57
 
58
+ def test_tryread_shared
59
+ a = "." * 0x1000
60
+ b = a.dup
61
+ @wr.syswrite("a")
62
+ assert_equal "a", @rd.kgio_tryread(0x1000, b)
63
+ assert_equal "a", b
64
+ assert_equal "." * 0x1000, a
65
+ end
66
+
67
+ def test_tryread_shared_2
68
+ a = "." * 0x1000
69
+ b = a.dup
70
+ @wr.syswrite("a")
71
+ assert_equal "a", @rd.kgio_tryread(0x1000, a)
72
+ assert_equal "a", a
73
+ assert_equal "." * 0x1000, b
74
+ end
75
+
40
76
  def test_read_eof
41
77
  @wr.close
42
78
  assert_nil @rd.kgio_read(5)
@@ -1,4 +1,5 @@
1
1
  require 'test/unit'
2
+ require 'fcntl'
2
3
  require 'io/nonblock'
3
4
  $-w = true
4
5
  require 'kgio'
@@ -19,6 +20,24 @@ module LibServerAccept
19
20
  assert_equal @host, b.kgio_addr
20
21
  end
21
22
 
23
+ def test_tryaccept_flags
24
+ a = client_connect
25
+ IO.select([@srv])
26
+ b = @srv.kgio_tryaccept nil, 0
27
+ assert_kind_of Kgio::Socket, b
28
+ assert_equal false, b.nonblock?
29
+ assert_equal 0, b.fcntl(Fcntl::F_GETFD)
30
+ end
31
+
32
+ def test_blocking_accept_flags
33
+ a = client_connect
34
+ IO.select([@srv])
35
+ b = @srv.kgio_accept nil, 0
36
+ assert_kind_of Kgio::Socket, b
37
+ assert_equal false, b.nonblock?
38
+ assert_equal 0, b.fcntl(Fcntl::F_GETFD)
39
+ end
40
+
22
41
  def test_tryaccept_fail
23
42
  assert_equal nil, @srv.kgio_tryaccept
24
43
  end
@@ -94,7 +94,7 @@ class TestAutopush < Test::Unit::TestCase
94
94
  @rd.kgio_write "HI\n"
95
95
  @wr.kgio_read(3, rbuf)
96
96
  diff = Time.now - t0
97
- assert(diff >= 0.200, "nopush broken? diff=#{diff} > 200ms")
97
+ assert(diff >= 0.190, "nopush broken? diff=#{diff} > 200ms")
98
98
  assert_equal "HI\n", rbuf
99
99
  end
100
100
 
@@ -0,0 +1,26 @@
1
+ require 'test/unit'
2
+ $-w = true
3
+ require 'kgio'
4
+
5
+ class TestCrossThreadClose < Test::Unit::TestCase
6
+
7
+ def test_cross_thread_close
8
+ host = ENV["TEST_HOST"] || '127.0.0.1'
9
+ srv = Kgio::TCPServer.new(host, 0)
10
+ thr = Thread.new do
11
+ begin
12
+ srv.kgio_accept
13
+ rescue => e
14
+ e
15
+ end
16
+ end
17
+ sleep(0.1) until thr.stop?
18
+ srv.close
19
+ unless defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" &&
20
+ RUBY_VERSION == "1.9.3"
21
+ thr.run rescue nil
22
+ end
23
+ thr.join
24
+ assert_kind_of IOError, thr.value
25
+ end
26
+ end if defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby"
@@ -42,27 +42,52 @@ class TestPoll < Test::Unit::TestCase
42
42
  assert_nil res
43
43
  end
44
44
 
45
- def test_poll_interrupt
45
+ def test_poll_close
46
46
  foo = nil
47
- oldquit = trap(:QUIT) { foo = :bar }
48
- thr = Thread.new { sleep 0.100; Process.kill(:QUIT, $$) }
47
+ thr = Thread.new { sleep 0.100; @wr.close }
49
48
  t0 = Time.now
50
- assert_raises(Errno::EINTR) { Kgio.poll({}) }
49
+ res = Kgio.poll({@rd => Kgio::POLLIN})
51
50
  diff = Time.now - t0
52
51
  thr.join
52
+ assert_equal([ @rd ], res.keys)
53
53
  assert diff >= 0.010, "diff=#{diff}"
54
+ end
55
+
56
+ def test_poll_EINTR
57
+ ok = false
58
+ orig = trap(:USR1) { ok = true }
59
+ thr = Thread.new do
60
+ sleep 0.100
61
+ Process.kill(:USR1, $$)
62
+ end
63
+ t0 = Time.now
64
+ res = Kgio.poll({@rd => Kgio::POLLIN}, 1000)
65
+ diff = Time.now - t0
66
+ thr.join
67
+ assert_nil res
68
+ assert diff >= 1.0, "diff=#{diff}"
69
+ assert ok
54
70
  ensure
55
- trap(:QUIT, "DEFAULT")
71
+ trap(:USR1, orig)
56
72
  end
57
73
 
58
- def test_poll_close
59
- foo = nil
60
- thr = Thread.new { sleep 0.100; @wr.close }
74
+ def test_poll_EINTR_changed
75
+ ok = false
76
+ orig = trap(:USR1) { ok = true }
77
+ pollset = { @rd => Kgio::POLLIN }
78
+ thr = Thread.new do
79
+ sleep 0.100
80
+ pollset[@wr] = Kgio::POLLOUT
81
+ Process.kill(:USR1, $$)
82
+ end
61
83
  t0 = Time.now
62
- res = Kgio.poll({@rd => Kgio::POLLIN})
84
+ res = Kgio.poll(pollset, 1000)
63
85
  diff = Time.now - t0
64
86
  thr.join
65
- assert_equal([ @rd ], res.keys)
66
- assert diff >= 0.010, "diff=#{diff}"
87
+ assert_equal({@wr => Kgio::POLLOUT}, res)
88
+ assert diff < 1.0, "diff=#{diff}"
89
+ assert ok
90
+ ensure
91
+ trap(:USR1, orig)
67
92
  end
68
93
  end if Kgio.respond_to?(:poll)
@@ -66,7 +66,7 @@ class TestKgioTcpConnect < Test::Unit::TestCase
66
66
 
67
67
  def test_wait_writable_set
68
68
  sock = SubSocket.new(@addr)
69
- assert_equal "waited", sock.foo
69
+ assert_equal "waited", sock.foo if RUBY_PLATFORM =~ /linux/
70
70
  assert_equal nil, sock.kgio_write("HELLO")
71
71
  end
72
72
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kgio
3
3
  version: !ruby/object:Gem::Version
4
- hash: 5
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
- - 3
9
- - 3
10
- version: 2.3.3
8
+ - 4
9
+ - 0
10
+ version: 2.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - kgio hackers
@@ -15,8 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-15 00:00:00 +00:00
19
- default_executable:
18
+ date: 2011-05-05 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: wrongdoc
@@ -26,11 +25,11 @@ dependencies:
26
25
  requirements:
27
26
  - - ~>
28
27
  - !ruby/object:Gem::Version
29
- hash: 7
28
+ hash: 5
30
29
  segments:
31
30
  - 1
32
- - 4
33
- version: "1.4"
31
+ - 5
32
+ version: "1.5"
34
33
  type: :development
35
34
  version_requirements: *id001
36
35
  - !ruby/object:Gem::Dependency
@@ -97,6 +96,7 @@ files:
97
96
  - ext/kgio/ancient_ruby.h
98
97
  - ext/kgio/autopush.c
99
98
  - ext/kgio/blocking_io_region.h
99
+ - ext/kgio/broken_system_compat.h
100
100
  - ext/kgio/connect.c
101
101
  - ext/kgio/extconf.rb
102
102
  - ext/kgio/kgio.h
@@ -117,6 +117,7 @@ files:
117
117
  - test/test_accept_class.rb
118
118
  - test/test_autopush.rb
119
119
  - test/test_connect_fd_leak.rb
120
+ - test/test_cross_thread_close.rb
120
121
  - test/test_default_wait.rb
121
122
  - test/test_kgio_addr.rb
122
123
  - test/test_no_dns_on_tcp_connect.rb
@@ -135,7 +136,6 @@ files:
135
136
  - test/test_unix_connect.rb
136
137
  - test/test_unix_server.rb
137
138
  - test/test_unix_server_read_client_write.rb
138
- has_rdoc: true
139
139
  homepage: http://bogomips.org/kgio/
140
140
  licenses: []
141
141
 
@@ -147,7 +147,6 @@ rdoc_options:
147
147
  - http://bogomips.org/kgio.git/tree/%s
148
148
  require_paths:
149
149
  - lib
150
- - ext
151
150
  required_ruby_version: !ruby/object:Gem::Requirement
152
151
  none: false
153
152
  requirements:
@@ -169,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
168
  requirements: []
170
169
 
171
170
  rubyforge_project: rainbows
172
- rubygems_version: 1.6.1
171
+ rubygems_version: 1.7.2
173
172
  signing_key:
174
173
  specification_version: 3
175
174
  summary: kinder, gentler I/O for Ruby
@@ -184,6 +183,7 @@ test_files:
184
183
  - test/test_socketpair_read_write.rb
185
184
  - test/test_tcp_server.rb
186
185
  - test/test_unix_server_read_client_write.rb
186
+ - test/test_cross_thread_close.rb
187
187
  - test/test_tcp_connect.rb
188
188
  - test/test_autopush.rb
189
189
  - test/test_connect_fd_leak.rb