kgio 2.3.3 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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