polyphony 1.4 → 1.6

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +22 -0
  4. data/TODO.md +5 -14
  5. data/examples/pipes/http_server.rb +42 -12
  6. data/examples/pipes/http_server2.rb +45 -0
  7. data/ext/polyphony/backend_common.h +5 -0
  8. data/ext/polyphony/backend_io_uring.c +174 -121
  9. data/ext/polyphony/backend_io_uring_context.c +24 -18
  10. data/ext/polyphony/backend_io_uring_context.h +4 -2
  11. data/ext/polyphony/backend_libev.c +46 -22
  12. data/ext/polyphony/event.c +21 -0
  13. data/ext/polyphony/extconf.rb +25 -19
  14. data/ext/polyphony/fiber.c +0 -2
  15. data/ext/polyphony/pipe.c +1 -1
  16. data/ext/polyphony/polyphony.c +2 -20
  17. data/ext/polyphony/polyphony.h +5 -5
  18. data/ext/polyphony/ring_buffer.c +1 -0
  19. data/ext/polyphony/runqueue_ring_buffer.c +1 -0
  20. data/ext/polyphony/thread.c +63 -0
  21. data/ext/polyphony/win_uio.h +18 -0
  22. data/lib/polyphony/adapters/open3.rb +190 -0
  23. data/lib/polyphony/core/sync.rb +83 -13
  24. data/lib/polyphony/core/timer.rb +7 -25
  25. data/lib/polyphony/extensions/exception.rb +15 -0
  26. data/lib/polyphony/extensions/fiber.rb +14 -13
  27. data/lib/polyphony/extensions/io.rb +56 -14
  28. data/lib/polyphony/extensions/kernel.rb +1 -1
  29. data/lib/polyphony/extensions/object.rb +1 -13
  30. data/lib/polyphony/extensions/process.rb +76 -1
  31. data/lib/polyphony/extensions/socket.rb +0 -14
  32. data/lib/polyphony/extensions/thread.rb +19 -27
  33. data/lib/polyphony/extensions/timeout.rb +5 -1
  34. data/lib/polyphony/version.rb +1 -1
  35. data/lib/polyphony.rb +11 -5
  36. data/test/helper.rb +46 -4
  37. data/test/open3/envutil.rb +380 -0
  38. data/test/open3/find_executable.rb +24 -0
  39. data/test/stress.rb +11 -7
  40. data/test/test_backend.rb +11 -4
  41. data/test/test_event.rb +10 -3
  42. data/test/test_ext.rb +16 -1
  43. data/test/test_fiber.rb +16 -4
  44. data/test/test_global_api.rb +17 -16
  45. data/test/test_io.rb +39 -0
  46. data/test/test_kernel.rb +2 -2
  47. data/test/test_monitor.rb +356 -0
  48. data/test/test_open3.rb +338 -0
  49. data/test/test_signal.rb +5 -1
  50. data/test/test_socket.rb +6 -98
  51. data/test/test_sync.rb +46 -0
  52. data/test/test_thread.rb +10 -1
  53. data/test/test_thread_pool.rb +5 -0
  54. data/test/test_throttler.rb +1 -1
  55. data/test/test_timer.rb +8 -2
  56. data/test/test_trace.rb +2 -0
  57. data/vendor/liburing/.github/workflows/build.yml +8 -0
  58. data/vendor/liburing/.gitignore +1 -0
  59. data/vendor/liburing/CHANGELOG +8 -0
  60. data/vendor/liburing/configure +17 -25
  61. data/vendor/liburing/debian/liburing-dev.manpages +2 -0
  62. data/vendor/liburing/debian/rules +2 -1
  63. data/vendor/liburing/examples/Makefile +2 -1
  64. data/vendor/liburing/examples/io_uring-udp.c +11 -3
  65. data/vendor/liburing/examples/rsrc-update-bench.c +100 -0
  66. data/vendor/liburing/liburing.spec +1 -1
  67. data/vendor/liburing/make-debs.sh +4 -2
  68. data/vendor/liburing/src/Makefile +5 -5
  69. data/vendor/liburing/src/arch/aarch64/lib.h +1 -1
  70. data/vendor/liburing/src/include/liburing/io_uring.h +41 -16
  71. data/vendor/liburing/src/include/liburing.h +86 -11
  72. data/vendor/liburing/src/int_flags.h +1 -0
  73. data/vendor/liburing/src/liburing-ffi.map +12 -0
  74. data/vendor/liburing/src/liburing.map +8 -0
  75. data/vendor/liburing/src/register.c +7 -2
  76. data/vendor/liburing/src/setup.c +373 -81
  77. data/vendor/liburing/test/232c93d07b74.c +3 -3
  78. data/vendor/liburing/test/Makefile +10 -3
  79. data/vendor/liburing/test/accept.c +2 -1
  80. data/vendor/liburing/test/buf-ring.c +35 -75
  81. data/vendor/liburing/test/connect-rep.c +204 -0
  82. data/vendor/liburing/test/coredump.c +59 -0
  83. data/vendor/liburing/test/fallocate.c +9 -0
  84. data/vendor/liburing/test/fd-pass.c +34 -3
  85. data/vendor/liburing/test/file-verify.c +27 -6
  86. data/vendor/liburing/test/helpers.c +3 -1
  87. data/vendor/liburing/test/io_uring_register.c +25 -28
  88. data/vendor/liburing/test/io_uring_setup.c +1 -1
  89. data/vendor/liburing/test/poll-cancel-all.c +29 -5
  90. data/vendor/liburing/test/poll-race-mshot.c +6 -22
  91. data/vendor/liburing/test/read-write.c +53 -0
  92. data/vendor/liburing/test/recv-msgall.c +21 -23
  93. data/vendor/liburing/test/reg-fd-only.c +55 -0
  94. data/vendor/liburing/test/reg-hint.c +56 -0
  95. data/vendor/liburing/test/regbuf-merge.c +91 -0
  96. data/vendor/liburing/test/ringbuf-read.c +2 -10
  97. data/vendor/liburing/test/send_recvmsg.c +5 -16
  98. data/vendor/liburing/test/shutdown.c +2 -1
  99. data/vendor/liburing/test/socket-io-cmd.c +215 -0
  100. data/vendor/liburing/test/socket-rw-eagain.c +2 -1
  101. data/vendor/liburing/test/socket-rw-offset.c +2 -1
  102. data/vendor/liburing/test/socket-rw.c +2 -1
  103. data/vendor/liburing/test/timeout.c +276 -0
  104. data/vendor/liburing/test/xattr.c +38 -25
  105. metadata +20 -7
  106. data/vendor/liburing/test/timeout-overflow.c +0 -204
