polyphony 0.55.0 → 0.59.1

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.
@@ -4,7 +4,6 @@
4
4
  #include <sys/socket.h>
5
5
  #include <sys/uio.h>
6
6
  #include <unistd.h>
7
- #include <fcntl.h>
8
7
  #include <netinet/in.h>
9
8
  #include <arpa/inet.h>
10
9
  #include <stdnoreturn.h>
@@ -19,6 +18,7 @@
19
18
  #include "backend_io_uring_context.h"
20
19
  #include "ruby/thread.h"
21
20
  #include "ruby/io.h"
21
+ #include "backend_common.h"
22
22
 
23
23
  VALUE SYM_io_uring;
24
24
  VALUE SYM_send;
@@ -26,34 +26,13 @@ VALUE SYM_splice;
26
26
  VALUE SYM_write;
27
27
 
28
28
  #ifdef POLYPHONY_UNSET_NONBLOCK
29
- ID ID_ivar_is_nonblocking;
30
-
31
- // One of the changes introduced in Ruby 3.0 as part of the work on the
32
- // FiberScheduler interface is that all created sockets are marked as
33
- // non-blocking. This prevents the io_uring backend from working correctly,
34
- // since it will return an EAGAIN error just like a normal syscall. So here
35
- // instead of setting O_NONBLOCK (which is required for the libev backend), we
36
- // unset it.
37
- inline void io_unset_nonblock(rb_io_t *fptr, VALUE io) {
38
- VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
39
- if (is_nonblocking == Qfalse) return;
40
-
41
- rb_ivar_set(io, ID_ivar_is_nonblocking, Qfalse);
42
-
43
- int oflags = fcntl(fptr->fd, F_GETFL);
44
- if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
45
- oflags &= !O_NONBLOCK;
46
- fcntl(fptr->fd, F_SETFL, oflags);
47
- }
29
+ #define io_unset_nonblock(fptr, io) io_verify_blocking_mode(fptr, io, Qtrue)
48
30
  #else
49
- // NOP
50
31
  #define io_unset_nonblock(fptr, io)
51
32
  #endif
52
33
 
