polyphony 0.52.0 → 0.53.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2056b02e3b364911ab627ffc49a7251ad2eed68ba6b5b61d380c599127c181f6
4
- data.tar.gz: 440f57d240b90a255b470f405980b5fa2f806ba3ff2640f31d78b6403de7ce0e
3
+ metadata.gz: '0682736e3fdca0ada986f4210f1bb25d682c75a7b9d297451c186b90c8025e13'
4
+ data.tar.gz: 5ff46620651c056983a6f73c879ca1c49acea22c0c7abcefb1a119cbe248cb77
5
5
  SHA512:
6
- metadata.gz: b5bf34a4ea7addf2b71c2e0acc75041d95f5d76ae425061bd5475e6523111a05cf969b0109ec90874f7f1a1929111182ed193b084b3388be060886145dd20567
7
- data.tar.gz: 6165128a0393664c97baa494119e453db177b7d0a12c5a5ade0ab5bb8e77a4143f6156b9cb594ef2edbbe9e0ac3bb85a2c846da8e925c3a6ec1c294120c18bf2
6
+ metadata.gz: cfbb8141b21cc9e56cdcd9a34fd55cd3b48573dcc3011f127ece5049b13ca2c7fa8a17c8525ba5996ab97afcbfc8e46064a3d68c79d71d44d40c274ff9051d2d
7
+ data.tar.gz: f31d711c88fe354b8ffd63e37f30887b1bfb5c3709d3760412ee4ec4ea06d14c0a17e668ddd750958295dc12307b96cf64874b1be4ed46b8564d04236114ee67
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.53.0
2
+
3
+ - Implement `Backend#splice`, `Backend#splice_to_eof`, along with `IO#splice`, `IO#splice_to_eof`
4
+
1
5
  ## 0.52.0
2
6
 
3
7
  - Polyphony is now compatible with Ruby 3.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.52.0)
4
+ polyphony (0.53.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/TODO.md CHANGED
@@ -1,8 +1,7 @@
1
- - Implement io_uring Backend_send with variable arity.
2
- - Implement a buffer store for use in:
3
- - io_uring Backend_send_m
4
- - io_uring Backend_writev (for iov)
5
- - libvev Backend_writev (for iov)
1
+ - Add support for IPv6:
2
+ https://www.reddit.com/r/ruby/comments/lyen23/understanding_ipv6_and_why_its_important_to_you/
3
+
4
+ - Add support for UDP sockets
6
5
 
7
6
  - Check segfault when resetting a `cancel_after` timeout lots of times at very high rate
8
7
  - Check why `throttled_loop` inside of `move_on_after` fails to stop
@@ -19,17 +18,46 @@
19
18
  - `Fiber#receive_loop` (very little effort, should be implemented in C)
20
19
 
21
20
 
22
- - Add `Backend#splice`, `Backend#splice_loop` for implementing stuff like proxying:
21
+ - Add `Backend#splice`, `Backend#splice_to_eof` for implementing stuff like proxying:
23
22
 
24
23
  ```ruby
25
24
  def two_way_proxy(socket1, socket2)
26
25
  backend = Thread.current.backend
27
- f1 = spin { backend.splice_loop(socket1, socket2) }
28
- f2 = spin { backend.splice_loop(socket2, socket1) }
26
+ f1 = spin { backend.splice_to_eof(socket1, socket2) }
27
+ f2 = spin { backend.splice_to_eof(socket2, socket1) }
29
28
  Fiber.await(f1, f2)
30
29
  end
31
30
  ```
32
31
 
32
+ - Add support for `close` to io_uring backend
33
+
34
+ - Add support for submission of multiple requests to io_uring backend:
35
+
36
+ ```ruby
37
+ Thread.current.backend.submit(
38
+ [sock, :<<, chunk_header(len)],
39
+ [sock, :splice, file, len]
40
+ )
41
+ ```
42
+
43
+ Full example (for writing chunks from a file to an HTTP response):
44
+
45
+ ```ruby
46
+ def serve_io(io)
47
+ i, o = IO.pipe
48
+ backend = Thread.current.backend
49
+ while true
50
+ len = o.splice(io, 8192)
51
+ break if len == 0
52
+
53
+ backend.submit(
54
+ [sock, :<<, chunk_header(len)],
55
+ [sock, :splice, file, len]
56
+ )
57
+ end
58
+ end
59
+ ```
60
+
33
61
  - Graceful shutdown again:
34
62
  - What happens to children when doing a graceful shutdown?
35
63
  - What are the implications of passing graceful shutdown flag to children?
@@ -9,6 +9,7 @@ puts 'Echoing on port 1234...'
9
9
  while (client = server.accept)
10
10
  spin do
11
11
  while (data = client.gets)
12
+ # client.send("you said: #{data.chomp}!\n", 0)
12
13
  client.write('you said: ', data.chomp, "!\n")
13
14
  end
14
15
  rescue Errno::ECONNRESET
@@ -15,7 +15,7 @@ f1 = spin {
15
15
  client1 = server1.accept
16
16
  loop do
17
17
  if client2
18
- Thread.current.backend.splice_loop(client1, client2)
18
+ Thread.current.backend.splice_to_eof(client1, client2)
19
19
  end
20
20
  end
21
21
  }
@@ -24,7 +24,7 @@ f2 = spin {
24
24
  client2 = server2.accept
25
25
  loop do
26
26
  if client1
27
- Thread.current.backend.splice_loop(client2, client1)
27
+ Thread.current.backend.splice_to_eof(client2, client1)
28
28
  end
29
29
  end
30
30
  }
@@ -43,6 +43,7 @@ inline void io_unset_nonblock(rb_io_t *fptr, VALUE io) {
43
43
  fcntl(fptr->fd, F_SETFL, oflags);
44
44
  }
45
45
  #else
46
+ // NOP
46
47
  #define io_unset_nonblock(fptr, io)
47
48
  #endif
48
49
 
@@ -824,6 +825,60 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
824
825
  return self;
825
826
  }