@@ -6,20 +6,23 @@
6
6
 
7
7
  const char *op_type_to_str(enum op_type type) {
8
8
  switch (type) {
9
- case OP_ACCEPT: return "ACCEPT";
10
- case OP_CHAIN: return "CHAIN";
11
- case OP_CLOSE: return "CLOSE";
12
- case OP_CONNECT: return "CONNECT";
13
- case OP_POLL: return "POLL";
14
- case OP_READ: return "READ";
15
- case OP_RECV: return "RECV";
16
- case OP_RECVMSG: return "RECVMSG";
17
- case OP_SEND: return "SEND";
18
- case OP_SENDMSG: return "SENDMSG";
19
- case OP_SPLICE: return "SPLICE";
20
- case OP_TIMEOUT: return "TIMEOUT";
21
- case OP_WRITEV: return "WRITEV";
22
- case OP_WRITE: return "WRITE";
9
+ case OP_ACCEPT: return "ACCEPT";
10
+ case OP_CHAIN: return "CHAIN";
11
+ case OP_CLOSE: return "CLOSE";
12
+ case OP_CONNECT: return "CONNECT";
13
+ case OP_POLL: return "POLL";
14
+ case OP_READ: return "READ";
15
+ case OP_RECV: return "RECV";
16
+ case OP_RECVMSG: return "RECVMSG";
17
+ case OP_SEND: return "SEND";
18
+ case OP_SENDMSG: return "SENDMSG";
19
+ case OP_SPLICE: return "SPLICE";
20
+ case OP_TIMEOUT: return "TIMEOUT";
21
+ case OP_WRITEV: return "WRITEV";
22
+ case OP_WRITE: return "WRITE";
23
+
24
+ case OP_MULTISHOT_ACCEPT: return "ACCEPT MULTISHOT";
25
+ case OP_MULTISHOT_TIMEOUT: return "ACCEPT MULTISHOT";
23
26
 
24
27
  default: return "";
25
28
  };
@@ -71,14 +74,15 @@ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
71
74
 
72
75
  // If a multishot ctx is released, we pretend its ref count is 1, so it will
73
76
  // be returned to the store.
74
- if (ctx->ref_count == MULTISHOT_REFCOUNT) {
75
- ctx->ref_count = 1;
76
- }
77
+ if (ctx->ref_count == MULTISHOT_REFCOUNT) ctx->ref_count = 1;
77
78
 
78
79
  ctx->ref_count--;
79
80
  if (ctx->ref_count) return 0;
80
81
 
81
- if (ctx->buffer_count > 1) free(ctx->buffers);
82
+ if (ctx->buffer_count > 1) {
83
+ TRACE_FREE(ctx->buffers);
84
+ free(ctx->buffers);
85
+ }
82
86
 
83
87
  store->taken_count--;
84
88
  store->available_count++;
@@ -97,11 +101,13 @@ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
97
101
  void context_store_free(op_context_store_t *store) {
98
102
  while (store->available) {
99
103
  op_context_t *next = store->available->next;
104
+ TRACE_FREE(store->available);
100
105
  free(store->available);
101
106
  store->available = next;
102
107
  }
103
108
  while (store->taken) {
104
109
  op_context_t *next = store->taken->next;
110
+ TRACE_FREE(store->taken);
105
111
  free(store->taken);
106
112
  store->taken = next;
107
113
  }
@@ -8,7 +8,6 @@ enum op_type {
8
8
  OP_CHAIN,
9
9
  OP_CLOSE,
10
10
  OP_CONNECT,
11
- OP_MULTISHOT_ACCEPT,
12
11
  OP_NONE,
13
12
  OP_POLL,
14
13
  OP_READ,
@@ -19,7 +18,9 @@ enum op_type {
19
18
  OP_SPLICE,
20
19
  OP_TIMEOUT,
21
20
  OP_WRITEV,
22
- OP_WRITE
21
+ OP_WRITE,
22
+ OP_MULTISHOT_ACCEPT,
23
+ OP_MULTISHOT_TIMEOUT
23
24
  };
24
25
 
25
26
  #define MULTISHOT_REFCOUNT 0xFFFF
@@ -36,6 +37,7 @@ typedef struct op_context {
36
37
  unsigned int buffer_count;
37
38
  VALUE buffer0;
38
39
  VALUE *buffers;
40
+ double ts; // timestamp for tracing
39
41
  } op_context_t;
40
42
 
41
43
  typedef struct op_context_store {
@@ -42,21 +42,24 @@ thread.
42
42
  #define _GNU_SOURCE 1
43
43
  #endif
44
44
 
45
- #include <netdb.h>
46
- #include <sys/socket.h>
47
- #include <sys/uio.h>
48
45
  #include <unistd.h>
49
- #include <netinet/in.h>
50
- #include <arpa/inet.h>
51
46
  #include <stdnoreturn.h>
52
47
  #include <sys/types.h>
48
+
49
+ #ifdef POLYPHONY_USE_PIDFD_OPEN
53
50
  #include <sys/wait.h>
51
+ #endif
52
+
54
53
  #include <fcntl.h>
55
54
 
56
55
  #include "polyphony.h"
57
56
  #include "../libev/ev.h"
58
57
  #include "ruby/io.h"
59
58
 
59
+ #ifndef POLYPHONY_WINDOWS
60
+ #include <sys/uio.h>
61
+ #endif
62
+
60
63
  #include "../libev/ev.h"
61
64
  #include "backend_common.h"
62
65
 
@@ -267,21 +270,25 @@ VALUE libev_wait_fd(Backend_t *backend, int fd, int events, int raise_exception)
267
270
  }
268
271
 
269
272
  static inline int fd_from_io(VALUE io, rb_io_t **fptr, int write_mode, int rectify_file_pos) {
273
+ if (TYPE(io) == T_FIXNUM) {
274
+ *fptr = NULL;
275
+ return FIX2INT(io);
276
+ }
277
+
270
278
  if (rb_obj_class(io) == cPipe) {
271
279
  *fptr = NULL;
272
280
  Pipe_verify_blocking_mode(io, Qfalse);
273
281
  return Pipe_get_fd(io, write_mode);
274
282
  }
275
- else {
276
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
277
- if (underlying_io != Qnil) io = underlying_io;
278
-
279
- GetOpenFile(io, *fptr);
280
- int fd = rb_io_descriptor(io);
281
- io_verify_blocking_mode(io, fd, Qfalse);
282
- if (rectify_file_pos) rectify_io_file_pos(*fptr);
283
- return fd;
284
- }
283
+
284
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
285
+ if (underlying_io != Qnil) io = underlying_io;
286
+
287
+ GetOpenFile(io, *fptr);
288
+ int fd = rb_io_descriptor(io);
289
+ io_verify_blocking_mode(io, fd, Qfalse);
290
+ if (rectify_file_pos) rectify_io_file_pos(*fptr);
291
+ return fd;
285
292
  }
286
293
 
287
294
  VALUE Backend_read(VALUE self, VALUE io, VALUE buffer, VALUE length, VALUE to_eof, VALUE pos) {
@@ -558,6 +565,7 @@ error:
558
565
  return RAISE_EXCEPTION(switchpoint_result);
559
566
  }
560
567
 
568
+ #ifndef POLYPHONY_WINDOWS
561
569
  VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
562
570
  Backend_t *backend;
563
571
  struct libev_io watcher;
@@ -629,15 +637,23 @@ error:
629
637
  free(iov);
630
638
  return RAISE_EXCEPTION(switchpoint_result);
631
639
  }
640
+ #endif
632
641
 
633
642
  VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
634
643
  if (argc < 2)
635
644
  // TODO: raise ArgumentError
636
645
  rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
637
646
 
647
+ #ifdef POLYPHONY_WINDOWS
648
+ int total = 0;
649
+ for (int i = 1; i < argc; i++)
650
+ total += FIX2INT(Backend_write(self, argv[0], argv[i]));
651
+ return INT2FIX(total);
652
+ #else
638
653
  return (argc == 2) ?
639
654
  Backend_write(self, argv[0], argv[1]) :
640
655
  Backend_writev(self, argv[0], argc - 1, argv + 1);
656
+ #endif
641
657
  }
642
658
 
643
659
  VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
@@ -1155,7 +1171,7 @@ VALUE Backend_close(VALUE self, VALUE io) {
1155
1171
  RAISE_IF_EXCEPTION(resume_value);
1156
1172
  RB_GC_GUARD(resume_value);
1157
1173
 
1158
- fptr_finalize(fptr);
1174
+ if (fptr) fptr_finalize(fptr);
1159
1175
  // fd = -1;
1160
1176
  return io;
1161
1177
  }
@@ -1309,7 +1325,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1309
1325
  int e = errno;
1310
1326
  rb_syserr_fail(e, strerror(e));
1311
1327
  }
