polyphony 0.71 → 0.74

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/test.yml +15 -11
  4. data/.github/workflows/test_io_uring.yml +32 -0
  5. data/.gitignore +3 -1
  6. data/CHANGELOG.md +33 -4
  7. data/Gemfile.lock +16 -13
  8. data/TODO.md +1 -1
  9. data/bin/pdbg +1 -1
  10. data/docs/_user-guide/all-about-timers.md +1 -1
  11. data/docs/api-reference/exception.md +5 -1
  12. data/docs/api-reference/fiber.md +2 -2
  13. data/docs/faq.md +1 -1
  14. data/docs/getting-started/overview.md +8 -8
  15. data/docs/getting-started/tutorial.md +3 -3
  16. data/docs/main-concepts/concurrency.md +1 -1
  17. data/docs/main-concepts/extending.md +3 -3
  18. data/docs/main-concepts/fiber-scheduling.md +1 -1
  19. data/examples/core/calc.rb +37 -0
  20. data/examples/core/calc_with_restart.rb +40 -0
  21. data/examples/core/calc_with_supervise.rb +37 -0
  22. data/examples/core/message_based_supervision.rb +1 -1
  23. data/examples/core/ring.rb +29 -0
  24. data/examples/io/rack_server.rb +1 -1
  25. data/examples/io/tunnel.rb +1 -1
  26. data/examples/performance/fiber_transfer.rb +1 -1
  27. data/examples/performance/line_splitting.rb +1 -1
  28. data/examples/performance/thread-vs-fiber/compare.rb +1 -1
  29. data/ext/polyphony/backend_common.c +88 -18
  30. data/ext/polyphony/backend_common.h +8 -1
  31. data/ext/polyphony/backend_io_uring.c +280 -164
  32. data/ext/polyphony/backend_io_uring_context.c +2 -1
  33. data/ext/polyphony/backend_io_uring_context.h +3 -2
  34. data/ext/polyphony/backend_libev.c +42 -38
  35. data/ext/polyphony/event.c +5 -2
  36. data/ext/polyphony/extconf.rb +25 -13
  37. data/ext/polyphony/polyphony.c +10 -1
  38. data/ext/polyphony/polyphony.h +7 -1
  39. data/ext/polyphony/queue.c +12 -7
  40. data/ext/polyphony/runqueue_ring_buffer.c +6 -3
  41. data/ext/polyphony/socket_extensions.c +5 -2
  42. data/ext/polyphony/thread.c +1 -1
  43. data/lib/polyphony/adapters/irb.rb +11 -1
  44. data/lib/polyphony/{extensions → core}/debug.rb +0 -0
  45. data/lib/polyphony/core/global_api.rb +3 -6
  46. data/lib/polyphony/core/timer.rb +2 -2
  47. data/lib/polyphony/debugger.rb +3 -3
  48. data/lib/polyphony/extensions/exception.rb +45 -0
  49. data/lib/polyphony/extensions/fiber.rb +87 -11
  50. data/lib/polyphony/extensions/io.rb +2 -2
  51. data/lib/polyphony/extensions/{core.rb → kernel.rb} +0 -73
  52. data/lib/polyphony/extensions/openssl.rb +20 -5
  53. data/lib/polyphony/extensions/process.rb +19 -0
  54. data/lib/polyphony/extensions/socket.rb +20 -9
  55. data/lib/polyphony/extensions/thread.rb +9 -3
  56. data/lib/polyphony/extensions/timeout.rb +10 -0
  57. data/lib/polyphony/extensions.rb +9 -0
  58. data/lib/polyphony/version.rb +1 -1
  59. data/lib/polyphony.rb +2 -4
  60. data/polyphony.gemspec +1 -1
  61. data/test/coverage.rb +2 -2
  62. data/test/test_backend.rb +15 -17
  63. data/test/test_event.rb +1 -1
  64. data/test/test_ext.rb +1 -1
  65. data/test/test_fiber.rb +31 -7
  66. data/test/test_global_api.rb +23 -14
  67. data/test/test_io.rb +5 -5
  68. data/test/test_kernel.rb +2 -2
  69. data/test/test_process_supervision.rb +1 -1
  70. data/test/test_queue.rb +6 -6
  71. data/test/test_signal.rb +20 -1
  72. data/test/test_socket.rb +45 -10
  73. data/test/test_supervise.rb +85 -0
  74. data/test/test_sync.rb +2 -2
  75. data/test/test_thread.rb +22 -2
  76. data/test/test_thread_pool.rb +2 -2
  77. data/test/test_throttler.rb +3 -3
  78. data/test/test_timer.rb +3 -3
  79. data/test/test_trace.rb +1 -1
  80. metadata +19 -9
