polyphony 0.58 → 0.61

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/Gemfile.lock +15 -29
  4. data/examples/core/message_based_supervision.rb +51 -0
  5. data/examples/io/echo_server.rb +16 -7
  6. data/ext/polyphony/backend_common.c +160 -7
  7. data/ext/polyphony/backend_common.h +34 -2
  8. data/ext/polyphony/backend_io_uring.c +119 -40
  9. data/ext/polyphony/backend_io_uring_context.c +10 -1
  10. data/ext/polyphony/backend_io_uring_context.h +5 -3
  11. data/ext/polyphony/backend_libev.c +109 -31
  12. data/ext/polyphony/extconf.rb +2 -2
  13. data/ext/polyphony/fiber.c +1 -34
  14. data/ext/polyphony/polyphony.c +12 -19
  15. data/ext/polyphony/polyphony.h +9 -20
  16. data/ext/polyphony/polyphony_ext.c +0 -4
  17. data/ext/polyphony/queue.c +12 -12
  18. data/ext/polyphony/runqueue.c +21 -98
  19. data/ext/polyphony/runqueue.h +26 -0
  20. data/ext/polyphony/thread.c +6 -113
  21. data/lib/polyphony/core/timer.rb +2 -2
  22. data/lib/polyphony/extensions/fiber.rb +102 -82
  23. data/lib/polyphony/extensions/io.rb +10 -9
  24. data/lib/polyphony/extensions/openssl.rb +14 -4
  25. data/lib/polyphony/extensions/socket.rb +15 -15
  26. data/lib/polyphony/extensions/thread.rb +1 -1
  27. data/lib/polyphony/version.rb +1 -1
  28. data/polyphony.gemspec +0 -7
  29. data/test/test_backend.rb +46 -9
  30. data/test/test_ext.rb +1 -1
  31. data/test/test_fiber.rb +106 -18
  32. data/test/test_global_api.rb +1 -1
  33. data/test/test_io.rb +29 -0
  34. data/test/test_supervise.rb +100 -100
  35. data/test/test_thread.rb +5 -11
  36. data/test/test_thread_pool.rb +1 -1
  37. data/test/test_trace.rb +28 -49
  38. metadata +5 -109
  39. data/ext/polyphony/tracing.c +0 -11
  40. data/lib/polyphony/adapters/trace.rb +0 -138
@@ -44,8 +44,12 @@ typedef struct Backend_t {
44
44
 
45
45
  static void Backend_mark(void *ptr) {
46
46
  Backend_t *backend = ptr;
47
- if (backend->base.idle_block != Qnil)
48
- rb_gc_mark(backend->base.idle_block);
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);
49
53
  }
50
54
 