826
827
 
828
+ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen, int loop) {
829
+ rb_io_t *src_fptr;
830
+ rb_io_t *dest_fptr;
831
+ VALUE underlying_io;
832
+ int total = 0;
833
+
834
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
835
+ if (underlying_io != Qnil) src = underlying_io;
836
+ GetOpenFile(src, src_fptr);
837
+ io_unset_nonblock(src_fptr, src);
838
+
839
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
840
+ if (underlying_io != Qnil) dest = underlying_io;
841
+ dest = rb_io_get_write_io(dest);
842
+ GetOpenFile(dest, dest_fptr);
843
+ io_unset_nonblock(dest_fptr, dest);
844
+
845
+ VALUE resume_value = Qnil;
846
+
847
+ while (1) {
848
+ op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_SPLICE);
849
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
850
+ io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
851
+
852
+ int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
853
+ OP_CONTEXT_RELEASE(&backend->store, ctx);
854
+ RAISE_IF_EXCEPTION(resume_value);
855
+ if (!ctx->completed) return resume_value;
856
+
857
+ if (result < 0)
858
+ rb_syserr_fail(-result, strerror(-result));
859
+
860
+ if (result == 0 || !loop) return INT2NUM(total);
861
+ total += result;
862
+ }
863
+
864
+ RB_GC_GUARD(resume_value);
865
+ }
866
+
867
+ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
868
+ Backend_t *backend;
869
+ GetBackend(self, backend);
870
+
871
+ return io_uring_backend_splice(backend, src, dest, maxlen, 0);
872
+ }
873
+
874
+ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
875
+ Backend_t *backend;
876
+ GetBackend(self, backend);
877
+
878
+ return io_uring_backend_splice(backend, src, dest, chunksize, 1);
879
+ }
880
+
881
+
827
882
  VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
828
883
  Backend_t *backend;
829
884
  rb_io_t *fptr;