1312
- return rb_ary_new_from_args(2, INT2FIX(ret), INT2FIX(WEXITSTATUS(status)));
1328
+ return rb_ary_new_from_args(2, INT2FIX(ret), INT2FIX(status));
1313
1329
  }
1314
1330
  #else
1315
1331
  struct libev_child {
@@ -1317,16 +1333,18 @@ struct libev_child {
1317
1333
  VALUE fiber;
1318
1334
  };
1319
1335
 
1336
+ #ifndef POLYPHONY_WINDOWS
1320
1337
  void Backend_child_callback(EV_P_ ev_child *w, int revents) {
1321
1338
  struct libev_child *watcher = (struct libev_child *)w;
1322
- int exit_status = WEXITSTATUS(w->rstatus);
1323
- VALUE status;
1324
-
1325
- status = rb_ary_new_from_args(2, INT2FIX(w->rpid), INT2FIX(exit_status));
1339
+ VALUE status = rb_ary_new_from_args(2, INT2FIX(w->rpid), INT2FIX(w->rstatus));
1326
1340
  Fiber_make_runnable(watcher->fiber, status);
1327
1341
  }
1342
+ #endif
1328
1343
 
1329
1344
  VALUE Backend_waitpid(VALUE self, VALUE pid) {
1345
+ #ifdef POLYPHONY_WINDOWS
1346
+ rb_raise(rb_eStandardError, "Not implemented");
1347
+ #else
1330
1348
  Backend_t *backend;
1331
1349
  struct libev_child watcher;
1332
1350
  VALUE switchpoint_result = Qnil;
@@ -1344,8 +1362,9 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1344
1362
  RB_GC_GUARD(watcher.fiber);
1345
1363
  RB_GC_GUARD(switchpoint_result);
1346
1364
  return switchpoint_result;
1365
+ #endif // #ifdef POLYPHONY_WINDOWS
1347
1366
  }
1348
- #endif
1367
+ #endif // #ifdef POLYPHONY_USE_PIDFD_OPEN
1349
1368
 
1350
1369
  void Backend_async_callback(EV_P_ ev_async *w, int revents) { }
1351
1370
 
@@ -1497,6 +1516,7 @@ done:
1497
1516
  #endif
1498
1517
  }