51
55
  static size_t Backend_size(const void *ptr) {
@@ -54,7 +58,7 @@ static size_t Backend_size(const void *ptr) {
54
58
 
55
59
  static const rb_data_type_t Backend_type = {
56
60
  "IOUringBackend",
57
- {Backend_mark, 0, Backend_size,},
61
+ {Backend_mark, Backend_free, Backend_size,},
58
62
  0, 0, RUBY_TYPED_FREE_IMMEDIATELY
59
63
  };
60
64
 
@@ -71,7 +75,7 @@ static VALUE Backend_initialize(VALUE self) {
71
75
  Backend_t *backend;
72
76
  GetBackend(self, backend);
73
77
 
74
- initialize_backend_base(&backend->base);
78
+ backend_base_initialize(&backend->base);
75
79
  backend->pending_sqes = 0;
76
80
  backend->prepared_limit = 2048;
77
81
 
@@ -106,13 +110,6 @@ VALUE Backend_post_fork(VALUE self) {
106
110
  return self;
107
111
  }
108
112
 
109
- unsigned int Backend_pending_count(VALUE self) {
110
- Backend_t *backend;
111
- GetBackend(self, backend);
112
-
113
- return backend->base.pending_count;
114
- }
115
-
116
113
  typedef struct poll_context {
117
114
  struct io_uring *ring;
118
115
  struct io_uring_cqe *cqe;
@@ -189,24 +186,63 @@ void io_uring_backend_poll(Backend_t *backend) {
189
186
  io_uring_cqe_seen(&backend->ring, poll_ctx.cqe);
190
187
  }
191
188
 
192
- VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
193
- int is_nowait = nowait == Qtrue;
189
+ inline VALUE Backend_poll(VALUE self, VALUE blocking) {
190
+ int is_blocking = blocking == Qtrue;
194
191
  Backend_t *backend;
195
192
  GetBackend(self, backend);
196
193
 
197
- if (is_nowait && backend->pending_sqes) {
194
+ backend->base.poll_count++;
195
+
196
+ if (!is_blocking && backend->pending_sqes) {
198
197
  backend->pending_sqes = 0;
199
198
  io_uring_submit(&backend->ring);
200
199
  }
201
200
 
202
- COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
203
- if (!is_nowait) io_uring_backend_poll(backend);
201
+ COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
202
+ // if (SHOULD_TRACE(&backend->base))
203
+ // printf(
204
+ // "io_uring_poll(blocking_mode: %d, pending: %d, taken: %d, available: %d, runqueue: %d\n",
205
+ // is_blocking,
206
+ // backend->base.pending_count,
207
+ // backend->store.taken_count,
208
+ // backend->store.available_count,
209
+ // backend->base.runqueue.entries.count
210
+ // );
211
+ if (is_blocking) io_uring_backend_poll(backend);
204
212
  io_uring_backend_handle_ready_cqes(backend);
205
- COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
213
+ COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
206
214
 
207
215
  return self;
208
216
  }
209
217
 
218
+ inline void Backend_schedule_fiber(VALUE thread, VALUE self, VALUE fiber, VALUE value, int prioritize) {
219
+ Backend_t *backend;
220
+ GetBackend(self, backend);
221
+
222
+ backend_base_schedule_fiber(thread, self, &backend->base, fiber, value, prioritize);
223
+ }
224
+
225
+ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
226
+ Backend_t *backend;
227
+ GetBackend(self, backend);
228
+
229
+ runqueue_delete(&backend->base.runqueue, fiber);
230
+ }
231
+
232
+ inline VALUE Backend_switch_fiber(VALUE self) {
233
+ Backend_t *backend;
234
+ GetBackend(self, backend);
235
+
236
+ return backend_base_switch_fiber(self, &backend->base);
237
+ }
238
+
239
+ inline struct backend_stats backend_get_stats(VALUE self) {
240
+ Backend_t *backend;
241
+ GetBackend(self, backend);
242
+
243
+ return backend_base_stats(&backend->base);
244
+ }
245
+
210
246
  VALUE Backend_wakeup(VALUE self) {
211
247
  Backend_t *backend;
212
248
  GetBackend(self, backend);
@@ -242,6 +278,7 @@ int io_uring_backend_defer_submit_and_await(
242
278
  {
243
279
  VALUE switchpoint_result = Qnil;
244
280
 
281
+ backend->base.op_count++;
245
282
  if (sqe) {
246
283
  io_uring_sqe_set_data(sqe, ctx);
247
284
  io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
@@ -273,17 +310,25 @@ VALUE io_uring_backend_wait_fd(Backend_t *backend, int fd, int write) {
273
310
  io_uring_prep_poll_add(sqe, fd, write ? POLLOUT : POLLIN);
274
311
 
275
312
  io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resumed_value);
313
+ context_store_release(&backend->store, ctx);
314
+
276
315
  RB_GC_GUARD(resumed_value);
277
316
  return resumed_value;
278
317
  }
279
318
 
280
- VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
319
+ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos) {
281
320
  Backend_t *backend;
282
321
  rb_io_t *fptr;
283
322
  long dynamic_len = length == Qnil;
284
323
  long buffer_size = dynamic_len ? 4096 : NUM2INT(length);
285
- int shrinkable = io_setstrbuf(&str, buffer_size);
286
- char *buf = RSTRING_PTR(str);
324
+ long buf_pos = NUM2INT(pos);
325
+ if (str != Qnil) {
326
+ int current_len = RSTRING_LEN(str);
327
+ if (buf_pos < 0 || buf_pos > current_len) buf_pos = current_len;
328
+ }
329
+ else buf_pos = 0;
330
+ int shrinkable = io_setstrbuf(&str, buf_pos + buffer_size);
331
+ char *buf = RSTRING_PTR(str) + buf_pos;
287
332
  long total = 0;
288
333
  int read_to_eof = RTEST(to_eof);
289
334
  VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
@@ -320,9 +365,9 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
320
365
  if (!dynamic_len) break;
321
366
 
322
367
  // resize buffer
323
- rb_str_resize(str, total);
368
+ rb_str_resize(str, buf_pos + total);
324
369
  rb_str_modify_expand(str, buffer_size);
325
- buf = RSTRING_PTR(str) + total;
370
+ buf = RSTRING_PTR(str) + buf_pos + total;
326
371
  shrinkable = 0;
327
372
  buffer_size += buffer_size;
328
373
  }
@@ -330,7 +375,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
330
375
  }
331
376
  }
332
377
 
333
- io_set_read_length(str, total, shrinkable);
378
+ io_set_read_length(str, buf_pos + total, shrinkable);
334
379
  io_enc_str(str, fptr);
335
380
 
336
381
  if (!total) return Qnil;
@@ -338,12 +383,12 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
338
383
  return str;
339
384
  }
340
385
 
341
- VALUE Backend_read_loop(VALUE self, VALUE io) {
386
+ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
342
387
  Backend_t *backend;
343
388
  rb_io_t *fptr;
344
389
  VALUE str;
345
390
  long total;
346
- long len = 8192;
391
+ long len = NUM2INT(maxlen);
347
392
  int shrinkable;
348
393
  char *buf;
349
394
  VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
@@ -551,13 +596,19 @@ VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
551
596
  Backend_writev(self, argv[0], argc - 1, argv + 1);
552
597
  }
553
598
 
554
- VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
599
+ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
555
600
  Backend_t *backend;
556
601
  rb_io_t *fptr;
557
602
  long dynamic_len = length == Qnil;
558
603
  long len = dynamic_len ? 4096 : NUM2INT(length);
559
- int shrinkable = io_setstrbuf(&str, len);
560
- char *buf = RSTRING_PTR(str);
604
+ long buf_pos = NUM2INT(pos);
605
+ if (str != Qnil) {
606
+ int current_len = RSTRING_LEN(str);
607
+ if (buf_pos < 0 || buf_pos > current_len) buf_pos = current_len;
608
+ }
609
+ else buf_pos = 0;
610
+ int shrinkable = io_setstrbuf(&str, buf_pos + len);
611
+ char *buf = RSTRING_PTR(str) + buf_pos;
561
612
  long total = 0;
562
613
  VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
563
614
 
@@ -589,7 +640,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
589
640
  }
590
641
  }
591
642
 
592
- io_set_read_length(str, total, shrinkable);
643
+ io_set_read_length(str, buf_pos + total, shrinkable);
593
644
  io_enc_str(str, fptr);
594
645
 
595
646
  if (!total) return Qnil;
@@ -597,12 +648,12 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
597
648
  return str;
598
649
  }