@@ -1045,27 +1100,29 @@ void Init_Backend() {
1045
1100
 
1046
1101
  rb_define_method(cBackend, "poll", Backend_poll, 3);
1047
1102
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1103
+ rb_define_method(cBackend, "kind", Backend_kind, 0);
1048
1104
 
1105
+ rb_define_method(cBackend, "accept", Backend_accept, 2);
1106
+ rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
1107
+ rb_define_method(cBackend, "connect", Backend_connect, 3);
1108
+ rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
1049
1109
  rb_define_method(cBackend, "read", Backend_read, 4);
1050
1110
  rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
1051
- rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
1052
- rb_define_method(cBackend, "write", Backend_write_m, -1);
1053
1111
  rb_define_method(cBackend, "recv", Backend_recv, 3);
1054
- rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
1055
1112
  rb_define_method(cBackend, "recv_feed_loop", Backend_recv_feed_loop, 3);
1113
+ rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
1056
1114
  rb_define_method(cBackend, "send", Backend_send, 3);
1057
1115
  rb_define_method(cBackend, "sendv", Backend_sendv, 3);
1058
- rb_define_method(cBackend, "accept", Backend_accept, 2);
1059
- rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
1060
- rb_define_method(cBackend, "connect", Backend_connect, 3);
1061
- rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
1062
1116
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
1063
- rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1117
+ rb_define_method(cBackend, "splice", Backend_splice, 3);
1118
+ rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
1064
1119
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
1065
- rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1120
+ rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1066
1121
  rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
1122
+ rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
1123
+ rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1124
+ rb_define_method(cBackend, "write", Backend_write_m, -1);
1067
1125
 
1068
- rb_define_method(cBackend, "kind", Backend_kind, 0);
1069
1126
 
1070
1127
  #ifdef POLYPHONY_UNSET_NONBLOCK
1071
1128
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
@@ -10,6 +10,7 @@ const char *op_type_to_str(enum op_type type) {
10
10
  case OP_WRITE: return "WRITE";
11
11
  case OP_RECV: return "RECV";
12
12
  case OP_SEND: return "SEND";
13
+ case OP_SPLICE: return "SPLICE";
13
14
  case OP_TIMEOUT: return "TIMEOUT";
14
15
  case OP_POLL: return "POLL";
15
16
  case OP_ACCEPT: return "ACCEPT";
@@ -10,6 +10,7 @@ enum op_type {
10
10
  OP_WRITE,
11
11
  OP_RECV,
12
12
  OP_SEND,
13
+ OP_SPLICE,
13
14
  OP_TIMEOUT,
14
15
  OP_POLL,
15
16
  OP_ACCEPT,
@@ -38,11 +38,15 @@ thread.
38
38
 
39
39
  #ifdef POLYPHONY_BACKEND_LIBEV
40
40
 
41
+ #ifdef POLYPHONY_LINUX
42
+ #define _GNU_SOURCE 1
43
+ #endif
44
+
45
+ #include <fcntl.h>
41
46
  #include <netdb.h>
42
47
  #include <sys/socket.h>
43
48
  #include <sys/uio.h>
44
49
  #include <unistd.h>
45
- #include <fcntl.h>
46
50
  #include <netinet/in.h>
47
51
  #include <arpa/inet.h>
48
52
  #include <stdnoreturn.h>
@@ -769,6 +773,124 @@ error:
769
773
  return RAISE_EXCEPTION(switchpoint_result);
770
774
  }
771
775
 
776
+ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
777
+ Backend_t *backend;
778
+ struct libev_io watcher;
779
+ VALUE switchpoint_result = Qnil;
780
+ VALUE underlying_io;
781
+ rb_io_t *src_fptr;
782
+ rb_io_t *dest_fptr;
783
+ int len;
784
+
785
+ #ifndef POLYPHONY_LINUX
786
+ rb_raise(rb_eRuntimeError, "splice not supported");
787
+ #endif
788
+
789
+ GetBackend(self, backend);
790
+
791
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
792
+ if (underlying_io != Qnil) src = underlying_io;
793
+ GetOpenFile(src, src_fptr);
794
+ io_set_nonblock(src_fptr, src);
795
+
796
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
797
+ if (underlying_io != Qnil) dest = underlying_io;
798
+ dest = rb_io_get_write_io(dest);
799
+ GetOpenFile(dest, dest_fptr);
800
+ io_set_nonblock(dest_fptr, dest);
801
+
802
+ watcher.fiber = Qnil;
803
+ while (1) {
804
+ len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
805
+ if (len < 0) {
806
+ int e = errno;
807
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
808
+
809
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
810
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
811
+
812
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
813
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
814
+ }
815
+ else {
816
+ break;
817
+ }
818
+ }
819
+
820
+ if (watcher.fiber == Qnil) {
821
+ switchpoint_result = backend_snooze();
822
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
823
+ }
824
+
825
+ RB_GC_GUARD(watcher.fiber);
826
+ RB_GC_GUARD(switchpoint_result);
827
+
828
+ return INT2NUM(len);
829
+ error:
830
+ return RAISE_EXCEPTION(switchpoint_result);
831
+ }
832
+
833
+ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
834
+ Backend_t *backend;
835
+ struct libev_io watcher;
836
+ VALUE switchpoint_result = Qnil;
837
+ VALUE underlying_io;
838
+ rb_io_t *src_fptr;
839
+ rb_io_t *dest_fptr;
840
+ int len;
841
+ int total = 0;
842
+
843
+ #ifndef POLYPHONY_LINUX
844
+ rb_raise(rb_eRuntimeError, "splice not supported");
845
+ #endif
846
+
847
+ GetBackend(self, backend);
848
+
849
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
850
+ if (underlying_io != Qnil) src = underlying_io;
851
+ GetOpenFile(src, src_fptr);
852
+ io_set_nonblock(src_fptr, src);
853
+
854
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
855
+ if (underlying_io != Qnil) dest = underlying_io;
856
+ dest = rb_io_get_write_io(dest);
857
+ GetOpenFile(dest, dest_fptr);
858
+ io_set_nonblock(dest_fptr, dest);
859
+
860
+ watcher.fiber = Qnil;
861
+ while (1) {
862
+ len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
863
+ if (len < 0) {
864
+ int e = errno;
865
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
866
+
867
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
868
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
869
+
870
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
871
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
872
+ }
873
+ else if (len == 0) {
874
+ break;
875
+ }
876
+ else {
877
+ total += len;
878
+ }
879
+ }
880
+
881
+ if (watcher.fiber == Qnil) {
882
+ switchpoint_result = backend_snooze();
883
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
884
+ }
885
+
886
+ RB_GC_GUARD(watcher.fiber);
887
+ RB_GC_GUARD(switchpoint_result);
888
+
889
+ return INT2NUM(total);
890
+ error:
891
+ return RAISE_EXCEPTION(switchpoint_result);
892
+ }
893
+
772
894
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
773
895
  Backend_t *backend;
