polyphony 0.64 → 0.68

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.
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