polyphony 0.58 → 0.61

Sign up to get free protection for your applications and to get access to all the features.
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