1499
1518
 
1519
+ #ifdef POLYPHONY_LINUX
1500
1520
  VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
1501
1521
  Backend_t *backend;
1502
1522
  GetBackend(self, backend);
@@ -1586,6 +1606,7 @@ error:
1586
1606
  if (pipefd[1] != -1) close(pipefd[1]);
1587
1607
  return RAISE_EXCEPTION(result);
1588
1608
  }
1609
+ #endif
1589
1610
 
1590
1611
  VALUE Backend_trace(int argc, VALUE *argv, VALUE self) {
1591
1612
  Backend_t *backend;
@@ -1651,7 +1672,10 @@ void Init_Backend(void) {
1651
1672
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1652
1673
  rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1653
1674
  rb_define_method(cBackend, "idle_proc=", Backend_idle_proc_set, 1);
1675
+
1676
+ #ifdef POLYPHONY_LINUX
1654
1677
  rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1678
+ #endif
1655
1679
 
1656
1680
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1657
1681
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
@@ -3,6 +3,8 @@
3
3
 
4
4
  typedef struct event {
5
5
  VALUE waiting_fiber;
6
+ int signaled;
7
+ VALUE result;
6
8
  } Event_t;
7
9
 
8
10
  VALUE cEvent = Qnil;
@@ -10,6 +12,7 @@ VALUE cEvent = Qnil;
10
12
  static void Event_mark(void *ptr) {
11
13
  Event_t *event = ptr;
12
14
  rb_gc_mark(event->waiting_fiber);
15
+ rb_gc_mark(event->result);
13
16
  }
14
17
 
15
18
  static void Event_free(void *ptr) {
@@ -41,6 +44,8 @@ static VALUE Event_initialize(VALUE self) {
41
44
  GetEvent(self, event);
42
45
 
43
46
  event->waiting_fiber = Qnil;
47
+ event->signaled = 0;
48
+ event->result = Qnil;
44
49
 
45
50
  return self;
46
51
  }
@@ -50,10 +55,17 @@ VALUE Event_signal(int argc, VALUE *argv, VALUE self) {
50
55
  Event_t *event;
51
56
  GetEvent(self, event);
52
57
 
58
+ if (event->signaled) goto done;
59
+
60
+ event->signaled = 1;
61
+ event->result = value;
62
+
53
63
  if (event->waiting_fiber != Qnil) {
54
64
  Fiber_make_runnable(event->waiting_fiber, value);
55
65
  event->waiting_fiber = Qnil;
56
66
  }
67
+
68
+ done:
57
69
  return self;
58
70
  }
59
71
 
@@ -67,10 +79,19 @@ VALUE Event_await(VALUE self) {
67
79
  if (event->waiting_fiber != Qnil)
68
80
  rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
69
81
 
82
+ if (event->signaled) {
83
+ VALUE result = event->result;
84
+ event->signaled = 0;
85
+ event->result = Qnil;
86
+ return result;
87
+ }
88
+
70
89
  backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
71
90
  event->waiting_fiber = rb_fiber_current();
72
91
  switchpoint_result = Backend_wait_event(backend, Qnil);
73
92
  event->waiting_fiber = Qnil;
93
+ event->signaled = 0;
94
+ event->result = Qnil;
74
95
 
75
96
  RAISE_IF_EXCEPTION(switchpoint_result);
76
97
  RB_GC_GUARD(backend);
@@ -2,12 +2,14 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'mkmf'
5
+ require 'rbconfig'
5
6
 
6
7
  dir_config 'polyphony_ext'
7
8
 
8
9
  KERNEL_INFO_RE = /Linux (\d)\.(\d+)(?:\.)?((?:\d+\.?)*)(?:\-)?([\w\-]+)?/
9
10
  def get_config
10
11
  config = { linux: !!(RUBY_PLATFORM =~ /linux/) }
12
+ config[:windows] = !!(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
11
13
  return config if !config[:linux]
12
14
 
13
15
  kernel_info = `uname -sr`
@@ -17,11 +19,13 @@ def get_config
17
19
  version, major_revision, distribution = m[1].to_i, m[2].to_i, m[4]
18
20
 
19
21
  combined_version = version.to_i * 100 + major_revision.to_i
20
-
22
+
23
+ config[:kernel_version] = combined_version
21
24
  config[:pidfd_open] = combined_version > 503
25
+ config[:multishot_accept] = combined_version >= 519
22
26
  config[:multishot_recv] = combined_version >= 600
23
27
  config[:multishot_recvmsg] = combined_version >= 600
24
- config[:multishot_accept] = combined_version >= 519
28
+ config[:multishot_timeout] = combined_version >= 640
25
29
  config[:submit_all_flag] = combined_version >= 518
26
30
  config[:coop_taskrun_flag] = combined_version >= 519
27
31
 
@@ -31,7 +35,7 @@ def get_config
31
35
  end
32
36
 
33
37
  config = get_config
34
- puts "Building Polyphony... (#{config.inspect})"
38
+ puts "Building Polyphony (\n#{config.map { |(k, v)| " #{k}: #{v}\n"}.join})"
35
39
 
36
40
  require_relative 'zlib_conf'
37
41
 
@@ -59,20 +63,22 @@ end
59
63
 
60
64
  $defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if config[:pidfd_open]
61
65
  if config[:io_uring]
62
- $defs << "-DPOLYPHONY_BACKEND_LIBURING"
63
- $defs << "-DPOLYPHONY_LINUX"
64
- $defs << "-DPOLYPHONY_UNSET_NONBLOCK" if RUBY_VERSION =~ /^3/
65
- $defs << "-DHAVE_IO_URING_PREP_MULTISHOT_ACCEPT" if config[:multishot_accept]
66
- $defs << "-DHAVE_IO_URING_PREP_RECV_MULTISHOT" if config[:multishot_recv]
67
- $defs << "-DHAVE_IO_URING_PREP_RECVMSG_MULTISHOT" if config[:multishot_recvmsg]
68
- $defs << "-DHAVE_IORING_SETUP_SUBMIT_ALL" if config[:submit_all_flag]
69
- $defs << "-DHAVE_IORING_SETUP_COOP_TASKRUN" if config[:coop_taskrun_flag]
70
- $CFLAGS << " -Wno-pointer-arith"
66
+ $defs << '-DPOLYPHONY_BACKEND_LIBURING'
67
+ $defs << '-DPOLYPHONY_LINUX'
68
+ $defs << '-DPOLYPHONY_UNSET_NONBLOCK' if RUBY_VERSION =~ /^3/
69
+ $defs << '-DHAVE_IO_URING_PREP_MULTISHOT_ACCEPT' if config[:multishot_accept]
70
+ $defs << '-DHAVE_IO_URING_PREP_RECV_MULTISHOT' if config[:multishot_recv]
71
+ $defs << '-DHAVE_IO_URING_PREP_RECVMSG_MULTISHOT' if config[:multishot_recvmsg]
72
+ $defs << '-DHAVE_IO_URING_TIMEOUT_MULTISHOT' if config[:multishot_timeout]
73
+ $defs << '-DHAVE_IORING_SETUP_SUBMIT_ALL' if config[:submit_all_flag]
74
+ $defs << '-DHAVE_IORING_SETUP_COOP_TASKRUN' if config[:coop_taskrun_flag]
75
+ $CFLAGS << ' -Wno-pointer-arith'
71
76
  else
72
- $defs << "-DPOLYPHONY_BACKEND_LIBEV"
73
- $defs << "-DPOLYPHONY_LINUX" if config[:linux]
77
+ $defs << '-DPOLYPHONY_BACKEND_LIBEV'
78
+ $defs << '-DPOLYPHONY_LINUX' if config[:linux]
79
+ $defs << '-DPOLYPHONY_WINDOWS' if config[:windows]
74
80
 
75
- $defs << "-DEV_STANDALONE" # prevent libev from assuming "config.h" exists
81
+ $defs << '-DEV_STANDALONE' # prevent libev from assuming "config.h" exists
76
82
 
77
83
  define_bool('EV_USE_EPOLL', have_header('sys/epoll.h'))
78
84
  define_bool('EV_USE_KQUEUE', have_header('sys/event.h') && have_header('sys/queue.h'))
@@ -84,10 +90,10 @@ else
84
90
 
85
91
  $defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
86
92
 
87
- $CFLAGS << " -Wno-comment"
88
- $CFLAGS << " -Wno-unused-result"
89
- $CFLAGS << " -Wno-dangling-else"
90
- $CFLAGS << " -Wno-parentheses"
93
+ $CFLAGS << ' -Wno-comment'
94
+ $CFLAGS << ' -Wno-unused-result'
95
+ $CFLAGS << ' -Wno-dangling-else'
96
+ $CFLAGS << ' -Wno-parentheses'
91
97
  end
92
98
 
93
99
  $defs << '-DPOLYPHONY_PLAYGROUND' if ENV['POLYPHONY_PLAYGROUND']
@@ -3,7 +3,6 @@
3
3
 
4
4
  ID ID_ivar_auto_watcher;
5
5
  ID ID_ivar_mailbox;
6
- ID ID_ivar_result;
7
6
  ID ID_ivar_waiting_fibers;
8
7
 
9
8
  VALUE SYM_dead;
@@ -239,7 +238,6 @@ void Init_Fiber(void) {
239
238
 
240
239
  ID_ivar_auto_watcher = rb_intern("@auto_watcher");
241
240
  ID_ivar_mailbox = rb_intern("@mailbox");
242
- ID_ivar_result = rb_intern("@result");
243
241
  ID_ivar_waiting_fibers = rb_intern("@waiting_fibers");
244
242
 
245
243
  SYM_spin = ID2SYM(rb_intern("spin"));
data/ext/polyphony/pipe.c CHANGED
@@ -97,8 +97,8 @@ VALUE Pipe_close(VALUE self) {
97
97
  if (pipe->w_closed)
98
98
  rb_raise(rb_eRuntimeError, "Pipe is already closed for writing");
99
99
 
100
+ Backend_close(BACKEND(), INT2FIX(pipe->fds[1]));
100
101
  pipe->w_closed = 1;
101
- close(pipe->fds[1]);
102
102
  return self;
103
103
  }
104
104
 
@@ -13,6 +13,7 @@ ID ID_ivar_blocking_mode;
13
13
  ID ID_ivar_io;
14
14
  ID ID_ivar_multishot_accept_queue;
15
15
  ID ID_ivar_parked;
16
+ ID ID_ivar_result;
16
17
  ID ID_ivar_runnable;
17
18
  ID ID_ivar_running;
18
19
  ID ID_ivar_thread;
@@ -80,20 +81,6 @@ VALUE Polyphony_backend_accept_loop(VALUE self, VALUE server_socket, VALUE socke
80
81
  return Backend_accept_loop(BACKEND(), server_socket, socket_class);
81
82
  }
82
83
 
83
- #ifdef HAVE_IO_URING_PREP_MULTISHOT_ACCEPT
84
- /* Starts a multishot accept operation on the given server socket. This API is
85
- * available only for the io_uring backend.
86
- *
87
- * @param server_socket [Socket] socket to accept on
88
- * @return [any] block return value
89
- */
90
-
91
- VALUE Polyphony_backend_multishot_accept(VALUE self, VALUE server_socket) {
92
- return Backend_multishot_accept(BACKEND(), server_socket);
93
- }
94
- #endif
95
-
96
-
97
84
  /* Connects the given socket to the given address and port.
98
85
  *
99
86
  * @param io [Socket] socket to connect
@@ -433,12 +420,6 @@ void Init_Polyphony(void) {
433
420
  rb_define_singleton_method(mPolyphony, "backend_accept_loop", Polyphony_backend_accept_loop, 2);
434
421
  rb_define_singleton_method(mPolyphony, "backend_connect", Polyphony_backend_connect, 3);
435
422
  rb_define_singleton_method(mPolyphony, "backend_feed_loop", Polyphony_backend_feed_loop, 3);
436
-
437
- #ifdef HAVE_IO_URING_PREP_MULTISHOT_ACCEPT
438
- rb_define_singleton_method(mPolyphony, "backend_multishot_accept", Polyphony_backend_multishot_accept, 1);
439
- #endif
440
-
441
-
442
423
  rb_define_singleton_method(mPolyphony, "backend_read", Polyphony_backend_read, 5);
443
424
  rb_define_singleton_method(mPolyphony, "backend_read_loop", Polyphony_backend_read_loop, 2);
444
425
  rb_define_singleton_method(mPolyphony, "backend_recv", Polyphony_backend_recv, 4);
@@ -494,6 +475,7 @@ void Init_Polyphony(void) {
494
475
  ID_ivar_io = rb_intern("@io");
495
476
  ID_ivar_multishot_accept_queue = rb_intern("@multishot_accept_queue");
496
477
  ID_ivar_parked = rb_intern("@parked");
478
+ ID_ivar_result = rb_intern("@result");
497
479
  ID_ivar_runnable = rb_intern("@runnable");
498
480
  ID_ivar_running = rb_intern("@running");
499
481
  ID_ivar_thread = rb_intern("@thread");
@@ -10,6 +10,7 @@
10
10
  #define INSPECT(str, obj) { printf(str); VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf(": %s\n", StringValueCStr(s)); }
11
11
  #define CALLER() rb_funcall(rb_mKernel, rb_intern("caller"), 0)
12
12
  #define TRACE_CALLER() INSPECT("caller: ", CALLER())
13
+ #define TRACE_FREE(ptr) //printf("Free %p %s:%d\n", ptr, __FILE__, __LINE__)
13
14
 
14
15
  // exceptions
15
16
  #define TEST_EXCEPTION(ret) (rb_obj_is_kind_of(ret, rb_eException) == Qtrue)
@@ -47,6 +48,7 @@ extern ID ID_ivar_blocking_mode;
47
48
  extern ID ID_ivar_io;
48
49
  extern ID ID_ivar_multishot_accept_queue;
49
50
  extern ID ID_ivar_parked;
51
+ extern ID ID_ivar_result;
50
52
  extern ID ID_ivar_runnable;
51
53
  extern ID ID_ivar_running;
52
54
  extern ID ID_ivar_thread;
@@ -101,11 +103,6 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class);
101
103
  VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class);
102
104
  VALUE Backend_connect(VALUE self, VALUE io, VALUE addr, VALUE port);
103
105
  VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method);
104
-
105
- #ifdef HAVE_IO_URING_PREP_MULTISHOT_ACCEPT
106
- VALUE Backend_multishot_accept(VALUE self, VALUE io);
107
- #endif
108
-
109
106
  VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos);
110
107
  VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen);
111
108
  VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos);
@@ -152,6 +149,9 @@ void Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
152
149
  void Thread_schedule_fiber_with_priority(VALUE thread, VALUE fiber, VALUE value);
153
150
  VALUE Thread_switch_fiber(VALUE thread);
154
151
 
152
+ VALUE Event_signal(int argc, VALUE *argv, VALUE event);
153
+ VALUE Event_await(VALUE event);
154
+
155
155
  VALUE Polyphony_snooze(VALUE self);
156
156
 
157
157
  #endif /* POLYPHONY_H */
@@ -10,6 +10,7 @@ void ring_buffer_init(ring_buffer *buffer) {
10
10
  }
11
11
 
12
12
  void ring_buffer_free(ring_buffer *buffer) {
13
+ TRACE_FREE(buffer->entries);
13
14
  free(buffer->entries);
14
15
  }
15
16
 
@@ -10,6 +10,7 @@ inline void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
10
10
  }
11
11
 
12
12
  inline void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer) {
13
+ TRACE_FREE(buffer->entries);
13
14
  free(buffer->entries);
14
15
  }
15
16
 
@@ -3,9 +3,12 @@
3
3
 
4
4
  ID ID_deactivate_all_watchers_post_fork;
5
5
  ID ID_ivar_backend;
6
+ ID ID_ivar_done;
6
7
  ID ID_ivar_join_wait_queue;
7
8
  ID ID_ivar_main_fiber;
9
+ ID ID_ivar_ready;
8
10
  ID ID_ivar_terminated;
11
+ ID ID_ivar_waiters;
9
12
  ID ID_stop;
10
13
 
11
14
  /* :nop-doc: */
@@ -83,20 +86,80 @@ VALUE Thread_class_backend(VALUE _self) {
83
86
  return rb_ivar_get(rb_thread_current(), ID_ivar_backend);
84
87
  }
85
88
 
89
+
90
+ VALUE Thread_done_p(VALUE self)
91
+ {
92
+ return rb_ivar_get(self, ID_ivar_done);
93
+ }
94
+
95
+ VALUE Thread_kill_safe(VALUE self)
96
+ {
97
+ static VALUE eTerminate = Qnil;
98
+ if (rb_ivar_get(self, ID_ivar_done) == Qtrue) return self;
99
+
100
+ if (eTerminate == Qnil)
101
+ eTerminate = rb_const_get(mPolyphony, rb_intern("Terminate"));
102
+
103
+ while (rb_ivar_get(self, ID_ivar_ready) != Qtrue)
104
+ rb_thread_schedule();
105
+
106
+ VALUE main_fiber = rb_ivar_get(self, ID_ivar_main_fiber);
107
+ VALUE exception = rb_funcall(eTerminate, ID_new, 0);
108
+ Thread_schedule_fiber(self, main_fiber, exception);
109
+ return self;
110
+ }
111
+
112
+ VALUE Thread_mark_as_done(VALUE self, VALUE result)
113
+ {
114
+ rb_ivar_set(self, ID_ivar_done, Qtrue);
115
+ VALUE waiters = rb_ivar_get(self, ID_ivar_waiters);
116
+ if (waiters == Qnil) return self;
117
+
118
+ int len = RARRAY_LEN(waiters);
119
+ for (int i = 0; i < len; i++) {
120
+ VALUE waiter = RARRAY_AREF(waiters, i);
121
+ Event_signal(1, &result, waiter);
122
+ }
123
+ return self;
124
+ }
125
+
126
+ VALUE Thread_await_done(VALUE self)
127
+ {
128
+ if (Thread_done_p(self) == Qtrue) return rb_ivar_get(self, ID_ivar_result);
129
+
130
+ VALUE waiter = Fiber_auto_watcher(rb_fiber_current());
131
+ VALUE waiters = rb_ivar_get(self, ID_ivar_waiters);
132
+ if (waiters == Qnil) {
133
+ waiters = rb_ary_new();
134
+ rb_ivar_set(self, ID_ivar_waiters, waiters);
135
+ }
136
+ rb_ary_push(waiters, waiter);
137
+
138
+ return Event_await(waiter);
139
+ }
140
+
86
141
  void Init_Thread(void) {
87
142
  rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
88
143
  rb_define_method(rb_cThread, "schedule_and_wakeup", Thread_fiber_schedule_and_wakeup, 2);
89
144
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
90
145
  rb_define_method(rb_cThread, "fiber_unschedule", Thread_fiber_unschedule, 1);
91
146
 
147
+ rb_define_method(rb_cThread, "done?", Thread_done_p, 0);
148
+ rb_define_method(rb_cThread, "kill_safe", Thread_kill_safe, 0);
149
+ rb_define_method(rb_cThread, "mark_as_done", Thread_mark_as_done, 1);
150
+ rb_define_method(rb_cThread, "await_done", Thread_await_done, 0);
151
+
92
152
  rb_define_singleton_method(rb_cThread, "backend", Thread_class_backend, 0);
93
153
 
94
154
  rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
95
155
 
96
156
  ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
97
157
  ID_ivar_backend = rb_intern("@backend");
158
+ ID_ivar_done = rb_intern("@done");
98
159
  ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
99
160
  ID_ivar_main_fiber = rb_intern("@main_fiber");
161
+ ID_ivar_ready = rb_intern("@ready");
100
162
  ID_ivar_terminated = rb_intern("@terminated");
163
+ ID_ivar_waiters = rb_intern("@waiters");
101
164
  ID_stop = rb_intern("stop");
102
165
  }
@@ -0,0 +1,18 @@
1
+ // source: https://stackoverflow.com/questions/57897314/fatal-error-when-compiling-include-sys-uio-h-on-project-windows
2
+
3
+ #ifndef SYS_UIO_H
4
+ #define SYS_UIO_H
5
+
6
+ #include <inttypes.h>
7
+ #include <unistd.h>
8
+
9
+ struct iovec
10
+ {
11
+ void *iov_base; /* Base address of a memory region for input or output */
12
+ size_t iov_len; /* The size of the memory pointed to by iov_base */
13
+ };
14
+
15
+ ssize_t readv(int fildes, const struct iovec *iov, int iovcnt);
16
+ ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);
17
+
18
+ #endif /* SYS_UIO_H */