polyphony 0.52.0 → 0.55.0

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.
@@ -1,4 +1,5 @@
1
1
  #include <stdlib.h>
2
+ #include <assert.h>
2
3
  #include "ruby.h"
3
4
  #include "polyphony.h"
4
5
  #include "backend_io_uring_context.h"
@@ -10,10 +11,12 @@ const char *op_type_to_str(enum op_type type) {
10
11
  case OP_WRITE: return "WRITE";
11
12
  case OP_RECV: return "RECV";
12
13
  case OP_SEND: return "SEND";
14
+ case OP_SPLICE: return "SPLICE";
13
15
  case OP_TIMEOUT: return "TIMEOUT";
14
16
  case OP_POLL: return "POLL";
15
17
  case OP_ACCEPT: return "ACCEPT";
16
18
  case OP_CONNECT: return "CONNECT";
19
+ case OP_CHAIN: return "CHAIN";
17
20
  default: return "";
18
21
  };
19
22
  }
@@ -34,6 +37,7 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
34
37
  ctx = malloc(sizeof(op_context_t));
35
38
  }
36
39
  ctx->id = (++store->last_id);
40
+ // printf("acquire %d (%s)\n", ctx->id, op_type_to_str(type));
37
41
 
38
42
  ctx->prev = NULL;
39
43
  ctx->next = store->taken;
@@ -43,13 +47,21 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
43
47
  ctx->type = type;
44
48
  ctx->fiber = rb_fiber_current();
45
49
  ctx->resume_value = Qnil;
46
- ctx->completed = 0;
50
+ ctx->ref_count = 2;
47
51
  ctx->result = 0;
48
52
 
49
53
  return ctx;
50
54
  }
51
55
 
52
- inline void context_store_release(op_context_store_t *store, op_context_t *ctx) {
56
+ // returns true if ctx was released
57
+ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
58
+ // printf("release %d (%s, ref_count: %d)\n", ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
59
+
60
+ assert(ctx->ref_count);
61
+
62
+ ctx->ref_count--;
63
+ if (ctx->ref_count) return 0;
64
+
53
65
  if (ctx->next) ctx->next->prev = ctx->prev;
54
66
  if (ctx->prev) ctx->prev->next = ctx->next;
55
67
  if (store->taken == ctx) store->taken = ctx->next;
@@ -58,6 +70,7 @@ inline void context_store_release(op_context_store_t *store, op_context_t *ctx)
58
70
  ctx->next = store->available;
59
71
  if (ctx->next) ctx->next->prev = ctx;
60
72
  store->available = ctx;
73
+ return 1;
61
74
  }
62
75
 