599
650
 
600
- VALUE Backend_recv_loop(VALUE self, VALUE io) {
651
+ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
601
652
  Backend_t *backend;
602
653
  rb_io_t *fptr;
603
654
  VALUE str;
604
655
  long total;
605
- long len = 8192;
656
+ long len = NUM2INT(maxlen);
606
657
  int shrinkable;
607
658
  char *buf;
608
659
  VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
@@ -992,6 +1043,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
992
1043
  io_uring_prep_timeout(sqe, &ts, 0, 0);
993
1044
  io_uring_sqe_set_data(sqe, ctx);
994
1045
  io_uring_backend_defer_submit(backend);
1046
+ backend->base.op_count++;
995
1047
 
996
1048
  struct Backend_timeout_ctx timeout_ctx = {backend, ctx};
997
1049
  result = rb_ensure(Backend_timeout_ensure_safe, Qnil, Backend_timeout_ensure, (VALUE)&timeout_ctx);
@@ -1159,6 +1211,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1159
1211
  sqe_count++;
1160
1212
  }
1161
1213
 
1214
+ backend->base.op_count += sqe_count;
1162
1215
  ctx->ref_count = sqe_count + 1;
1163
1216
  io_uring_backend_defer_submit(backend);
1164
1217
  resume_value = backend_await((struct Backend_base *)backend);
@@ -1187,10 +1240,10 @@ VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
1187
1240
  return self;
1188
1241
  }
1189
1242
 
1190
- VALUE Backend_idle_block_set(VALUE self, VALUE block) {
1243
+ VALUE Backend_idle_proc_set(VALUE self, VALUE block) {
1191
1244
  Backend_t *backend;
1192
1245
  GetBackend(self, backend);
1193
- backend->base.idle_block = block;
1246
+ backend->base.idle_proc = block;
1194
1247
  return self;
1195
1248
  }
1196
1249
 
@@ -1295,6 +1348,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1295
1348
  if (prefix != Qnil) {
1296
1349
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1297
1350
  splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, prefix);
1351
+ backend->base.op_count++;
1298
1352
  }
1299
1353
 
1300
1354
  while (1) {
@@ -1304,7 +1358,8 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1304
1358
 
1305
1359
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
1306
1360
  splice_chunks_prep_splice(ctx, sqe, src_fptr->fd, pipefd[1], maxlen);
1307
-
1361
+ backend->base.op_count++;
1362
+
1308
1363
  SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, &chunk_len, &switchpoint_result);
1309
1364
  if (chunk_len == 0) break;
1310
1365
 
@@ -1316,15 +1371,18 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1316
1371
  chunk_prefix_str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
1317
1372
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1318
1373
  splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_prefix_str);
1374
+ backend->base.op_count++;
1319
1375
  }
1320
1376
 
1321
1377
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
1322
1378
  splice_chunks_prep_splice(ctx, sqe, pipefd[0], dest_fptr->fd, chunk_len);
1379
+ backend->base.op_count++;
1323
1380
 
1324
1381
  if (chunk_postfix != Qnil) {
1325
1382
  chunk_postfix_str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
1326
1383
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1327
1384
  splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_postfix_str);
1385
+ backend->base.op_count++;
1328
1386
  }
1329
1387
 
1330
1388
  RB_GC_GUARD(chunk_prefix_str);
@@ -1334,6 +1392,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1334
1392
  if (postfix != Qnil) {
1335
1393
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1336
1394
  splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, postfix);
1395
+ backend->base.op_count++;
1337
1396
  }
1338
1397
  if (ctx) {
1339
1398
  SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, 0, &switchpoint_result);
@@ -1355,6 +1414,21 @@ error:
1355
1414
  return RAISE_EXCEPTION(switchpoint_result);
