polyphony 0.66 → 0.70

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: b8e2aa3d011286dda93222164cbf66f1ec4c18b9c212ee7a70dbebd4bc523943
4
- data.tar.gz: 99b5fb95c682dfad8df33f07d98bfab7f2109b9d4ec0e24720869c2d331f23fb
3
+ metadata.gz: 65be64df5933c24a4afb38b24c139213dcc71a7fbde7689c46c862d7bb04eef3
4
+ data.tar.gz: 3feec6f48ea17b15468790c91a790438d163b1d905d5ef6c7a2ffd6d3e47bac8
5
5
  SHA512:
6
- metadata.gz: 27c65b6e340d1aad41d38b4b322b98d282f96fe6d136c897ab8de14651b2da3cfc392650600d63983f76b5fb1dc70e4e49116efb289a790a509bec63caa9b9e4
7
- data.tar.gz: 94e62ac7fbcb6d085af7475d987eca45be191aaf7654c3bacb07c4f7793d450281d3da1b08b12636a49398e97c1043c3a2cd628018925bdc082e02a0859743bc
6
+ metadata.gz: 3417d863dcd7e99c3123fbc81923627a77c6a5b45ed20d6e84148eabb945ecb38f429873be01fc53d5c35f36a225045c42e51b2c36f543fb3539ebc7d91f8bcf
7
+ data.tar.gz: be39ff1ec9616998fc2a5df0bd9af368687e3424cf4b97776466e803624953440bbf75fc166cd273f8f3d4502eefee4a13283c4129c3ed02113cc2015d314428
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 0.70 2021-08-19
2
+
3
+ - New implementation for `#supervise`
4
+ - Reset backend state and runqueue on fork
5
+
6
+ ## 0.69 2021-08-16
7
+
8
+ - Rename `#__polyphony_read_method__` to `#__parser_read_method__`
9
+
10
+ ## 0.68 2021-08-13
11
+
12
+ - Fix missing default value in socket classes' `#readpartial`
13
+ - Fix linking of operations in `Backend#chain` (io_uring version)
14
+ - Rename `Fiber#attach` to `Fiber#attach_to`
15
+ - Expose original `SSLServer#accept`
16
+
17
+ ## 0.67 2021-08-06
18
+
19
+ - Improve fiber monitoring
20
+ - Add fiber parking (a parked fiber is prevented from running). This is in
21
+ preparation for the upcoming work on an integrated debugger.
22
+
1
23
  ## 0.66 2021-08-01
2
24
 
3
25
  - Fix all splicing APIs on non-linux OSes (#63)
@@ -5,7 +27,7 @@
5
27
 
6
28
  ## 0.65 2021-07-29
7
29
 
8
- - Add `#__polyphony_read_method__` method for read method detection
30
+ - Add `#__parser_read_method__` method for read method detection
9
31
 
10
32
  ## 0.64 2021-07-26
11
33
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.66)
4
+ polyphony (0.70)
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,72 +24,10 @@
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
- - Graceful shutdown again:
59
- - What happens to children when doing a graceful shutdown?
60
- - What are the implications of passing graceful shutdown flag to children?
61
- - What about errors while doing a graceful shutdown?
62
- - What about graceful restarts?
63
- - Some interesting discussions:
64
- - https://trio.discourse.group/search?q=graceful%20shutdown
65
- - https://github.com/python-trio/trio/issues/147
66
- - https://github.com/python-trio/trio/issues/143
67
- - https://trio.discourse.group/t/graceful-shutdown/93/2
68
- - https://250bpm.com/blog:146/
69
- - https://www.rodrigoaraujo.me/posts/golang-pattern-graceful-shutdown-of-concurrent-events/
70
- - https://github.com/tj/go-gracefully
71
- - `Fiber#finalize_children` should pass graceful shutdown flag to children
72
- - A good use case is an HTTP server that on graceful shutdown:
73
- - stops listening
74
- - waits for all ongoing requests to finish, optionally with a timeout
75
-
76
29
  ## Roadmap for Polyphony 1.0
