polyphony 0.66 → 0.70

Sign up to get free protection for your applications and to get access to all the features.
@@ -114,6 +114,22 @@ VALUE Fiber_receive_all_pending(VALUE self) {
114
114
  return (mailbox == Qnil) ? rb_ary_new() : Queue_shift_all(mailbox);
115
115
  }
116
116
 
117
+ VALUE Fiber_park(VALUE self) {
118
+ rb_ivar_set(self, ID_ivar_parked, Qtrue);
119
+ Backend_park_fiber(BACKEND(), self);
120
+ return self;
121
+ }
122
+
123
+ VALUE Fiber_unpark(VALUE self) {
124
+ rb_ivar_set(self, ID_ivar_parked, Qnil);
125
+ Backend_unpark_fiber(BACKEND(), self);
126
+ return self;
127
+ }
128
+
129
+ VALUE Fiber_parked_p(VALUE self) {
130
+ return rb_ivar_get(self, ID_ivar_parked);
131
+ }
132
+
117
133
  void Init_Fiber() {
118
134
  VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
119
135
  rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
@@ -128,6 +144,10 @@ void Init_Fiber() {
128
144
  rb_define_method(cFiber, "receive_all_pending", Fiber_receive_all_pending, 0);
129
145
  rb_define_method(cFiber, "mailbox", Fiber_mailbox, 0);
130
146
 
147
+ rb_define_method(cFiber, "__park__", Fiber_park, 0);
148
+ rb_define_method(cFiber, "__unpark__", Fiber_unpark, 0);
149
+ rb_define_method(cFiber, "__parked__?", Fiber_parked_p, 0);
150
+
131
151
  SYM_dead = ID2SYM(rb_intern("dead"));
132
152
  SYM_running = ID2SYM(rb_intern("running"));
133
153
  SYM_runnable = ID2SYM(rb_intern("runnable"));
@@ -12,6 +12,7 @@ ID ID_invoke;
12
12
  ID ID_new;
13
13
  ID ID_ivar_blocking_mode;
14
14
  ID ID_ivar_io;
15
+ ID ID_ivar_parked;
15
16
  ID ID_ivar_runnable;
16
17
  ID ID_ivar_running;
17
18
  ID ID_ivar_thread;
@@ -160,6 +161,7 @@ void Init_Polyphony() {
160
161
  ID_invoke = rb_intern("invoke");
161
162
  ID_ivar_blocking_mode = rb_intern("@blocking_mode");
162
163
  ID_ivar_io = rb_intern("@io");
164
+ ID_ivar_parked = rb_intern("@parked");
163
165
  ID_ivar_runnable = rb_intern("@runnable");
164
166
  ID_ivar_running = rb_intern("@running");
165
167
  ID_ivar_thread = rb_intern("@thread");
@@ -44,6 +44,7 @@ extern ID ID_invoke;
44
44
  extern ID ID_ivar_backend;
45
45
  extern ID ID_ivar_blocking_mode;
46
46
  extern ID ID_ivar_io;
47
+ extern ID ID_ivar_parked;
47
48
  extern ID ID_ivar_runnable;
48
49
  extern ID ID_ivar_running;
49
50
  extern ID ID_ivar_thread;
@@ -115,9 +116,11 @@ VALUE Backend_run_idle_tasks(VALUE self);
115
116
  VALUE Backend_switch_fiber(VALUE self);
116
117
  void Backend_schedule_fiber(VALUE thread, VALUE self, VALUE fiber, VALUE value, int prioritize);
117
118
  void Backend_unschedule_fiber(VALUE self, VALUE fiber);
119
+ void Backend_park_fiber(VALUE self, VALUE fiber);
120
+ void Backend_unpark_fiber(VALUE self, VALUE fiber);
118
121
 
119
- VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
120
- VALUE Thread_schedule_fiber_with_priority(VALUE thread, VALUE fiber, VALUE value);
122
+ void Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
123
+ void Thread_schedule_fiber_with_priority(VALUE thread, VALUE fiber, VALUE value);
121
124
  VALUE Thread_switch_fiber(VALUE thread);
122
125
 
123
126
  VALUE Polyphony_snooze(VALUE self);
@@ -40,6 +40,10 @@ inline int runqueue_index_of(runqueue_t *runqueue, VALUE fiber) {
40
40
  return runqueue_ring_buffer_index_of(&runqueue->entries, fiber);
41
41
  }
42
42
 
43
+ inline void runqueue_migrate(runqueue_t *src, runqueue_t *dest, VALUE fiber) {
44
+ runqueue_ring_buffer_migrate(&src->entries, &dest->entries, fiber);
45
+ }
46
+
43
47
  inline void runqueue_clear(runqueue_t *runqueue) {
44
48
  runqueue_ring_buffer_clear(&runqueue->entries);
45
49
  }
@@ -18,6 +18,7 @@ void runqueue_unshift(runqueue_t *runqueue, VALUE fiber, VALUE value, int resche
18
18
  runqueue_entry runqueue_shift(runqueue_t *runqueue);
19
19
  void runqueue_delete(runqueue_t *runqueue, VALUE fiber);
20
20
  int runqueue_index_of(runqueue_t *runqueue, VALUE fiber);
21
+ void runqueue_migrate(runqueue_t *src, runqueue_t *dest, VALUE fiber);
21
22
  void runqueue_clear(runqueue_t *runqueue);
22
23
  unsigned int runqueue_size(runqueue_t *runqueue);
23
24
  unsigned int runqueue_len(runqueue_t *runqueue);
@@ -1,7 +1,7 @@
1
1
  #include "polyphony.h"
2
2
  #include "runqueue_ring_buffer.h"
3
3
 
4
- void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
4
+ inline void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
5
5
  buffer->size = 1;
6
6
  buffer->count = 0;
7
7
  buffer->entries = malloc(buffer->size * sizeof(runqueue_entry));
@@ -9,17 +9,21 @@ void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
9
9
  buffer->tail = 0;
10
10
  }
11
11
 
12
- void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer) {
12
+ inline void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer) {
13
13
  free(buffer->entries);
14
14
  }
15
15
 
16
- int runqueue_ring_buffer_empty_p(runqueue_ring_buffer *buffer) {
16
+ inline int runqueue_ring_buffer_empty_p(runqueue_ring_buffer *buffer) {
17
17
  return buffer->count == 0;
18
18
  }
19
19
 
20
+ inline void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer) {
21
+ buffer->count = buffer->head = buffer->tail = 0;
22
+ }
23
+
20
24
  static runqueue_entry nil_runqueue_entry = {(Qnil), (Qnil)};
21
25
 
22
- runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
26
+ inline runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
23
27
  if (buffer->count == 0) return nil_runqueue_entry;
24
28
 
25
29
  runqueue_entry value = buffer->entries[buffer->head];
@@ -28,7 +32,7 @@ runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
28
32
  return value;
29
33
  }
30
34
 
31
- void runqueue_ring_buffer_resize(runqueue_ring_buffer *buffer) {
35
+ inline void runqueue_ring_buffer_resize(runqueue_ring_buffer *buffer) {
32
36
  unsigned int old_size = buffer->size;
33
37
  buffer->size = old_size == 1 ? 4 : old_size * 2;
34
38
  buffer->entries = realloc(buffer->entries, buffer->size * sizeof(runqueue_entry));
@@ -37,7 +41,7 @@ void runqueue_ring_buffer_resize(runqueue_ring_buffer *buffer) {
37
41
  buffer->tail = buffer->head + buffer->count;
38
42
  }
39
43
 
40
- void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
44
+ inline void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
41
45
  if (buffer->count == buffer->size) runqueue_ring_buffer_resize(buffer);
42
46
 
43
47
  buffer->head = (buffer->head - 1) % buffer->size;
@@ -46,7 +50,7 @@ void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VAL
46
50
  buffer->count++;
47
51
  }
48
52
 
49
- void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
53
+ inline void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
50
54
  if (buffer->count == buffer->size) runqueue_ring_buffer_resize(buffer);
51
55
 
52
56
  buffer->entries[buffer->tail].fiber = fiber;
@@ -55,14 +59,14 @@ void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE
55
59
  buffer->count++;
56
60
  }
57
61
 
58
- void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
62
+ inline void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
59
63
  for (unsigned int i = 0; i < buffer->count; i++) {
60
64
  rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].fiber);