1356
1415
  }
1357
1416
 
1417
+ VALUE Backend_trace(int argc, VALUE *argv, VALUE self) {
1418
+ Backend_t *backend;
1419
+ GetBackend(self, backend);
1420
+ backend_trace(&backend->base, argc, argv);
1421
+ return self;
1422
+ }
1423
+
1424
+ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
1425
+ Backend_t *backend;
1426
+ GetBackend(self, backend);
1427
+
1428
+ backend->base.trace_proc = block;
1429
+ return self;
1430
+ }
1431
+
1358
1432
  void Init_Backend() {
1359
1433
  VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
1360
1434
  rb_define_alloc_func(cBackend, Backend_allocate);
@@ -1362,24 +1436,27 @@ void Init_Backend() {
1362
1436
  rb_define_method(cBackend, "initialize", Backend_initialize, 0);
1363
1437
  rb_define_method(cBackend, "finalize", Backend_finalize, 0);
1364
1438
  rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
1439
+ rb_define_method(cBackend, "trace", Backend_trace, -1);
1440
+ rb_define_method(cBackend, "trace_proc=", Backend_trace_proc_set, 1);
1441
+ rb_define_method(cBackend, "stats", Backend_stats, 0);
1365
1442
 
1366
- rb_define_method(cBackend, "poll", Backend_poll, 3);
1443
+ rb_define_method(cBackend, "poll", Backend_poll, 1);
1367
1444
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1368
1445
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1369
1446
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1370
1447
  rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1371
- rb_define_method(cBackend, "idle_block=", Backend_idle_block_set, 1);
1448
+ rb_define_method(cBackend, "idle_proc=", Backend_idle_proc_set, 1);
1372
1449
  rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1373
1450
 
1374
1451
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1375
1452
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
1376
1453
  rb_define_method(cBackend, "connect", Backend_connect, 3);
1377
1454
  rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
1378
- rb_define_method(cBackend, "read", Backend_read, 4);
1379
- rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
1380
- rb_define_method(cBackend, "recv", Backend_recv, 3);
1455
+ rb_define_method(cBackend, "read", Backend_read, 5);
1456
+ rb_define_method(cBackend, "read_loop", Backend_read_loop, 2);
1457
+ rb_define_method(cBackend, "recv", Backend_recv, 4);
1381
1458
  rb_define_method(cBackend, "recv_feed_loop", Backend_recv_feed_loop, 3);
1382
- rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
1459
+ rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 2);
1383
1460
  rb_define_method(cBackend, "send", Backend_send, 3);
1384
1461
  rb_define_method(cBackend, "sendv", Backend_sendv, 3);
1385
1462
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
@@ -1401,6 +1478,8 @@ void Init_Backend() {
1401
1478
  SYM_send = ID2SYM(rb_intern("send"));
1402
1479
  SYM_splice = ID2SYM(rb_intern("splice"));
1403
1480
  SYM_write = ID2SYM(rb_intern("write"));
1481
+
1482
+ backend_setup_stats_symbols();
1404
1483
  }
1405
1484
 
1406
1485
  #endif // POLYPHONY_BACKEND_LIBURING
@@ -25,6 +25,8 @@ void context_store_initialize(op_context_store_t *store) {
25
25
  store->last_id = 0;
26
26
  store->available = NULL;
27
27
  store->taken = NULL;
28
+ store->available_count = 0;
29
+ store->taken_count = 0;
28
30
  }
29
31
 
30
32
  inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type) {
@@ -32,12 +34,12 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
32
34
  if (ctx) {
33
35
  if (ctx->next) ctx->next->prev = NULL;
34
36
  store->available = ctx->next;
37
+ store->available_count--;
35
38
  }
36
39
  else {
37
40
  ctx = malloc(sizeof(op_context_t));
38
41
  }
39
42
  ctx->id = (++store->last_id);
40
- // printf("acquire %p %d (%s)\n", ctx, ctx->id, op_type_to_str(type));
41
43
  ctx->prev = NULL;
42
44
  ctx->next = store->taken;
43
45
  if (store->taken) store->taken->prev = ctx;
@@ -49,6 +51,10 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
49
51
  ctx->ref_count = 2;
50
52
  ctx->result = 0;
51
53
 
54
+ store->taken_count++;
55
+
56
+ // printf("acquire %p %d (%s, ref_count: %d) taken: %d\n", ctx, ctx->id, op_type_to_str(type), ctx->ref_count, store->taken_count);
57
+
52
58
  return ctx;
53
59
  }
54
60
 
@@ -61,6 +67,9 @@ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
61
67
  ctx->ref_count--;
62
68
  if (ctx->ref_count) return 0;
63
69
 
70
+ store->taken_count--;
71
+ store->available_count++;
72
+
64
73
  if (ctx->next) ctx->next->prev = ctx->prev;
65
74
  if (ctx->prev) ctx->prev->next = ctx->next;
66
75
  if (store->taken == ctx) store->taken = ctx->next;
