polyphony 0.52.0 → 0.55.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: 2f0f6698a072e4ba2913477d20f5884d5d7579b99cca1db0f2ffa3aeca42ebcc
4
+ data.tar.gz: 97fec9986f6693d685bfb0c1745995c8a85413a9b6cc1a1ff69393d55a5f3b74
5
5
  SHA512:
6
- metadata.gz: b5bf34a4ea7addf2b71c2e0acc75041d95f5d76ae425061bd5475e6523111a05cf969b0109ec90874f7f1a1929111182ed193b084b3388be060886145dd20567
7
- data.tar.gz: 6165128a0393664c97baa494119e453db177b7d0a12c5a5ade0ab5bb8e77a4143f6156b9cb594ef2edbbe9e0ac3bb85a2c846da8e925c3a6ec1c294120c18bf2
6
+ metadata.gz: 053beffc7c27658d129002b5efc29f64c7fcf1ff7fbf3f2eea1d588b2a9e40e89251640bf9bb82ce478837f38c32863987d8ac0af987d374d4ac242d012363dd
7
+ data.tar.gz: e89df4044f09f4df64fc57622f22e70d932f2f09ea3b2a5c2090698784c286367df43d352231068b8ef96d6a7ba89e5492b4c237c9960aeb5fa79af9ee738768
@@ -26,6 +26,6 @@ jobs:
26
26
  - name: Show Linux kernel version
27
27
  run: uname -r
28
28
  - name: Compile C-extension
29
- run: bundle exec rake compile
29
+ run: POLYPHONY_USE_LIBEV=1 bundle exec rake compile
30
30
  - name: Run tests
31
31
  run: bundle exec rake test