63
76
  void context_store_free(op_context_store_t *store) {
@@ -10,17 +10,19 @@ enum op_type {
10
10
  OP_WRITE,
11
11
  OP_RECV,
12
12
  OP_SEND,
13
+ OP_SPLICE,
13
14
  OP_TIMEOUT,
14
15
  OP_POLL,
15
16
  OP_ACCEPT,
16
- OP_CONNECT
17
+ OP_CONNECT,
18
+ OP_CHAIN
17
19
  };
18
20
 
19
21
  typedef struct op_context {
20
22
  struct op_context *prev;
21
23
  struct op_context *next;
22
24
  enum op_type type: 16;
23
- int completed : 16;
25
+ unsigned int ref_count : 16;
24
26
  int id;
25
27
  int result;
26
28
  VALUE fiber;
@@ -37,17 +39,16 @@ const char *op_type_to_str(enum op_type type);
37
39
 
38
40
  void context_store_initialize(op_context_store_t *store);
39
41
  op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type);
40
- void context_store_release(op_context_store_t *store, op_context_t *ctx);
42
+ int context_store_release(op_context_store_t *store, op_context_t *ctx);
41
43
  void context_store_free(op_context_store_t *store);
42
44
 
43
- #define OP_CONTEXT_ACQUIRE(store, op_type) context_store_acquire(store, op_type)
44
- #define OP_CONTEXT_RELEASE(store, ctx) { \
45
- if (ctx->completed) {\
46
- context_store_release(store, ctx); \
47
- } \
48
- else { \
49
- ctx->completed = 1; \
50
- } \
45
+ inline unsigned int OP_CONTEXT_RELEASE(op_context_store_t *store, op_context_t *ctx) {
46
+ int completed = !ctx->ref_count;
47
+ if (ctx->ref_count)
48
+ ctx->ref_count -= 1;
49
+ else
50
+ context_store_release(store, ctx);
51
+ return completed;
51
52
  }
52
53
 
53
54
  #endif /* BACKEND_IO_URING_CONTEXT_H */
@@ -38,11 +38,15 @@ thread.
38
38
 
39
39
  #ifdef POLYPHONY_BACKEND_LIBEV
40
40
 
41
+ #ifdef POLYPHONY_LINUX
42
+ #define _GNU_SOURCE 1
43
+ #endif
44
+
45
+ #include <fcntl.h>
41
46
  #include <netdb.h>
42
47
  #include <sys/socket.h>
43
48
  #include <sys/uio.h>
44
49
  #include <unistd.h>
45
- #include <fcntl.h>
46
50
  #include <netinet/in.h>
47
51
  #include <arpa/inet.h>
48
52
  #include <stdnoreturn.h>
@@ -54,6 +58,9 @@ thread.
54
58
  #include "ruby/io.h"
55
59
 
56
60
  VALUE SYM_libev;
61
+ VALUE SYM_send;
62
+ VALUE SYM_splice;
63
+ VALUE SYM_write;
57
64
 
58
65
  ID ID_ivar_is_nonblocking;
59
66
 
@@ -83,7 +90,6 @@ typedef struct Backend_t {
83
90
  // common fields
84
91
  unsigned int currently_polling;
85
92
  unsigned int pending_count;
86
- unsigned int poll_no_wait_count;
87
93
 
88
94
  // implementation-specific fields
89
95
  struct ev_loop *ev_loop;
@@ -138,7 +144,6 @@ static VALUE Backend_initialize(VALUE self) {
138
144
 
139
145
  backend->currently_polling = 0;
140
146
  backend->pending_count = 0;
141
- backend->poll_no_wait_count = 0;
142
147
 
143
148
  return Qnil;
144
149
  }
@@ -177,23 +182,12 @@ unsigned int Backend_pending_count(VALUE self) {
177
182
  }
178
183
 
179
184
  VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
180
- int is_nowait = nowait == Qtrue;
181
185
  Backend_t *backend;
182
186
  GetBackend(self, backend);
183
187
 
184
- if (is_nowait) {
185
- backend->poll_no_wait_count++;
186
- if (backend->poll_no_wait_count < 10) return self;
187
-
188
- long runnable_count = Runqueue_len(runqueue);
189
- if (backend->poll_no_wait_count < runnable_count) return self;
190
- }
191
-
192
- backend->poll_no_wait_count = 0;
193
-
194
188
  COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
195
189
  backend->currently_polling = 1;
196
- ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
190
+ ev_run(backend->ev_loop, nowait == Qtrue ? EVRUN_NOWAIT : EVRUN_ONCE);
197
191
  backend->currently_polling = 0;
198
192
  COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
199
193
 
@@ -769,6 +763,118 @@ error:
769
763
  return RAISE_EXCEPTION(switchpoint_result);
770
764
  }
771
765
 
766
+ #ifdef POLYPHONY_LINUX
767
+ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
768
+ Backend_t *backend;
769
+ struct libev_io watcher;
770
+ VALUE switchpoint_result = Qnil;
771
+ VALUE underlying_io;
772
+ rb_io_t *src_fptr;
773
+ rb_io_t *dest_fptr;
774
+ int len;
775
+
776
+ GetBackend(self, backend);
777
+
778
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
779
+ if (underlying_io != Qnil) src = underlying_io;
780
+ GetOpenFile(src, src_fptr);
781
+ io_set_nonblock(src_fptr, src);
782
+
783
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
784
+ if (underlying_io != Qnil) dest = underlying_io;
785
+ dest = rb_io_get_write_io(dest);
786
+ GetOpenFile(dest, dest_fptr);
787
+ io_set_nonblock(dest_fptr, dest);
788
+
789
+ watcher.fiber = Qnil;
790
+ while (1) {
791
+ len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
792
+ if (len < 0) {
793
+ int e = errno;
794
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
795
+
796
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
797
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
798
+
799
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
800
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
801
+ }
802
+ else {
803
+ break;
804
+ }
805
+ }
806
+
807
+ if (watcher.fiber == Qnil) {
808
+ switchpoint_result = backend_snooze();
809
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
810
+ }
811
+
812
+ RB_GC_GUARD(watcher.fiber);
813
+ RB_GC_GUARD(switchpoint_result);
814
+
815
+ return INT2NUM(len);
816
+ error:
817
+ return RAISE_EXCEPTION(switchpoint_result);
818
+ }
819
+
820
+ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
821
+ Backend_t *backend;
822
+ struct libev_io watcher;
823
+ VALUE switchpoint_result = Qnil;
824
+ VALUE underlying_io;
825
+ rb_io_t *src_fptr;
826
+ rb_io_t *dest_fptr;
827
+ int len;
828
+ int total = 0;
829
+
830
+ GetBackend(self, backend);
831
+
832
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
833
+ if (underlying_io != Qnil) src = underlying_io;
834
+ GetOpenFile(src, src_fptr);
835
+ io_set_nonblock(src_fptr, src);
836
+
837
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
838
+ if (underlying_io != Qnil) dest = underlying_io;
839
+ dest = rb_io_get_write_io(dest);
840
+ GetOpenFile(dest, dest_fptr);
841
+ io_set_nonblock(dest_fptr, dest);
842
+
843
+ watcher.fiber = Qnil;
844
+ while (1) {
845
+ len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
846
+ if (len < 0) {
847
+ int e = errno;
848
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
849
+
850
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
851
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
852
+
853
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
854
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
855
+ }
856
+ else if (len == 0) {
857
+ break;
858
+ }
859
+ else {
860
+ total += len;
861
+ }
862
+ }
863
+
864
+ if (watcher.fiber == Qnil) {
865
+ switchpoint_result = backend_snooze();
866
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
867
+ }
868
+
869
+ RB_GC_GUARD(watcher.fiber);
870
+ RB_GC_GUARD(switchpoint_result);
871
+
872
+ return INT2NUM(total);
873
+ error:
874
+ return RAISE_EXCEPTION(switchpoint_result);
875
+ }
876
+ #endif
877
+
772
878
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
773
879
  Backend_t *backend;