53
34
  typedef struct Backend_t {
54
- // common fields
55
- unsigned int currently_polling;
56
- unsigned int pending_count;
35
+ struct Backend_base base;
57
36
 
58
37
  // implementation-specific fields
59
38
  struct io_uring ring;
@@ -63,7 +42,15 @@ typedef struct Backend_t {
63
42
  int event_fd;
64
43
  } Backend_t;
65
44
 
66
- #include "backend_common.h"
45
+ static void Backend_mark(void *ptr) {
46
+ Backend_t *backend = ptr;
47
+ backend_base_mark(&backend->base);
48
+ }
49
+
50
+ static void Backend_free(void *ptr) {
51
+ Backend_t *backend = ptr;
52
+ backend_base_finalize(&backend->base);
53
+ }
67
54
 
68
55
  static size_t Backend_size(const void *ptr) {
69
56
  return sizeof(Backend_t);
@@ -71,7 +58,7 @@ static size_t Backend_size(const void *ptr) {
71
58
 
72
59
  static const rb_data_type_t Backend_type = {
73
60
  "IOUringBackend",
74
- {0, 0, Backend_size,},
61
+ {Backend_mark, Backend_free, Backend_size,},
75
62
  0, 0, RUBY_TYPED_FREE_IMMEDIATELY
76
63
  };
77
64
 
@@ -88,8 +75,7 @@ static VALUE Backend_initialize(VALUE self) {
88
75
  Backend_t *backend;
89
76
  GetBackend(self, backend);
90
77
 
91
- backend->currently_polling = 0;
92
- backend->pending_count = 0;
78
+ backend_base_initialize(&backend->base);
93
79
  backend->pending_sqes = 0;
94
80
  backend->prepared_limit = 2048;
95
81
 
@@ -117,20 +103,13 @@ VALUE Backend_post_fork(VALUE self) {
117
103
  io_uring_queue_exit(&backend->ring);
118
104
  io_uring_queue_init(backend->prepared_limit, &backend->ring, 0);
119
105
  context_store_free(&backend->store);
120
- backend->currently_polling = 0;
121
- backend->pending_count = 0;
106
+ backend->base.currently_polling = 0;
107
+ backend->base.pending_count = 0;
122
108
  backend->pending_sqes = 0;
123
109
 
124
110
  return self;
125
111
  }
126
112
 
127
- unsigned int Backend_pending_count(VALUE self) {
128
- Backend_t *backend;
129
- GetBackend(self, backend);
130
-
131
- return backend->pending_count;
132
- }
133
-
134
113
  typedef struct poll_context {
135
114
  struct io_uring *ring;
136
115
  struct io_uring_cqe *cqe;
@@ -154,6 +133,7 @@ static inline void io_uring_backend_handle_completion(struct io_uring_cqe *cqe,
154
133
  op_context_t *ctx = io_uring_cqe_get_data(cqe);
155
134
  if (!ctx) return;
156
135
 
136
+ // printf("cqe ctx %p id: %d result: %d (%s, ref_count: %d)\n", ctx, ctx->id, cqe->res, op_type_to_str(ctx->type), ctx->ref_count);
157
137
  ctx->result = cqe->res;
158
138
  if (ctx->ref_count == 2 && ctx->result != -ECANCELED && ctx->fiber)
159
139
  Fiber_make_runnable(ctx->fiber, ctx->resume_value);
@@ -197,38 +177,70 @@ void io_uring_backend_poll(Backend_t *backend) {
197
177
  io_uring_submit(&backend->ring);
198
178
  }
199
179
 
200
- backend->currently_polling = 1;
180
+ backend->base.currently_polling = 1;
201
181
  rb_thread_call_without_gvl(io_uring_backend_poll_without_gvl, (void *)&poll_ctx, RUBY_UBF_IO, 0);
202
- backend->currently_polling = 0;
182
+ backend->base.currently_polling = 0;
203
183
  if (poll_ctx.result < 0) return;
204
184
 
205
185
  io_uring_backend_handle_completion(poll_ctx.cqe, backend);
206
186
  io_uring_cqe_seen(&backend->ring, poll_ctx.cqe);
207
187
  }
208
188
 
209
- VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
210
- int is_nowait = nowait == Qtrue;
189
+ inline VALUE Backend_poll(VALUE self, VALUE blocking) {
190
+ int is_blocking = blocking == Qtrue;
211
191
  Backend_t *backend;
212
192
  GetBackend(self, backend);
213
193
 
214
- if (is_nowait && backend->pending_sqes) {
194
+ if (!is_blocking && backend->pending_sqes) {
215
195
  backend->pending_sqes = 0;
216
196
  io_uring_submit(&backend->ring);
217
197
  }
218
198
 
219
- COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
220
- if (!is_nowait) io_uring_backend_poll(backend);
199
+ COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
200
+ if (is_blocking) io_uring_backend_poll(backend);
221
201
  io_uring_backend_handle_ready_cqes(backend);
222
- COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
202
+ COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
223
203
 
224
204
  return self;
225
205
  }
226
206
 
207
+ inline void Backend_schedule_fiber(VALUE thread, VALUE self, VALUE fiber, VALUE value, int prioritize) {
208
+ Backend_t *backend;
209
+ GetBackend(self, backend);
210
+
211
+ backend_base_schedule_fiber(thread, self, &backend->base, fiber, value, prioritize);
212
+ }
213
+
214
+ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
215
+ Backend_t *backend;
216
+ GetBackend(self, backend);
217
+
218
+ runqueue_delete(&backend->base.runqueue, fiber);
219
+ }
220
+
221
+ inline VALUE Backend_switch_fiber(VALUE self) {
222
+ Backend_t *backend;
223
+ GetBackend(self, backend);
224
+
225
+ return backend_base_switch_fiber(self, &backend->base);
226
+ }
227
+
228
+ inline struct backend_stats Backend_stats(VALUE self) {
229
+ Backend_t *backend;
230
+ GetBackend(self, backend);
231
+
232
+ return (struct backend_stats){
233
+ .scheduled_fibers = runqueue_len(&backend->base.runqueue),
234
+ .waiting_fibers = 0,
235
+ .pending_ops = backend->base.pending_count
236
+ };
237
+ }
238
+
227
239
  VALUE Backend_wakeup(VALUE self) {
228
240
  Backend_t *backend;
229
241
  GetBackend(self, backend);
230
242
 
231
- if (backend->currently_polling) {
243
+ if (backend->base.currently_polling) {
232
244
  // Since we're currently blocking while waiting for a completion, we add a
233
245
  // NOP which would cause the io_uring_enter syscall to return
234
246
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
@@ -259,11 +271,13 @@ int io_uring_backend_defer_submit_and_await(
259
271
  {
260
272
  VALUE switchpoint_result = Qnil;
261
273
 
262
- io_uring_sqe_set_data(sqe, ctx);
263
- io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
274
+ if (sqe) {
275
+ io_uring_sqe_set_data(sqe, ctx);
276
+ io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
277
+ }
264
278
  io_uring_backend_defer_submit(backend);
265
279
 
266
- switchpoint_result = backend_await(backend);
280
+ switchpoint_result = backend_await((struct Backend_base *)backend);
267
281
 
268
282
  if (ctx->ref_count > 1) {
269
283
  // op was not completed (an exception was raised), so we need to cancel it
@@ -466,7 +480,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
466
480
  VALUE resume_value = Qnil;
467
481
  op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
468
482
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
469
- io_uring_prep_write(sqe, fptr->fd, buf, left, -1);
483
+ io_uring_prep_write(sqe, fptr->fd, buf, left, 0);
470
484
 
471
485
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
472
486
  int completed = context_store_release(&backend->store, ctx);
@@ -926,7 +940,6 @@ int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duratio
926
940
 
927
941
  op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
928
942
  io_uring_prep_timeout(sqe, &ts, 0, 0);
929
-
930
943
  io_uring_backend_defer_submit_and_await(backend, sqe, ctx, resume_value);
931
944
  return context_store_release(&backend->store, ctx);
932
945
  }
@@ -1085,7 +1098,7 @@ struct io_uring_sqe *Backend_chain_prepare_write(Backend_t *backend, VALUE io, V
1085
1098
  long len = RSTRING_LEN(str);
1086
1099
 
1087
1100
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1088
- io_uring_prep_write(sqe, fptr->fd, buf, len, -1);
1101
+ io_uring_prep_write(sqe, fptr->fd, buf, len, 0);
1089
1102
  return sqe;
1090
1103
  }
1091
1104
 
@@ -1177,7 +1190,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1177
1190
 
1178
1191
  ctx->ref_count = sqe_count + 1;
1179
1192
  io_uring_backend_defer_submit(backend);
1180
- resume_value = backend_await(backend);
1193
+ resume_value = backend_await((struct Backend_base *)backend);
1181
1194
  int result = ctx->result;
1182
1195
  int completed = context_store_release(&backend->store, ctx);
1183
1196
  if (!completed) {
@@ -1195,6 +1208,197 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1195
1208
  return INT2NUM(result);
1196
1209
  }
1197
1210
 
1211
+ VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
1212
+ Backend_t *backend;
1213
+ GetBackend(self, backend);
1214
+ backend->base.idle_gc_period = NUM2DBL(period);
1215
+ backend->base.idle_gc_last_time = current_time();
1216
+ return self;
1217
+ }
1218
+
1219
+ VALUE Backend_idle_proc_set(VALUE self, VALUE block) {
1220
+ Backend_t *backend;
1221
+ GetBackend(self, backend);
1222
+ backend->base.idle_proc = block;
1223
+ return self;
1224
+ }
1225
+
1226
+ inline VALUE Backend_run_idle_tasks(VALUE self) {
1227
+ Backend_t *backend;
1228
+ GetBackend(self, backend);
1229
+ backend_run_idle_tasks(&backend->base);
1230
+ return self;
1231
+ }
1232
+
1233
+ static inline void splice_chunks_prep_write(op_context_t *ctx, struct io_uring_sqe *sqe, int fd, VALUE str) {
1234
+ char *buf = RSTRING_PTR(str);
1235
+ int len = RSTRING_LEN(str);
1236
+ io_uring_prep_write(sqe, fd, buf, len, 0);
1237
+ // io_uring_prep_send(sqe, fd, buf, len, 0);
1238
+ io_uring_sqe_set_data(sqe, ctx);
1239
+ }
1240
+
1241
+ static inline void splice_chunks_prep_splice(op_context_t *ctx, struct io_uring_sqe *sqe, int src, int dest, int maxlen) {
1242
+ io_uring_prep_splice(sqe, src, -1, dest, -1, maxlen, 0);
1243
+ io_uring_sqe_set_data(sqe, ctx);
1244
+ }
1245
+
1246
+ static inline void splice_chunks_get_sqe(
1247
+ Backend_t *backend,
1248
+ op_context_t **ctx,
1249
+ struct io_uring_sqe **sqe,
1250
+ enum op_type type
1251
+ )
1252
+ {
1253
+ if (*ctx) {
1254
+ if (*sqe) (*sqe)->flags |= IOSQE_IO_LINK;
1255
+ (*ctx)->ref_count++;
1256
+ }
1257
+ else
1258
+ *ctx = context_store_acquire(&backend->store, type);
1259
+ (*sqe) = io_uring_get_sqe(&backend->ring);
1260
+ }
1261
+
1262
+ static inline void splice_chunks_cancel(Backend_t *backend, op_context_t *ctx) {
1263
+ ctx->result = -ECANCELED;
1264
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1265
+ io_uring_prep_cancel(sqe, ctx, 0);
1266
+ backend->pending_sqes = 0;
1267
+ io_uring_submit(&backend->ring);
1268
+ }
1269
+
1270
+ static inline int splice_chunks_await_ops(
1271
+ Backend_t *backend,
1272
+ op_context_t **ctx,
1273
+ int *result,
1274
+ VALUE *switchpoint_result
1275
+ )
1276
+ {
1277
+ int res = io_uring_backend_defer_submit_and_await(backend, 0, *ctx, switchpoint_result);
1278
+ if (result) (*result) = res;
1279
+ int completed = context_store_release(&backend->store, *ctx);
1280
+ if (!completed) {
1281
+ splice_chunks_cancel(backend, *ctx);
1282
+ if (TEST_EXCEPTION(*switchpoint_result)) return 1;
1283
+ }
1284
+ *ctx = 0;
1285
+ return 0;
1286
+ }
1287
+
1288
+ #define SPLICE_CHUNKS_AWAIT_OPS(backend, ctx, result, switchpoint_result) \
1289
+ if (splice_chunks_await_ops(backend, ctx, result, switchpoint_result)) goto error;
1290
+
1291
+ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
1292
+ Backend_t *backend;
1293
+ GetBackend(self, backend);
1294
+ int total = 0;
1295
+ int err = 0;
1296
+ VALUE switchpoint_result = Qnil;
1297
+ op_context_t *ctx = 0;
1298
+ struct io_uring_sqe *sqe = 0;
1299
+
1300
+ rb_io_t *src_fptr;
1301
+ rb_io_t *dest_fptr;
1302
+
1303
+ VALUE underlying_io = rb_ivar_get(src, ID_ivar_io);
1304
+ if (underlying_io != Qnil) src = underlying_io;
1305
+ GetOpenFile(src, src_fptr);
1306
+ io_verify_blocking_mode(src_fptr, src, Qtrue);
1307
+
1308
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
1309
+ if (underlying_io != Qnil) dest = underlying_io;
1310
+ dest = rb_io_get_write_io(dest);
1311
+ GetOpenFile(dest, dest_fptr);
1312
+ io_verify_blocking_mode(dest_fptr, dest, Qtrue);
1313
+
1314
+ int maxlen = NUM2INT(chunk_size);
1315
+ VALUE str = Qnil;
1316
+ VALUE chunk_len_value = Qnil;
1317
+
1318
+ int pipefd[2] = { -1, -1 };
1319
+ if (pipe(pipefd) == -1) {
1320
+ err = errno;
1321
+ goto syscallerror;
1322
+ }
1323
+
1324
+ if (prefix != Qnil) {
1325
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1326
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, prefix);
1327
+ }
1328
+
1329
+ while (1) {
1330
+ int chunk_len;
1331
+ VALUE chunk_prefix_str = Qnil;
1332
+ VALUE chunk_postfix_str = Qnil;
1333
+
1334
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
1335
+ splice_chunks_prep_splice(ctx, sqe, src_fptr->fd, pipefd[1], maxlen);
1336
+
1337
+ SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, &chunk_len, &switchpoint_result);
1338
+ if (chunk_len == 0) break;
1339
+
1340
+ total += chunk_len;
1341
+ chunk_len_value = INT2NUM(chunk_len);
1342
+
1343
+
1344
+ if (chunk_prefix != Qnil) {
1345
+ chunk_prefix_str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
1346
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1347
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_prefix_str);
1348
+ }
1349
+
1350
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
1351
+ splice_chunks_prep_splice(ctx, sqe, pipefd[0], dest_fptr->fd, chunk_len);
1352
+
1353
+ if (chunk_postfix != Qnil) {
1354
+ chunk_postfix_str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
1355
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1356
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_postfix_str);
1357
+ }
1358
+
1359
+ RB_GC_GUARD(chunk_prefix_str);
1360
+ RB_GC_GUARD(chunk_postfix_str);
1361
+ }
1362
+
1363
+ if (postfix != Qnil) {
1364
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1365
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, postfix);
1366
+ }
1367
+ if (ctx) {
1368
+ SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, 0, &switchpoint_result);
1369
+ }
1370
+
1371
+ RB_GC_GUARD(str);
1372
+ RB_GC_GUARD(chunk_len_value);
1373
+ RB_GC_GUARD(switchpoint_result);
1374
+ if (pipefd[0] != -1) close(pipefd[0]);
1375
+ if (pipefd[1] != -1) close(pipefd[1]);
1376
+ return INT2NUM(total);
1377
+ syscallerror:
1378
+ if (pipefd[0] != -1) close(pipefd[0]);
1379
+ if (pipefd[1] != -1) close(pipefd[1]);
1380
+ rb_syserr_fail(err, strerror(err));
1381
+ error:
1382
+ if (pipefd[0] != -1) close(pipefd[0]);
1383
+ if (pipefd[1] != -1) close(pipefd[1]);
1384
+ return RAISE_EXCEPTION(switchpoint_result);
1385
+ }
1386
+
1387
+ VALUE Backend_trace(int argc, VALUE *argv, VALUE self) {
1388
+ Backend_t *backend;
1389
+ GetBackend(self, backend);
1390
+ backend_trace(&backend->base, argc, argv);
1391
+ return self;
1392
+ }
1393
+
1394
+ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
1395
+ Backend_t *backend;
1396
+ GetBackend(self, backend);
1397
+
1398
+ backend->base.trace_proc = block;
1399
+ return self;
1400
+ }
1401
+
1198
1402
  void Init_Backend() {
1199
1403
  VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
1200
1404
  rb_define_alloc_func(cBackend, Backend_allocate);
@@ -1202,11 +1406,16 @@ void Init_Backend() {
1202
1406
  rb_define_method(cBackend, "initialize", Backend_initialize, 0);
1203
1407
  rb_define_method(cBackend, "finalize", Backend_finalize, 0);
1204
1408
  rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
1409
+ rb_define_method(cBackend, "trace", Backend_trace, -1);
1410
+ rb_define_method(cBackend, "trace_proc=", Backend_trace_proc_set, 1);
1205
1411
 
1206
- rb_define_method(cBackend, "poll", Backend_poll, 3);
1412
+ rb_define_method(cBackend, "poll", Backend_poll, 1);
1207
1413
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1208
1414
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1209
1415
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1416
+ rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1417
+ rb_define_method(cBackend, "idle_proc=", Backend_idle_proc_set, 1);
1418
+ rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1210
1419
 
1211
1420
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1212
1421
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);