polyphony 0.64 → 0.68

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -1
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile.lock +1 -1
  5. data/TODO.md +10 -40
  6. data/bin/pdbg +112 -0
  7. data/examples/core/await.rb +9 -1
  8. data/ext/polyphony/backend_common.c +14 -1
  9. data/ext/polyphony/backend_common.h +3 -1
  10. data/ext/polyphony/backend_io_uring.c +85 -25
  11. data/ext/polyphony/backend_io_uring_context.c +42 -0
  12. data/ext/polyphony/backend_io_uring_context.h +6 -9
  13. data/ext/polyphony/backend_libev.c +85 -39
  14. data/ext/polyphony/fiber.c +20 -0
  15. data/ext/polyphony/polyphony.c +2 -0
  16. data/ext/polyphony/polyphony.h +5 -2
  17. data/ext/polyphony/queue.c +1 -1
  18. data/ext/polyphony/runqueue.c +7 -3
  19. data/ext/polyphony/runqueue.h +4 -3
  20. data/ext/polyphony/runqueue_ring_buffer.c +25 -14
  21. data/ext/polyphony/runqueue_ring_buffer.h +2 -0
  22. data/ext/polyphony/thread.c +2 -8
  23. data/lib/polyphony.rb +6 -0
  24. data/lib/polyphony/debugger.rb +225 -0
  25. data/lib/polyphony/extensions/debug.rb +1 -1
  26. data/lib/polyphony/extensions/fiber.rb +64 -71
  27. data/lib/polyphony/extensions/io.rb +4 -2
  28. data/lib/polyphony/extensions/openssl.rb +66 -0
  29. data/lib/polyphony/extensions/socket.rb +8 -2
  30. data/lib/polyphony/net.rb +1 -0
  31. data/lib/polyphony/version.rb +1 -1
  32. data/test/helper.rb +6 -5
  33. data/test/stress.rb +6 -2
  34. data/test/test_backend.rb +13 -4
  35. data/test/test_fiber.rb +35 -11
  36. data/test/test_global_api.rb +9 -4
  37. data/test/test_io.rb +2 -0
  38. data/test/test_socket.rb +14 -11
  39. data/test/test_supervise.rb +24 -24
  40. data/test/test_thread.rb +3 -0
  41. data/test/test_thread_pool.rb +1 -1
  42. data/test/test_throttler.rb +2 -2
  43. data/test/test_timer.rb +5 -3
  44. metadata +5 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e3938b66b15caff258b95c7f1421f71bc0bb4f7b94dd9db4a3ad0614fd35ded
4
- data.tar.gz: 2198ea0483f959491372074007d5c32416de69ebcf73a8aca51832ea6036ea87
3
+ metadata.gz: d23a9c0f60530fdee1d367f34359e4f694bd4a42ba3c0112e61433df05c29a81
4
+ data.tar.gz: bf75e3f1a633f5c94a43fd45a2de2545d87b1e751753980abb8e1d3eddd4eade
5
5
  SHA512:
6
- metadata.gz: 318b3b2549a6d7d3c2bb1b8396adcf95109ef17f6235e25bcf1540fd3c5777d6f2666858d5f539cf17dd0c2102553eb853ba3b38b96d9476d2727a7ddfb0ed70
7
- data.tar.gz: 025f5037c922531235bcf56076ca6986cb1d983b37eceda8e1585fa90e23f67c0abe17a81b0329851e80c050e9ccce8ba53f7a0d95565e6568546dbb923f9a25
6
+ metadata.gz: b810c7ddbd383039e5f34fe76dbea089807417d4415e79a952f6d6a46538cf99c55ae7a34f3eadf61d22af23ac29a0910c62ea5c97dff6e86b94083d98444edf
7
+ data.tar.gz: '063459edd8f669a024e0b7b8f1daa47983db4cf9ac7f3ca81a69359de92eace4831d843e9b74645db08a50c26d83a2878a5fdb6b125adc260afd13a3135e710c'
@@ -7,7 +7,7 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- os: [ubuntu-latest]
10
+ os: [ubuntu-latest, ubuntu-18.04, macos-10.15]
11
11
  ruby: [2.6, 2.7, 3.0]
12
12
 