774
880
  rb_io_t *fptr;
@@ -981,6 +1087,31 @@ VALUE Backend_kind(VALUE self) {
981
1087
  return SYM_libev;
982
1088
  }
983
1089
 
1090
+ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1091
+ VALUE result = Qnil;
1092
+ if (argc == 0) return result;
1093
+
1094
+ for (int i = 0; i < argc; i++) {
1095
+ VALUE op = argv[i];
1096
+ VALUE op_type = RARRAY_AREF(op, 0);
1097
+ VALUE op_len = RARRAY_LEN(op);
1098
+
1099
+ if (op_type == SYM_write && op_len == 3)
1100
+ result = Backend_write(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
1101
+ else if (op_type == SYM_send && op_len == 4)
1102
+ result = Backend_send(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1103
+ #ifdef POLYPHONY_LINUX
1104
+ else if (op_type == SYM_splice && op_len == 4)
1105
+ result = Backend_splice(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1106
+ #endif
1107
+ else
1108
+ rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1109
+ }
1110
+
1111
+ RB_GC_GUARD(result);
1112
+ return result;
1113
+ }
1114
+
984
1115
  void Init_Backend() {
985
1116
  ev_set_allocator(xrealloc);
986
1117
 
@@ -993,30 +1124,40 @@ void Init_Backend() {
993
1124
 
994
1125
  rb_define_method(cBackend, "poll", Backend_poll, 3);
995
1126
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1127
+ rb_define_method(cBackend, "kind", Backend_kind, 0);
1128
+ rb_define_method(cBackend, "chain", Backend_chain, -1);
996
1129
 
997
- rb_define_method(cBackend, "read", Backend_read, 4);
998
- rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
999
- rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
1000
- rb_define_method(cBackend, "write", Backend_write_m, -1);
1001
1130
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1002
1131
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
1003
1132
  rb_define_method(cBackend, "connect", Backend_connect, 3);
1133
+ rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
1134
+ rb_define_method(cBackend, "read", Backend_read, 4);
1135
+ rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
1004
1136
  rb_define_method(cBackend, "recv", Backend_recv, 3);
1005
1137
  rb_define_method(cBackend, "recv_loop", Backend_read_loop, 1);
1006
1138
  rb_define_method(cBackend, "recv_feed_loop", Backend_feed_loop, 3);
1007
1139
  rb_define_method(cBackend, "send", Backend_send, 3);
1008
1140
  rb_define_method(cBackend, "sendv", Backend_sendv, 3);
1009
- rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
1010
1141
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
1011
- rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1142
+
1143
+ #ifdef POLYPHONY_LINUX
1144
+ rb_define_method(cBackend, "splice", Backend_splice, 3);
1145
+ rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
1146
+ #endif
1147
+
1012
1148
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
1013
- rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1149
+ rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1014
1150
  rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
1015
-
1016
- rb_define_method(cBackend, "kind", Backend_kind, 0);
1151
+ rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
1152
+ rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1153
+ rb_define_method(cBackend, "write", Backend_write_m, -1);
1017
1154
 
1018
1155
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
1019
1156
  SYM_libev = ID2SYM(rb_intern("libev"));
1157
+
1158
+ SYM_send = ID2SYM(rb_intern("send"));
1159
+ SYM_splice = ID2SYM(rb_intern("splice"));
1160
+ SYM_write = ID2SYM(rb_intern("write"));
1020
1161
  }
1021
1162
 
1022
1163
  #endif // POLYPHONY_BACKEND_LIBEV
@@ -6,8 +6,9 @@ require 'mkmf'
6
6
  use_liburing = false
7
7
  use_pidfd_open = false
8
8
  force_use_libev = ENV['POLYPHONY_USE_LIBEV'] != nil
9
+ linux = RUBY_PLATFORM =~ /linux/
9
10
 
10
- if RUBY_PLATFORM =~ /linux/ && `uname -sr` =~ /Linux 5\.([\d+])/
11
+ if linux && `uname -sr` =~ /Linux 5\.([\d+])/
11
12
  kernel_minor_version = $1.gsub('.', '').to_i
12
13
  use_liburing = !force_use_libev && kernel_minor_version >= 6
13
14
  use_pidfd_open = kernel_minor_version >= 3
@@ -20,6 +21,7 @@ if use_liburing
20
21
  $CFLAGS << " -Wno-pointer-arith"
21
22
  else
22
23
  $defs << "-DPOLYPHONY_BACKEND_LIBEV"
24
+ $defs << "-DPOLYPHONY_LINUX" if linux
23
25
  $defs << '-DEV_USE_LINUXAIO' if have_header('linux/aio_abi.h')
24
26
  $defs << '-DEV_USE_SELECT' if have_header('sys/select.h')
25
27
  $defs << '-DEV_USE_POLL' if have_type('port_event_t', 'poll.h')
@@ -46,8 +46,6 @@ VALUE Polyphony_trace(VALUE self, VALUE enabled) {
46
46
  return Qnil;
47
47
  }
48
48
 
49
- #define BACKEND() (rb_ivar_get(rb_thread_current(), ID_ivar_backend))
50
-
51
49
  VALUE Polyphony_backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
52
50
  return Backend_accept(BACKEND(), server_socket, socket_class);
53
51
  }
@@ -96,6 +94,14 @@ VALUE Polyphony_backend_sleep(VALUE self, VALUE duration) {
96
94
  return Backend_sleep(BACKEND(), duration);
97
95
  }
98
96
 
97
+ VALUE Polyphony_backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
98
+ return Backend_splice(BACKEND(), src, dest, maxlen);
99
+ }
100
+
101
+ VALUE Polyphony_backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
102
+ return Backend_splice_to_eof(BACKEND(), src, dest, chunksize);
103
+ }
104
+
99
105
  VALUE Polyphony_backend_timeout(int argc,VALUE *argv, VALUE self) {
100
106
  return Backend_timeout(argc, argv, BACKEND());
101
107
  }