774
896
  rb_io_t *fptr;
@@ -993,27 +1115,26 @@ void Init_Backend() {
993
1115
 
994
1116
  rb_define_method(cBackend, "poll", Backend_poll, 3);
995
1117
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1118
+ rb_define_method(cBackend, "kind", Backend_kind, 0);
996
1119
 
997
- rb_define_method(cBackend, "read", Backend_read, 4);
998
- rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
999
- rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
1000
- rb_define_method(cBackend, "write", Backend_write_m, -1);
1001
1120
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1002
1121
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
1003
1122
  rb_define_method(cBackend, "connect", Backend_connect, 3);
1123
+ rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
1124
+ rb_define_method(cBackend, "read", Backend_read, 4);
1125
+ rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
1004
1126
  rb_define_method(cBackend, "recv", Backend_recv, 3);
1005
1127
  rb_define_method(cBackend, "recv_loop", Backend_read_loop, 1);
1006
1128
  rb_define_method(cBackend, "recv_feed_loop", Backend_feed_loop, 3);
1007
1129
  rb_define_method(cBackend, "send", Backend_send, 3);
1008
1130
  rb_define_method(cBackend, "sendv", Backend_sendv, 3);
1009
- rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
1010
1131
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
1011
- rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1012
1132
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
1013
- rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1133
+ rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1014
1134
  rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
1015
-
1016
- rb_define_method(cBackend, "kind", Backend_kind, 0);
1135
+ rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
1136
+ rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1137
+ rb_define_method(cBackend, "write", Backend_write_m, -1);
1017
1138
 
1018
1139
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
1019
1140
  SYM_libev = ID2SYM(rb_intern("libev"));
@@ -6,8 +6,9 @@ require 'mkmf'
6
6
  use_liburing = false
7
7
  use_pidfd_open = false
8
8
  force_use_libev = ENV['POLYPHONY_USE_LIBEV'] != nil
9
+ linux = RUBY_PLATFORM =~ /linux/
9
10
 
10
- if RUBY_PLATFORM =~ /linux/ && `uname -sr` =~ /Linux 5\.([\d+])/
11
+ if linux && `uname -sr` =~ /Linux 5\.([\d+])/
11
12
  kernel_minor_version = $1.gsub('.', '').to_i
12
13
  use_liburing = !force_use_libev && kernel_minor_version >= 6
13
14
  use_pidfd_open = kernel_minor_version >= 3
@@ -20,6 +21,7 @@ if use_liburing
20
21
  $CFLAGS << " -Wno-pointer-arith"
21
22
  else
22
23
  $defs << "-DPOLYPHONY_BACKEND_LIBEV"
24
+ $defs << "-DPOLYPHONY_LINUX" if linux
23
25
  $defs << '-DEV_USE_LINUXAIO' if have_header('linux/aio_abi.h')
24
26
  $defs << '-DEV_USE_SELECT' if have_header('sys/select.h')
25
27
  $defs << '-DEV_USE_POLL' if have_type('port_event_t', 'poll.h')
@@ -46,8 +46,6 @@ VALUE Polyphony_trace(VALUE self, VALUE enabled) {
46
46
  return Qnil;
47
47
  }
48
48
 
49
- #define BACKEND() (rb_ivar_get(rb_thread_current(), ID_ivar_backend))
50
-
51
49
  VALUE Polyphony_backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
52
50
  return Backend_accept(BACKEND(), server_socket, socket_class);
53
51
  }