77
30
 
78
- - check integration with rb-inotify
79
-
80
- - Improve `#supervise`. It does not work as advertised, and seems to exhibit an
81
- inconsistent behaviour (see supervisor example).
82
-
83
31
  - Add test that mimics the original design for Monocrono:
84
32
  - 256 fibers each waiting for a message
85
33
  - When message received do some blocking work using a `ThreadPool`
@@ -92,7 +40,6 @@
92
40
 
93
41
  -----------------------------------------------------
94
42
 
95
- - Add `Backend#splice(in, out, nbytes)` API
96
43
  - Adapter for io/console (what does `IO#raw` do?)
97
44
  - Adapter for Pry and IRB (Which fixes #5 and #6)
98
45
  - 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,32 @@ 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);
32
+ }
33
+
34
+ void backend_base_reset(struct Backend_base *base) {
35
+ runqueue_finalize(&base->runqueue);
36
+ runqueue_finalize(&base->parked_runqueue);
37
+
38
+ runqueue_initialize(&base->runqueue);
39
+ runqueue_initialize(&base->parked_runqueue);
40
+
41
+ base->currently_polling = 0;
42
+ base->op_count = 0;
43
+ base->switch_count = 0;
44
+ base->poll_count = 0;
45
+ base->pending_count = 0;
46
+ base->idle_gc_period = 0;
47
+ base->idle_gc_last_time = 0;
48
+ base->idle_proc = Qnil;
49
+ base->trace_proc = Qnil;
29
50
  }
30
51
 
31
52
  const unsigned int ANTI_STARVE_SWITCH_COUNT_THRESHOLD = 64;
@@ -91,7 +112,10 @@ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_bas
91
112
 
92
113
  COND_TRACE(base, 4, SYM_fiber_schedule, fiber, value, prioritize ? Qtrue : Qfalse);
93
114
 
94
- (prioritize ? runqueue_unshift : runqueue_push)(&base->runqueue, fiber, value, already_runnable);
115
+ runqueue_t *runqueue = rb_ivar_get(fiber, ID_ivar_parked) == Qtrue ?
116
+ &base->parked_runqueue : &base->runqueue;
117
+
118
+ (prioritize ? runqueue_unshift : runqueue_push)(runqueue, fiber, value, already_runnable);
95
119
  if (!already_runnable) {
96
120
  rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
97
121
  if (rb_thread_current() != thread) {
@@ -105,6 +129,13 @@ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_bas
105
129
  }
106
130
  }
107
131
 
132
+ inline void backend_base_park_fiber(struct Backend_base *base, VALUE fiber) {
133
+ runqueue_migrate(&base->runqueue, &base->parked_runqueue, fiber);
134
+ }
135
+
136
+ inline void backend_base_unpark_fiber(struct Backend_base *base, VALUE fiber) {
137
+ runqueue_migrate(&base->parked_runqueue, &base->runqueue, fiber);
138
+ }
108
139
 
109
140
  inline void backend_trace(struct Backend_base *base, int argc, VALUE *argv) {
110
141
  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;
@@ -31,8 +32,11 @@ struct Backend_base {
31
32
  void backend_base_initialize(struct Backend_base *base);
32
33
  void backend_base_finalize(struct Backend_base *base);
33
34
  void backend_base_mark(struct Backend_base *base);
35
+ void backend_base_reset(struct Backend_base *base);
34
36
  VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base);
35
37
  void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize);
38
+ void backend_base_park_fiber(struct Backend_base *base, VALUE fiber);
39
+ void backend_base_unpark_fiber(struct Backend_base *base, VALUE fiber);
36
40
  void backend_trace(struct Backend_base *base, int argc, VALUE *argv);
