kgio 2.7.4 → 2.8.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.7.4.GIT
4
+ DEF_VER=v2.8.0
5
5
 
6
6
  LF='
7
7
  '
@@ -79,15 +79,6 @@ static VALUE xaccept(void *ptr)
79
79
  #ifdef HAVE_RB_THREAD_BLOCKING_REGION
80
80
  # include <time.h>
81
81
  # include "blocking_io_region.h"
82
- /*
83
- * Try to use a (real) blocking accept() since that can prevent
84
- * thundering herds under Linux:
85
- * http://www.citi.umich.edu/projects/linux-scalability/reports/accept.html
86
- *
87
- * So we periodically disable non-blocking, but not too frequently
88
- * because other processes may set non-blocking (especially during
89
- * a process upgrade) with Rainbows! concurrency model changes.
90
- */
91
82
  static int thread_accept(struct accept_args *a, int force_nonblock)
92
83
  {
93
84
  if (force_nonblock)
@@ -95,28 +86,6 @@ static int thread_accept(struct accept_args *a, int force_nonblock)
95
86
  return (int)rb_thread_io_blocking_region(xaccept, a, a->fd);
96
87
  }
97
88
 
98
- static void set_blocking_or_block(int fd)
99
- {
100
- static time_t last_set_blocking;
101
- time_t now = time(NULL);
102
-
103
- if (last_set_blocking == 0) {
104
- last_set_blocking = now;
105
- (void)rb_io_wait_readable(fd);
106
- } else if ((now - last_set_blocking) <= 5) {
107
- (void)rb_io_wait_readable(fd);
108
- } else {
109
- int flags = fcntl(fd, F_GETFL);
110
- if (flags == -1)
111
- rb_sys_fail("fcntl(F_GETFL)");
112
- if (flags & O_NONBLOCK) {
113
- flags = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
114
- if (flags == -1)
115
- rb_sys_fail("fcntl(F_SETFL)");
116
- }
117
- last_set_blocking = now;
118
- }
119
- }
120
89
  #else /* ! HAVE_RB_THREAD_BLOCKING_REGION */
121
90
  # include <rubysig.h>
122
91
  static int thread_accept(struct accept_args *a, int force_nonblock)
@@ -134,7 +103,6 @@ static int thread_accept(struct accept_args *a, int force_nonblock)
134
103
  TRAP_END;
135
104
  return rv;
136
105
  }
137
- #define set_blocking_or_block(fd) (void)rb_io_wait_readable(fd)
138
106
  #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
139
107
 
140
108
  static void
@@ -209,7 +177,8 @@ retry:
209
177
  return Qnil;
210
178
  a->fd = my_fileno(a->accept_io);
211
179
  errno = EAGAIN;
212
- set_blocking_or_block(a->fd);
180
+ (void)rb_io_wait_readable(a->fd);
181
+ /* fall-through to EINTR case */
213
182
  #ifdef ECONNABORTED
214
183
  case ECONNABORTED:
215
184
  #endif /* ECONNABORTED */
@@ -217,6 +186,7 @@ retry:
217
186
  case EPROTO:
218
187
  #endif /* EPROTO */
219
188
  case EINTR:
189
+ /* raise IOError if closed during sleep */
220
190
  a->fd = my_fileno(a->accept_io);
221
191
  goto retry;
222
192
  case ENOMEM:
@@ -1,5 +1,7 @@
1
1
  #include "kgio.h"
2
+ #include "my_fileno.h"
2
3
  #include "sock_for_fd.h"
4
+ #include "blocking_io_region.h"
3
5
 
4
6
  static void close_fail(int fd, const char *msg)
5
7
  {
@@ -26,8 +28,8 @@ static int MY_SOCK_STREAM =
26
28
  # define rb_fd_fix_cloexec(fd) for (;0;)
27
29
  #endif /* HAVE_RB_FD_FIX_CLOEXEC */
28
30
 
29
- static VALUE
30
- my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen)
31
+ /* try to use SOCK_NONBLOCK and SOCK_CLOEXEC */
32
+ static int my_socket(int domain)
31
33
  {
32
34
  int fd;
33
35
 
@@ -61,6 +63,14 @@ retry:
61
63
  rb_fd_fix_cloexec(fd);
62
64
  }
63
65
 