61
65
  rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].value);
62
66
  }
63
67
  }
64
68
 
65
- void runqueue_ring_buffer_delete_at(runqueue_ring_buffer *buffer, unsigned int idx) {
69
+ inline void runqueue_ring_buffer_delete_at(runqueue_ring_buffer *buffer, unsigned int idx) {
66
70
  for (unsigned int idx2 = idx; idx2 != buffer->tail; idx2 = (idx2 + 1) % buffer->size) {
67
71
  buffer->entries[idx2] = buffer->entries[(idx2 + 1) % buffer->size];
68
72
  }
@@ -70,7 +74,7 @@ void runqueue_ring_buffer_delete_at(runqueue_ring_buffer *buffer, unsigned int i
70
74
  buffer->tail = (buffer->tail - 1) % buffer->size;
71
75
  }
72
76
 
73
- void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber) {
77
+ inline void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber) {
74
78
  for (unsigned int i = 0; i < buffer->count; i++) {
75
79
  unsigned int idx = (buffer->head + i) % buffer->size;
76
80
  if (buffer->entries[idx].fiber == fiber) {
@@ -80,7 +84,7 @@ void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber) {
80
84
  }
81
85
  }
82
86
 
83
- int runqueue_ring_buffer_index_of(runqueue_ring_buffer *buffer, VALUE fiber) {
87
+ inline int runqueue_ring_buffer_index_of(runqueue_ring_buffer *buffer, VALUE fiber) {
84
88
  for (unsigned int i = 0; i < buffer->count; i++) {
85
89
  unsigned int idx = (buffer->head + i) % buffer->size;
86
90
  if (buffer->entries[idx].fiber == fiber)
@@ -89,6 +93,13 @@ int runqueue_ring_buffer_index_of(runqueue_ring_buffer *buffer, VALUE fiber) {
89
93
  return -1;
90
94
  }
91
95
 
92
- void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer) {
93
- buffer->count = buffer->head = buffer->tail = 0;
94
- }
96
+ inline void runqueue_ring_buffer_migrate(runqueue_ring_buffer *src, runqueue_ring_buffer *dest, VALUE fiber) {
97
+ for (unsigned int i = 0; i < src->count; i++) {
98
+ unsigned int idx = (src->head + i) % src->size;
99
+ if (src->entries[idx].fiber == fiber) {
100
+ runqueue_ring_buffer_push(dest, src->entries[idx].fiber, src->entries[idx].value);
101
+ runqueue_ring_buffer_delete_at(src, idx);
102
+ return;
103
+ }
104
+ }
105
+ }
@@ -29,4 +29,6 @@ void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE
29
29
  void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber);
30
30
  int runqueue_ring_buffer_index_of(runqueue_ring_buffer *buffer, VALUE fiber);
31
31
 
32
+ void runqueue_ring_buffer_migrate(runqueue_ring_buffer *src, runqueue_ring_buffer *dest, VALUE fiber);
33
+
32
34
  #endif /* RUNQUEUE_RING_BUFFER_H */
@@ -22,14 +22,12 @@ VALUE Thread_fiber_unschedule(VALUE self, VALUE fiber) {
22
22
  return self;
23
23
  }
24
24
 
25
- VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
25
+ inline void Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
26
26
  schedule_fiber(self, fiber, value, 0);
27
- return self;
28
27
  }
29
28
 
30
- VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
29
+ inline void Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
31
30
  schedule_fiber(self, fiber, value, 1);
32
- return self;
33
31
  }
34
32
 
35
33
  VALUE Thread_switch_fiber(VALUE self) {
@@ -61,10 +59,6 @@ VALUE Thread_class_backend(VALUE _self) {
61
59
  void Init_Thread() {
62
60
  rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
63
61
  rb_define_method(rb_cThread, "schedule_and_wakeup", Thread_fiber_schedule_and_wakeup, 2);
64
-
65
- rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
66
- rb_define_method(rb_cThread, "schedule_fiber_with_priority",
67
- Thread_schedule_fiber_with_priority, 2);
68
62
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
69
63
  rb_define_method(rb_cThread, "fiber_unschedule", Thread_fiber_unschedule, 1);
70
64
 
data/lib/polyphony.rb CHANGED
@@ -121,3 +121,9 @@ end
121
121
 
122
122
  Polyphony.install_terminating_signal_handlers
123
123
  Polyphony.install_at_exit_handler
124
+
125
+ if (debug_socket_path = ENV['POLYPHONY_DEBUG_SOCKET_PATH'])
126
+ puts "Starting debug server on #{debug_socket_path}"
127
+ require 'polyphony/debugger'
128
+ Polyphony.start_debug_server(debug_socket_path)
129
+ end
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'polyphony/extensions/debug'
4
+
5
+ module Polyphony
6
+ TP_EVENTS = [
7
+ :line,
8
+ :call,
9
+ :return,
10
+ :b_call,
11
+ :b_return
12
+ ]
13
+
14
+ def self.start_debug_server(socket_path)
15
+ server = DebugServer.new(socket_path)
16
+ controller = DebugController.new(server)
17
+ trace = TracePoint.new(*TP_EVENTS) { |tp| controller.handle_tp(trace, tp) }
18
+ trace.enable
19
+
20
+ at_exit do
21
+ Kernel.trace "program terminated"
22
+ trace.disable
23
+ server.stop
24
+ end
25
+ end
26
+
27
+ class DebugController
28
+ def initialize(server)
29
+ @server = server
30
+ @server.wait_for_client
31
+ @state = { fibers: {} }
32
+ @control_fiber = Fiber.new { |f| control_loop(f) }
33
+ @control_fiber.transfer Fiber.current
34
+ end
35
+
36
+ def control_loop(source_fiber)
37
+ @peer = source_fiber
38
+ cmd = { cmd: :initial }
39
+ loop do
40
+ cmd = send(:"cmd_#{cmd[:cmd]}", cmd)
41
+ end
42
+ end
43
+
44
+ POLYPHONY_LIB_DIR = File.expand_path('..', __dir__)
45
+
46
+ def get_next_trace_event
47
+ @peer.transfer.tap { |e| update_state(e) }
48
+ end
49
+
50
+ def update_state(event)
51
+ trace update_state: event
52
+ @state[:fiber] = event[:fiber]
53
+ @state[:path] = event[:path]
54
+ @state[:lineno] = event[:lineno]
55
+ update_fiber_state(event)
56
+ end
57
+
58
+ def update_fiber_state(event)
59
+ fiber_state = @state[:fibers][event[:fiber]] ||= { stack: [] }
60
+ case event[:kind]
61
+ when :call, :c_call, :b_call
62
+ fiber_state[:stack] << event
63
+ when :return, :c_return, :b_return
64
+ fiber_state[:stack].pop
65
+ end
66
+ fiber_state[:binding] = event[:binding]
67
+ fiber_state[:path] = event[:path]
68
+ fiber_state[:lineno] = event[:lineno]
69
+ end
70
+
71
+ def state_presentation(state)
72
+ {
73
+ fiber: fiber_id(state[:fiber]),
74
+ path: state[:path],
75
+ lineno: state[:lineno]
76
+ }
77
+ end
78
+
79
+ def fiber_id(fiber)
80
+ {
81
+ object_id: fiber.object_id,
82
+ tag: fiber.tag
83
+ }
84
+ end
85
+
86
+ def fiber_representation(fiber)
87
+ {
88
+ object_id: fiber.object_id,
89
+ tag: fiber.tag,
90
+ parent: fiber.parent && fiber_id(fiber.parent),
91
+ children: fiber.children.map { |c| fiber_id(c) }
92
+ }
93
+ end
94
+
95
+ def get_next_command(info)
96
+ @server.get_command(info)
97
+ end
98
+
99
+ def cmd_initial(cmd)
100
+ get_next_command(nil)
101
+ end
102
+
103
+ def info_listing(state)
104
+ {
105
+ kind: :listing,
106
+ fiber: fiber_id(state[:fiber]),
107
+ path: state[:path],
108
+ lineno: state[:lineno]
109
+ }
110
+ end
111
+
112
+ def info_state(state)
113
+ info_listing(state).merge(
114
+ kind: :state,
115
+ fibers: info_fiber_states(state[:fibers])
116
+ )
117
+ end
118
+
119
+ def info_fiber_states(fiber_states)
120
+ fiber_states.inject({}) do |h, (f, s)|
121
+ h[fiber_id(f)] = {
122
+ stack: s[:stack].map { |e| { path: e[:path], lineno: e[:lineno] } }
123
+ }
124
+ h
125
+ end
126
+ end
127
+
128
+ def cmd_step(cmd)
129
+ tp = nil
130
+ fiber = nil
131
+ while true
132
+ event = get_next_trace_event
133
+ @peer = event[:fiber]
134
+ if event[:kind] == :line && event[:path] !~ /#{POLYPHONY_LIB_DIR}/
135
+ return get_next_command(info_listing(@state))
136
+ end
137
+ end
138
+ rescue => e
139
+ trace "Uncaught error: #{e.inspect}"
140
+ @trace&.disable
141
+ end
142
+
143
+ def cmd_help(cmd)
144
+ get_next_command(kind: :help)
145
+ end
146
+
147
+ def cmd_list(cmd)
148
+ get_next_command(info_listing(@state))
149
+ end
150
+
151
+ def cmd_state(cmd)
152
+ get_next_command(info_state(@state))
153
+ end
154
+
155
+ def handle_tp(trace, tp)
156
+ return if Thread.current == @server.thread
157
+ return if Fiber.current == @control_fiber
158
+
159
+ kind = tp.event
160
+ event = {
161
+ fiber: Fiber.current,
162
+ kind: kind,
163
+ path: tp.path,
164
+ lineno: tp.lineno,
165
+ binding: tp.binding
166
+ }
167
+ case kind
168
+ when :call, :c_call, :b_call
169
+ event[:method_id] = tp.method_id
170
+ event[:parameters] = tp.parameters
171
+ when :return, :c_return, :b_return
172
+ event[:method_id] = tp.method_id
173
+ event[:return_value] = tp.return_value
174
+ end
175
+ @control_fiber.transfer(event)
176
+ end
177
+ end
178
+
179
+ class DebugServer
180
+ attr_reader :thread
181
+
182
+ def initialize(socket_path)
183
+ @socket_path = socket_path
184
+ @fiber = Fiber.current
185
+ start_server_thread
186
+ end
187
+
188
+ def start_server_thread
189
+ @thread = Thread.new do
190
+ puts("Listening on #{@socket_path}")
191
+ FileUtils.rm(@socket_path) if File.exists?(@socket_path)
192
+ socket = UNIXServer.new(@socket_path)
193
+ loop do
194
+ @client = socket.accept
195
+ end
196
+ end
197
+ end
198
+
199
+ def stop
200
+ @thread.kill
201
+ end
202
+
203
+ def handle_client(client)
204
+ @client = client
205
+ end
206
+
207
+ def wait_for_client
208
+ sleep 0.1 until @client
209
+ msg = @client.gets
210
+ @client.puts msg
211
+ end
212
+
213
+ def get_command(info)
214
+ @client&.orig_write "#{info.inspect}\n"
215
+ cmd = @client&.orig_gets&.chomp
216
+ eval(cmd)
217
+ rescue SystemCallError
218
+ nil
219
+ rescue => e
220
+ trace "Error in interact_with_client: #{e.inspect}"
221
+ e.backtrace[0..3].each { |l| trace l }
222
+ @client = nil
223
+ end
224
+ end
225
+ end