@@ -96,6 +94,14 @@ VALUE Polyphony_backend_sleep(VALUE self, VALUE duration) {
96
94
  return Backend_sleep(BACKEND(), duration);
97
95
  }
98
96
 
97
+ VALUE Polyphony_backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
98
+ return Backend_splice(BACKEND(), src, dest, maxlen);
99
+ }
100
+
101
+ VALUE Polyphony_backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
102
+ return Backend_splice_to_eof(BACKEND(), src, dest, chunksize);
103
+ }
104
+
99
105
  VALUE Polyphony_backend_timeout(int argc,VALUE *argv, VALUE self) {
100
106
  return Backend_timeout(argc, argv, BACKEND());
101
107
  }
@@ -138,6 +144,8 @@ void Init_Polyphony() {
138
144
  rb_define_singleton_method(mPolyphony, "backend_send", Polyphony_backend_send, 3);
139
145
  rb_define_singleton_method(mPolyphony, "backend_sendv", Polyphony_backend_sendv, 3);
140
146
  rb_define_singleton_method(mPolyphony, "backend_sleep", Polyphony_backend_sleep, 1);
147
+ rb_define_singleton_method(mPolyphony, "backend_splice", Polyphony_backend_splice, 3);
148
+ rb_define_singleton_method(mPolyphony, "backend_splice_to_eof", Polyphony_backend_splice_to_eof, 3);
141
149
  rb_define_singleton_method(mPolyphony, "backend_timeout", Polyphony_backend_timeout, -1);
142
150
  rb_define_singleton_method(mPolyphony, "backend_timer_loop", Polyphony_backend_timer_loop, 1);
143
151
  rb_define_singleton_method(mPolyphony, "backend_wait_event", Polyphony_backend_wait_event, 1);
@@ -31,6 +31,8 @@
31
31
  // Fiber#transfer
32
32
  #define FIBER_TRANSFER(fiber, value) rb_funcall(fiber, ID_transfer, 1, value)
33
33
 
34
+ #define BACKEND() (rb_ivar_get(rb_thread_current(), ID_ivar_backend))
35
+
34
36
  extern VALUE mPolyphony;
35
37
  extern VALUE cQueue;
36
38
  extern VALUE cEvent;
@@ -108,6 +110,8 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
108
110
  VALUE Backend_send(VALUE self, VALUE io, VALUE msg, VALUE flags);
109
111
  VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
110
112
  VALUE Backend_sleep(VALUE self, VALUE duration);
113
+ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
114
+ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize);
111
115
  VALUE Backend_timeout(int argc,VALUE *argv, VALUE self);