data/.gitignore CHANGED
@@ -56,4 +56,6 @@ lib/*.bundle
56
56
  lib/*.so
57
57
 
58
58
  _site
59
- .sass-cache
59
+ .sass-cache
60
+
61
+ log
data/CHANGELOG.md CHANGED
@@ -1,4 +1,27 @@
1
- ## 0.52.0
1
+ ## 0.55.0 2021-06-17
2
+
3
+ - Finish io_uring implementation of Backend#chain
4
+ - Reimplement io_uring op_context acquire/release algorithm (using ref count)
5
+ - Fix #gets on sockets
6
+ - Redesign event anti-starvation mechanism
7
+
8
+ ## 0.54.0 2021-06-14
9
+
10
+ - Implement Mutex#owned?, #locked? (#50)
11
+ - Fix arity for SSLSocket#peeraddr (#55)
12
+ - Add missing SSLServer#accept_loop method (#53)
13
+ - Fix SSLSocket buffering behaviour
14
+ - Add recv_loop alias for SSLSocket (#54)
15
+
16
+ ## 0.53.2 2021-05-10
17
+
18
+ - Remove `splice` methods on libev backend on non-Linux OS (#43)
19
+
20
+ ## 0.53.0 2021-04-23
21
+
22
+ - Implement `Backend#splice`, `Backend#splice_to_eof`, along with `IO#splice`, `IO#splice_to_eof`
23
+
24
+ ## 0.52.0 2021-02-28
2
25
 
3
26
  - Polyphony is now compatible with Ruby 3.0
4
27
  - Add `Backend#sendv` method for sending multiple strings
@@ -8,19 +31,19 @@
8
31
  - libev backend: Use` pidfd_open` for Linux 5.3+, otherwise use a libev child watcher
9
32
  - Use `:call` as default method in `#feed_loop`
10
33
 
11
- ## 0.51.0
34
+ ## 0.51.0 2021-02-02
12
35
 
13
36
  - Implement `IO#feed_loop`, `Socket#feed_loop`
14
37
  - Fix error handling in `Process.kill_and_await`
15
38
 
16
- ## 0.50.1
39
+ ## 0.50.1 2021-01-31
17
40
 
18
41
  - Set `IOSQE_ASYNC` flag in io_uring backend
19
42
  - Fix error handling in `Backend#waitpid`
20
43
  - Reimplement libev backend's `#waitpid` by using pidfd_open (in similar manner
21
44
  to the io_uring backend)
22
45
 
23
- ## 0.50.0
46
+ ## 0.50.0 2021-01-28
24
47
 
25
48
  - Use `Process::CLOCK_MONOTONIC` in Timer
26
49
  - Add `Timer#sleep`, `Timer#after`, `Timer#every`
@@ -28,50 +51,50 @@
28
51
  - Add `Thread#fiber_index_of` method
29
52
  - Use `Backend#wait_event` in `Fiber#await`
30
53
 
31
- ## 0.49.2
54
+ ## 0.49.2 2021-01-19
32
55
 
33
56
  - Fix hang with 100s or more child fibers when terminating
34
57
  - Fix double pending_count increment in io_uring backend
35
58
 
36
- ## 0.49.1
59
+ ## 0.49.1 2021-01-13
37
60
 
38
61
  - Use `TCPSocket` instead of `Socket` in `Net.tcp_connect`
39
62
  - Catch `Errno::ERSCH` in `Process.kill_and_await`
40
63
  - Set io_uring queue size to 2048
41
64
 
42
- ## 0.49.0
65
+ ## 0.49.0 2021-01-11
43
66
 
44
67
  - Implement `Polyphony::Timer` for performant timeouts
45
68
 
46
- ## 0.48.0
69
+ ## 0.48.0 2021-01-05
47
70
 
48
71
  - Implement graceful shutdown
49
72
  - Add support for `break` / `StopIteration` in `spin_loop`
50
73
  - Fix `IO#gets`, `IO#readpartial`
51
74
 
52
- ## 0.47.5.1
75
+ ## 0.47.5.1 2020-11-20
53
76
 
54
77
  - Add missing `Socket#accept_loop` method
55
78
 
56
- ## 0.47.5
79
+ ## 0.47.5 2020-11-20
57
80
 
58
81
  - Add `socket_class` argument to `Backend#accept`, `Backend#accept_loop`
59
82
  - Fix `#supervise` to stop when all children fibers are done
60
83
 
61
- ## 0.47.4
84
+ ## 0.47.4 2020-11-14
62
85
 
63
86
  - Add support for Unix sockets
64
87
 
65
- ## 0.47.3
88
+ ## 0.47.3 2020-11-12
66
89
 
67
90
  - Enable I/O in signal handlers (#45)
68
91
  - Accept `:interval` argument in `#spin_loop`
69
92
 
70
- ## 0.47.2
93
+ ## 0.47.2 2020-11-10
71
94
 
72
95
  - Fix API compatibility between TCPSocket and IO
73
96
 
74
- ## 0.47.0
97
+ ## 0.47.0 2020-11-10
75
98
 
76
99
  - Implement `#spin_scope` used for creating blocking fiber scopes
77
100
  - Reimplement `move_on_after`, `cancel_after`, `Timeout.timeout` using
@@ -79,18 +102,18 @@
79
102
  - Implement `Backend#timeout` API
80
103
  - Implemented capped queues
81
104
 
82
- ## 0.46.1
105
+ ## 0.46.1 2020-11-04
83
106
 
84
107
  - Add `TCPServer#accept_loop`, `OpenSSL::SSL::SSLSocket#accept_loop` method
85
108
  - Fix compilation error on MacOS (#43)
86
109
  - Fix backtrace for `Timeout.timeout`
87
110
  - Add `Backend#timer_loop`
88
111
 
89
- ## 0.46.0
112
+ ## 0.46.0 2020-10-08
90
113
 
91
114
  - Implement [io_uring backend](https://github.com/digital-fabric/polyphony/pull/44)
92
115
 
93
- ## 0.45.5
116
+ ## 0.45.5 2020-10-04
94
117
 
95
118
  - Fix compilation error (#43)
96
119
  - Add support for resetting move_on_after, cancel_after timeouts
@@ -99,22 +122,22 @@
99
122
  - Schedule parent with priority on uncaught exception
100
123
  - Fix race condition in `Mutex#synchronize` (#41)
101
124
 
102
- ## 0.45.4
125
+ ## 0.45.4 2020-09-06
103
126
 
104
127
  - Improve signal trapping mechanism
105
128
 
106
- ## 0.45.3
129
+ ## 0.45.3 2020-09-02
107
130
 
108
131
  - Don't swallow error in `Process#kill_and_await`
109
132
  - Add `Fiber#mailbox` attribute reader
110
133
  - Fix bug in `Fiber.await`
111
134
  - Implement `IO#getc`, `IO#getbyte`
112
135
 
113
- ## 0.45.2
136
+ ## 0.45.2 2020-08-03
114
137
 
115
138
  - Rewrite `Fiber#<<`, `Fiber#await`, `Fiber#receive` in C
116
139
 
117
- ## 0.45.1
140
+ ## 0.45.1 2020-08-01
118
141
 
119
142
  - Fix Net::HTTP compatibility
120
143
  - Fix fs adapter
@@ -124,7 +147,7 @@
124
147
  - Cleanup code
125
148
  - Improve support for Ruby 3 keyword args
126
149
 
127
- ## 0.45.0
150
+ ## 0.45.0 2020-07-29
128
151
 
129
152
  - Cleanup code
130
153
  - Rename `Agent` to `Backend`
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.55.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -17,6 +17,7 @@ GEM
17
17
  mime-types (~> 3.0)
18
18
  multi_xml (>= 0.5.2)
19
19
  json (2.3.0)
20
+ localhost (1.1.8)
20
21
  method_source (1.0.0)
21
22
  mime-types (3.3.1)
22
23
  mime-types-data (~> 3.2015)
@@ -72,6 +73,7 @@ DEPENDENCIES
72
73
  hiredis (= 0.6.3)
73
74
  http_parser.rb (~> 0.6.0)
74
75
  httparty (= 0.17.1)
76
+ localhost (~> 1.1.4)
75
77
  minitest (= 5.14.4)
76
78
  minitest-reporters (= 1.4.2)
77
79
  msgpack (= 1.4.2)
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
@@ -11,25 +10,51 @@
11
10
 
12
11
  - Add support for `break` and `StopIteration` in all loops (with tests)
13
12
 
14
- - Change `IO#gets` to use `String#split` to cut into lines, much faster (see
15
- examples/performance/line_splitting.rb)
16
-
17
13
  - More tight loops
18
14
  - `IO#gets_loop`, `Socket#gets_loop`, `OpenSSL::Socket#gets_loop` (medium effort)
19
15
  - `Fiber#receive_loop` (very little effort, should be implemented in C)
20
16
 
21
17
 
22
- - Add `Backend#splice`, `Backend#splice_loop` for implementing stuff like proxying:
18
+ - Add `Backend#splice`, `Backend#splice_to_eof` for implementing stuff like proxying:
23
19
 
24
20
  ```ruby
25
21
  def two_way_proxy(socket1, socket2)
26
22
  backend = Thread.current.backend
27
- f1 = spin { backend.splice_loop(socket1, socket2) }
28
- f2 = spin { backend.splice_loop(socket2, socket1) }
23
+ f1 = spin { backend.splice_to_eof(socket1, socket2) }
24
+ f2 = spin { backend.splice_to_eof(socket2, socket1) }
29
25
  Fiber.await(f1, f2)
30
26
  end
31
27
  ```
32
28
 
29
+ - Add support for `close` to io_uring backend
30
+
31
+ - Add support for submission of multiple requests to io_uring backend:
32
+
33
+ ```ruby
34
+ Thread.current.backend.submit(
35
+ [:send, sock, chunk_header(len)],
36
+ [:splice, file, sock, len]
37
+ )
38
+ ```
39
+
40
+ Full example (for writing chunks from a file to an HTTP response):
41
+
42
+ ```ruby
43
+ def serve_io(io)
44
+ i, o = IO.pipe
45
+ backend = Thread.current.backend
46
+ while true
47
+ len = o.splice(io, 8192)
48
+ break if len == 0
49
+
50
+ backend.submit(
51
+ [:write, sock, chunk_header(len)],
52
+ [:splice, i, sock, len]
53
+ )
54
+ end
55
+ end
56
+ ```
57
+
33
58
  - Graceful shutdown again:
34
59
  - What happens to children when doing a graceful shutdown?
35
60
  - What are the implications of passing graceful shutdown flag to children?
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ queue = Queue.new
7
+
8
+ 4.times { |i|
9
+ spin_loop {
10
+ job = queue.pop
11
+ puts("worker %d job %s" % [i, job.inspect])
12
+ }
13
+ }
14
+
15
+ (1..10).each do |i|
16
+ queue << "job#{i}"
17
+ end
18
+
19
+ sleep 0.1 until queue.empty?
@@ -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
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'localhost/authority'
6
+
7
+ authority = Localhost::Authority.fetch
8
+ opts = {
9
+ reuse_addr: true,
10
+ dont_linger: true,
11
+ secure_context: authority.server_context
12
+ }
13
+
14
+ server = Polyphony::Net.tcp_listen('localhost', 1234, opts)
15
+
16
+ puts 'Serving HTTPS on port 1234'
17
+
18
+ spin_loop(interval: 1) { STDOUT << '.' }
19
+
20
+ # server.accept_loop do |socket|
21
+ while (socket = server.accept)
22
+ spin do
23
+ while (data = socket.gets("\n", 8192))
24
+ if data.chomp.empty?
25
+ socket << "HTTP/1.1 200 OK\nConnection: close\nContent-Length: 4\n\nfoo\n"
26
+ break
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ puts 'Please enter your name:'
7
+ name = gets.chomp
8
+ puts "Hello, #{name}!"
@@ -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
  }
@@ -26,7 +26,7 @@ struct io_internal_read_struct {
26
26
 
27
27
  #define StringValue(v) rb_string_value(&(v))
28
28
 
29
- int io_setstrbuf(VALUE *str, long len) {
29
+ inline int io_setstrbuf(VALUE *str, long len) {
30
30
  #ifdef _WIN32
31
31
  len = (len + 1) & ~1L; /* round up for wide char */
