polyphony 0.66 → 0.70

Sign up to get free protection for your applications and to get access to all the features.
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