112
116
  VALUE Backend_timer_loop(VALUE self, VALUE interval);
113
117
  VALUE Backend_wait_event(VALUE self, VALUE raise);
@@ -6,6 +6,7 @@ void Init_Backend();
6
6
  void Init_Queue();
7
7
  void Init_Event();
8
8
  void Init_Runqueue();
9
+ void Init_SocketExtensions();
9
10
  void Init_Thread();
10
11
  void Init_Tracing();
11
12
 
@@ -24,6 +25,8 @@ void Init_polyphony_ext() {
24
25
  Init_Thread();
25
26
  Init_Tracing();
26
27
 
28
+ Init_SocketExtensions();
29
+
27
30
  #ifdef POLYPHONY_PLAYGROUND
28
31
  playground();
29
32
  #endif
@@ -0,0 +1,33 @@
1
+ #include "polyphony.h"
2
+
3
+ VALUE Socket_send(VALUE self, VALUE msg, VALUE flags) {
4
+ return Backend_send(BACKEND(), self, msg, flags);
5
+ }
6
+
7
+ VALUE Socket_write(int argc, VALUE *argv, VALUE self) {
8
+ VALUE ary = rb_ary_new_from_values(argc, argv);
9
+ VALUE result = Backend_sendv(BACKEND(), self, ary, INT2NUM(0));
10
+ RB_GC_GUARD(ary);
11
+ return result;
12
+ }
13
+
14
+ VALUE Socket_double_chevron(VALUE self, VALUE msg) {
15
+ Backend_send(BACKEND(), self, msg, INT2NUM(0));
16
+ return self;
17
+ }
18
+
19
+ void Init_SocketExtensions() {
20
+ rb_require("socket");
21
+
22
+ VALUE cSocket = rb_const_get(rb_cObject, rb_intern("Socket"));
23
+ VALUE cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
24
+
25
+ rb_define_method(cSocket, "send", Socket_send, 2);
26
+ rb_define_method(cTCPSocket, "send", Socket_send, 2);
27
+
28
+ rb_define_method(cSocket, "write", Socket_write, -1);
29
+ rb_define_method(cTCPSocket, "write", Socket_write, -1);
30
+
31
+ rb_define_method(cSocket, "<<", Socket_double_chevron, 1);
32
+ rb_define_method(cTCPSocket, "<<", Socket_double_chevron, 1);
33
+ }
@@ -247,4 +247,12 @@ class ::IO
247
247
  self
248
248
  end
249
249
  end
250
+
251
+ def splice(src, maxlen)
252
+ Polyphony.backend_splice(src, self, maxlen)
253
+ end
254
+
255
+ def splice_to_eof(src, chunksize = 8192)
256
+ Polyphony.backend_splice_to_eof(src, self, chunksize)
257
+ end
250
258
  end
@@ -48,14 +48,17 @@ class ::Socket
48
48
  end
49
49
  end
50
50
 
51
- def send(mesg, flags)
52
- Polyphony.backend_send(self, mesg, flags)
53
- end
51
+ # def send(mesg, flags)
52
+ # Polyphony.backend_send(self, mesg, flags)
53
+ # end
54
54
 
55
- def write(*args)
56
- Polyphony.backend_sendv(self, args, 0)
57
- end
58
- alias_method :<<, :write
55
+ # def write(*args)
56
+ # Polyphony.backend_sendv(self, args, 0)
57
+ # end
58
+
59
+ # def <<(mesg)
60
+ # Polyphony.backend_send(self, mesg, 0)
61
+ # end
59
62
 
60
63
  def readpartial(maxlen, str = +'')
61
64
  Polyphony.backend_recv(self, str, maxlen)
@@ -150,14 +153,17 @@ class ::TCPSocket
150
153
  Polyphony.backend_recv_feed_loop(self, receiver, method, &block)
151
154
  end
152
155
 
153
- def send(mesg, flags)
154
- Polyphony.backend_send(self, mesg, flags)
155
- end
156
+ # def send(mesg, flags)
157
+ # Polyphony.backend_send(self, mesg, flags)
158
+ # end
156
159
 
157
- def write(*args)
158
- Polyphony.backend_sendv(self, args, 0)
159
- end
160
- alias_method :<<, :write
160
+ # def write(*args)
161
+ # Polyphony.backend_sendv(self, args, 0)
162
+ # end
163
+
164
+ # def <<(mesg)
165
+ # Polyphony.backend_send(self, mesg, 0)
166
+ # end
161
167
 
162
168
  def readpartial(maxlen, str = nil)
163
169
  @read_buffer ||= +''
@@ -238,7 +244,10 @@ class ::UNIXSocket
238
244
  def write(*args)
239
245
  Polyphony.backend_sendv(self, args, 0)
240
246
  end
241
- alias_method :<<, :write
247
+
248
+ def <<(mesg)
249
+ Polyphony.backend_send(self, mesg, 0)
250
+ end
242
251
 
243
252
  def readpartial(maxlen, str = nil)
244
253
  @read_buffer ||= +''
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.52.0'
4
+ VERSION = '0.53.0'
5
5
  end
data/test/test_backend.rb CHANGED
@@ -237,4 +237,40 @@ class BackendTest < MiniTest::Test
237
237
  end
238
238
  assert_equal [1], buffer
239
239
  end
240
+
241
+ def test_splice
242
+ i1, o1 = IO.pipe
243
+ i2, o2 = IO.pipe
244
+
245
+ spin {
246
+ o2.splice(i1, 1000)
247
+ o2.close
248
+ }
249
+
250
+ o1.write('foobar')
251
+ result = i2.read
252
+
253
+ assert_equal 'foobar', result
254
+ end
255
+
256
+ def test_splice_to_eof
257
+ i1, o1 = IO.pipe
258
+ i2, o2 = IO.pipe
259
+
260
+ f = spin {
261
+ o2.splice_to_eof(i1, 1000)
262
+ o2.close
263
+ }
264
+
265
+ o1.write('foo')
266
+ result = i2.readpartial(1000)
267
+ assert_equal 'foo', result
268
+
269
+ o1.write('bar')
270
+ result = i2.readpartial(1000)
271
+ assert_equal 'bar', result
272
+ ensure
273
+ f.interrupt
274
+ f.await
275
+ end
240
276
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polyphony
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.52.0
4
+ version: 0.53.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-28 00:00:00.000000000 Z
11
+ date: 2021-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -418,6 +418,7 @@ files:
418
418
  - ext/polyphony/runqueue.c
419
419
  - ext/polyphony/runqueue_ring_buffer.c
420
420
  - ext/polyphony/runqueue_ring_buffer.h
421
+ - ext/polyphony/socket_extensions.c
421
422
  - ext/polyphony/thread.c
422
423
  - ext/polyphony/tracing.c
423
424
  - lib/polyphony.rb