13
13
  name: >-
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 0.68 2021-08-13
2
+
3
+ - Fix missing default value in socket classes' `#readpartial`
4
+ - Fix linking of operations in `Backend#chain` (io_uring version)
5
+ - Rename `Fiber#attach` to `Fiber#attach_to`
6
+ - Expose original `SSLServer#accept`
7
+
8
+ ## 0.67 2021-08-06
9
+
10
+ - Improve fiber monitoring
11
+ - Add fiber parking (a parked fiber is prevented from running). This is in
12
+ preparation for the upcoming work on an integrated debugger.
13
+
14
+ ## 0.66 2021-08-01
15
+
16
+ - Fix all splicing APIs on non-linux OSes (#63)
17
+ - Add GC marking of buffers when cancelling read/write ops in io_uring backend
18
+
19
+ ## 0.65 2021-07-29
20
+
21
+ - Add `#__polyphony_read_method__` method for read method detection
22
+
1
23
  ## 0.64 2021-07-26
2
24
 
3
25
  - Add optional raise_on_eof argument to `#readpartial`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.64)
4
+ polyphony (0.68)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/TODO.md CHANGED
@@ -1,3 +1,13 @@
1
+ - io_uring backend:
2
+ - if `io_uring_get_sqe` returns null, call `io_uring_submit`, (snooze fiber)?
3
+ and try again
4
+
5
+ - Tracing:
6
+ - Emit events on I/O ops, e.g.:
7
+ - [:op_read_submit, id, io, len]
8
+ - [:op_read_complete, id, io, len, buffer]
9
+ - Prevent tracing while an event is being emitted (to allow the trace proc to perform I/O)
10
+
1
11
  - Add support for IPv6:
2
12
  https://www.reddit.com/r/ruby/comments/lyen23/understanding_ipv6_and_why_its_important_to_you/
3
13
 
@@ -14,47 +24,8 @@
14
24
  - `IO#gets_loop`, `Socket#gets_loop`, `OpenSSL::Socket#gets_loop` (medium effort)
15
25
  - `Fiber#receive_loop` (very little effort, should be implemented in C)
16
26
 
17
-
18
- - Add `Backend#splice`, `Backend#splice_to_eof` for implementing stuff like proxying:
19
-
20
- ```ruby
21
- def two_way_proxy(socket1, socket2)
22
- backend = Thread.current.backend
23
- f1 = spin { backend.splice_to_eof(socket1, socket2) }
24
- f2 = spin { backend.splice_to_eof(socket2, socket1) }
25
- Fiber.await(f1, f2)
26
- end
27
- ```
28
-
29
27
  - Add support for `close` to io_uring backend
30
28
 
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
-
58
29
  - Graceful shutdown again:
59
30
  - What happens to children when doing a graceful shutdown?
60
31
  - What are the implications of passing graceful shutdown flag to children?
@@ -92,7 +63,6 @@
92
63
 
93
64
  -----------------------------------------------------
94
65
 
95
- - Add `Backend#splice(in, out, nbytes)` API
96
66
  - Adapter for io/console (what does `IO#raw` do?)