@@ -138,6 +144,8 @@ void Init_Polyphony() {
138
144
  rb_define_singleton_method(mPolyphony, "backend_send", Polyphony_backend_send, 3);
139
145
  rb_define_singleton_method(mPolyphony, "backend_sendv", Polyphony_backend_sendv, 3);
140
146
  rb_define_singleton_method(mPolyphony, "backend_sleep", Polyphony_backend_sleep, 1);
147
+ rb_define_singleton_method(mPolyphony, "backend_splice", Polyphony_backend_splice, 3);
148
+ rb_define_singleton_method(mPolyphony, "backend_splice_to_eof", Polyphony_backend_splice_to_eof, 3);
141
149
  rb_define_singleton_method(mPolyphony, "backend_timeout", Polyphony_backend_timeout, -1);
142
150
  rb_define_singleton_method(mPolyphony, "backend_timer_loop", Polyphony_backend_timer_loop, 1);
143
151
  rb_define_singleton_method(mPolyphony, "backend_wait_event", Polyphony_backend_wait_event, 1);
@@ -31,6 +31,8 @@
31
31
  // Fiber#transfer
32
32
  #define FIBER_TRANSFER(fiber, value) rb_funcall(fiber, ID_transfer, 1, value)
33
33
 
34
+ #define BACKEND() (rb_ivar_get(rb_thread_current(), ID_ivar_backend))
35
+
34
36
  extern VALUE mPolyphony;
35
37
  extern VALUE cQueue;
36
38
  extern VALUE cEvent;
@@ -88,6 +90,7 @@ int Runqueue_index_of(VALUE self, VALUE fiber);
88
90
  void Runqueue_clear(VALUE self);
89
91
  long Runqueue_len(VALUE self);
90
92
  int Runqueue_empty_p(VALUE self);
93
+ int Runqueue_should_poll_nonblocking(VALUE self);
91
94
 
92
95
  #ifdef POLYPHONY_BACKEND_LIBEV
93
96
  #define Backend_recv_loop Backend_read_loop
@@ -108,6 +111,8 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
108
111
  VALUE Backend_send(VALUE self, VALUE io, VALUE msg, VALUE flags);
109
112
  VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
110
113
  VALUE Backend_sleep(VALUE self, VALUE duration);
114
+ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
115
+ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize);
111
116
  VALUE Backend_timeout(int argc,VALUE *argv, VALUE self);
112
117
  VALUE Backend_timer_loop(VALUE self, VALUE interval);
113
118
  VALUE Backend_wait_event(VALUE self, VALUE raise);