@@ -17,6 +17,7 @@ const char *op_type_to_str(enum op_type type) {
17
17
  case OP_ACCEPT: return "ACCEPT";
18
18
  case OP_CONNECT: return "CONNECT";
19
19
  case OP_CHAIN: return "CHAIN";
20
+ case OP_CLOSE: return "CLOSE";
20
21
  default: return "";
21
22
  };
22
23
  }
@@ -64,7 +65,7 @@ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
64
65
  // printf("release %p %d (%s, ref_count: %d)\n", ctx, ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
65
66
 
66
67
  assert(ctx->ref_count);
67
-
68
+
68
69
  ctx->ref_count--;
69
70
  if (ctx->ref_count) return 0;
70
71
 
@@ -15,14 +15,15 @@ enum op_type {
15
15
  OP_POLL,
16
16
  OP_ACCEPT,
17
17
  OP_CONNECT,
18
- OP_CHAIN
18
+ OP_CHAIN,
19
+ OP_CLOSE
19
20
  };
20
21
 
21
22
  typedef struct op_context {
22
23
  struct op_context *prev;
23
24
  struct op_context *next;
24
25
  enum op_type type: 16;
25
- unsigned int ref_count : 16;
26
+ unsigned int ref_count : 16;
26
27
  int id;
27
28
  int result;
28
29
  VALUE fiber;
@@ -118,7 +118,7 @@ inline struct ev_loop *libev_new_loop() {
118
118
 
119
119
  static VALUE Backend_initialize(VALUE self) {
120
120
  Backend_t *backend;
121
-
121
+
122
122
  GetBackend(self, backend);
123
123
 
124
124
  backend_base_initialize(&backend->base);
@@ -188,7 +188,7 @@ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
188
188
  Backend_t *backend;
189
189
  GetBackend(self, backend);
190
190
 
191
- runqueue_delete(&backend->base.runqueue, fiber);
191
+ runqueue_delete(&backend->base.runqueue, fiber);
192
192
  }
193
193
 
194
194
  inline VALUE Backend_switch_fiber(VALUE self) {
@@ -287,7 +287,6 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
287
287
  io_verify_blocking_mode(fptr, io, Qfalse);
288
288
  rectify_io_file_pos(fptr);
289
289
  watcher.fiber = Qnil;
290
- OBJ_TAINT(str);
291
290
 
292
291
  while (1) {
293
292
  backend->base.op_count++;
@@ -698,10 +697,13 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
698
697
  Backend_t *backend;
699
698
  struct libev_io watcher;
700
699
  rb_io_t *fptr;
701
- struct sockaddr_in addr;
702
- char *host_buf = StringValueCStr(host);
700
+ struct sockaddr *ai_addr;
701
+ int ai_addrlen;
703
702
  VALUE switchpoint_result = Qnil;
704
703
  VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
704
+
705
+ ai_addrlen = backend_getaddrinfo(host, port, &ai_addr);
706
+
705
707
  if (underlying_sock != Qnil) sock = underlying_sock;
706
708
 
707
709
  GetBackend(self, backend);
@@ -709,12 +711,8 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
709
711
  io_verify_blocking_mode(fptr, sock, Qfalse);
710
712
  watcher.fiber = Qnil;
711
713
 
712
- addr.sin_family = AF_INET;
713
- addr.sin_addr.s_addr = inet_addr(host_buf);
714
- addr.sin_port = htons(NUM2INT(port));
715
-
716
714
  backend->base.op_count++;
717
- int result = connect(fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
715
+ int result = connect(fptr->fd, ai_addr, ai_addrlen);
718
716
  if (result < 0) {
719
717
  int e = errno;
720
718
  if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
@@ -971,7 +969,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
971
969
  io_verify_blocking_mode(dest_fptr, dest, Qfalse);
972
970
 
973
971
  watcher.fiber = Qnil;
974
-
972
+
975
973
  while (1) {
976
974
  backend->base.op_count++;
977
975
  ssize_t n = read(src_fptr->fd, buf, len);
@@ -1047,7 +1045,7 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1047
1045
 
1048
1046
  watcher.fiber = Qnil;
1049
1047
 
1050
- while (1) {
1048
+ while (1) {
1051
1049
  char *ptr = buf;
1052
1050
  while (1) {
1053
1051
  backend->base.op_count++;
@@ -1147,33 +1145,39 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
1147
1145
  noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
1148
1146
  Backend_t *backend;
1149
1147
  struct libev_timer watcher;
1150
- double interval_d = NUM2DBL(interval);
1151
-
1152
- GetBackend(self, backend);
1153
1148
  watcher.fiber = rb_fiber_current();
1149
+ uint64_t interval_ns = NUM2DBL(interval) * 1e9;
1150
+ uint64_t next_time_ns = 0;
1151
+ VALUE resume_value = Qnil;
1154
1152
 
1155
- double next_time = 0.;
1153
+ GetBackend(self, backend);
1156
1154
 
1157
1155
  while (1) {
1158
- double now = current_time();
1159
- if (next_time == 0.) next_time = current_time() + interval_d;
1160
- double sleep_duration = next_time - now;
1161
- if (sleep_duration < 0) sleep_duration = 0;
1162
-
1163
- VALUE switchpoint_result = Qnil;
1164
- ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
1165
- ev_timer_start(backend->ev_loop, &watcher.timer);
1166
- backend->base.op_count++;
1167
- switchpoint_result = backend_await((struct Backend_base *)backend);
1168
- ev_timer_stop(backend->ev_loop, &watcher.timer);
1169
- RAISE_IF_EXCEPTION(switchpoint_result);
1170
- RB_GC_GUARD(switchpoint_result);
1156
+ uint64_t now_ns = current_time_ns();
1157
+ if (next_time_ns == 0) next_time_ns = now_ns + interval_ns;
1158
+
1159
+ if (next_time_ns > now_ns) {
1160
+ double sleep_duration = ((double)(next_time_ns - now_ns))/1e9;
1161
+ ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
1162
+ ev_timer_start(backend->ev_loop, &watcher.timer);
1163
+ backend->base.op_count++;
1164
+ resume_value = backend_await((struct Backend_base *)backend);
1165
+ ev_timer_stop(backend->ev_loop, &watcher.timer);
1166
+ RAISE_IF_EXCEPTION(resume_value);
1167
+ }
1168
+ else {
1169
+ resume_value = backend_snooze();
1170
+ RAISE_IF_EXCEPTION(resume_value);
1171
+ }
1171
1172
 
1172
1173
  rb_yield(Qnil);
1173
- do {
1174
- next_time += interval_d;
1175
- } while (next_time <= now);
1174
+
1175
+ while (1) {
1176
+ next_time_ns += interval_ns;
1177
+ if (next_time_ns > now_ns) break;
1178
+ }
1176
1179
  }
1180
+ RB_GC_GUARD(resume_value);
1177
1181
  }
1178
1182
 
1179
1183
  struct libev_timeout {
@@ -1219,7 +1223,7 @@ VALUE Backend_timeout(int argc,VALUE *argv, VALUE self) {
1219
1223
 
1220
1224
  struct Backend_timeout_ctx timeout_ctx = {backend, &watcher};
1221
1225
  result = rb_ensure(Backend_timeout_ensure_safe, Qnil, Backend_timeout_ensure, (VALUE)&timeout_ctx);
1222
-
1226
+
1223
1227
  if (result == timeout) {
1224
1228
  if (exception == Qnil) return move_on_value;
1225
1229
  RAISE_EXCEPTION(backend_timeout_exception(exception));
@@ -1337,7 +1341,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1337
1341
  else
1338
1342
  rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1339
1343
  }
1340
-
1344
+
1341
1345
  RB_GC_GUARD(result);
1342
1346
  return result;
1343
1347
  }
@@ -1364,7 +1368,7 @@ inline VALUE Backend_run_idle_tasks(VALUE self) {
1364
1368
  return self;
1365
1369
  }
1366
1370
 
1367
- inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
1371
+ static inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
1368
1372
  char *buf = RSTRING_PTR(str);
1369
1373
  int len = RSTRING_LEN(str);
1370
1374
  int left = len;
@@ -1386,14 +1390,14 @@ inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct lib
1386
1390
  return 0;
1387
1391
  }
1388
1392
 
1389
- static inline int splice_chunks_splice(Backend_t *backend, int src_fd, int dest_fd, int maxlen,
1393
+ static inline int splice_chunks_splice(Backend_t *backend, int src_fd, int dest_fd, int maxlen,
1390
1394
  struct libev_rw_io *watcher, VALUE *result, int *chunk_len) {
1391
1395
  #ifdef POLYPHONY_LINUX
1392
1396
  backend->base.op_count++;
1393
1397
  while (1) {
1394
1398
  *chunk_len = splice(src_fd, 0, dest_fd, 0, maxlen, 0);
1395
1399
  if (*chunk_len >= 0) return 0;
1396
-
1400
+
1397
1401
  int err = errno;
1398
1402
  if (err != EWOULDBLOCK && err != EAGAIN) return err;
1399
1403
 
@@ -1489,7 +1493,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1489
1493
  err = splice_chunks_splice(backend, src_fptr->fd, pipefd[1], maxlen, &watcher, &result, &chunk_len);
1490
1494
  if (err == -1) goto error; else if (err) goto syscallerror;
1491
1495
  if (chunk_len == 0) break;
1492
-
1496
+
1493
1497
  total += chunk_len;
1494
1498
  chunk_len_value = INT2NUM(chunk_len);
1495
1499
 
@@ -59,14 +59,17 @@ VALUE Event_signal(int argc, VALUE *argv, VALUE self) {
59
59
 
60
60
  VALUE Event_await(VALUE self) {
61
61
  Event_t *event;
62
+ VALUE switchpoint_result;
63
+ VALUE backend;
64
+
62
65
  GetEvent(self, event);
63
66
 
64
67
  if (event->waiting_fiber != Qnil)
65
68
  rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
66
69
 
67
- VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
70
+ backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
68
71
  event->waiting_fiber = rb_fiber_current();
69
- VALUE switchpoint_result = Backend_wait_event(backend, Qnil);
72
+ switchpoint_result = Backend_wait_event(backend, Qnil);
70
73
  event->waiting_fiber = Qnil;
71
74
 
72
75
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -3,32 +3,43 @@
3
3
  require 'rubygems'
4
4
  require 'mkmf'
5
5
 
6
- use_liburing = false
7
- use_pidfd_open = false
8
- force_use_libev = ENV['POLYPHONY_USE_LIBEV'] != nil
9
- linux = RUBY_PLATFORM =~ /linux/
10
-
11
- if linux && `uname -sr` =~ /Linux 5\.(\d+)/
12
- kernel_minor_version = $1.to_i
13
- use_liburing = !force_use_libev && kernel_minor_version >= 6
14
- use_pidfd_open = kernel_minor_version >= 3
6
+
7
+ KERNEL_INFO_RE = /Linux (\d)\.(\d+)\.(?:\d+)\-(?:\d+\-)?(\w+)/
8
+ def get_config
9
+ config = { linux: !!(RUBY_PLATFORM =~ /linux/) }
10
+ return config if !config[:linux]
11
+
12
+ kernel_info = `uname -sr`
13
+ m = kernel_info.match(KERNEL_INFO_RE)
14
+ raise "Could not parse Linux kernel information (#{kernel_info.inspect})" if !m
15
+
16
+ version, major_revision, distribution = m[1].to_i, m[2].to_i, m[3]
17
+ config[:pidfd_open] = (version == 5) && (major_revision >= 3)
18
+
19
+ force_libev = ENV['POLYPHONY_USE_LIBEV'] != nil
20
+ config[:io_uring] = !force_libev &&
21
+ (version == 5) && (major_revision >= 6) && (distribution != 'linuxkit')
22
+ config
15
23
  end
16
24
 
17
- $defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if use_pidfd_open
18
- if use_liburing
25
+ config = get_config
26
+ puts "Building Polyphony... (#{config.inspect})"
27
+
28
+ $defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if config[:pidfd_open]
29
+ if config[:io_uring]
19
30
  $defs << "-DPOLYPHONY_BACKEND_LIBURING"
20
31
  $defs << "-DPOLYPHONY_UNSET_NONBLOCK" if RUBY_VERSION =~ /^3/
21
32
  $CFLAGS << " -Wno-pointer-arith"
22
33
  else
23
34
  $defs << "-DPOLYPHONY_BACKEND_LIBEV"
24
- $defs << "-DPOLYPHONY_LINUX" if linux
35
+ $defs << "-DPOLYPHONY_LINUX" if config[:linux]
25
36
  $defs << '-DEV_USE_LINUXAIO' if have_header('linux/aio_abi.h')
26
37
  $defs << '-DEV_USE_SELECT' if have_header('sys/select.h')
27
38
  $defs << '-DEV_USE_POLL' if have_type('port_event_t', 'poll.h')
28
39
  $defs << '-DEV_USE_EPOLL' if have_header('sys/epoll.h')
29
40
  $defs << '-DEV_USE_KQUEUE' if have_header('sys/event.h') && have_header('sys/queue.h')
30
41
  $defs << '-DEV_USE_PORT' if have_type('port_event_t', 'port.h')
31
- $defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
42
+ $defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
32
43
 
33
44
  $CFLAGS << " -Wno-comment"
34
45
  $CFLAGS << " -Wno-unused-result"
@@ -40,6 +51,7 @@ $defs << '-DPOLYPHONY_PLAYGROUND' if ENV['POLYPHONY_PLAYGROUND']
40
51
 
41
52
  CONFIG['optflags'] << ' -fno-strict-aliasing' unless RUBY_PLATFORM =~ /mswin/
42
53
 
54
+ have_func('rb_fiber_transfer', 'ruby.h')
43
55
 
44
56
  dir_config 'polyphony_ext'
45
57
  create_makefile 'polyphony_ext'
@@ -9,16 +9,18 @@ ID ID_clear;
9
9
  ID ID_each;
10
10
  ID ID_inspect;
11
11
  ID ID_invoke;
12
- ID ID_new;
13
12
  ID ID_ivar_blocking_mode;
14
13
  ID ID_ivar_io;
15
14
  ID ID_ivar_parked;
16
15
  ID ID_ivar_runnable;
17
16
  ID ID_ivar_running;
18
17
  ID ID_ivar_thread;
18
+ ID ID_new;
19
+ ID ID_raise;
19
20
  ID ID_size;
20
21
  ID ID_signal;
21
22
  ID ID_switch_fiber;
23
+ ID ID_to_s;
22
24
  ID ID_transfer;
23
25
  ID ID_R;
24
26
  ID ID_W;
@@ -123,6 +125,10 @@ VALUE Polyphony_backend_write(int argc, VALUE *argv, VALUE self) {
123
125
  return Backend_write_m(argc, argv, BACKEND());
124
126
  }
125
127
 
128
+ VALUE Polyphony_backend_close(VALUE self, VALUE io) {
129
+ return Backend_close(BACKEND(), io);
130
+ }
131
+
126
132
  void Init_Polyphony() {
127
133
  mPolyphony = rb_define_module("Polyphony");
128
134
 
@@ -147,6 +153,7 @@ void Init_Polyphony() {
147
153
  rb_define_singleton_method(mPolyphony, "backend_wait_io", Polyphony_backend_wait_io, 2);
148
154
  rb_define_singleton_method(mPolyphony, "backend_waitpid", Polyphony_backend_waitpid, 1);
149
155
  rb_define_singleton_method(mPolyphony, "backend_write", Polyphony_backend_write, -1);
156
+ rb_define_singleton_method(mPolyphony, "backend_close", Polyphony_backend_close, 1);
150
157
 
151
158
  rb_define_global_function("snooze", Polyphony_snooze, 0);
152
159
  rb_define_global_function("suspend", Polyphony_suspend, 0);
@@ -166,8 +173,10 @@ void Init_Polyphony() {
166
173
  ID_ivar_running = rb_intern("@running");
167
174
  ID_ivar_thread = rb_intern("@thread");
168
175
  ID_new = rb_intern("new");
176
+ ID_raise = rb_intern("raise");
169
177
  ID_signal = rb_intern("signal");
170
178
  ID_size = rb_intern("size");
171
179
  ID_switch_fiber = rb_intern("switch_fiber");
180
+ ID_to_s = rb_intern("to_s");
172
181
  ID_transfer = rb_intern("transfer");
173
182
  }
@@ -26,7 +26,11 @@
26
26
  #define RAISE_IF_NOT_NIL(ret) if (ret != Qnil) { RAISE_EXCEPTION(ret); }
27
27
 
28
28
  // Fiber#transfer
29
- #define FIBER_TRANSFER(fiber, value) rb_funcall(fiber, ID_transfer, 1, value)
29
+ #if HAVE_RB_FIBER_TRANSFER
30
+ #define FIBER_TRANSFER(fiber, value) rb_fiber_transfer(fiber, 1, &value)
31
+ #else
32
+ #define FIBER_TRANSFER(fiber, value) rb_funcall(fiber, ID_transfer, 1, value)
33
+ #endif
30
34
 
31
35
  #define BACKEND() (rb_ivar_get(rb_thread_current(), ID_ivar_backend))
32
36
 
@@ -53,6 +57,7 @@ extern ID ID_raise;
53
57
  extern ID ID_signal;
54
58
  extern ID ID_size;
55
59
  extern ID ID_switch_fiber;
60
+ extern ID ID_to_s;
56
61
  extern ID ID_transfer;
57
62
 
58
63
  extern VALUE SYM_fiber_create;
@@ -108,6 +113,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise);
108
113
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write);
109
114
  VALUE Backend_waitpid(VALUE self, VALUE pid);
110
115
  VALUE Backend_write_m(int argc, VALUE *argv, VALUE self);
116
+ VALUE Backend_close(VALUE self, VALUE io);
111
117
 
112
118
  VALUE Backend_poll(VALUE self, VALUE blocking);
113
119
  VALUE Backend_wait_event(VALUE self, VALUE raise_on_exception);
@@ -121,24 +121,27 @@ VALUE Queue_unshift(VALUE self, VALUE value) {
121
121
 
122
122
  VALUE Queue_shift(VALUE self) {
123
123
  Queue_t *queue;
124
- GetQueue(self, queue);
125
-
126
124
  VALUE fiber = rb_fiber_current();
127
125
  VALUE thread = rb_thread_current();
128
126
  VALUE backend = rb_ivar_get(thread, ID_ivar_backend);
127
+ VALUE value;
128
+
129
+ GetQueue(self, queue);
129
130
 
130
131
  while (1) {
132
+ VALUE switchpoint_result;
133
+
131
134
  if (queue->values.count) Fiber_make_runnable(fiber, Qnil);
132
135
 
133
136
  ring_buffer_push(&queue->shift_queue, fiber);
134
- VALUE switchpoint_result = Backend_wait_event(backend, Qnil);
137
+ switchpoint_result = Backend_wait_event(backend, Qnil);
135
138
  ring_buffer_delete(&queue->shift_queue, fiber);
136
139
 
137
140
  RAISE_IF_EXCEPTION(switchpoint_result);
138
141
  RB_GC_GUARD(switchpoint_result);
139
142
  if (queue->values.count) break;
140
143
  }
141
- VALUE value = ring_buffer_shift(&queue->values);
144
+ value = ring_buffer_shift(&queue->values);
142
145
  if ((queue->capacity) && (queue->capacity > queue->values.count))
143
146
  queue_schedule_first_blocked_fiber(&queue->push_queue);
144
147
  RB_GC_GUARD(value);
@@ -162,12 +165,12 @@ VALUE Queue_cap(VALUE self, VALUE cap) {
162
165
  Queue_t *queue;
163
166
  GetQueue(self, queue);
164
167
  queue->capacity = new_capacity;
165
-
168
+
166
169
  if (queue->capacity)
167
170
  queue_schedule_blocked_fibers_to_capacity(queue);
168
171
  else
169
172
  queue_schedule_all_blocked_fibers(&queue->push_queue);
170
-
173
+
171
174
  return self;
172
175
  }
173
176
 
@@ -206,9 +209,11 @@ VALUE Queue_shift_each(VALUE self) {
206
209
 
207
210
  VALUE Queue_shift_all(VALUE self) {
208
211
  Queue_t *queue;
212
+ VALUE result;
213
+
209
214
  GetQueue(self, queue);
210
215
 
211
- VALUE result = ring_buffer_shift_all(&queue->values);
216
+ result = ring_buffer_shift_all(&queue->values);
212
217
  if (queue->capacity) queue_schedule_blocked_fibers_to_capacity(queue);
213
218
  return result;
214
219
  }
@@ -24,9 +24,11 @@ inline void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer) {
24
24
  static runqueue_entry nil_runqueue_entry = {(Qnil), (Qnil)};
25
25
 
26
26
  inline runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
27
+ runqueue_entry value;
28
+
27
29
  if (buffer->count == 0) return nil_runqueue_entry;
28
30
 
29
- runqueue_entry value = buffer->entries[buffer->head];
31
+ value = buffer->entries[buffer->head];
30
32
  buffer->head = (buffer->head + 1) % buffer->size;
31
33
  buffer->count--;
32
34
  return value;
@@ -61,8 +63,9 @@ inline void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber,
61
63
 
62
64
  inline void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
63
65
  for (unsigned int i = 0; i < buffer->count; i++) {
64
- rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].fiber);
65
- rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].value);
66
+ runqueue_entry entry = buffer->entries[(buffer->head + i) % buffer->size];
67
+ rb_gc_mark(entry.fiber);
68
+ rb_gc_mark(entry.value);
66
69
  }
67
70
  }
68
71
 
@@ -17,10 +17,13 @@ VALUE Socket_double_chevron(VALUE self, VALUE msg) {
17
17
  }
18
18
 
19
19
  void Init_SocketExtensions() {
20
+ VALUE cSocket;
21
+ VALUE cTCPSocket;
22
+
20
23
  rb_require("socket");
21
24
 
22
- VALUE cSocket = rb_const_get(rb_cObject, rb_intern("Socket"));
23
- VALUE cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
25
+ cSocket = rb_const_get(rb_cObject, rb_intern("Socket"));
26
+ cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
24
27
 
25
28
  rb_define_method(cSocket, "send", Socket_send, 2);
26
29
  rb_define_method(cTCPSocket, "send", Socket_send, 2);
@@ -40,7 +40,7 @@ VALUE Thread_fiber_schedule_and_wakeup(VALUE self, VALUE fiber, VALUE resume_obj
40
40
  }
41
41
 
42
42
  if (Backend_wakeup(rb_ivar_get(self, ID_ivar_backend)) == Qnil) {
43
- // we're not inside the ev_loop, so we just do a switchpoint
43
+ // we're not inside Backend_poll, so we just do a switchpoint
44
44
  Thread_switch_fiber(self);
45
45
  }
46
46
 
@@ -3,26 +3,36 @@
3
3
  require 'polyphony'
4
4
 
5
5
  if Object.constants.include?(:Reline)
6
+ puts "reline"
6
7
  class Reline::ANSI
7
8
  def self.select(read_ios = [], write_ios = [], error_ios = [], timeout = nil)
8
- p [:select, read_ios]
9
+ # p [:select, read_ios, timeout]
10
+ # puts caller.join("\n")
9
11
  raise if read_ios.size > 1
10
12
  raise if write_ios.size > 0
11
13
  raise if error_ios.size > 0
12
14
 
15
+ # p 1
13
16
  fiber = Fiber.current
14
17
  timer = spin do
15
18
  sleep timeout
16
19
  fiber.cancel
17
20
  end
21
+ # p 2
18
22
  read_ios.each do |io|
23
+ # p wait: io
19
24
  Polyphony.backend_wait_io(io, false)
25
+ # p :done_wait
20
26
  return [io]
21
27
  end
28
+ # p 3
22
29
  rescue Polyphony::Cancel
30
+ # p :cancel
23
31
  return nil
24
32
  ensure
33
+ # p :ensure
25
34
  timer.stop
35
+ # p :ensure_done
26
36
  end
27
37
  end
28
38
  else
File without changes
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../extensions/core'
4
- require_relative '../extensions/fiber'
5
- require_relative './exceptions'
6
3
  require_relative './throttler'
7
4
 
8
5
  module Polyphony
@@ -73,7 +70,7 @@ module Polyphony
73
70
 
74
71
  def spin_scope
75
72
  raise unless block_given?
76
-
73
+
77
74
  spin do
78
75
  result = yield
79
76
  Fiber.current.await_all_children
@@ -122,8 +119,8 @@ module Polyphony
122
119
  Fiber.current.receive_all_pending
123
120
  end
124
121
 
125
- def supervise(*args, &block)
126
- Fiber.current.supervise(*args, &block)
122
+ def supervise(*args, **opts, &block)
123
+ Fiber.current.supervise(*args, **opts, &block)
127
124
  end
128
125
 
129
126
  def sleep(duration = nil)
@@ -44,7 +44,7 @@ module Polyphony
44
44
  ensure
45
45
  @timeouts.delete(fiber)
46
46
  end
47
-
47
+
48
48
  def cancel_after(interval, with_exception: Polyphony::Cancel)
49
49
  fiber = Fiber.current
50
50
  @timeouts[fiber] = {
@@ -74,7 +74,7 @@ module Polyphony
74
74
  def reset
75
75
  record = @timeouts[Fiber.current]
76
76
  return unless record
77
-
77
+
78
78
  record[:target_stamp] = now + record[:interval]
79
79
  end
80
80
 
@@ -9,11 +9,11 @@ module Polyphony
9
9
  :return,
10
10
  :b_call,
11
11
  :b_return
12
- ]
12
+ ]
13
13
 
14
14
  def self.start_debug_server(socket_path)
15
15
  server = DebugServer.new(socket_path)
16
- controller = DebugController.new(server)
16
+ controller = DebugController.new(server)
17
17
  trace = TracePoint.new(*TP_EVENTS) { |tp| controller.handle_tp(trace, tp) }
18
18
  trace.enable
19
19
 
@@ -124,7 +124,7 @@ module Polyphony
124
124
  h
125
125
  end
126
126
  end
127
-
127
+
128
128
  def cmd_step(cmd)
129
129
  tp = nil
130
130
  fiber = nil
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Exeption overrides
4
+ class ::Exception
5
+ class << self
6
+ attr_accessor :__disable_sanitized_backtrace__
7
+ end
8
+
9
+ attr_accessor :source_fiber, :raising_fiber
10
+
11
+ alias_method :orig_initialize, :initialize
12
+ def initialize(*args)
13
+ @raising_fiber = Fiber.current
14
+ orig_initialize(*args)
15
+ end
16
+
17
+ alias_method :orig_backtrace, :backtrace
18
+ def backtrace
19
+ unless @backtrace_called
20
+ @backtrace_called = true
21
+ return orig_backtrace
22
+ end
23
+
24
+ sanitized_backtrace
25
+ end
26
+
27
+ def sanitized_backtrace
28
+ return sanitize(orig_backtrace) unless @raising_fiber
29
+
30
+ backtrace = orig_backtrace || []
31
+ sanitize(backtrace + @raising_fiber.caller)
32
+ end
33
+
34
+ POLYPHONY_DIR = File.expand_path(File.join(__dir__, '..'))
35
+
36
+ def sanitize(backtrace)
37
+ return backtrace if ::Exception.__disable_sanitized_backtrace__
38
+
39
+ backtrace.reject { |l| l[POLYPHONY_DIR] }
40
+ end
41
+
42
+ def invoke
43
+ Kernel.raise(self)
44
+ end
45
+ end