37
41
  struct backend_stats backend_base_stats(struct Backend_base *base);
38
42
 
@@ -104,7 +108,6 @@ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
104
108
  VALUE Backend_stats(VALUE self);
105
109
  void backend_run_idle_tasks(struct Backend_base *base);
106
110
  void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
107
-
108
111
  void backend_setup_stats_symbols();
109
112
 
110
113
  #endif /* BACKEND_COMMON_H */
@@ -106,9 +106,7 @@ VALUE Backend_post_fork(VALUE self) {
106
106
  io_uring_queue_exit(&backend->ring);
107
107
  io_uring_queue_init(backend->prepared_limit, &backend->ring, 0);
108
108
  context_store_free(&backend->store);
109
- backend->base.currently_polling = 0;
110
- backend->base.pending_count = 0;
111
- backend->pending_sqes = 0;
109
+ backend_base_reset(&backend->base);
112
110
 
113
111
  return self;
114
112
  }
@@ -229,7 +227,7 @@ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
229
227
  Backend_t *backend;
230
228
  GetBackend(self, backend);
231
229
 
232
- runqueue_delete(&backend->base.runqueue, fiber);
230
+ runqueue_delete(&backend->base.runqueue, fiber);
233
231
  }
234
232
 
235
233
  inline VALUE Backend_switch_fiber(VALUE self) {
@@ -1248,7 +1246,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1248
1246
  }
1249
1247
 
1250
1248
  io_uring_sqe_set_data(last_sqe, ctx);
1251
- unsigned int flags = (i == argc - 1) ? IOSQE_ASYNC : IOSQE_ASYNC & IOSQE_IO_LINK;
1249
+ unsigned int flags = (i == (argc - 1)) ? IOSQE_ASYNC : IOSQE_ASYNC | IOSQE_IO_LINK;
1252
1250
  io_uring_sqe_set_flags(last_sqe, flags);
1253
1251
  sqe_count++;
1254
1252
  }
@@ -1474,6 +1472,20 @@ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
1474
1472
  return self;
1475
1473
  }
1476
1474
 
1475
+ void Backend_park_fiber(VALUE self, VALUE fiber) {
1476
+ Backend_t *backend;
1477
+ GetBackend(self, backend);
1478
+
1479
+ backend_base_park_fiber(&backend->base, fiber);
1480
+ }
1481
+
1482
+ void Backend_unpark_fiber(VALUE self, VALUE fiber) {
1483
+ Backend_t *backend;
1484
+ GetBackend(self, backend);
1485
+
1486
+ backend_base_unpark_fiber(&backend->base, fiber);
1487
+ }
1488
+
1477
1489
  void Init_Backend() {
1478
1490
  VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
1479
1491
  rb_define_alloc_func(cBackend, Backend_allocate);
@@ -157,6 +157,8 @@ VALUE Backend_post_fork(VALUE self) {
157
157
  ev_loop_destroy(backend->ev_loop);
158
158
  backend->ev_loop = EV_DEFAULT;
159
159
 
160
+ backend_base_reset(&backend->base);
161
+
160
162
  return self;
161
163
  }
162
164
 
@@ -1553,6 +1555,20 @@ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
1553
1555
  return self;
1554
1556
  }
1555
1557
 
1558
+ void Backend_park_fiber(VALUE self, VALUE fiber) {
1559
+ Backend_t *backend;
1560
+ GetBackend(self, backend);
1561
+
1562
+ backend_base_park_fiber(&backend->base, fiber);
1563
+ }
1564
+
1565
+ void Backend_unpark_fiber(VALUE self, VALUE fiber) {
1566
+ Backend_t *backend;
1567
+ GetBackend(self, backend);
1568
+
1569
+ backend_base_unpark_fiber(&backend->base, fiber);
1570
+ }
1571
+
1556
1572
  void Init_Backend() {
1557
1573
  ev_set_allocator(xrealloc);
1558
1574