@@ -22,7 +22,7 @@ typedef struct op_context {
22
22
  struct op_context *prev;
23
23
  struct op_context *next;
24
24
  enum op_type type: 16;
25
- unsigned int ref_count : 16;
25
+ unsigned int ref_count : 16;
26
26
  int id;
27
27
  int result;
28
28
  VALUE fiber;
@@ -31,8 +31,10 @@ typedef struct op_context {
31
31
 
32
32
  typedef struct op_context_store {
33
33
  int last_id;
34
- op_context_t *available;
35
- op_context_t *taken;
34
+ op_context_t *available;
35
+ op_context_t *taken;
36
+ int available_count;
37
+ int taken_count;
36
38
  } op_context_store_t;
37
39
 
38
40
  const char *op_type_to_str(enum op_type type);
@@ -75,8 +75,12 @@ typedef struct Backend_t {
75
75
 
76
76
  static void Backend_mark(void *ptr) {
77
77
  Backend_t *backend = ptr;
78
- if (backend->base.idle_block != Qnil)
79
- rb_gc_mark(backend->base.idle_block);
78
+ backend_base_mark(&backend->base);
79
+ }
80
+
81
+ static void Backend_free(void *ptr) {
82
+ Backend_t *backend = ptr;
83
+ backend_base_finalize(&backend->base);
80
84
  }
81
85
 
82
86
  static size_t Backend_size(const void *ptr) {
@@ -85,7 +89,7 @@ static size_t Backend_size(const void *ptr) {
85
89
 
86
90
  static const rb_data_type_t Backend_type = {
87
91
  "LibevBackend",
88
- {Backend_mark, 0, Backend_size,},
92
+ {Backend_mark, Backend_free, Backend_size,},
89
93
  0, 0, RUBY_TYPED_FREE_IMMEDIATELY
90
94
  };
91
95
 
@@ -117,7 +121,7 @@ static VALUE Backend_initialize(VALUE self) {
117
121
 
118
122
  GetBackend(self, backend);
119
123
 
120
- initialize_backend_base(&backend->base);
124
+ backend_base_initialize(&backend->base);
121
125
  backend->ev_loop = libev_new_loop();
122
126
 
123
127
  // start async watcher used for breaking a poll op (from another thread)
@@ -156,24 +160,40 @@ VALUE Backend_post_fork(VALUE self) {
156
160
  return self;
157
161
  }
158
162
 
159
- inline unsigned int Backend_pending_count(VALUE self) {
163
+ inline VALUE Backend_poll(VALUE self, VALUE blocking) {
160
164
  Backend_t *backend;
161
165
  GetBackend(self, backend);
162
166
 
163
- return backend->base.pending_count;
167
+ backend->base.poll_count++;
168
+
169
+ COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
170
+ backend->base.currently_polling = 1;
171
+ ev_run(backend->ev_loop, blocking == Qtrue ? EVRUN_ONCE : EVRUN_NOWAIT);
172
+ backend->base.currently_polling = 0;
173
+ COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
174
+
175
+ return self;
164
176
  }
165
177
 
166
- VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
178
+ inline void Backend_schedule_fiber(VALUE thread, VALUE self, VALUE fiber, VALUE value, int prioritize) {
167
179
  Backend_t *backend;
168
180
  GetBackend(self, backend);
169
181
 
170
- COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
171
- backend->base.currently_polling = 1;
172
- ev_run(backend->ev_loop, nowait == Qtrue ? EVRUN_NOWAIT : EVRUN_ONCE);
173
- backend->base.currently_polling = 0;
174
- COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
182
+ backend_base_schedule_fiber(thread, self, &backend->base, fiber, value, prioritize);
183
+ }
175
184
 
176
- return self;
185
+ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
186
+ Backend_t *backend;
187
+ GetBackend(self, backend);
188
+
189
+ runqueue_delete(&backend->base.runqueue, fiber);
190
+ }
191
+
192
+ inline VALUE Backend_switch_fiber(VALUE self) {
193
+ Backend_t *backend;
194
+ GetBackend(self, backend);
195
+
196
+ return backend_base_switch_fiber(self, &backend->base);
177
197
  }
178
198
 
179
199
  VALUE Backend_wakeup(VALUE self) {
@@ -193,6 +213,13 @@ VALUE Backend_wakeup(VALUE self) {
193
213
  return Qnil;
194
214
  }
195
215
 
216
+ inline struct backend_stats backend_get_stats(VALUE self) {
217
+ Backend_t *backend;
218
+ GetBackend(self, backend);
219
+
220
+ return backend_base_stats(&backend->base);
221
+ }
222
+
196
223
  struct libev_io {
197
224
  struct ev_io io;
198
225
  VALUE fiber;
@@ -232,14 +259,20 @@ VALUE libev_wait_fd(Backend_t *backend, int fd, int events, int raise_exception)
232
259
  return switchpoint_result;
233
260
  }
234
261
 
235
- VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
262
+ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos) {
236
263
  Backend_t *backend;
237
264
  struct libev_io watcher;
238
265
  rb_io_t *fptr;
239
266
  long dynamic_len = length == Qnil;
240
267
  long len = dynamic_len ? 4096 : NUM2INT(length);
241
- int shrinkable = io_setstrbuf(&str, len);
242
- char *buf = RSTRING_PTR(str);
268
+ long buf_pos = NUM2INT(pos);
269
+ if (str != Qnil) {
270
+ int current_len = RSTRING_LEN(str);
271
+ if (buf_pos < 0 || buf_pos > current_len) buf_pos = current_len;
272
+ }
273
+ else buf_pos = 0;
274
+ int shrinkable = io_setstrbuf(&str, buf_pos + len);
275
+ char *buf = RSTRING_PTR(str) + buf_pos;
243
276
  long total = 0;
244
277
  VALUE switchpoint_result = Qnil;
245
278
  int read_to_eof = RTEST(to_eof);
@@ -255,6 +288,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
255
288
  OBJ_TAINT(str);
256
289
 
257
290
  while (1) {
291
+ backend->base.op_count++;
258
292
  ssize_t n = read(fptr->fd, buf, len - total);
259
293
  if (n < 0) {
260
294
  int e = errno;
@@ -275,9 +309,9 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
275
309
  if (total == len) {
276
310
  if (!dynamic_len) break;
277
311
 
278
- rb_str_resize(str, total);
312
+ rb_str_resize(str, buf_pos + total);
279
313
  rb_str_modify_expand(str, len);
280
- buf = RSTRING_PTR(str) + total;
314
+ buf = RSTRING_PTR(str) + buf_pos + total;
281
315
  shrinkable = 0;
282
316
  len += len;
283
317
  }
@@ -285,7 +319,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
285
319
  }
286
320
  }
287
321
 
288
- io_set_read_length(str, total, shrinkable);
322
+ io_set_read_length(str, buf_pos + total, shrinkable);
289
323
  io_enc_str(str, fptr);
290
324
 
291
325
  if (total == 0) return Qnil;
@@ -298,17 +332,17 @@ error:
298
332
  return RAISE_EXCEPTION(switchpoint_result);
299
333
  }
300
334
 
301
- VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
302
- return Backend_read(self, io, str, length, Qnil);
335
+ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
336
+ return Backend_read(self, io, str, length, Qnil, pos);
303
337
  }
304
338
 
305
- VALUE Backend_read_loop(VALUE self, VALUE io) {
339
+ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
306
340
  Backend_t *backend;
307
341
  struct libev_io watcher;
308
342
  rb_io_t *fptr;
309
343
  VALUE str;
310
344
  long total;
311
- long len = 8192;
345
+ long len = NUM2INT(maxlen);
312
346
  int shrinkable;
313
347
  char *buf;
314
348
  VALUE switchpoint_result = Qnil;
@@ -325,6 +359,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
325
359
  watcher.fiber = Qnil;
326
360
 
327
361
  while (1) {
362
+ backend->base.op_count++;
328
363
  ssize_t n = read(fptr->fd, buf, len);
329
364
  if (n < 0) {
330
365
  int e = errno;
@@ -377,6 +412,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
377
412
  watcher.fiber = Qnil;
378
413
 
379
414
  while (1) {
415
+ backend->base.op_count++;
380
416
  ssize_t n = read(fptr->fd, buf, len);
381
417
  if (n < 0) {
382
418
  int e = errno;
@@ -424,6 +460,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
424
460
  watcher.fiber = Qnil;
425
461
 
426
462
  while (left > 0) {
463
+ backend->base.op_count++;
427
464
  ssize_t n = write(fptr->fd, buf, left);
428
465
  if (n < 0) {
429
466
  int e = errno;
@@ -483,6 +520,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
483
520
  iov_ptr = iov;
484
521
 
485
522
  while (1) {
523
+ backend->base.op_count++;
486
524
  ssize_t n = writev(fptr->fd, iov_ptr, iov_count);
487
525
  if (n < 0) {
488
526
  int e = errno;
@@ -554,6 +592,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
554
592
  io_verify_blocking_mode(fptr, server_socket, Qfalse);
555
593
  watcher.fiber = Qnil;
556
594
  while (1) {
595
+ backend->base.op_count++;
557
596
  fd = accept(fptr->fd, &addr, &len);
558
597
  if (fd < 0) {
559
598
  int e = errno;
@@ -612,6 +651,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
612
651
  watcher.fiber = Qnil;
613
652
 
614
653
  while (1) {
654
+ backend->base.op_count++;
615
655
  fd = accept(fptr->fd, &addr, &len);
616
656
  if (fd < 0) {
617
657
  int e = errno;
@@ -671,6 +711,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
671
711
  addr.sin_addr.s_addr = inet_addr(host_buf);
672
712
  addr.sin_port = htons(NUM2INT(port));
673
713
 
714
+ backend->base.op_count++;
674
715
  int result = connect(fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
675
716
  if (result < 0) {
676
717
  int e = errno;
@@ -711,6 +752,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
711
752
  watcher.fiber = Qnil;
712
753
 
713
754
  while (left > 0) {
755
+ backend->base.op_count++;
714
756
  ssize_t n = send(fptr->fd, buf, left, flags_int);
715
757
  if (n < 0) {
716
758
  int e = errno;
@@ -818,6 +860,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
818
860
 
819
861
  watcher.ctx.fiber = Qnil;
820
862
  while (1) {
863
+ backend->base.op_count++;
821
864
  len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
822
865
  if (len < 0) {
823
866
  int e = errno;
@@ -869,6 +912,7 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
869
912
 
870
913
  watcher.ctx.fiber = Qnil;
871
914
  while (1) {
915
+ backend->base.op_count++;
872
916
  len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
873
917
  if (len < 0) {
874
918
  int e = errno;
@@ -928,6 +972,7 @@ VALUE Backend_fake_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
928
972
  watcher.fiber = Qnil;
929
973
 
930
974
  while (1) {
975
+ backend->base.op_count++;
931
976
  ssize_t n = read(src_fptr->fd, buf, len);
932
977
  if (n < 0) {
933
978
  int e = errno;
@@ -943,6 +988,7 @@ VALUE Backend_fake_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
943
988
  }
944
989
 
945
990
  while (left > 0) {
991
+ backend->base.op_count++;
946
992
  ssize_t n = write(dest_fptr->fd, buf, left);
947
993
  if (n < 0) {
948
994
  int e = errno;
@@ -1003,6 +1049,7 @@ VALUE Backend_fake_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen
1003
1049
  while (1) {
1004
1050
  char *ptr = buf;
1005
1051
  while (1) {
1052
+ backend->base.op_count++;
1006
1053
  ssize_t n = read(src_fptr->fd, ptr, len);
1007
1054
  if (n < 0) {
1008
1055
  int e = errno;
@@ -1020,6 +1067,7 @@ VALUE Backend_fake_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen
1020
1067
  }
1021
1068
 
1022
1069
  while (left > 0) {
1070
+ backend->base.op_count++;
1023
1071
  ssize_t n = write(dest_fptr->fd, ptr, left);
1024
1072
  if (n < 0) {
1025
1073
  int e = errno;
@@ -1059,6 +1107,7 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
1059
1107
  GetBackend(self, backend);
1060
1108
  GetOpenFile(io, fptr);
1061
1109
 
1110
+ backend->base.op_count++;
1062
1111
  return libev_wait_fd(backend, fptr->fd, events, 1);
1063
1112
  }
1064
1113
 
@@ -1082,6 +1131,7 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
1082
1131
  watcher.fiber = rb_fiber_current();
1083
1132
  ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
1084
1133
  ev_timer_start(backend->ev_loop, &watcher.timer);
1134
+ backend->base.op_count++;
1085
1135
 
1086
1136
  switchpoint_result = backend_await((struct Backend_base *)backend);
1087
1137
 
@@ -1111,6 +1161,7 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
1111
1161
  VALUE switchpoint_result = Qnil;
1112
1162
  ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
1113
1163
  ev_timer_start(backend->ev_loop, &watcher.timer);
1164
+ backend->base.op_count++;
1114
1165
  switchpoint_result = backend_await((struct Backend_base *)backend);
1115
1166
  ev_timer_stop(backend->ev_loop, &watcher.timer);
1116
1167
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -1162,6 +1213,7 @@ VALUE Backend_timeout(int argc,VALUE *argv, VALUE self) {
1162
1213
  watcher.resume_value = timeout;
1163
1214
  ev_timer_init(&watcher.timer, Backend_timeout_callback, NUM2DBL(duration), 0.);
1164
1215
  ev_timer_start(backend->ev_loop, &watcher.timer);
1216
+ backend->base.op_count++;
1165
1217
 
1166
1218
  struct Backend_timeout_ctx timeout_ctx = {backend, &watcher};
1167
1219
  result = rb_ensure(Backend_timeout_ensure_safe, Qnil, Backend_timeout_ensure, (VALUE)&timeout_ctx);
@@ -1184,6 +1236,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1184
1236
  if (fd >= 0) {
1185
1237
  Backend_t *backend;
1186
1238
  GetBackend(self, backend);
1239
+ backend->base.op_count++;
1187
1240
 
1188
1241
  VALUE resume_value = libev_wait_fd(backend, fd, EV_READ, 0);
1189
1242
  close(fd);
@@ -1227,6 +1280,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1227
1280
  watcher.fiber = rb_fiber_current();
1228
1281
  ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
1229
1282
  ev_child_start(backend->ev_loop, &watcher.child);
1283
+ backend->base.op_count++;
1230
1284
 
1231
1285
  switchpoint_result = backend_await((struct Backend_base *)backend);
1232
1286
 
@@ -1249,6 +1303,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
1249
1303
 
1250
1304
  ev_async_init(&async, Backend_async_callback);
1251
1305
  ev_async_start(backend->ev_loop, &async);
1306
+ backend->base.op_count++;
1252
1307
 
1253
1308
  switchpoint_result = backend_await((struct Backend_base *)backend);
1254
1309
 
@@ -1293,10 +1348,10 @@ VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
1293
1348
  return self;
1294
1349
  }
1295
1350
 
1296
- VALUE Backend_idle_block_set(VALUE self, VALUE block) {
1351
+ VALUE Backend_idle_proc_set(VALUE self, VALUE block) {
1297
1352
  Backend_t *backend;
1298
1353
  GetBackend(self, backend);
1299
- backend->base.idle_block = block;
1354
+ backend->base.idle_proc = block;
1300
1355
  return self;
1301
1356
  }
1302
1357
 
@@ -1312,6 +1367,7 @@ inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct lib
1312
1367
  int len = RSTRING_LEN(str);
1313
1368
  int left = len;
1314
1369
  while (left > 0) {
1370
+ backend->base.op_count++;
1315
1371
  ssize_t n = write(fd, buf, left);
1316
1372
  if (n < 0) {
1317
1373
  int err = errno;
@@ -1372,6 +1428,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1372
1428
  int chunk_len;
1373
1429
  // splice to pipe
1374
1430
  while (1) {
1431
+ backend->base.op_count++;
1375
1432
  chunk_len = splice(src_fptr->fd, 0, pipefd[1], 0, maxlen, 0);
1376
1433
  if (chunk_len < 0) {
1377
1434
  err = errno;
@@ -1397,6 +1454,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1397
1454
 
1398
1455
  int left = chunk_len;
1399
1456
  while (1) {
1457
+ backend->base.op_count++;
1400
1458
  int n = splice(pipefd[0], 0, dest_fptr->fd, 0, left, 0);
1401
1459
  if (n < 0) {
1402
1460
  err = errno;
@@ -1443,6 +1501,21 @@ error:
1443
1501
  return RAISE_EXCEPTION(result);
1444
1502
  }
1445
1503
 
1504
+ VALUE Backend_trace(int argc, VALUE *argv, VALUE self) {
1505
+ Backend_t *backend;
1506
+ GetBackend(self, backend);
1507
+ backend_trace(&backend->base, argc, argv);
1508
+ return self;
1509
+ }
1510
+
1511
+ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
1512
+ Backend_t *backend;
1513
+ GetBackend(self, backend);
1514
+
1515
+ backend->base.trace_proc = block;
1516
+ return self;
1517
+ }
1518
+
1446
1519
  void Init_Backend() {
1447
1520
  ev_set_allocator(xrealloc);
1448
1521
 
@@ -1452,23 +1525,26 @@ void Init_Backend() {
1452
1525
  rb_define_method(cBackend, "initialize", Backend_initialize, 0);
1453
1526
  rb_define_method(cBackend, "finalize", Backend_finalize, 0);
1454
1527
  rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
1528
+ rb_define_method(cBackend, "trace", Backend_trace, -1);
1529
+ rb_define_method(cBackend, "trace_proc=", Backend_trace_proc_set, 1);
1530
+ rb_define_method(cBackend, "stats", Backend_stats, 0);
1455
1531
 
1456
- rb_define_method(cBackend, "poll", Backend_poll, 3);
1532
+ rb_define_method(cBackend, "poll", Backend_poll, 1);
1457
1533
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1458
1534
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1459
1535
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1460
1536
  rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1461
- rb_define_method(cBackend, "idle_block=", Backend_idle_block_set, 1);
1537
+ rb_define_method(cBackend, "idle_proc=", Backend_idle_proc_set, 1);
1462
1538
  rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1463
1539
 
1464
1540
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1465
1541
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
1466
1542
  rb_define_method(cBackend, "connect", Backend_connect, 3);
1467
1543
  rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
1468
- rb_define_method(cBackend, "read", Backend_read, 4);
1469
- rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
1470
- rb_define_method(cBackend, "recv", Backend_recv, 3);
1471
- rb_define_method(cBackend, "recv_loop", Backend_read_loop, 1);
1544
+ rb_define_method(cBackend, "read", Backend_read, 5);
1545
+ rb_define_method(cBackend, "read_loop", Backend_read_loop, 2);
1546
+ rb_define_method(cBackend, "recv", Backend_recv, 4);
1547
+ rb_define_method(cBackend, "recv_loop", Backend_read_loop, 2);
1472
1548
  rb_define_method(cBackend, "recv_feed_loop", Backend_feed_loop, 3);
1473
1549
  rb_define_method(cBackend, "send", Backend_send, 3);
1474
1550
  rb_define_method(cBackend, "sendv", Backend_sendv, 3);
@@ -1494,6 +1570,8 @@ void Init_Backend() {
1494
1570
  SYM_send = ID2SYM(rb_intern("send"));
1495
1571
  SYM_splice = ID2SYM(rb_intern("splice"));
1496
1572
  SYM_write = ID2SYM(rb_intern("write"));
1573
+
1574
+ backend_setup_stats_symbols();
1497
1575
  }
1498
1576
 
1499
1577
  #endif // POLYPHONY_BACKEND_LIBEV