32
32
  #endif
@@ -55,7 +55,7 @@ inline void io_shrink_read_string(VALUE str, long n) {
55
55
  }
56
56
  }
57
57
 
58
- void io_set_read_length(VALUE str, long n, int shrinkable) {
58
+ inline void io_set_read_length(VALUE str, long n, int shrinkable) {
59
59
  if (RSTRING_LEN(str) != n) {
60
60
  rb_str_modify(str);
61
61
  rb_str_set_len(str, n);
@@ -64,16 +64,16 @@ void io_set_read_length(VALUE str, long n, int shrinkable) {
64
64
  }
65
65
 
66
66
  inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
67
- if (fptr->encs.enc) {
68
- return fptr->encs.enc;
69
- }
70
- return rb_default_external_encoding();
67
+ if (fptr->encs.enc) {
68
+ return fptr->encs.enc;
69
+ }
70
+ return rb_default_external_encoding();
71
71
  }
72
72
 
73
- VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
74
- OBJ_TAINT(str);
75
- rb_enc_associate(str, io_read_encoding(fptr));
76
- return str;
73
+ inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
74
+ OBJ_TAINT(str);
75
+ rb_enc_associate(str, io_read_encoding(fptr));
76
+ return str;
77
77
  }
78
78
 
79
79
  //////////////////////////////////////////////////////////////////////
@@ -21,6 +21,9 @@
21
21
  #include "ruby/io.h"
22
22
 
23
23
  VALUE SYM_io_uring;
24
+ VALUE SYM_send;
25
+ VALUE SYM_splice;
26
+ VALUE SYM_write;
24
27
 
25
28
  #ifdef POLYPHONY_UNSET_NONBLOCK
26
29
  ID ID_ivar_is_nonblocking;
@@ -43,6 +46,7 @@ inline void io_unset_nonblock(rb_io_t *fptr, VALUE io) {
43
46
  fcntl(fptr->fd, F_SETFL, oflags);
44
47
  }
45
48
  #else
49
+ // NOP
46
50
  #define io_unset_nonblock(fptr, io)
47
51
  #endif
48
52
 
@@ -50,7 +54,6 @@ typedef struct Backend_t {
50
54
  // common fields
51
55
  unsigned int currently_polling;
52
56
  unsigned int pending_count;
53
- unsigned int poll_no_wait_count;
54
57
 
55
58
  // implementation-specific fields
56
59
  struct io_uring ring;
@@ -87,7 +90,6 @@ static VALUE Backend_initialize(VALUE self) {
87
90
 
88
91
  backend->currently_polling = 0;
89
92
  backend->pending_count = 0;
90
- backend->poll_no_wait_count = 0;
91
93
  backend->pending_sqes = 0;
92
94
  backend->prepared_limit = 2048;
93
95
 
@@ -117,7 +119,6 @@ VALUE Backend_post_fork(VALUE self) {
117
119
  context_store_free(&backend->store);
118
120
  backend->currently_polling = 0;
119
121
  backend->pending_count = 0;
120
- backend->poll_no_wait_count = 0;
121
122
  backend->pending_sqes = 0;
122
123
 
123
124
  return self;
@@ -149,26 +150,18 @@ static inline bool cq_ring_needs_flush(struct io_uring *ring) {
149
150
  return IO_URING_READ_ONCE(*ring->sq.kflags) & IORING_SQ_CQ_OVERFLOW;
150
151
  }
151
152
 
152
- void io_uring_backend_handle_completion(struct io_uring_cqe *cqe, Backend_t *backend) {
153
+ static inline void io_uring_backend_handle_completion(struct io_uring_cqe *cqe, Backend_t *backend) {
153
154
  op_context_t *ctx = io_uring_cqe_get_data(cqe);
154
155
  if (!ctx) return;
155
156
 
156
157
  ctx->result = cqe->res;
157
-
158
- if (ctx->completed)
159
- // already marked as deleted as result of fiber resuming before op
160
- // completion, so we can release the context
161
- context_store_release(&backend->store, ctx);
162
- else {
163
- // otherwise, we mark it as completed, schedule the fiber and let it deal
164
- // with releasing the context
165
- ctx->completed = 1;
166
- if (ctx->result != -ECANCELED) Fiber_make_runnable(ctx->fiber, ctx->resume_value);
167
- }
158
+ if (ctx->ref_count == 2 && ctx->result != -ECANCELED && ctx->fiber)
159
+ Fiber_make_runnable(ctx->fiber, ctx->resume_value);
160
+ context_store_release(&backend->store, ctx);
168
161
  }
169
162
 
170
163
  // adapted from io_uring_peek_batch_cqe in queue.c
171
- // this peeks at cqes and for each one
164
+ // this peeks at cqes and handles each available cqe
172
165
  void io_uring_backend_handle_ready_cqes(Backend_t *backend) {
173
166
  struct io_uring *ring = &backend->ring;
174
167
  bool overflow_checked = false;
@@ -218,16 +211,6 @@ VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue
218
211
  Backend_t *backend;
219
212
  GetBackend(self, backend);
220
213
 
221
- if (is_nowait) {
222
- backend->poll_no_wait_count++;
223
- if (backend->poll_no_wait_count < 10) return self;
224
-
225
- long runnable_count = Runqueue_len(runqueue);
226
- if (backend->poll_no_wait_count < runnable_count) return self;
227
- }
228
-
229
- backend->poll_no_wait_count = 0;
230
-
231
214
  if (is_nowait && backend->pending_sqes) {
232
215
  backend->pending_sqes = 0;
233
216
  io_uring_submit(&backend->ring);
@@ -282,10 +265,9 @@ int io_uring_backend_defer_submit_and_await(
282
265
 
283
266
  switchpoint_result = backend_await(backend);
284
267
 
285
- if (!ctx->completed) {
268
+ if (ctx->ref_count > 1) {
269
+ // op was not completed (an exception was raised), so we need to cancel it
286
270
  ctx->result = -ECANCELED;
287
-
288
- // op was not completed, so we need to cancel it
289
271
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
290
272
  io_uring_prep_cancel(sqe, ctx, 0);
291
273
  backend->pending_sqes = 0;
@@ -299,7 +281,7 @@ int io_uring_backend_defer_submit_and_await(
299
281
  }
300
282
 
301
283
  VALUE io_uring_backend_wait_fd(Backend_t *backend, int fd, int write) {
302
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_POLL);
284
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_POLL);
303
285
  VALUE resumed_value = Qnil;
304
286
 
305
287
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
@@ -314,8 +296,8 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
314
296
  Backend_t *backend;
315
297
  rb_io_t *fptr;
316
298
  long dynamic_len = length == Qnil;
317
- long len = dynamic_len ? 4096 : NUM2INT(length);
318
- int shrinkable = io_setstrbuf(&str, len);
299
+ long buffer_size = dynamic_len ? 4096 : NUM2INT(length);
300
+ int shrinkable = io_setstrbuf(&str, buffer_size);
319
301
  char *buf = RSTRING_PTR(str);
320
302
  long total = 0;
321
303
  int read_to_eof = RTEST(to_eof);
@@ -331,14 +313,14 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
331
313
 
332
314
  while (1) {
333
315
  VALUE resume_value = Qnil;
334
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
316
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
335
317
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
336
- io_uring_prep_read(sqe, fptr->fd, buf, len - total, -1);
318
+ io_uring_prep_read(sqe, fptr->fd, buf, buffer_size - total, -1);
337
319
 
338
320
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
339
- OP_CONTEXT_RELEASE(&backend->store, ctx);
321
+ int completed = context_store_release(&backend->store, ctx);
340
322
  RAISE_IF_EXCEPTION(resume_value);
341
- if (!ctx->completed) return resume_value;
323
+ if (!completed) return resume_value;
342
324
  RB_GC_GUARD(resume_value);
343
325
 
344
326
  if (result < 0)
@@ -349,14 +331,15 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
349
331
  total += result;
350
332
  if (!read_to_eof) break;
351
333
 
352
- if (total == len) {
334
+ if (total == buffer_size) {
353
335
  if (!dynamic_len) break;
354
336
 
337
+ // resize buffer
355
338
  rb_str_resize(str, total);
356
- rb_str_modify_expand(str, len);
339
+ rb_str_modify_expand(str, buffer_size);
357
340
  buf = RSTRING_PTR(str) + total;
358
341
  shrinkable = 0;
359
- len += len;
342
+ buffer_size += buffer_size;
360
343
  }
361
344
  else buf += result;
362
345
  }
@@ -391,14 +374,14 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
391
374
 
392
375
  while (1) {
393
376
  VALUE resume_value = Qnil;
394
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
377
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
395
378
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
396
379
  io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
397
380
 
398
381
  ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
399
- OP_CONTEXT_RELEASE(&backend->store, ctx);
382
+ int completed = context_store_release(&backend->store, ctx);
400
383
  RAISE_IF_EXCEPTION(resume_value);
401
- if (!ctx->completed) return resume_value;
384
+ if (!completed) return resume_value;
402
385
  RB_GC_GUARD(resume_value);
403
386
 
404
387
  if (result < 0)
@@ -438,14 +421,14 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
438
421
 
439
422
  while (1) {
440
423
  VALUE resume_value = Qnil;
441
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
424
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
442
425
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
443
426
  io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
444
427
 
445
428
  ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
446
- OP_CONTEXT_RELEASE(&backend->store, ctx);
429
+ int completed = context_store_release(&backend->store, ctx);
447
430
  RAISE_IF_EXCEPTION(resume_value);
448
- if (!ctx->completed) return resume_value;
431
+ if (!completed) return resume_value;
449
432
  RB_GC_GUARD(resume_value);
450
433
 
451
434
  if (result < 0)
@@ -481,14 +464,14 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
481
464
 
482
465
  while (left > 0) {
483
466
  VALUE resume_value = Qnil;
484
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_WRITE);
467
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
485
468
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
486
469
  io_uring_prep_write(sqe, fptr->fd, buf, left, -1);
487
470
 
488
471
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
489
- OP_CONTEXT_RELEASE(&backend->store, ctx);
472
+ int completed = context_store_release(&backend->store, ctx);
490
473
  RAISE_IF_EXCEPTION(resume_value);
491
- if (!ctx->completed) return resume_value;
474
+ if (!completed) return resume_value;
492
475
  RB_GC_GUARD(resume_value);
493
476
 
494
477
  if (result < 0)
@@ -530,17 +513,17 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
530
513
 
531
514
  while (1) {
532
515
  VALUE resume_value = Qnil;
533
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_WRITEV);
516
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITEV);
534
517
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
535
518
  io_uring_prep_writev(sqe, fptr->fd, iov_ptr, iov_count, -1);
536
519
 
537
520
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
538
- OP_CONTEXT_RELEASE(&backend->store, ctx);
521
+ int completed = context_store_release(&backend->store, ctx);
539
522
  if (TEST_EXCEPTION(resume_value)) {
540
523
  free(iov);
541
524
  RAISE_EXCEPTION(resume_value);
542
525
  }
543
- if (!ctx->completed) {
526
+ if (!completed) {
544
527
  free(iov);
545
528
  return resume_value;
546
529
  }
@@ -603,14 +586,14 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
603
586
 
604
587
  while (1) {
605
588
  VALUE resume_value = Qnil;
606
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_RECV);
589
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
607
590
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
608
591
  io_uring_prep_recv(sqe, fptr->fd, buf, len - total, 0);
609
592
 
610
593
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
611
- OP_CONTEXT_RELEASE(&backend->store, ctx);
594
+ int completed = context_store_release(&backend->store, ctx);
612
595
  RAISE_IF_EXCEPTION(resume_value);
613
- if (!ctx->completed) return resume_value;
596
+ if (!completed) return resume_value;
614
597
  RB_GC_GUARD(resume_value);
615
598
 
616
599
  if (result < 0)
@@ -650,14 +633,14 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
650
633
 
651
634
  while (1) {
652
635
  VALUE resume_value = Qnil;
653
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_RECV);
636
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
654
637
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
655
638
  io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
656
639
 
657
640
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
658
- OP_CONTEXT_RELEASE(&backend->store, ctx);
641
+ int completed = context_store_release(&backend->store, ctx);
659
642
  RAISE_IF_EXCEPTION(resume_value);
660
- if (!ctx->completed) return resume_value;
643
+ if (!completed) return resume_value;
661
644
  RB_GC_GUARD(resume_value);
662
645
 
663
646
  if (result < 0)
@@ -696,14 +679,14 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
696
679
 
697
680
  while (1) {
698
681
  VALUE resume_value = Qnil;
699
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_RECV);
682
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
700
683
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
701
684
  io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
702
685
 
703
686
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
704
- OP_CONTEXT_RELEASE(&backend->store, ctx);
687
+ int completed = context_store_release(&backend->store, ctx);
705
688
  RAISE_IF_EXCEPTION(resume_value);
706
- if (!ctx->completed) return resume_value;
689
+ if (!completed) return resume_value;
707
690
  RB_GC_GUARD(resume_value);
708
691
 
709
692
  if (result < 0)
@@ -739,14 +722,14 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
739
722
 
740
723
  while (left > 0) {
741
724
  VALUE resume_value = Qnil;
742
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_SEND);
725
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_SEND);
743
726
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
744
727
  io_uring_prep_send(sqe, fptr->fd, buf, left, flags_int);
745
728
 
746
729
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
747
- OP_CONTEXT_RELEASE(&backend->store, ctx);
730
+ int completed = context_store_release(&backend->store, ctx);
748
731
  RAISE_IF_EXCEPTION(resume_value);
749
- if (!ctx->completed) return resume_value;
732
+ if (!completed) return resume_value;
750
733
  RB_GC_GUARD(resume_value);
751
734
 
752
735
  if (result < 0)
@@ -773,14 +756,14 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
773
756
 
774
757
  while (1) {
775
758
  VALUE resume_value = Qnil;
776
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_ACCEPT);
759
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_ACCEPT);
777
760
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
778
761
  io_uring_prep_accept(sqe, fptr->fd, &addr, &len, 0);
779
762
 
780
763
  int fd = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
781
- OP_CONTEXT_RELEASE(&backend->store, ctx);
764
+ int completed = context_store_release(&backend->store, ctx);
782
765
  RAISE_IF_EXCEPTION(resume_value);
783
- if (!ctx->completed) return resume_value;
766
+ if (!completed) return resume_value;
784
767
  RB_GC_GUARD(resume_value);
785
768
 
786
769
  if (fd < 0)
@@ -824,6 +807,60 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
824
807
  return self;
825
808
  }
826
809
 
810
+ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen, int loop) {
811
+ rb_io_t *src_fptr;
812
+ rb_io_t *dest_fptr;
813
+ VALUE underlying_io;
814
+ int total = 0;
815
+
816
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
817
+ if (underlying_io != Qnil) src = underlying_io;
818
+ GetOpenFile(src, src_fptr);
819
+ io_unset_nonblock(src_fptr, src);
820
+
821
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
822
+ if (underlying_io != Qnil) dest = underlying_io;
823
+ dest = rb_io_get_write_io(dest);
824
+ GetOpenFile(dest, dest_fptr);
825
+ io_unset_nonblock(dest_fptr, dest);
826
+
827
+ VALUE resume_value = Qnil;
828
+
829
+ while (1) {
830
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
831
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
832
+ io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
833
+
834
+ int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
835
+ int completed = context_store_release(&backend->store, ctx);
836
+ RAISE_IF_EXCEPTION(resume_value);
837
+ if (!completed) return resume_value;
838
+
839
+ if (result < 0)
840
+ rb_syserr_fail(-result, strerror(-result));
841
+
842
+ total += result;
843
+ if (result == 0 || !loop) return INT2NUM(total);
844
+ }
845
+
846
+ RB_GC_GUARD(resume_value);
847
+ }
848
+
849
+ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
850
+ Backend_t *backend;
851
+ GetBackend(self, backend);
852
+
853
+ return io_uring_backend_splice(backend, src, dest, maxlen, 0);
854
+ }
855
+
856
+ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
857
+ Backend_t *backend;
858
+ GetBackend(self, backend);
859
+
860
+ return io_uring_backend_splice(backend, src, dest, chunksize, 1);
861
+ }
862
+
863
+
827
864
  VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
828
865
  Backend_t *backend;
829
866
  rb_io_t *fptr;
@@ -841,13 +878,13 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
841
878
  addr.sin_port = htons(NUM2INT(port));
842
879
 
843
880
  VALUE resume_value = Qnil;
844
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_CONNECT);
881
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_CONNECT);
845
882
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
846
883
  io_uring_prep_connect(sqe, fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
847
884
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
848
- OP_CONTEXT_RELEASE(&backend->store, ctx);
885
+ int completed = context_store_release(&backend->store, ctx);
849
886
  RAISE_IF_EXCEPTION(resume_value);
850
- if (!ctx->completed) return resume_value;
887
+ if (!completed) return resume_value;
851
888
  RB_GC_GUARD(resume_value);
852
889
 
853
890
  if (result < 0) rb_syserr_fail(-result, strerror(-result));
@@ -887,12 +924,11 @@ int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duratio
887
924
  struct __kernel_timespec ts = double_to_timespec(duration);
888
925
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
889
926
 
890
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_TIMEOUT);
927
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
891
928
  io_uring_prep_timeout(sqe, &ts, 0, 0);
892
929
 
893
930
  io_uring_backend_defer_submit_and_await(backend, sqe, ctx, resume_value);
894
- OP_CONTEXT_RELEASE(&backend->store, ctx);
895
- return ctx->completed;
931
+ return context_store_release(&backend->store, ctx);
896
932
  }
897
933
 
898
934
  VALUE Backend_sleep(VALUE self, VALUE duration) {
@@ -940,7 +976,7 @@ struct Backend_timeout_ctx {
940
976
 
941
977
  VALUE Backend_timeout_ensure(VALUE arg) {
942
978
  struct Backend_timeout_ctx *timeout_ctx = (struct Backend_timeout_ctx *)arg;
943
- if (!timeout_ctx->ctx->completed) {
979
+ if (timeout_ctx->ctx->ref_count) {
944
980
  timeout_ctx->ctx->result = -ECANCELED;
945
981
 
946
982
  // op was not completed, so we need to cancel it
@@ -949,7 +985,7 @@ VALUE Backend_timeout_ensure(VALUE arg) {
949
985
  timeout_ctx->backend->pending_sqes = 0;
950
986
  io_uring_submit(&timeout_ctx->backend->ring);
951
987
  }
952
- OP_CONTEXT_RELEASE(&timeout_ctx->backend->store, timeout_ctx->ctx);
988
+ context_store_release(&timeout_ctx->backend->store, timeout_ctx->ctx);
953
989
  return Qnil;
954
990
  }
955
991
 
@@ -967,7 +1003,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
967
1003
 
968
1004
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
969
1005
 
970
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_TIMEOUT);
1006
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
971
1007
  ctx->resume_value = timeout;
972
1008
  io_uring_prep_timeout(sqe, &ts, 0, 0);
973
1009
  io_uring_sqe_set_data(sqe, ctx);
@@ -1035,6 +1071,130 @@ VALUE Backend_kind(VALUE self) {
1035
1071
  return SYM_io_uring;
1036
1072
  }
1037
1073
 
1074
+ struct io_uring_sqe *Backend_chain_prepare_write(Backend_t *backend, VALUE io, VALUE str) {
1075
+ rb_io_t *fptr;
1076
+ VALUE underlying_io;
1077
+
1078
+ underlying_io = rb_ivar_get(io, ID_ivar_io);
1079
+ if (underlying_io != Qnil) io = underlying_io;
1080
+ io = rb_io_get_write_io(io);
1081
+ GetOpenFile(io, fptr);
1082
+ io_unset_nonblock(fptr, io);
1083
+
1084
+ char *buf = StringValuePtr(str);
1085
+ long len = RSTRING_LEN(str);
1086
+
1087
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1088
+ io_uring_prep_write(sqe, fptr->fd, buf, len, -1);
1089
+ return sqe;
1090
+ }
1091
+
1092
+ struct io_uring_sqe *Backend_chain_prepare_send(Backend_t *backend, VALUE io, VALUE str, VALUE flags) {
1093
+ rb_io_t *fptr;
1094
+ VALUE underlying_io;
1095
+
1096
+ underlying_io = rb_ivar_get(io, ID_ivar_io);
1097
+ if (underlying_io != Qnil) io = underlying_io;
1098
+ io = rb_io_get_write_io(io);
1099
+ GetOpenFile(io, fptr);
1100
+ io_unset_nonblock(fptr, io);
1101
+
1102
+ char *buf = StringValuePtr(str);
1103
+ long len = RSTRING_LEN(str);
1104
+ int flags_int = NUM2INT(flags);
1105
+
1106
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1107
+ io_uring_prep_send(sqe, fptr->fd, buf, len, flags_int);
1108
+ return sqe;
1109
+ }
1110
+
1111
+ struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen) {
1112
+ rb_io_t *src_fptr;
1113
+ rb_io_t *dest_fptr;
1114
+ VALUE underlying_io;
1115
+
1116
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
1117
+ if (underlying_io != Qnil) src = underlying_io;
1118
+ GetOpenFile(src, src_fptr);
1119
+ io_unset_nonblock(src_fptr, src);
1120
+
1121
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
1122
+ if (underlying_io != Qnil) dest = underlying_io;
1123
+ dest = rb_io_get_write_io(dest);
1124
+ GetOpenFile(dest, dest_fptr);
1125
+ io_unset_nonblock(dest_fptr, dest);
1126
+
1127
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1128
+ io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
1129
+ return sqe;
1130
+ }
1131
+
1132
+ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1133
+ VALUE resume_value = Qnil;
1134
+ unsigned int sqe_count = 0;
1135
+ struct io_uring_sqe *last_sqe = 0;
1136
+ Backend_t *backend;
1137
+ GetBackend(self, backend);
1138
+ if (argc == 0) return resume_value;
1139
+
1140
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_CHAIN);
1141
+ for (int i = 0; i < argc; i++) {
1142
+ VALUE op = argv[i];
1143
+ VALUE op_type = RARRAY_AREF(op, 0);
1144
+ VALUE op_len = RARRAY_LEN(op);
1145
+
1146
+ if (op_type == SYM_write && op_len == 3) {
1147
+ last_sqe = Backend_chain_prepare_write(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
1148
+ }
1149
+ else if (op_type == SYM_send && op_len == 4)
1150
+ last_sqe = Backend_chain_prepare_send(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1151
+ else if (op_type == SYM_splice && op_len == 4)
1152
+ last_sqe = Backend_chain_prepare_splice(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1153
+ else {
1154
+ if (sqe_count) {
1155
+ io_uring_sqe_set_data(last_sqe, ctx);
1156
+ io_uring_sqe_set_flags(last_sqe, IOSQE_ASYNC);
1157
+
1158
+ ctx->ref_count = sqe_count;
1159
+ ctx->result = -ECANCELED;
1160
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1161
+ io_uring_prep_cancel(sqe, ctx, 0);
1162
+ backend->pending_sqes = 0;
1163
+ io_uring_submit(&backend->ring);
1164
+ }
1165
+ else {
1166
+ ctx->ref_count = 1;
1167
+ context_store_release(&backend->store, ctx);
1168
+ }
1169
+ rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1170
+ }
1171
+
1172
+ io_uring_sqe_set_data(last_sqe, ctx);
1173
+ unsigned int flags = (i == argc - 1) ? IOSQE_ASYNC : IOSQE_ASYNC & IOSQE_IO_LINK;
1174
+ io_uring_sqe_set_flags(last_sqe, flags);
1175
+ sqe_count++;
1176
+ }
1177
+
1178
+ ctx->ref_count = sqe_count + 1;
1179
+ io_uring_backend_defer_submit(backend);
1180
+ resume_value = backend_await(backend);
1181
+ int result = ctx->result;
1182
+ int completed = context_store_release(&backend->store, ctx);
1183
+ if (!completed) {
1184
+ // op was not completed (an exception was raised), so we need to cancel it
1185
+ ctx->result = -ECANCELED;
1186
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1187
+ io_uring_prep_cancel(sqe, ctx, 0);
1188
+ backend->pending_sqes = 0;
1189
+ io_uring_submit(&backend->ring);
1190
+ RAISE_IF_EXCEPTION(resume_value);
1191
+ return resume_value;
1192
+ }
1193
+
1194
+ RB_GC_GUARD(resume_value);
1195
+ return INT2NUM(result);
1196
+ }
1197
+
1038
1198
  void Init_Backend() {
1039
1199
  VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
1040
1200
  rb_define_alloc_func(cBackend, Backend_allocate);
@@ -1045,33 +1205,39 @@ void Init_Backend() {
1045
1205
 
1046
1206
  rb_define_method(cBackend, "poll", Backend_poll, 3);
1047
1207
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1208
+ rb_define_method(cBackend, "kind", Backend_kind, 0);
1209
+ rb_define_method(cBackend, "chain", Backend_chain, -1);
1048
1210
 
1211
+ rb_define_method(cBackend, "accept", Backend_accept, 2);
1212
+ rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
1213
+ rb_define_method(cBackend, "connect", Backend_connect, 3);
1214
+ rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
1049
1215
  rb_define_method(cBackend, "read", Backend_read, 4);
1050
1216
  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
1217
  rb_define_method(cBackend, "recv", Backend_recv, 3);
1054
- rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
1055
1218
  rb_define_method(cBackend, "recv_feed_loop", Backend_recv_feed_loop, 3);
1219
+ rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
1056
1220
  rb_define_method(cBackend, "send", Backend_send, 3);
1057
1221
  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
1222
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
1063
- rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1223
+ rb_define_method(cBackend, "splice", Backend_splice, 3);
1224
+ rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
1064
1225
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
1065
- rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1226
+ rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1066
1227
  rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
1228
+ rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
1229
+ rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1230
+ rb_define_method(cBackend, "write", Backend_write_m, -1);
1067
1231
 
1068
- rb_define_method(cBackend, "kind", Backend_kind, 0);
1069
1232
 
1070
1233
  #ifdef POLYPHONY_UNSET_NONBLOCK
1071
1234
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
1072
1235
  #endif
1073
1236
 
1074
1237
  SYM_io_uring = ID2SYM(rb_intern("io_uring"));
1238
+ SYM_send = ID2SYM(rb_intern("send"));
1239
+ SYM_splice = ID2SYM(rb_intern("splice"));
1240
+ SYM_write = ID2SYM(rb_intern("write"));
1075
1241
  }
1076
1242
 
1077
1243
  #endif // POLYPHONY_BACKEND_LIBURING