97
67
  - Adapter for Pry and IRB (Which fixes #5 and #6)
98
68
  - allow backend selection at runtime
data/bin/pdbg ADDED
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'polyphony'
6
+
7
+ UNIX_SOCKET_PATH = '/tmp/pdbg.sock'
8
+
9
+ cmd = ARGV.join(' ')
10
+ injected_lib_path = File.expand_path('../lib/polyphony/debugger/server_inject.rb', __dir__)
11
+ pid = fork { exec("env POLYPHONY_DEBUG_SOCKET_PATH=#{UNIX_SOCKET_PATH} ruby #{cmd}") }
12
+ puts "Started debugged process (#{pid})"
13
+
14
+ socket = nil
15
+ while !socket
16
+ socket = UNIXSocket.new(UNIX_SOCKET_PATH) rescue nil
17
+ end
18
+
19
+ def parse_command(cmd)
20
+ case cmd
21
+ when /^(step|s)$/
22
+ { cmd: :step }
23
+ when /^(state|st)$/
24
+ { cmd: :state }
25
+ when /^(help|h)$/
26
+ { cmd: :help }
27
+ when /^(list|l)$/
28
+ { cmd: :list }
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ def display_info(info)
35
+ info = eval(info)
36
+ case (info && info[:kind])
37
+ when :listing
38
+ print_listing(info)
39
+ when :state
40
+ print_state(info)
41
+ else
42
+ p info
43
+ end
44
+ rescue SyntaxError
45
+ puts "Failed to eval:"
46
+ p info
47
+ end
48
+
49
+ FILE_LINES_CACHE = {}
50
+
51
+ def self.get_snippet(path, lineno)
52
+ lines = FILE_LINES_CACHE[path] ||= IO.read(path).lines
53
+ start_idx = lineno - 5
54
+ stop_idx = lineno + 3
55
+ stop_idx = lines.size - 1 if stop_idx >= lines.size
56
+ start_idx = 0 if start_idx < 0
57
+ (start_idx..stop_idx).map { |idx| [idx + 1, lines[idx]]}
58
+ end
59
+
60
+ def print_snippet(info, snippet, cur_line)
61
+ places = FILE_LINES_CACHE[info[:path]].size.to_s.size
62
+ snippet.each do |(lineno, line)|
63
+ is_cur = lineno == cur_line
64
+ formatted = format("%s% #{places}d %s", is_cur ? '=> ' : ' ', lineno, line)
65
+ puts formatted
66
+ end
67
+ end
68
+
69
+ def print_listing(info)
70
+ snippet = get_snippet(info[:path], info[:lineno])
71
+ puts "Fiber: #{info[:fiber]} Location: #{info[:path]}:#{info[:lineno]}"
72
+ puts
73
+ print_snippet(info, snippet, info[:lineno])
74
+ puts
75
+ end
76
+
77
+ def print_help
78
+ puts
79
+ puts "Here's some help..."
80
+ puts
81
+ end
82
+
83
+ def print_state(info)
84
+ p info
85
+ end
86
+
87
+ def get_user_cmd
88
+ while true
89
+ STDOUT << "(pdbg) "
90
+ cmd = parse_command(STDIN.gets)
91
+ next unless cmd
92
+
93
+ if cmd[:cmd] == :help
94
+ print_help
95
+ else
96
+ return cmd if cmd
97
+ end
98
+ end
99
+ end
100
+
101
+ socket.puts 'pdbg'
102
+ response = socket.gets
103
+ if response.chomp == 'pdbg'
104
+ puts 'Connected to process'
105
+ end
106
+ loop do
107
+ info = socket.gets.chomp
108
+ display_info(info)
109
+
110
+ cmd = get_user_cmd
111
+ socket.puts cmd.inspect
112
+ end
@@ -2,7 +2,11 @@
2
2
 
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
+ require 'polyphony/extensions/debug'
5
6
 
7
+ Exception.__disable_sanitized_backtrace__ = true
8
+
9
+ puts '----- start await example ------'
6
10
  sleeper = spin do
7
11
  puts 'going to sleep'
8
12
  sleep 1
@@ -17,4 +21,8 @@ waiter = spin do
17
21
  puts 'done waiting'
18
22
  end
19
23
 
20
- waiter.await
24
+ trace :before_await
25
+
26
+ sleep 2
27
+ waiter.await
28
+ trace :after_await
@@ -7,6 +7,7 @@
7
7
 
8
8
  inline void backend_base_initialize(struct Backend_base *base) {
9
9
  runqueue_initialize(&base->runqueue);
10
+ runqueue_initialize(&base->parked_runqueue);
10
11
  base->currently_polling = 0;
11
12
  base->op_count = 0;
12
13
  base->switch_count = 0;
@@ -20,12 +21,14 @@ inline void backend_base_initialize(struct Backend_base *base) {
20
21
 
21
22
  inline void backend_base_finalize(struct Backend_base *base) {
22
23
  runqueue_finalize(&base->runqueue);
24
+ runqueue_finalize(&base->parked_runqueue);
23
25
  }
24
26
 
25
27
  inline void backend_base_mark(struct Backend_base *base) {
26
28
  if (base->idle_proc != Qnil) rb_gc_mark(base->idle_proc);
27
29
  if (base->trace_proc != Qnil) rb_gc_mark(base->trace_proc);
28
30
  runqueue_mark(&base->runqueue);
31
+ runqueue_mark(&base->parked_runqueue);
29
32
  }
30
33
 
31
34
  const unsigned int ANTI_STARVE_SWITCH_COUNT_THRESHOLD = 64;
@@ -91,7 +94,10 @@ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_bas
91
94
 
92
95
  COND_TRACE(base, 4, SYM_fiber_schedule, fiber, value, prioritize ? Qtrue : Qfalse);
93
96
 
94
- (prioritize ? runqueue_unshift : runqueue_push)(&base->runqueue, fiber, value, already_runnable);
97
+ runqueue_t *runqueue = rb_ivar_get(fiber, ID_ivar_parked) == Qtrue ?
98
+ &base->parked_runqueue : &base->runqueue;
99
+
100
+ (prioritize ? runqueue_unshift : runqueue_push)(runqueue, fiber, value, already_runnable);
95
101
  if (!already_runnable) {
96
102
  rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
97
103
  if (rb_thread_current() != thread) {
@@ -105,6 +111,13 @@ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_bas
105
111
  }
106
112
  }
107
113
 
114
+ inline void backend_base_park_fiber(struct Backend_base *base, VALUE fiber) {
115
+ runqueue_migrate(&base->runqueue, &base->parked_runqueue, fiber);
116
+ }
117
+
118
+ inline void backend_base_unpark_fiber(struct Backend_base *base, VALUE fiber) {
119
+ runqueue_migrate(&base->parked_runqueue, &base->runqueue, fiber);
120
+ }
108
121
 
109
122
  inline void backend_trace(struct Backend_base *base, int argc, VALUE *argv) {
110
123
  if (base->trace_proc == Qnil) return;
@@ -17,6 +17,7 @@ struct backend_stats {
17
17
 
18
18
  struct Backend_base {
19
19
  runqueue_t runqueue;
20
+ runqueue_t parked_runqueue;
20
21
  unsigned int currently_polling;
21
22
  unsigned int op_count;
22
23
  unsigned int switch_count;
@@ -33,6 +34,8 @@ void backend_base_finalize(struct Backend_base *base);
33
34
  void backend_base_mark(struct Backend_base *base);
34
35
  VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base);
35
36
  void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize);
37
+ void backend_base_park_fiber(struct Backend_base *base, VALUE fiber);
38
+ void backend_base_unpark_fiber(struct Backend_base *base, VALUE fiber);
36
39
  void backend_trace(struct Backend_base *base, int argc, VALUE *argv);
37
40
  struct backend_stats backend_base_stats(struct Backend_base *base);
38
41
 
@@ -104,7 +107,6 @@ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
104
107
  VALUE Backend_stats(VALUE self);
105
108
  void backend_run_idle_tasks(struct Backend_base *base);
106
109
  void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
107
-
108
110
  void backend_setup_stats_symbols();
109
111
 
110
112
  #endif /* BACKEND_COMMON_H */
@@ -25,6 +25,8 @@ VALUE SYM_send;
25
25
  VALUE SYM_splice;
26
26
  VALUE SYM_write;
27
27
 
28
+ VALUE eArgumentError;
29
+
28
30
  #ifdef POLYPHONY_UNSET_NONBLOCK
29
31
  #define io_unset_nonblock(fptr, io) io_verify_blocking_mode(fptr, io, Qtrue)
30
32
  #else
@@ -45,6 +47,7 @@ typedef struct Backend_t {
45
47
  static void Backend_mark(void *ptr) {
46
48
  Backend_t *backend = ptr;
47
49
  backend_base_mark(&backend->base);
50
+ context_store_mark_taken_buffers(&backend->store);
48
51
  }
49
52
 
50
53
  static void Backend_free(void *ptr) {
@@ -226,7 +229,7 @@ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
226
229
  Backend_t *backend;
227
230
  GetBackend(self, backend);
228
231
 
229
- runqueue_delete(&backend->base.runqueue, fiber);
232
+ runqueue_delete(&backend->base.runqueue, fiber);
230
233
  }
231
234
 
232
235
  inline VALUE Backend_switch_fiber(VALUE self) {
@@ -349,8 +352,11 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
349
352
 
350
353
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
351
354
  int completed = context_store_release(&backend->store, ctx);
352
- RAISE_IF_EXCEPTION(resume_value);
353
- if (!completed) return resume_value;
355
+ if (!completed) {
356
+ context_attach_buffers(ctx, 1, &str);
357
+ RAISE_IF_EXCEPTION(resume_value);
358
+ return resume_value;
359
+ }
354
360
  RB_GC_GUARD(resume_value);
355
361
 
356
362
  if (result < 0)
@@ -410,8 +416,11 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
410
416
 
411
417
  ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
412
418
  int completed = context_store_release(&backend->store, ctx);
413
- RAISE_IF_EXCEPTION(resume_value);
414
- if (!completed) return resume_value;
419
+ if (!completed) {
420
+ context_attach_buffers(ctx, 1, &str);
421
+ RAISE_IF_EXCEPTION(resume_value);
422
+ return resume_value;
423
+ }
415
424
  RB_GC_GUARD(resume_value);
416
425
 
417
426
  if (result < 0)
@@ -457,8 +466,11 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
457
466
 
458
467
  ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
459
468
  int completed = context_store_release(&backend->store, ctx);
460
- RAISE_IF_EXCEPTION(resume_value);
461
- if (!completed) return resume_value;
469
+ if (!completed) {
470
+ context_attach_buffers(ctx, 1, &str);
471
+ RAISE_IF_EXCEPTION(resume_value);
472
+ return resume_value;
473
+ }
462
474
  RB_GC_GUARD(resume_value);
463
475
 
464
476
  if (result < 0)
@@ -500,8 +512,11 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
500
512
 
501
513
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
502
514
  int completed = context_store_release(&backend->store, ctx);
503
- RAISE_IF_EXCEPTION(resume_value);
504
- if (!completed) return resume_value;
515
+ if (!completed) {
516
+ context_attach_buffers(ctx, 1, &str);
517
+ RAISE_IF_EXCEPTION(resume_value);
518
+ return resume_value;
519
+ }
505
520
  RB_GC_GUARD(resume_value);
506
521
 
507
522
  if (result < 0)
@@ -549,12 +564,10 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
549
564
 
550
565
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
551
566
  int completed = context_store_release(&backend->store, ctx);
552
- if (TEST_EXCEPTION(resume_value)) {
553
- free(iov);
554
- RAISE_EXCEPTION(resume_value);
555
- }
556
567
  if (!completed) {
557
568
  free(iov);
569
+ context_attach_buffers(ctx, argc, argv);
570
+ RAISE_IF_EXCEPTION(resume_value);
558
571
  return resume_value;
559
572
  }
560
573
  RB_GC_GUARD(resume_value);
@@ -588,8 +601,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
588
601
 
589
602
  VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
590
603
  if (argc < 2)
591
- // TODO: raise ArgumentError
592
- rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
604
+ rb_raise(eArgumentError, "(wrong number of arguments (expected 2 or more))");
593
605
 
594
606
  return (argc == 2) ?
595
607
  Backend_write(self, argv[0], argv[1]) :
@@ -628,8 +640,11 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
628
640
 
629
641
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
630
642
  int completed = context_store_release(&backend->store, ctx);
631
- RAISE_IF_EXCEPTION(resume_value);
632
- if (!completed) return resume_value;
643
+ if (!completed) {
644
+ context_attach_buffers(ctx, 1, &str);
645
+ RAISE_IF_EXCEPTION(resume_value);
646
+ return resume_value;
647
+ }
633
648
  RB_GC_GUARD(resume_value);
634
649
 
635
650
  if (result < 0)
@@ -675,8 +690,11 @@ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
675
690
 
676
691
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
677
692
  int completed = context_store_release(&backend->store, ctx);
678
- RAISE_IF_EXCEPTION(resume_value);
679
- if (!completed) return resume_value;
693
+ if (!completed) {
694
+ context_attach_buffers(ctx, 1, &str);
695
+ RAISE_IF_EXCEPTION(resume_value);
696
+ return resume_value;
697
+ }
680
698
  RB_GC_GUARD(resume_value);
681
699
 
682
700
  if (result < 0)
@@ -721,8 +739,11 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
721
739
 
722
740
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
723
741
  int completed = context_store_release(&backend->store, ctx);
724
- RAISE_IF_EXCEPTION(resume_value);
725
- if (!completed) return resume_value;
742
+ if (!completed) {
743
+ context_attach_buffers(ctx, 1, &str);
744
+ RAISE_IF_EXCEPTION(resume_value);
745
+ return resume_value;
746
+ }
726
747
  RB_GC_GUARD(resume_value);
727
748
 
728
749
  if (result < 0)
@@ -764,8 +785,11 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
764
785
 
765
786
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
766
787
  int completed = context_store_release(&backend->store, ctx);
767
- RAISE_IF_EXCEPTION(resume_value);
768
- if (!completed) return resume_value;
788
+ if (!completed) {
789
+ context_attach_buffers(ctx, 1, &str);
790
+ RAISE_IF_EXCEPTION(resume_value);
791
+ return resume_value;
792
+ }
769
793
  RB_GC_GUARD(resume_value);
770
794
 
771
795
  if (result < 0)
@@ -1165,6 +1189,24 @@ struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src,
1165
1189
  return sqe;
1166
1190
  }
1167
1191
 
1192
+ void Backend_chain_ctx_attach_buffers(op_context_t *ctx, int argc, VALUE *argv) {
1193
+ int count = 0;
1194
+ if (argc > 1) ctx->buffers = malloc(sizeof(VALUE) * (argc - 1));
1195
+
1196
+ for (int i = 0; i < argc; i++) {
1197
+ VALUE op = argv[i];
1198
+ VALUE op_type = RARRAY_AREF(op, 0);
1199
+
1200
+ if (op_type == SYM_write || op_type == SYM_send) {
1201
+ if (!count) ctx->buffer0 = RARRAY_AREF(op, 2);
1202
+ else ctx->buffers[count - 1] = RARRAY_AREF(op, 2);
1203
+ count++;
1204
+ }
1205
+ }
1206
+ ctx->buffer_count = count;
1207
+ }
1208
+
1209
+
1168
1210
  VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1169
1211
  VALUE resume_value = Qnil;
1170
1212
  unsigned int sqe_count = 0;
@@ -1206,7 +1248,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1206
1248
  }
1207
1249
 
1208
1250
  io_uring_sqe_set_data(last_sqe, ctx);
1209
- unsigned int flags = (i == argc - 1) ? IOSQE_ASYNC : IOSQE_ASYNC & IOSQE_IO_LINK;
1251
+ unsigned int flags = (i == (argc - 1)) ? IOSQE_ASYNC : IOSQE_ASYNC | IOSQE_IO_LINK;
1210
1252
  io_uring_sqe_set_flags(last_sqe, flags);
1211
1253
  sqe_count++;
1212
1254
  }
@@ -1218,6 +1260,8 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1218
1260
  int result = ctx->result;
1219
1261
  int completed = context_store_release(&backend->store, ctx);
1220
1262
  if (!completed) {
1263
+ Backend_chain_ctx_attach_buffers(ctx, argc, argv);
1264
+
1221
1265
  // op was not completed (an exception was raised), so we need to cancel it
1222
1266
  ctx->result = -ECANCELED;
1223
1267
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
@@ -1409,6 +1453,7 @@ syscallerror:
1409
1453
  if (pipefd[1] != -1) close(pipefd[1]);
1410
1454
  rb_syserr_fail(err, strerror(err));
1411
1455
  error:
1456
+ context_attach_buffers_v(ctx, 4, prefix, postfix, chunk_prefix, chunk_postfix);
1412
1457
  if (pipefd[0] != -1) close(pipefd[0]);
1413
1458
  if (pipefd[1] != -1) close(pipefd[1]);
1414
1459
  return RAISE_EXCEPTION(switchpoint_result);
@@ -1429,6 +1474,20 @@ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
1429
1474
  return self;
1430
1475
  }
1431
1476
 
1477
+ void Backend_park_fiber(VALUE self, VALUE fiber) {
1478
+ Backend_t *backend;
1479
+ GetBackend(self, backend);
1480
+
1481
+ backend_base_park_fiber(&backend->base, fiber);
1482
+ }
1483
+
1484
+ void Backend_unpark_fiber(VALUE self, VALUE fiber) {
1485
+ Backend_t *backend;
1486
+ GetBackend(self, backend);
1487
+
1488
+ backend_base_unpark_fiber(&backend->base, fiber);
1489
+ }
1490
+
1432
1491
  void Init_Backend() {
1433
1492
  VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
1434
1493
  rb_define_alloc_func(cBackend, Backend_allocate);
@@ -1469,7 +1528,6 @@ void Init_Backend() {
1469
1528
  rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1470
1529
  rb_define_method(cBackend, "write", Backend_write_m, -1);
1471
1530
 
1472
-
1473
1531
  #ifdef POLYPHONY_UNSET_NONBLOCK
1474
1532
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
1475
1533
  #endif
@@ -1480,6 +1538,8 @@ void Init_Backend() {
1480
1538
  SYM_write = ID2SYM(rb_intern("write"));
1481
1539
 
1482
1540
  backend_setup_stats_symbols();
1541
+
1542
+ eArgumentError = rb_const_get(rb_cObject, rb_intern("ArgumentError"));
1483
1543
  }
1484
1544
 
1485
1545
  #endif // POLYPHONY_BACKEND_LIBURING