66
+ return fd;
67
+ }
68
+
69
+ static VALUE
70
+ my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen)
71
+ {
72
+ int fd = my_socket(domain);
73
+
64
74
  if (connect(fd, addr, addrlen) == -1) {
65
75
  if (errno == EINPROGRESS) {
66
76
  VALUE io = sock_for_fd(klass, fd);
@@ -76,10 +86,10 @@ retry:
76
86
  return sock_for_fd(klass, fd);
77
87
  }
78
88
 
79
- static VALUE tcp_connect(VALUE klass, VALUE ip, VALUE port, int io_wait)
89
+ static void
90
+ tcp_getaddr(struct addrinfo *hints, struct sockaddr_storage *addr,
91
+ VALUE ip, VALUE port)
80
92
  {
81
- struct addrinfo hints;
82
- struct sockaddr_storage addr;
83
93
  int rc;
84
94
  struct addrinfo *res;
85
95
  const char *ipname = StringValuePtr(ip);
@@ -93,28 +103,105 @@ static VALUE tcp_connect(VALUE klass, VALUE ip, VALUE port, int io_wait)
93
103
  rc = snprintf(ipport, sizeof(ipport), "%u", uport);
94
104
  if (rc >= (int)sizeof(ipport) || rc <= 0)
95
105
  rb_raise(rb_eArgError, "invalid TCP port: %u", uport);
96
- memset(&hints, 0, sizeof(hints));
97
- hints.ai_family = AF_UNSPEC;
98
- hints.ai_socktype = SOCK_STREAM;
99
- hints.ai_protocol = IPPROTO_TCP;
106
+ memset(hints, 0, sizeof(struct addrinfo));
107
+ hints->ai_family = AF_UNSPEC;
108
+ hints->ai_socktype = SOCK_STREAM;
109
+ hints->ai_protocol = IPPROTO_TCP;
100
110
  /* disallow non-deterministic DNS lookups */
101
- hints.ai_flags = AI_NUMERICHOST;
111
+ hints->ai_flags = AI_NUMERICHOST;
102
112
 
103
- rc = getaddrinfo(ipname, ipport, &hints, &res);
113
+ rc = getaddrinfo(ipname, ipport, hints, &res);
104
114
  if (rc != 0)
105
115
  rb_raise(rb_eArgError, "getaddrinfo(%s:%s): %s",
106
116
  ipname, ipport, gai_strerror(rc));
107
117
 
108
118
  /* copy needed data and free ASAP to avoid needing rb_ensure */
109
- hints.ai_family = res->ai_family;
110
- hints.ai_addrlen = res->ai_addrlen;
111
- memcpy(&addr, res->ai_addr, res->ai_addrlen);
119
+ hints->ai_family = res->ai_family;
120
+ hints->ai_addrlen = res->ai_addrlen;
121
+ memcpy(addr, res->ai_addr, res->ai_addrlen);
112
122
  freeaddrinfo(res);
123
+ }
124
+
125
+ static VALUE tcp_connect(VALUE klass, VALUE ip, VALUE port, int io_wait)
126
+ {
127
+ struct addrinfo hints;
128
+ struct sockaddr_storage addr;
129
+
130
+ tcp_getaddr(&hints, &addr, ip, port);
113
131
 
114
132
  return my_connect(klass, io_wait, hints.ai_family,
115
133
  &addr, hints.ai_addrlen);
116
134
  }
117
135
 
136
+ static struct sockaddr *sockaddr_from(socklen_t *addrlen, VALUE addr)
137
+ {
138
+ if (TYPE(addr) == T_STRING) {
139
+ *addrlen = (socklen_t)RSTRING_LEN(addr);
140
+ return (struct sockaddr *)(RSTRING_PTR(addr));
141
+ }
142
+ rb_raise(rb_eTypeError, "invalid address");
143
+ return NULL;
144
+ }
145
+
146
+ #if defined(MSG_FASTOPEN) && defined(HAVE_RB_THREAD_BLOCKING_REGION)
147
+ #ifndef HAVE_RB_STR_SUBSEQ
148
+ #define rb_str_subseq rb_str_substr
149
+ #endif
150
+ struct tfo_args {
151
+ int fd;
152
+ void *buf;
153
+ size_t buflen;
154
+ struct sockaddr *addr;
155
+ socklen_t addrlen;
156
+ };
157
+
158
+ static VALUE tfo_sendto(void *_a)
159
+ {
160
+ struct tfo_args *a = _a;
161
+ ssize_t w;
162
+
163
+ w = sendto(a->fd, a->buf, a->buflen, MSG_FASTOPEN, a->addr, a->addrlen);
164
+ return (VALUE)w;
165
+ }
166
+
167
+ /*
168
+ * call-seq:
169
+ *
170
+ * s = Kgio::Socket.new(:INET, :STREAM)
171
+ * addr = Socket.pack_sockaddr_in(80, "example.com")
172
+ * s.kgio_fastopen("hello world", addr) -> nil
173
+ *
174
+ * Starts a TCP connection using TCP Fast Open. This uses a blocking
175
+ * sendto() syscall and is only available on Ruby 1.9 or later.
176
+ * This raises exceptions (including Errno::EINPROGRESS/Errno::EAGAIN)
177
+ * on errors. Using this is only recommended for blocking sockets.
178
+ *
179
+ * Timeouts may be set with setsockopt:
180
+ *
181
+ * s.setsockopt(:SOCKET, :SNDTIMEO, [1,0].pack("l_l_"))
182
+ */
183
+ static VALUE fastopen(VALUE sock, VALUE buf, VALUE addr)
184
+ {
185
+ struct tfo_args a;
186
+ VALUE str = (TYPE(buf) == T_STRING) ? buf : rb_obj_as_string(buf);
187
+ ssize_t w;
188
+
189
+ a.fd = my_fileno(sock);
190
+ a.buf = RSTRING_PTR(str);
191
+ a.buflen = (size_t)RSTRING_LEN(str);
192
+ a.addr = sockaddr_from(&a.addrlen, addr);
193
+
194
+ /* n.b. rb_thread_blocking_region preserves errno */
195
+ w = (ssize_t)rb_thread_io_blocking_region(tfo_sendto, &a, a.fd);
196
+ if (w < 0)
197
+ rb_sys_fail("sendto");
198
+ if ((size_t)w == a.buflen)
199
+ return Qnil;
200
+
201
+ return rb_str_subseq(str, w, a.buflen - w);
202
+ }
203
+ #endif /* MSG_FASTOPEN */
204
+
118
205
  /*
119
206
  * call-seq:
120
207
  *
@@ -209,14 +296,8 @@ static VALUE stream_connect(VALUE klass, VALUE addr, int io_wait)
209
296
  {
210
297
  int domain;
211
298
  socklen_t addrlen;
212
- struct sockaddr *sockaddr;
299
+ struct sockaddr *sockaddr = sockaddr_from(&addrlen, addr);
213
300
 
214
- if (TYPE(addr) == T_STRING) {
215
- sockaddr = (struct sockaddr *)(RSTRING_PTR(addr));
216
- addrlen = (socklen_t)RSTRING_LEN(addr);
217
- } else {
218
- rb_raise(rb_eTypeError, "invalid address");
219
- }
220
301
  switch (((struct sockaddr_storage *)(sockaddr))->ss_family) {
221
302
  case AF_UNIX: domain = PF_UNIX; break;
222
303
  case AF_INET: domain = PF_INET; break;
@@ -247,6 +328,21 @@ static VALUE kgio_connect(VALUE klass, VALUE addr)
247
328
  return stream_connect(klass, addr, 1);
248
329
  }
249
330
 
331
+ /*
332
+ * If passed one argument, this is identical to Kgio::Socket.connect.
333
+ * If passed two or three arguments, it uses its superclass method:
334
+ *
335
+ * Socket.new(domain, socktype [, protocol ])
336
+ */
337
+ static VALUE kgio_new(int argc, VALUE *argv, VALUE klass)
338
+ {
339
+ if (argc == 1)
340
+ /* backwards compat, the only way for kgio <= 2.7.4 */
341
+ return stream_connect(klass, argv[0], 1);
342
+
343
+ return rb_call_super(argc, argv);
344
+ }
345
+
250
346
  /* call-seq:
251
347
  *
252
348
  * addr = Socket.pack_sockaddr_in(80, 'example.com')
@@ -282,9 +378,12 @@ void init_kgio_connect(void)
282
378
  */
283
379
  cKgio_Socket = rb_define_class_under(mKgio, "Socket", cSocket);
284
380
  rb_include_module(cKgio_Socket, mSocketMethods);
285
- rb_define_singleton_method(cKgio_Socket, "new", kgio_connect, 1);
381
+ rb_define_singleton_method(cKgio_Socket, "new", kgio_new, -1);
382
+ rb_define_singleton_method(cKgio_Socket, "connect", kgio_connect, 1);
286
383
  rb_define_singleton_method(cKgio_Socket, "start", kgio_start, 1);
287
-
384
+ #if defined(MSG_FASTOPEN) && defined(HAVE_RB_THREAD_BLOCKING_REGION)
385
+ rb_define_method(cKgio_Socket, "kgio_fastopen", fastopen, 2);
386
+ #endif
288
387
  /*
289
388
  * Document-class: Kgio::TCPSocket
290
389
  *
@@ -23,6 +23,8 @@ have_type("struct sockaddr_storage", %w(sys/types.h sys/socket.h)) or
23
23
  have_func('accept4', %w(sys/socket.h))
24
24
  have_header("sys/select.h")
25
25
 
26
+ have_func("writev", "sys/uio.h")
27
+
26
28
  if have_header('ruby/io.h')
27
29
  rubyio = %w(ruby.h ruby/io.h)
28
30
  have_struct_member("rb_io_t", "fd", rubyio)
@@ -49,5 +51,7 @@ have_func('rb_thread_io_blocking_region')
49
51
  have_func('rb_str_set_len')
50
52
  have_func('rb_time_interval')
51
53
  have_func('rb_wait_for_single_fd')
54
+ have_func('rb_str_subseq')
55
+ have_func('rb_ary_subseq')
52
56
 
53
57
  create_makefile('kgio_ext')
@@ -49,4 +49,27 @@ VALUE kgio_call_wait_readable(VALUE io);
49
49
  #ifndef HAVE_RB_UPDATE_MAX_FD
50
50
  # define rb_update_max_fd(fd) for (;0;)
51
51
  #endif
52
+
53
+ /*
54
+ * 2012/12/13 - Linux 3.7 was released on 2012/12/10 with TFO.
55
+ * Headers distributed with glibc will take some time to catch up and
56
+ * be officially released. Most GNU/Linux distros will take a few months
57
+ * to a year longer. "Enterprise" distros will probably take 5-7 years.
58
+ * So keep these until 2017 at least...
59
+ */
60
+ #ifdef __linux__
61
+ # ifndef MSG_FASTOPEN
62
+ # define MSG_FASTOPEN 0x20000000 /* for clients */
63
+ # endif
64
+ # ifndef TCP_FASTOPEN
65
+ # define TCP_FASTOPEN 23 /* for listeners */
66
+ # endif
67
+ /* we _may_ have TFO support */
68
+ # define KGIO_TFO_MAYBE (1)
69
+ #else /* rely entirely on standard system headers */
70
+ # define KGIO_TFO_MAYBE (0)
71
+ #endif
72
+
73
+ extern unsigned kgio_tfo;
74
+
52
75
  #endif /* KGIO_H */
@@ -1,7 +1,42 @@
1
1
  #include "kgio.h"
2
+ #include <sys/utsname.h>
3
+ #include <stdio.h>
4
+ /* true if TCP Fast Open is usable */
5
+ unsigned kgio_tfo;
6
+
7
+ static void tfo_maybe(void)
8
+ {
9
+ VALUE mKgio = rb_define_module("Kgio");
10
+
11
+ /* Deal with the case where system headers have not caught up */
12
+ if (KGIO_TFO_MAYBE) {
13
+ /* Ensure Linux 3.7 or later for TCP_FASTOPEN */
14
+ struct utsname buf;
15
+ unsigned maj, min;
16
+
17
+ if (uname(&buf) != 0)
18
+ rb_sys_fail("uname");
19
+ if (sscanf(buf.release, "%u.%u", &maj, &min) != 2)
20
+ return;
21
+ if (maj < 3 || (maj == 3 && min < 7))
22
+ return;
23
+ }
24
+
25
+ /*
26
+ * KGIO_TFO_MAYBE will be false if a distro backports TFO
27
+ * to a pre-3.7 kernel, but includes the necessary constants
28
+ * in system headers
29
+ */
30
+ #if defined(MSG_FASTOPEN) && defined(TCP_FASTOPEN)
31
+ rb_define_const(mKgio, "TCP_FASTOPEN", INT2NUM(TCP_FASTOPEN));
32
+ rb_define_const(mKgio, "MSG_FASTOPEN", INT2NUM(MSG_FASTOPEN));
33
+ kgio_tfo = 1;
34
+ #endif
35
+ }
2
36
 
3
37
  void Init_kgio_ext(void)
4
38
  {
39
+ tfo_maybe();
5
40
  init_kgio_wait();
6
41
  init_kgio_read_write();
7
42
  init_kgio_connect();
@@ -1,9 +1,33 @@
1
1
  #include "kgio.h"
2
2
  #include "my_fileno.h"
3
3
  #include "nonblock.h"
4
+ #ifdef HAVE_WRITEV
5
+ # include <sys/uio.h>
6
+ # define USE_WRITEV 1
7
+ #else
8
+ # define USE_WRITEV 0
9
+ static ssize_t assert_writev(int fd, void* iov, int len)
10
+ {
11
+ assert(0 && "you should not try to call writev");
12
+ return -1;
13
+ }
14
+ # define writev assert_writev
15
+ #endif
4
16
  static VALUE sym_wait_readable, sym_wait_writable;
5
17
  static VALUE eErrno_EPIPE, eErrno_ECONNRESET;
6
18
  static ID id_set_backtrace;
19
+ #ifndef HAVE_RB_STR_SUBSEQ
20
+ #define rb_str_subseq rb_str_substr
21
+ #endif
22
+
23
+ #ifndef HAVE_RB_ARY_SUBSEQ
24
+ static inline VALUE my_ary_subseq(VALUE ary, long idx, long len)
25
+ {
26
+ VALUE args[2] = {LONG2FIX(idx), LONG2FIX(len)};
27
+ return rb_ary_aref(2, args, ary);
28
+ }
29
+ #define rb_ary_subseq my_ary_subseq
30
+ #endif
7
31
 
8
32
  /*
9
33
  * we know MSG_DONTWAIT works properly on all stream sockets under Linux
@@ -338,7 +362,7 @@ done:
338
362
  a->ptr = RSTRING_PTR(a->buf) + written;
339
363
  return -1;
340
364
  } else if (written > 0) {
341
- a->buf = rb_str_new(a->ptr, a->len);
365
+ a->buf = rb_str_subseq(a->buf, written, a->len);
342
366
  } else {
343
367
  a->buf = sym_wait_writable;
344
368
  }
@@ -403,6 +427,253 @@ static VALUE kgio_trywrite(VALUE io, VALUE str)
403
427
  return my_write(io, str, 0);
404
428
  }
405
429
 
430
+ #ifndef HAVE_WRITEV
431
+ #define iovec my_iovec
432
+ struct my_iovec {
433
+ void *iov_base;
434
+ size_t iov_len;
435
+ };
436
+ #endif
437
+
438
+ /* tests for choosing following constants were done on Linux 3.0 x86_64
439
+ * (Ubuntu 12.04) Core i3 i3-2330M slowed to 1600MHz
440
+ * testing script https://gist.github.com/2850641
441
+ * fill free to make more thorough testing and choose better value
442
+ */
443
+
444
+ /* test shows that its meaningless to set WRITEV_MEMLIMIT more that 1M
445
+ * even when tcp_wmem set to relatively high value (2M) (in fact, it becomes
446
+ * even slower). 512K performs a bit better in average case. */
447
+ #define WRITEV_MEMLIMIT (512*1024)
448
+ /* same test shows that custom_writev is faster than glibc writev when
449
+ * average string is smaller than ~500 bytes and slower when average strings
450
+ * is greater then ~600 bytes. 512 bytes were choosen cause current compilers
451
+ * turns x/512 into x>>9 */
452
+ #define WRITEV_IMPL_THRESHOLD 512
453
+
454
+ static unsigned int iov_max = 1024; /* this could be overriden in init */
455
+
456
+ struct io_args_v {
457
+ VALUE io;
458
+ VALUE buf;
459
+ VALUE vec_buf;
460
+ struct iovec *vec;
461
+ unsigned long iov_cnt;
462
+ size_t batch_len;
463
+ int something_written;
464
+ int fd;
465
+ };
466
+
467
+ static ssize_t custom_writev(int fd, const struct iovec *vec, unsigned int iov_cnt, size_t total_len)
468
+ {
469
+ unsigned int i;
470
+ ssize_t result;
471
+ char *buf, *curbuf;
472
+ const struct iovec *curvec = vec;
473
+
474
+ /* we do not want to use ruby's xmalloc because
475
+ * it can fire GC, and we'll free buffer shortly anyway */
476
+ curbuf = buf = malloc(total_len);
477
+ if (buf == NULL) return -1;
478
+
479
+ for (i = 0; i < iov_cnt; i++, curvec++) {
480
+ memcpy(curbuf, curvec->iov_base, curvec->iov_len);
481
+ curbuf += curvec->iov_len;
482
+ }
483
+
484
+ result = write(fd, buf, total_len);
485
+
486
+ /* well, it seems that `free` could not change errno
487
+ * but lets save it anyway */
488
+ i = errno;
489
+ free(buf);
490
+ errno = i;
491
+
492
+ return result;
493
+ }
494
+
495
+ static void prepare_writev(struct io_args_v *a, VALUE io, VALUE ary)
496
+ {
497
+ a->io = io;
498
+ a->fd = my_fileno(io);
499
+ a->something_written = 0;
500
+
501
+ if (TYPE(ary) == T_ARRAY)
502
+ /* rb_ary_subseq will not copy array unless it modified */
503
+ a->buf = rb_ary_subseq(ary, 0, RARRAY_LEN(ary));
504
+ else
505
+ a->buf = rb_Array(ary);
506
+
507
+ a->vec_buf = rb_str_new(0, 0);
508
+ a->vec = NULL;
509
+ }
510
+
511
+ static void fill_iovec(struct io_args_v *a)
512
+ {
513
+ unsigned long i;
514
+ struct iovec *curvec;
515
+
516
+ a->iov_cnt = RARRAY_LEN(a->buf);
517
+ a->batch_len = 0;
518
+ if (a->iov_cnt == 0) return;
519
+ if (a->iov_cnt > iov_max) a->iov_cnt = iov_max;
520
+ rb_str_resize(a->vec_buf, sizeof(struct iovec) * a->iov_cnt);
521
+ curvec = a->vec = (struct iovec*)RSTRING_PTR(a->vec_buf);
522
+
523
+ for (i=0; i < a->iov_cnt; i++, curvec++) {
524
+ /* rb_ary_store could reallocate array,
525
+ * so that ought to use RARRAY_PTR */
526
+ VALUE str = RARRAY_PTR(a->buf)[i];
527
+ long str_len, next_len;
528
+
529
+ if (TYPE(str) != T_STRING) {
530
+ str = rb_obj_as_string(str);
531
+ rb_ary_store(a->buf, i, str);
532
+ }
533
+
534
+ str_len = RSTRING_LEN(str);
535
+
536
+ /* lets limit total memory to write,
537
+ * but always take first string */
538
+ next_len = a->batch_len + str_len;
539
+ if (i && next_len > WRITEV_MEMLIMIT) {
540
+ a->iov_cnt = i;
541
+ break;
542
+ }
543
+ a->batch_len = next_len;
544
+
545
+ curvec->iov_base = RSTRING_PTR(str);
546
+ curvec->iov_len = str_len;
547
+ }
548
+ }
549
+
550
+ static long trim_writev_buffer(struct io_args_v *a, long n)
551
+ {
552
+ long i;
553
+ long ary_len = RARRAY_LEN(a->buf);
554
+ VALUE *elem = RARRAY_PTR(a->buf);
555
+
556
+ if (n == (long)a->batch_len) {
557
+ i = a->iov_cnt;
558
+ n = 0;
559
+ } else {
560
+ for (i = 0; n && i < ary_len; i++, elem++) {
561
+ n -= RSTRING_LEN(*elem);
562
+ if (n < 0) break;
563
+ }
564
+ }
565
+
566
+ /* all done */
567
+ if (i == ary_len) {
568
+ assert(n == 0 && "writev system call is broken");
569
+ a->buf = Qnil;
570
+ return 0;
571
+ }
572
+
573
+ /* partially done, remove fully-written buffers */
574
+ if (i > 0)
575
+ a->buf = rb_ary_subseq(a->buf, i, ary_len - i);
576
+
577
+ /* setup+replace partially written buffer */
578
+ if (n < 0) {
579
+ VALUE str = RARRAY_PTR(a->buf)[0];
580
+ long str_len = RSTRING_LEN(str);
581
+ str = rb_str_subseq(str, str_len + n, -n);
582
+ rb_ary_store(a->buf, 0, str);
583
+ }
584
+ return RARRAY_LEN(a->buf);
585
+ }
586
+
587
+ static int writev_check(struct io_args_v *a, long n, const char *msg, int io_wait)
588
+ {
589
+ if (n >= 0) {
590
+ if (n > 0) a->something_written = 1;
591
+ return trim_writev_buffer(a, n);
592
+ } else if (n == -1) {
593
+ if (errno == EINTR) {
594
+ a->fd = my_fileno(a->io);
595
+ return -1;
596
+ }
597
+ if (errno == EAGAIN) {
598
+ if (io_wait) {
599
+ (void)kgio_call_wait_writable(a->io);
600
+ return -1;
601
+ } else if (!a->something_written) {
602
+ a->buf = sym_wait_writable;
603
+ }
604
+ return 0;
605
+ }
606
+ wr_sys_fail(msg);
607
+ }
608
+ return 0;
609
+ }
610
+
611
+ static VALUE my_writev(VALUE io, VALUE str, int io_wait)
612
+ {
613
+ struct io_args_v a;
614
+ long n;
615
+
616
+ prepare_writev(&a, io, str);
617
+ set_nonblocking(a.fd);
618
+
619
+ do {
620
+ fill_iovec(&a);
621
+ if (a.iov_cnt == 0)
622
+ n = 0;
623
+ else if (a.iov_cnt == 1)
624
+ n = (long)write(a.fd, a.vec[0].iov_base, a.vec[0].iov_len);
625
+ /* for big strings use library function */
626
+ else if (USE_WRITEV && a.batch_len / WRITEV_IMPL_THRESHOLD > a.iov_cnt)
627
+ n = (long)writev(a.fd, a.vec, a.iov_cnt);
628
+ else
629
+ n = (long)custom_writev(a.fd, a.vec, a.iov_cnt, a.batch_len);
630
+ } while (writev_check(&a, n, "writev", io_wait) != 0);
631
+ rb_str_resize(a.vec_buf, 0);
632
+
633
+ if (TYPE(a.buf) != T_SYMBOL)
634
+ kgio_autopush_write(io);
635
+ return a.buf;
636
+ }
637
+
638
+ /*
639
+ * call-seq:
640
+ *
641
+ * io.kgio_writev(array) -> nil
642
+ *
643
+ * Returns nil when the write completes.
644
+ *
645
+ * This may block and call any method defined to +kgio_wait_writable+
646
+ * for the class.
647
+ *
648
+ * Note: it uses +Array()+ semantic for converting argument, so that
649
+ * it will succeed if you pass something else.
650
+ */
651
+ static VALUE kgio_writev(VALUE io, VALUE ary)
652
+ {
653
+ return my_writev(io, ary, 1);
654
+ }
655
+
656
+ /*
657
+ * call-seq:
658
+ *
659
+ * io.kgio_trywritev(array) -> nil, Array or :wait_writable
660
+ *
661
+ * Returns nil if the write was completed in full.
662
+ *
663
+ * Returns an Array of strings containing the unwritten portion
664
+ * if EAGAIN was encountered, but some portion was successfully written.
665
+ *
666
+ * Returns :wait_writable if EAGAIN is encountered and nothing
667
+ * was written.
668
+ *
669
+ * Note: it uses +Array()+ semantic for converting argument, so that
670
+ * it will succeed if you pass something else.
671
+ */
672
+ static VALUE kgio_trywritev(VALUE io, VALUE ary)
673
+ {
674
+ return my_writev(io, ary, 0);
675
+ }
676
+
406
677
  #ifdef USE_MSG_DONTWAIT
407
678
  /*
408
679
  * This method behaves like Kgio::PipeMethods#kgio_write, except
@@ -486,6 +757,26 @@ static VALUE s_trywrite(VALUE mod, VALUE io, VALUE str)
486
757
  return my_write(io, str, 0);
487
758
  }
488
759
 
760
+ /*
761
+ * call-seq:
762
+ *
763
+ * Kgio.trywritev(io, array) -> nil, Array or :wait_writable
764
+ *
765
+ * Returns nil if the write was completed in full.
766
+ *
767
+ * Returns a Array of strings containing the unwritten portion if EAGAIN
768
+ * was encountered, but some portion was successfully written.
769
+ *
770
+ * Returns :wait_writable if EAGAIN is encountered and nothing
771
+ * was written.
772
+ *
773
+ * Maybe used in place of PipeMethods#kgio_trywritev for non-Kgio objects
774
+ */
775
+ static VALUE s_trywritev(VALUE mod, VALUE io, VALUE ary)
776
+ {
777
+ return kgio_trywritev(io, ary);
778
+ }
779
+
489
780
  void init_kgio_read_write(void)
490
781
  {
491
782
  VALUE mPipeMethods, mSocketMethods;
@@ -497,6 +788,7 @@ void init_kgio_read_write(void)
497
788
 
498
789
  rb_define_singleton_method(mKgio, "tryread", s_tryread, -1);
499
790
  rb_define_singleton_method(mKgio, "trywrite", s_trywrite, 2);
791
+ rb_define_singleton_method(mKgio, "trywritev", s_trywritev, 2);
500
792
  rb_define_singleton_method(mKgio, "trypeek", s_trypeek, -1);
501
793
 
502
794
  /*
@@ -510,8 +802,10 @@ void init_kgio_read_write(void)
510
802
  rb_define_method(mPipeMethods, "kgio_read", kgio_read, -1);
511
803
  rb_define_method(mPipeMethods, "kgio_read!", kgio_read_bang, -1);
512
804
  rb_define_method(mPipeMethods, "kgio_write", kgio_write, 1);
805
+ rb_define_method(mPipeMethods, "kgio_writev", kgio_writev, 1);
513
806
  rb_define_method(mPipeMethods, "kgio_tryread", kgio_tryread, -1);
514
807
  rb_define_method(mPipeMethods, "kgio_trywrite", kgio_trywrite, 1);
808
+ rb_define_method(mPipeMethods, "kgio_trywritev", kgio_trywritev, 1);
515
809
 
516
810
  /*
517
811
  * Document-module: Kgio::SocketMethods
@@ -524,8 +818,10 @@ void init_kgio_read_write(void)
524
818
  rb_define_method(mSocketMethods, "kgio_read", kgio_recv, -1);
525
819
  rb_define_method(mSocketMethods, "kgio_read!", kgio_recv_bang, -1);
526
820
  rb_define_method(mSocketMethods, "kgio_write", kgio_send, 1);
821
+ rb_define_method(mSocketMethods, "kgio_writev", kgio_writev, 1);
527
822
  rb_define_method(mSocketMethods, "kgio_tryread", kgio_tryrecv, -1);
528
823
  rb_define_method(mSocketMethods, "kgio_trywrite", kgio_trysend, 1);
824
+ rb_define_method(mSocketMethods, "kgio_trywritev", kgio_trywritev, 1);
529
825
  rb_define_method(mSocketMethods, "kgio_trypeek", kgio_trypeek, -1);
530
826
  rb_define_method(mSocketMethods, "kgio_peek", kgio_peek, -1);
531
827
 
@@ -541,4 +837,16 @@ void init_kgio_read_write(void)
541
837
  eErrno_ECONNRESET = rb_const_get(rb_mErrno, rb_intern("ECONNRESET"));
542
838
  rb_include_module(mPipeMethods, mWaiters);
543
839
  rb_include_module(mSocketMethods, mWaiters);
840
+
841
+ #ifdef HAVE_WRITEV
842
+ {
843
+ # ifdef IOV_MAX
844
+ unsigned int sys_iov_max = IOV_MAX;
845
+ # else
846
+ unsigned int sys_iov_max = sysconf(_SC_IOV_MAX);
847
+ # endif
848
+ if (sys_iov_max < iov_max)
849
+ iov_max = sys_iov_max;
850
+ }
851
+ #endif
544
852
  }
@@ -14,6 +14,7 @@
14
14
  #include <sys/types.h>
15
15
  #include <sys/stat.h>
16
16
  #include <fcntl.h>
17
+ #include <errno.h>
17
18
  #include "set_file_path.h"
18
19
  #include "ancient_ruby.h"
19
20
 
@@ -42,7 +43,7 @@ static VALUE nogvl_open(void *ptr)
42
43
  # include "rubysig.h"
43
44
  typedef void rb_unblock_function_t(void *);
44
45
  typedef VALUE rb_blocking_function_t(void *);
45
- static VALUE rb_thread_blocking_region(
46
+ static VALUE my_thread_blocking_region(
46
47
  rb_blocking_function_t *fn, void *data1,
47
48
  rb_unblock_function_t *ubf, void *data2)
48
49
  {
@@ -54,6 +55,8 @@ static VALUE rb_thread_blocking_region(
54
55
 
55
56
  return rv;
56
57
  }
58
+ #define rb_thread_blocking_region(fn,data1,ubf,data2) \
59
+ my_thread_blocking_region((fn),(data1),(ubf),(data2))
57
60
  #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
58
61
 
59
62
  /*
@@ -21,6 +21,14 @@ module LibReadWriteTest
21
21
  assert_nil @wr.kgio_trywrite("")
22
22
  end
23
23
 
24
+ def test_writev_empty
25
+ assert_nil @wr.kgio_writev([])
26
+ end
27
+
28
+ def test_trywritev_empty
29
+ assert_nil @wr.kgio_trywritev([])
30
+ end
31
+
24
32
  def test_read_zero
25
33
  assert_equal "", @rd.kgio_read(0)
26
34
  buf = "foo"
@@ -116,6 +124,28 @@ module LibReadWriteTest
116
124
  assert false, "should never get here (line:#{__LINE__})"
117
125
  end
118
126
 
127
+ def test_writev_closed
128
+ @rd.close
129
+ begin
130
+ loop { @wr.kgio_writev ["HI"] }
131
+ rescue Errno::EPIPE, Errno::ECONNRESET => e
132
+ assert_equal [], e.backtrace
133
+ return
134
+ end
135
+ assert false, "should never get here (line:#{__LINE__})"
136
+ end
137
+
138
+ def test_trywritev_closed
139
+ @rd.close
140
+ begin
141
+ loop { @wr.kgio_trywritev ["HI"] }
142
+ rescue Errno::EPIPE, Errno::ECONNRESET => e
143
+ assert_equal [], e.backtrace
144
+ return
145
+ end
146
+ assert false, "should never get here (line:#{__LINE__})"
147
+ end
148
+
119
149
  def test_trywrite_full
120
150
  buf = "\302\251" * 1024 * 1024
121
151
  buf2 = ""
@@ -153,6 +183,43 @@ module LibReadWriteTest
153
183
  assert_equal '8ff79d8115f9fe38d18be858c66aa08a1cc27a66', t.value
154
184
  end
155
185
 
186
+ def test_trywritev_full
187
+ buf = ["\302\251" * 128] * 8 * 1024
188
+ buf2 = ""
189
+ dig = Digest::SHA1.new
190
+ t = Thread.new do
191
+ sleep 1
192
+ nr = 0
193
+ begin
194
+ dig.update(@rd.readpartial(4096, buf2))
195
+ nr += buf2.size
196
+ rescue EOFError
197
+ break
198
+ rescue => e
199
+ end while true
200
+ dig.hexdigest
201
+ end
202
+ 50.times do
203
+ wr = buf
204
+ begin
205
+ rv = @wr.kgio_trywritev(wr)
206
+ case rv
207
+ when Array
208
+ wr = rv
209
+ when :wait_readable
210
+ assert false, "should never get here line=#{__LINE__}"
211
+ when :wait_writable
212
+ IO.select(nil, [ @wr ])
213
+ else
214
+ wr = false
215
+ end
216
+ end while wr
217
+ end
218
+ @wr.close
219
+ t.join
220
+ assert_equal '8ff79d8115f9fe38d18be858c66aa08a1cc27a66', t.value
221
+ end
222
+
156
223
  def test_write_conv
157
224
  assert_equal nil, @wr.kgio_write(10)
158
225
  assert_equal "10", @rd.kgio_read(2)
@@ -175,6 +242,7 @@ module LibReadWriteTest
175
242
 
176
243
  def test_tryread_too_much
177
244
  assert_equal nil, @wr.kgio_trywrite("hi")
245
+ assert_equal @rd, @rd.kgio_wait_readable
178
246
  assert_equal "hi", @rd.kgio_tryread(4)
179
247
  end
180
248
 
@@ -214,6 +282,19 @@ module LibReadWriteTest
214
282
  tmp.each { |count| assert_equal nil, count }
215
283
  end
216
284
 
285
+ def test_trywritev_return_wait_writable
286
+ tmp = []
287
+ tmp << @wr.kgio_trywritev(["HI"]) until tmp[-1] == :wait_writable
288
+ assert :wait_writable === tmp[-1]
289
+ assert(!(:wait_readable === tmp[-1]))
290
+ assert_equal :wait_writable, tmp.pop
291
+ assert tmp.size > 0
292
+ penultimate = tmp.pop
293
+ assert(penultimate == "I" || penultimate == nil)
294
+ assert tmp.size > 0
295
+ tmp.each { |count| assert_equal nil, count }
296
+ end
297
+
217
298
  def test_tryread_extra_buf_eagain_clears_buffer
218
299
  tmp = "hello world"
219
300
  rv = @rd.kgio_tryread(2, tmp)
@@ -248,6 +329,36 @@ module LibReadWriteTest
248
329
  assert_equal buf, readed
249
330
  end
250
331
 
332
+ def test_monster_trywritev
333
+ buf, start = [], 0
334
+ while start < RANDOM_BLOB.size
335
+ s = RANDOM_BLOB[start, 1000]
336
+ start += s.size
337
+ buf << s
338
+ end
339
+ rv = @wr.kgio_trywritev(buf)
340
+ assert_kind_of Array, rv
341
+ rv = rv.join
342
+ assert rv.size < RANDOM_BLOB.size
343
+ @rd.nonblock = false
344
+ assert_equal(RANDOM_BLOB, @rd.read(RANDOM_BLOB.size - rv.size) + rv)
345
+ end
346
+
347
+ def test_monster_writev
348
+ buf, start = [], 0
349
+ while start < RANDOM_BLOB.size
350
+ s = RANDOM_BLOB[start, 10000]
351
+ start += s.size
352
+ buf << s
353
+ end
354
+ thr = Thread.new { @wr.kgio_writev(buf) }
355
+ @rd.nonblock = false
356
+ readed = @rd.read(RANDOM_BLOB.size)
357
+ thr.join
358
+ assert_nil thr.value
359
+ assert_equal RANDOM_BLOB, readed
360
+ end
361
+
251
362
  def test_monster_write_wait_writable
252
363
  @wr.instance_variable_set :@nr, 0
253
364
  def @wr.kgio_wait_writable
@@ -256,6 +367,7 @@ module LibReadWriteTest
256
367
  end
257
368
  buf = "." * 1024 * 1024 * 10
258
369
  thr = Thread.new { @wr.kgio_write(buf) }
370
+ Thread.pass until thr.stop?
259
371
  readed = @rd.read(buf.size)
260
372
  thr.join
261
373
  assert_nil thr.value
@@ -263,6 +375,23 @@ module LibReadWriteTest
263
375
  assert @wr.instance_variable_get(:@nr) > 0
264
376
  end
265
377
 
378
+ def test_monster_writev_wait_writable
379
+ @wr.instance_variable_set :@nr, 0
380
+ def @wr.kgio_wait_writable
381
+ @nr += 1
382
+ IO.select(nil, [self])
383
+ end
384
+ buf = ["." * 1024] * 1024 * 10
385
+ buf_size = buf.inject(0){|c, s| c + s.size}
386
+ thr = Thread.new { @wr.kgio_writev(buf) }
387
+ Thread.pass until thr.stop?
388
+ readed = @rd.read(buf_size)
389
+ thr.join
390
+ assert_nil thr.value
391
+ assert_equal buf.join, readed
392
+ assert @wr.instance_variable_get(:@nr) > 0
393
+ end
394
+
266
395
  def test_wait_readable_ruby_default
267
396
  elapsed = 0
268
397
  foo = nil
@@ -47,7 +47,7 @@ module LibServerAccept
47
47
  elapsed = Time.now - t0
48
48
  assert_kind_of Kgio::Socket, b
49
49
  assert_equal @host, b.kgio_addr
50
- Process.kill(:TERM, pid)
50
+ Process.kill(:KILL, pid)
51
51
  Process.waitpid(pid)
52
52
  assert elapsed >= 1, "elapsed: #{elapsed}"
53
53
  end
@@ -60,7 +60,7 @@ module LibServerAccept
60
60
  elapsed = Time.now - t0
61
61
  assert_kind_of Kgio::Socket, b
62
62
  assert_equal @host, b.kgio_addr
63
- Process.kill(:TERM, pid)
63
+ Process.kill(:KILL, pid)
64
64
  Process.waitpid(pid)
65
65
  assert elapsed >= 1, "elapsed: #{elapsed}"
66
66
 
@@ -70,7 +70,7 @@ module LibServerAccept
70
70
  elapsed = Time.now - t0
71
71
  assert_kind_of Kgio::Socket, b
72
72
  assert_equal @host, b.kgio_addr
73
- Process.kill(:TERM, pid)
73
+ Process.kill(:KILL, pid)
74
74
  Process.waitpid(pid)
75
75
  assert elapsed >= 6, "elapsed: #{elapsed}"
76
76
 
@@ -80,7 +80,7 @@ module LibServerAccept
80
80
  elapsed = Time.now - t0
81
81
  assert_kind_of Kgio::Socket, b
82
82
  assert_equal @host, b.kgio_addr
83
- Process.kill(:TERM, pid)
83
+ Process.kill(:KILL, pid)
84
84
  Process.waitpid(pid)
85
85
  assert elapsed >= 1, "elapsed: #{elapsed}"
86
86
  end
@@ -126,9 +126,7 @@ class TestPoll < Test::Unit::TestCase
126
126
  _, status = Process.waitpid2(pid)
127
127
  assert status.success?, status.inspect
128
128
  assert usr1 > 0, "usr1: #{usr1}"
129
- rescue Object => err
130
- p [ :err, err ]
131
- ensure
132
- trap(:USR1, "DEFAULT")
133
- end
129
+ ensure
130
+ trap(:USR1, "DEFAULT")
131
+ end unless RUBY_PLATFORM =~ /kfreebsd-gnu/
134
132
  end if Kgio.respond_to?(:poll)
@@ -0,0 +1,14 @@
1
+ require 'test/unit'
2
+ require 'kgio'
3
+
4
+ class TestKgioSocket < Test::Unit::TestCase
5
+ def test_socket_args
6
+ s = Kgio::Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
7
+ assert_kind_of Socket, s
8
+ assert_instance_of Kgio::Socket, s
9
+
10
+ s = Kgio::Socket.new(Socket::AF_UNIX, Socket::SOCK_STREAM, 0)
11
+ assert_kind_of Socket, s
12
+ assert_instance_of Kgio::Socket, s
13
+ end
14
+ end
@@ -0,0 +1,70 @@
1
+ require 'test/unit'
2
+ require 'kgio'
3
+
4
+ class TestTFO < Test::Unit::TestCase
5
+ def test_constants
6
+ if `uname -s`.chomp == "Linux" && `uname -r`.to_f >= 3.7
7
+ assert_equal 23, Kgio::TCP_FASTOPEN
8
+ assert_equal 0x20000000, Kgio::MSG_FASTOPEN
9
+ end
10
+ end
11
+
12
+ def fastopen_ok?
13
+ if RUBY_PLATFORM =~ /linux/
14
+ tfo = File.read("/proc/sys/net/ipv4/tcp_fastopen").to_i
15
+ client_enable = 1
16
+ server_enable = 2
17
+ enable = client_enable | server_enable
18
+ (tfo & enable) == enable
19
+ else
20
+ false
21
+ end
22
+ end
23
+
24
+ def test_tfo_client_server
25
+ unless fastopen_ok?
26
+ warn "TCP Fast Open not enabled on this system (check kernel docs)"
27
+ return
28
+ end
29
+ addr = '127.0.0.1'
30
+ qlen = 1024
31
+ s = Kgio::TCPServer.new(addr, 0)
32
+ s.setsockopt(:TCP, Kgio::TCP_FASTOPEN, qlen)
33
+ port = s.local_address.ip_port
34
+ addr = Socket.pack_sockaddr_in(port, addr)
35
+ c = Kgio::Socket.new(:INET, :STREAM)
36
+ assert_nil c.kgio_fastopen("HELLO", addr)
37
+ a = s.accept
38
+ assert_equal "HELLO", a.read(5)
39
+ c.close
40
+ a.close
41
+
42
+ # ensure empty sends work
43
+ c = Kgio::Socket.new(:INET, :STREAM)
44
+ assert_nil c.kgio_fastopen("", addr)
45
+ a = s.accept
46
+ Thread.new { c.close }
47
+ assert_nil a.read(1)
48
+ a.close
49
+
50
+ # try a monster packet
51
+ buf = 'x' * (1024 * 1024 * 320)
52
+
53
+ c = Kgio::Socket.new(:INET, :STREAM)
54
+ thr = Thread.new do
55
+ a = s.accept
56
+ assert_equal buf.size, a.read(buf.size).size
57
+ a.close
58
+ end
59
+ assert_nil c.kgio_fastopen(buf, addr)
60
+ thr.join
61
+ c.close
62
+
63
+ # allow timeouts
64
+ c = Kgio::Socket.new(:INET, :STREAM)
65
+ c.setsockopt(:SOCKET, :SNDTIMEO, [ 0, 10 ].pack("l_l_"))
66
+ unsent = c.kgio_fastopen(buf, addr)
67
+ c.close
68
+ assert_equal s.accept.read.size + unsent.size, buf.size
69
+ end if defined?(Addrinfo) && defined?(Kgio::TCP_FASTOPEN)
70
+ end
@@ -1,7 +1,7 @@
1
1
  require './test/lib_read_write'
2
2
  require 'tempfile'
3
3
 
4
- class TestUnixServerReadClientWrite < Test::Unit::TestCase
4
+ class TestUnixClientReadServerWrite < Test::Unit::TestCase
5
5
  def setup
6
6
  tmp = Tempfile.new('kgio_unix')
7
7
  @path = tmp.path
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kgio
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.4
4
+ version: 2.8.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-24 00:00:00.000000000 Z
12
+ date: 2013-01-18 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! 'kgio provides non-blocking I/O methods for Ruby without raising
15
15
 
@@ -95,12 +95,14 @@ files:
95
95
  - test/test_pipe_read_write.rb
96
96
  - test/test_poll.rb
97
97
  - test/test_singleton_read_write.rb
98
+ - test/test_socket.rb
98
99
  - test/test_socketpair_read_write.rb
99
100
  - test/test_tcp6_client_read_server_write.rb
100
101
  - test/test_tcp_client_read_server_write.rb
101
102
  - test/test_tcp_connect.rb
102
103
  - test/test_tcp_server.rb
103
104
  - test/test_tcp_server_read_client_write.rb
105
+ - test/test_tfo.rb
104
106
  - test/test_tryopen.rb
105
107
  - test/test_unix_client_read_server_write.rb
106
108
  - test/test_unix_connect.rb
@@ -130,13 +132,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
132
  version: '0'
131
133
  requirements: []
132
134
  rubyforge_project: rainbows
133
- rubygems_version: 1.8.21
135
+ rubygems_version: 1.8.23
134
136
  signing_key:
135
137
  specification_version: 3
136
138
  summary: kinder, gentler I/O for Ruby
137
139
  test_files:
138
140
  - test/test_poll.rb
139
141
  - test/test_peek.rb
142
+ - test/test_socket.rb
140
143
  - test/test_default_wait.rb
141
144
  - test/test_no_dns_on_tcp_connect.rb
142
145
  - test/test_unix_connect.rb
@@ -144,6 +147,7 @@ test_files:
144
147
  - test/test_unix_server.rb
145
148
  - test/test_accept_flags.rb
146
149
  - test/test_socketpair_read_write.rb
150
+ - test/test_tfo.rb
147
151
  - test/test_tcp_server.rb
148
152
  - test/test_unix_server_read_client_write.rb
149
153
  - test/test_cross_thread_close.rb