uringmachine 0.18 → 0.19.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/TODO.md +42 -1
- data/examples/fiber_scheduler_demo.rb +71 -0
- data/ext/um/um.c +97 -38
- data/ext/um/um.h +4 -2
- data/ext/um/um_async_op.c +2 -1
- data/ext/um/um_class.c +6 -0
- data/ext/um/um_const.c +5 -2
- data/ext/um/um_stream.c +8 -8
- data/ext/um/um_stream_class.c +1 -1
- data/ext/um/um_sync.c +2 -2
- data/ext/um/um_utils.c +1 -1
- data/lib/uringmachine/fiber_scheduler.rb +104 -0
- data/lib/uringmachine/version.rb +1 -1
- data/test/test_stream.rb +3 -3
- data/test/test_um.rb +78 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff89f34d541e086f8016156d4b5290a6a2fdc94292f04e7a3104ef9e7cda256d
|
|
4
|
+
data.tar.gz: 01dbe57b83effbab744fbd07461dcd803246a88bbf3ce1aeaf1e4eace32fd0ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca0e44f1121384634b4234b2cd49643f87e8255e77eb250f64fb62be8bcdcb18e6a82480e5da003322c941c9ae2def119a048797d53bc064c4e1f77796b82c4d
|
|
7
|
+
data.tar.gz: 322090fd12498525d303b63f66ec1c559ed9933e89e74d3b01c9cc5c0f3f1dcee951c767625690c288478d5e460248418bbd693fbd7c31146c7aff76cf545a5d
|
data/CHANGELOG.md
CHANGED
data/TODO.md
CHANGED
|
@@ -1,3 +1,44 @@
|
|
|
1
|
+
## immediate
|
|
2
|
+
|
|
3
|
+
- make a reproducer for segfault on timeout, spin lots of fibers where a timeout
|
|
4
|
+
wraps a #shift call (from an empty queue).
|
|
5
|
+
- see also: https://mensfeld.pl/2025/11/ruby-ffi-gc-bug-hash-becomes-string/
|
|
6
|
+
|
|
7
|
+
Analysis:
|
|
8
|
+
|
|
9
|
+
- The segfault is related to timeouts
|
|
10
|
+
- Looking at process_runqueue_op (um.c):
|
|
11
|
+
|
|
12
|
+
```c
|
|
13
|
+
inline VALUE process_runqueue_op(struct um *machine, struct um_op *op) {
|
|
14
|
+
VALUE fiber = op->fiber;
|
|
15
|
+
VALUE value = op->value;
|
|
16
|
+
|
|
17
|
+
// on timeout, the op flags are changed to turn on OP_F_TRANSIENT
|
|
18
|
+
if (unlikely(op->flags & OP_F_TRANSIENT))
|
|
19
|
+
// here the op is freed, so the value is not visible to the GC anymoore
|
|
20
|
+
um_op_free(machine, op);
|
|
21
|
+
|
|
22
|
+
// if a GC occurs here, we risk a segfault
|
|
23
|
+
|
|
24
|
+
// value is used
|
|
25
|
+
return rb_fiber_transfer(fiber, 1, &value);
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- So, a possible solution is to put a `RB_GC_GUARD` after the `return`.
|
|
30
|
+
- But first, I want to be able to reproduce it. We can start by setting
|
|
31
|
+
`GC.stress = true` on tests and see if we segfault.
|
|
32
|
+
|
|
33
|
+
## FiberScheduler implementation
|
|
34
|
+
|
|
35
|
+
Some resources:
|
|
36
|
+
|
|
37
|
+
- https://github.com/socketry/async/blob/main/context/getting-started.md
|
|
38
|
+
- https://github.com/socketry/async/blob/main/context/scheduler.md
|
|
39
|
+
- https://github.com/socketry/async/blob/main/lib/async/scheduler.rb#L28
|
|
40
|
+
-
|
|
41
|
+
|
|
1
42
|
## useful concurrency tools
|
|
2
43
|
|
|
3
44
|
- debounce
|
|
@@ -16,7 +57,7 @@
|
|
|
16
57
|
- splice / - tee
|
|
17
58
|
- sendto
|
|
18
59
|
- recvfrom
|
|
19
|
-
-
|
|
60
|
+
- poll_multishot
|
|
20
61
|
- fsync
|
|
21
62
|
- mkdir / mkdirat
|
|
22
63
|
- link / linkat / unlink / unlinkat / symlink
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../lib/uringmachine'
|
|
4
|
+
require_relative '../lib/uringmachine/fiber_scheduler'
|
|
5
|
+
require 'net/http'
|
|
6
|
+
|
|
7
|
+
machine = UringMachine.new
|
|
8
|
+
scheduler = UM::FiberScheduler.new(machine)
|
|
9
|
+
Fiber.set_scheduler scheduler
|
|
10
|
+
|
|
11
|
+
i, o = IO.pipe
|
|
12
|
+
|
|
13
|
+
f1 = Fiber.schedule do
|
|
14
|
+
# sleep 0.4
|
|
15
|
+
# o.write 'Hello, world!'
|
|
16
|
+
# o.close
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
f2 = Fiber.schedule do
|
|
20
|
+
# puts "hi"
|
|
21
|
+
# 10.times do
|
|
22
|
+
# sleep 0.1
|
|
23
|
+
# puts "."
|
|
24
|
+
# end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Fiber.schedule do
|
|
28
|
+
# scheduler.block(:wait)
|
|
29
|
+
# end
|
|
30
|
+
|
|
31
|
+
f3 = Fiber.schedule do
|
|
32
|
+
# message = i.read
|
|
33
|
+
# puts message
|
|
34
|
+
# rescue => e
|
|
35
|
+
# scheduler.p e
|
|
36
|
+
# scheduler.p e.backtrace
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
f4 = Fiber.schedule do
|
|
40
|
+
puts '*' * 40
|
|
41
|
+
# tcp = TCPSocket.new('noteflakes.com', 80)
|
|
42
|
+
# tcp.timeout = 10
|
|
43
|
+
# scheduler.p tcp
|
|
44
|
+
# tcp << "GET / HTTP/1.1\r\nHost: noteflakes.com\r\n\r\n"
|
|
45
|
+
# s = tcp.read
|
|
46
|
+
# scheduler.p s: s
|
|
47
|
+
ret = Net::HTTP.get('noteflakes.com', '/ping')
|
|
48
|
+
scheduler.p ret: ret
|
|
49
|
+
rescue => e
|
|
50
|
+
scheduler.p e
|
|
51
|
+
scheduler.p e.backtrace
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
scheduler.join(f1, f2, f3, f4)
|
|
55
|
+
|
|
56
|
+
__END__
|
|
57
|
+
|
|
58
|
+
stdout_fd = STDOUT.fileno
|
|
59
|
+
stdin_fd = STDIN.fileno
|
|
60
|
+
machine.write(stdout_fd, "Hello, world!\n")
|
|
61
|
+
|
|
62
|
+
loop do
|
|
63
|
+
machine.write(stdout_fd, "Say something: ")
|
|
64
|
+
buf = +''
|
|
65
|
+
res = machine.read(stdin_fd, buf, 8192)
|
|
66
|
+
if res > 0
|
|
67
|
+
machine.write(stdout_fd, "You said: #{buf}")
|
|
68
|
+
else
|
|
69
|
+
break
|
|
70
|
+
end
|
|
71
|
+
end
|
data/ext/um/um.c
CHANGED
|
@@ -194,7 +194,10 @@ inline VALUE process_runqueue_op(struct um *machine, struct um_op *op) {
|
|
|
194
194
|
if (unlikely(op->flags & OP_F_TRANSIENT))
|
|
195
195
|
um_op_free(machine, op);
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
VALUE ret = rb_fiber_transfer(fiber, 1, &value);
|
|
198
|
+
RB_GC_GUARD(value);
|
|
199
|
+
RB_GC_GUARD(ret);
|
|
200
|
+
return ret;
|
|
198
201
|
}
|
|
199
202
|
|
|
200
203
|
inline VALUE um_fiber_switch(struct um *machine) {
|
|
@@ -252,8 +255,10 @@ inline int um_check_completion(struct um *machine, struct um_op *op) {
|
|
|
252
255
|
}
|
|
253
256
|
|
|
254
257
|
inline VALUE um_await(struct um *machine) {
|
|
255
|
-
VALUE
|
|
256
|
-
|
|
258
|
+
VALUE ret = um_fiber_switch(machine);
|
|
259
|
+
RAISE_IF_EXCEPTION(ret);
|
|
260
|
+
RB_GC_GUARD(ret);
|
|
261
|
+
return ret;
|
|
257
262
|
}
|
|
258
263
|
|
|
259
264
|
inline void um_prep_op(struct um *machine, struct um_op *op, enum op_kind kind, unsigned flags) {
|
|
@@ -264,6 +269,7 @@ inline void um_prep_op(struct um *machine, struct um_op *op, enum op_kind kind,
|
|
|
264
269
|
VALUE fiber = (flags & OP_F_FREE_ON_COMPLETE) ? Qnil : rb_fiber_current();
|
|
265
270
|
RB_OBJ_WRITE(machine->self, &op->fiber, fiber);
|
|
266
271
|
RB_OBJ_WRITE(machine->self, &op->value, Qnil);
|
|
272
|
+
RB_OBJ_WRITE(machine->self, &op->async_op, Qnil);
|
|
267
273
|
}
|
|
268
274
|
|
|
269
275
|
inline void um_schedule(struct um *machine, VALUE fiber, VALUE value) {
|
|
@@ -273,6 +279,7 @@ inline void um_schedule(struct um *machine, VALUE fiber, VALUE value) {
|
|
|
273
279
|
op->flags = OP_F_TRANSIENT;
|
|
274
280
|
RB_OBJ_WRITE(machine->self, &op->fiber, fiber);
|
|
275
281
|
RB_OBJ_WRITE(machine->self, &op->value, value);
|
|
282
|
+
RB_OBJ_WRITE(machine->self, &op->async_op, Qnil);
|
|
276
283
|
um_runqueue_push(machine, op);
|
|
277
284
|
}
|
|
278
285
|
|
|
@@ -309,6 +316,7 @@ VALUE um_timeout(struct um *machine, VALUE interval, VALUE class) {
|
|
|
309
316
|
op->ts = um_double_to_timespec(NUM2DBL(interval));
|
|
310
317
|
RB_OBJ_WRITE(machine->self, &op->fiber, rb_fiber_current());
|
|
311
318
|
RB_OBJ_WRITE(machine->self, &op->value, rb_funcall(class, ID_new, 0));
|
|
319
|
+
RB_OBJ_WRITE(machine->self, &op->async_op, Qnil);
|
|
312
320
|
|
|
313
321
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
314
322
|
io_uring_prep_timeout(sqe, &op->ts, 0, 0);
|
|
@@ -340,8 +348,9 @@ VALUE um_sleep(struct um *machine, double duration) {
|
|
|
340
348
|
ret = DBL2NUM(duration);
|
|
341
349
|
}
|
|
342
350
|
|
|
351
|
+
RAISE_IF_EXCEPTION(ret);
|
|
343
352
|
RB_GC_GUARD(ret);
|
|
344
|
-
return
|
|
353
|
+
return ret;
|
|
345
354
|
}
|
|
346
355
|
|
|
347
356
|
inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset) {
|
|
@@ -357,10 +366,11 @@ inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int b
|
|
|
357
366
|
ret = INT2NUM(op.result.res);
|
|
358
367
|
|
|
359
368
|
}
|
|
360
|
-
|
|
361
369
|
RB_GC_GUARD(buffer);
|
|
370
|
+
|
|
371
|
+
RAISE_IF_EXCEPTION(ret);
|
|
362
372
|
RB_GC_GUARD(ret);
|
|
363
|
-
return
|
|
373
|
+
return ret;
|
|
364
374
|
}
|
|
365
375
|
|
|
366
376
|
inline size_t um_read_raw(struct um *machine, int fd, char *buffer, int maxlen) {
|
|
@@ -375,7 +385,8 @@ inline size_t um_read_raw(struct um *machine, int fd, char *buffer, int maxlen)
|
|
|
375
385
|
|
|
376
386
|
}
|
|
377
387
|
|
|
378
|
-
|
|
388
|
+
RAISE_IF_EXCEPTION(ret);
|
|
389
|
+
RB_GC_GUARD(ret);
|
|
379
390
|
return 0;
|
|
380
391
|
}
|
|
381
392
|
|
|
@@ -393,14 +404,18 @@ VALUE um_write(struct um *machine, int fd, VALUE str, int len) {
|
|
|
393
404
|
ret = INT2NUM(op.result.res);
|
|
394
405
|
|
|
395
406
|
RB_GC_GUARD(str);
|
|
407
|
+
|
|
408
|
+
RAISE_IF_EXCEPTION(ret);
|
|
396
409
|
RB_GC_GUARD(ret);
|
|
397
|
-
return
|
|
410
|
+
return ret;
|
|
398
411
|
}
|
|
399
412
|
|
|
400
413
|
VALUE um_write_async(struct um *machine, int fd, VALUE str) {
|
|
401
414
|
struct um_op *op = um_op_alloc(machine);
|
|
402
415
|
um_prep_op(machine, op, OP_WRITE_ASYNC, OP_F_TRANSIENT | OP_F_FREE_ON_COMPLETE);
|
|
416
|
+
RB_OBJ_WRITE(machine->self, &op->fiber, Qnil);
|
|
403
417
|
RB_OBJ_WRITE(machine->self, &op->value, str);
|
|
418
|
+
RB_OBJ_WRITE(machine->self, &op->async_op, Qnil);
|
|
404
419
|
|
|
405
420
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
406
421
|
io_uring_prep_write(sqe, fd, RSTRING_PTR(str), RSTRING_LEN(str), -1);
|
|
@@ -419,13 +434,17 @@ VALUE um_close(struct um *machine, int fd) {
|
|
|
419
434
|
if (um_check_completion(machine, &op))
|
|
420
435
|
ret = INT2NUM(fd);
|
|
421
436
|
|
|
437
|
+
RAISE_IF_EXCEPTION(ret);
|
|
422
438
|
RB_GC_GUARD(ret);
|
|
423
|
-
return
|
|
439
|
+
return ret;
|
|
424
440
|
}
|
|
425
441
|
|
|
426
442
|
VALUE um_close_async(struct um *machine, int fd) {
|
|
427
443
|
struct um_op *op = um_op_alloc(machine);
|
|
428
444
|
um_prep_op(machine, op, OP_CLOSE_ASYNC, OP_F_FREE_ON_COMPLETE);
|
|
445
|
+
RB_OBJ_WRITE(machine->self, &op->fiber, Qnil);
|
|
446
|
+
RB_OBJ_WRITE(machine->self, &op->value, Qnil);
|
|
447
|
+
RB_OBJ_WRITE(machine->self, &op->async_op, Qnil);
|
|
429
448
|
|
|
430
449
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
431
450
|
io_uring_prep_close(sqe, fd);
|
|
@@ -443,8 +462,9 @@ VALUE um_accept(struct um *machine, int fd) {
|
|
|
443
462
|
if (um_check_completion(machine, &op))
|
|
444
463
|
ret = INT2NUM(op.result.res);
|
|
445
464
|
|
|
465
|
+
RAISE_IF_EXCEPTION(ret);
|
|
446
466
|
RB_GC_GUARD(ret);
|
|
447
|
-
return
|
|
467
|
+
return ret;
|
|
448
468
|
}
|
|
449
469
|
|
|
450
470
|
VALUE um_socket(struct um *machine, int domain, int type, int protocol, uint flags) {
|
|
@@ -457,8 +477,9 @@ VALUE um_socket(struct um *machine, int domain, int type, int protocol, uint fla
|
|
|
457
477
|
if (um_check_completion(machine, &op))
|
|
458
478
|
ret = INT2NUM(op.result.res);
|
|
459
479
|
|
|
480
|
+
RAISE_IF_EXCEPTION(ret);
|
|
460
481
|
RB_GC_GUARD(ret);
|
|
461
|
-
return
|
|
482
|
+
return ret;
|
|
462
483
|
}
|
|
463
484
|
|
|
464
485
|
VALUE um_connect(struct um *machine, int fd, const struct sockaddr *addr, socklen_t addrlen) {
|
|
@@ -471,8 +492,9 @@ VALUE um_connect(struct um *machine, int fd, const struct sockaddr *addr, sockle
|
|
|
471
492
|
if (um_check_completion(machine, &op))
|
|
472
493
|
ret = INT2NUM(op.result.res);
|
|
473
494
|
|
|
495
|
+
RAISE_IF_EXCEPTION(ret);
|
|
474
496
|
RB_GC_GUARD(ret);
|
|
475
|
-
return
|
|
497
|
+
return ret;
|
|
476
498
|
}
|
|
477
499
|
|
|
478
500
|
VALUE um_send(struct um *machine, int fd, VALUE buffer, int len, int flags) {
|
|
@@ -486,8 +508,10 @@ VALUE um_send(struct um *machine, int fd, VALUE buffer, int len, int flags) {
|
|
|
486
508
|
ret = INT2NUM(op.result.res);
|
|
487
509
|
|
|
488
510
|
RB_GC_GUARD(buffer);
|
|
511
|
+
|
|
512
|
+
RAISE_IF_EXCEPTION(ret);
|
|
489
513
|
RB_GC_GUARD(ret);
|
|
490
|
-
return
|
|
514
|
+
return ret;
|
|
491
515
|
}
|
|
492
516
|
|
|
493
517
|
VALUE um_send_bundle(struct um *machine, int fd, int bgid, VALUE strings) {
|
|
@@ -505,10 +529,8 @@ VALUE um_send_bundle(struct um *machine, int fd, int bgid, VALUE strings) {
|
|
|
505
529
|
if (um_check_completion(machine, &op))
|
|
506
530
|
ret = INT2NUM(op.result.res);
|
|
507
531
|
|
|
532
|
+
RAISE_IF_EXCEPTION(ret);
|
|
508
533
|
RB_GC_GUARD(ret);
|
|
509
|
-
return raise_if_exception(ret);
|
|
510
|
-
|
|
511
|
-
|
|
512
534
|
return ret;
|
|
513
535
|
}
|
|
514
536
|
|
|
@@ -526,8 +548,10 @@ VALUE um_recv(struct um *machine, int fd, VALUE buffer, int maxlen, int flags) {
|
|
|
526
548
|
}
|
|
527
549
|
|
|
528
550
|
RB_GC_GUARD(buffer);
|
|
551
|
+
|
|
552
|
+
RAISE_IF_EXCEPTION(ret);
|
|
529
553
|
RB_GC_GUARD(ret);
|
|
530
|
-
return
|
|
554
|
+
return ret;
|
|
531
555
|
}
|
|
532
556
|
|
|
533
557
|
VALUE um_bind(struct um *machine, int fd, struct sockaddr *addr, socklen_t addrlen) {
|
|
@@ -540,8 +564,9 @@ VALUE um_bind(struct um *machine, int fd, struct sockaddr *addr, socklen_t addrl
|
|
|
540
564
|
if (um_check_completion(machine, &op))
|
|
541
565
|
ret = INT2NUM(op.result.res);
|
|
542
566
|
|
|
567
|
+
RAISE_IF_EXCEPTION(ret);
|
|
543
568
|
RB_GC_GUARD(ret);
|
|
544
|
-
return
|
|
569
|
+
return ret;
|
|
545
570
|
}
|
|
546
571
|
|
|
547
572
|
VALUE um_listen(struct um *machine, int fd, int backlog) {
|
|
@@ -554,8 +579,9 @@ VALUE um_listen(struct um *machine, int fd, int backlog) {
|
|
|
554
579
|
if (um_check_completion(machine, &op))
|
|
555
580
|
ret = INT2NUM(op.result.res);
|
|
556
581
|
|
|
582
|
+
RAISE_IF_EXCEPTION(ret);
|
|
557
583
|
RB_GC_GUARD(ret);
|
|
558
|
-
return
|
|
584
|
+
return ret;
|
|
559
585
|
}
|
|
560
586
|
|
|
561
587
|
VALUE um_getsockopt(struct um *machine, int fd, int level, int opt) {
|
|
@@ -579,8 +605,9 @@ VALUE um_getsockopt(struct um *machine, int fd, int level, int opt) {
|
|
|
579
605
|
ret = INT2NUM(value);
|
|
580
606
|
#endif
|
|
581
607
|
|
|
608
|
+
RAISE_IF_EXCEPTION(ret);
|
|
582
609
|
RB_GC_GUARD(ret);
|
|
583
|
-
return
|
|
610
|
+
return ret;
|
|
584
611
|
}
|
|
585
612
|
|
|
586
613
|
VALUE um_setsockopt(struct um *machine, int fd, int level, int opt, int value) {
|
|
@@ -602,8 +629,9 @@ VALUE um_setsockopt(struct um *machine, int fd, int level, int opt, int value) {
|
|
|
602
629
|
ret = INT2NUM(0);
|
|
603
630
|
#endif
|
|
604
631
|
|
|
632
|
+
RAISE_IF_EXCEPTION(ret);
|
|
605
633
|
RB_GC_GUARD(ret);
|
|
606
|
-
return
|
|
634
|
+
return ret;
|
|
607
635
|
}
|
|
608
636
|
|
|
609
637
|
VALUE um_shutdown(struct um *machine, int fd, int how) {
|
|
@@ -618,13 +646,17 @@ VALUE um_shutdown(struct um *machine, int fd, int how) {
|
|
|
618
646
|
if (um_check_completion(machine, &op))
|
|
619
647
|
ret = INT2NUM(op.result.res);
|
|
620
648
|
|
|
649
|
+
RAISE_IF_EXCEPTION(ret);
|
|
621
650
|
RB_GC_GUARD(ret);
|
|
622
|
-
return
|
|
651
|
+
return ret;
|
|
623
652
|
}
|
|
624
653
|
|
|
625
654
|
VALUE um_shutdown_async(struct um *machine, int fd, int how) {
|
|
626
655
|
struct um_op *op = um_op_alloc(machine);
|
|
627
656
|
um_prep_op(machine, op, OP_SHUTDOWN_ASYNC, OP_F_FREE_ON_COMPLETE);
|
|
657
|
+
RB_OBJ_WRITE(machine->self, &op->fiber, Qnil);
|
|
658
|
+
RB_OBJ_WRITE(machine->self, &op->value, Qnil);
|
|
659
|
+
RB_OBJ_WRITE(machine->self, &op->async_op, Qnil);
|
|
628
660
|
|
|
629
661
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
630
662
|
io_uring_prep_shutdown(sqe, fd, how);
|
|
@@ -642,8 +674,24 @@ VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode) {
|
|
|
642
674
|
if (um_check_completion(machine, &op))
|
|
643
675
|
ret = INT2NUM(op.result.res);
|
|
644
676
|
|
|
677
|
+
RAISE_IF_EXCEPTION(ret);
|
|
678
|
+
RB_GC_GUARD(ret);
|
|
679
|
+
return ret;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
VALUE um_poll(struct um *machine, int fd, unsigned mask) {
|
|
683
|
+
struct um_op op;
|
|
684
|
+
um_prep_op(machine, &op, OP_POLL, 0);
|
|
685
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
686
|
+
io_uring_prep_poll_add(sqe, fd, mask);
|
|
687
|
+
|
|
688
|
+
VALUE ret = um_fiber_switch(machine);
|
|
689
|
+
if (um_check_completion(machine, &op))
|
|
690
|
+
ret = INT2NUM(op.result.res);
|
|
691
|
+
|
|
692
|
+
RAISE_IF_EXCEPTION(ret);
|
|
645
693
|
RB_GC_GUARD(ret);
|
|
646
|
-
return
|
|
694
|
+
return ret;
|
|
647
695
|
}
|
|
648
696
|
|
|
649
697
|
VALUE um_waitpid(struct um *machine, int pid, int options) {
|
|
@@ -658,8 +706,8 @@ VALUE um_waitpid(struct um *machine, int pid, int options) {
|
|
|
658
706
|
if (um_check_completion(machine, &op))
|
|
659
707
|
ret = INT2NUM(op.result.res);
|
|
660
708
|
|
|
709
|
+
RAISE_IF_EXCEPTION(ret);
|
|
661
710
|
RB_GC_GUARD(ret);
|
|
662
|
-
raise_if_exception(ret);
|
|
663
711
|
|
|
664
712
|
return rb_ary_new_from_args(2, INT2NUM(infop.si_pid), INT2NUM(infop.si_status));
|
|
665
713
|
}
|
|
@@ -688,24 +736,23 @@ VALUE statx_to_hash(struct statx *stat) {
|
|
|
688
736
|
}
|
|
689
737
|
|
|
690
738
|
VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned int mask) {
|
|
739
|
+
static char empty_path[] = "";
|
|
740
|
+
|
|
691
741
|
struct um_op op;
|
|
692
742
|
um_prep_op(machine, &op, OP_STATX, 0);
|
|
693
743
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
|
694
744
|
|
|
745
|
+
char *path_ptr = NIL_P(path) ? empty_path : StringValueCStr(path);
|
|
695
746
|
struct statx stat;
|
|
696
747
|
memset(&stat, 0, sizeof(stat));
|
|
697
|
-
|
|
698
|
-
if (NIL_P(path))
|
|
699
|
-
path = rb_str_new_literal("");
|
|
700
|
-
|
|
701
|
-
io_uring_prep_statx(sqe, dirfd, StringValueCStr(path), flags, mask, &stat);
|
|
748
|
+
io_uring_prep_statx(sqe, dirfd, path_ptr, flags, mask, &stat);
|
|
702
749
|
|
|
703
750
|
VALUE ret = um_fiber_switch(machine);
|
|
704
751
|
if (um_check_completion(machine, &op))
|
|
705
752
|
ret = INT2NUM(op.result.res);
|
|
706
753
|
|
|
754
|
+
RAISE_IF_EXCEPTION(ret);
|
|
707
755
|
RB_GC_GUARD(ret);
|
|
708
|
-
raise_if_exception(ret);
|
|
709
756
|
|
|
710
757
|
return statx_to_hash(&stat);
|
|
711
758
|
}
|
|
@@ -721,8 +768,11 @@ VALUE accept_each_start(VALUE arg) {
|
|
|
721
768
|
|
|
722
769
|
while (true) {
|
|
723
770
|
VALUE ret = um_fiber_switch(ctx->machine);
|
|
724
|
-
if (!um_op_completed_p(ctx->op))
|
|
725
|
-
|
|
771
|
+
if (!um_op_completed_p(ctx->op)) {
|
|
772
|
+
RAISE_IF_EXCEPTION(ret);
|
|
773
|
+
return ret;
|
|
774
|
+
}
|
|
775
|
+
RB_GC_GUARD(ret);
|
|
726
776
|
|
|
727
777
|
int more = false;
|
|
728
778
|
struct um_op_result *result = &ctx->op->result;
|
|
@@ -791,8 +841,11 @@ int um_read_each_singleshot_loop(struct op_ctx *ctx) {
|
|
|
791
841
|
rb_yield(buf);
|
|
792
842
|
RB_GC_GUARD(buf);
|
|
793
843
|
}
|
|
794
|
-
else
|
|
795
|
-
|
|
844
|
+
else {
|
|
845
|
+
RAISE_IF_EXCEPTION(ret);
|
|
846
|
+
return ret;
|
|
847
|
+
}
|
|
848
|
+
RB_GC_GUARD(ret);
|
|
796
849
|
}
|
|
797
850
|
}
|
|
798
851
|
|
|
@@ -840,8 +893,11 @@ VALUE read_recv_each_start(VALUE arg) {
|
|
|
840
893
|
|
|
841
894
|
while (true) {
|
|
842
895
|
VALUE ret = um_fiber_switch(ctx->machine);
|
|
843
|
-
if (!um_op_completed_p(ctx->op))
|
|
844
|
-
|
|
896
|
+
if (!um_op_completed_p(ctx->op)) {
|
|
897
|
+
RAISE_IF_EXCEPTION(ret);
|
|
898
|
+
return ret;
|
|
899
|
+
}
|
|
900
|
+
RB_GC_GUARD(ret);
|
|
845
901
|
|
|
846
902
|
int more = false;
|
|
847
903
|
struct um_op_result *result = &ctx->op->result;
|
|
@@ -888,8 +944,11 @@ VALUE periodically_start(VALUE arg) {
|
|
|
888
944
|
|
|
889
945
|
while (true) {
|
|
890
946
|
VALUE ret = um_fiber_switch(ctx->machine);
|
|
891
|
-
if (!um_op_completed_p(ctx->op))
|
|
892
|
-
|
|
947
|
+
if (!um_op_completed_p(ctx->op)) {
|
|
948
|
+
RAISE_IF_EXCEPTION(ret);
|
|
949
|
+
return ret;
|
|
950
|
+
}
|
|
951
|
+
RB_GC_GUARD(ret);
|
|
893
952
|
|
|
894
953
|
int more = false;
|
|
895
954
|
struct um_op_result *result = &ctx->op->result;
|
data/ext/um/um.h
CHANGED
|
@@ -45,7 +45,8 @@ enum op_kind {
|
|
|
45
45
|
OP_SETSOCKOPT,
|
|
46
46
|
OP_SHUTDOWN,
|
|
47
47
|
OP_SHUTDOWN_ASYNC,
|
|
48
|
-
|
|
48
|
+
|
|
49
|
+
OP_POLL,
|
|
49
50
|
OP_WAITPID,
|
|
50
51
|
|
|
51
52
|
OP_FUTEX_WAIT,
|
|
@@ -203,7 +204,7 @@ double um_timestamp_to_double(__s64 tv_sec, __u32 tv_nsec);
|
|
|
203
204
|
int um_value_is_exception_p(VALUE v);
|
|
204
205
|
VALUE um_raise_exception(VALUE v);
|
|
205
206
|
|
|
206
|
-
#define
|
|
207
|
+
#define RAISE_IF_EXCEPTION(v) if (um_value_is_exception_p(v)) { um_raise_exception(v); }
|
|
207
208
|
|
|
208
209
|
void um_prep_op(struct um *machine, struct um_op *op, enum op_kind kind, unsigned flags);
|
|
209
210
|
void um_raise_on_error_result(int result);
|
|
@@ -235,6 +236,7 @@ VALUE um_write(struct um *machine, int fd, VALUE str, int len);
|
|
|
235
236
|
VALUE um_close(struct um *machine, int fd);
|
|
236
237
|
VALUE um_close_async(struct um *machine, int fd);
|
|
237
238
|
VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode);
|
|
239
|
+
VALUE um_poll(struct um *machine, int fd, unsigned mask);
|
|
238
240
|
VALUE um_waitpid(struct um *machine, int pid, int options);
|
|
239
241
|
VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned int mask);
|
|
240
242
|
VALUE um_write_async(struct um *machine, int fd, VALUE str);
|
data/ext/um/um_async_op.c
CHANGED
|
@@ -30,7 +30,8 @@ VALUE um_async_op_await(struct um_async_op *async_op) {
|
|
|
30
30
|
if (!um_op_completed_p(async_op->op))
|
|
31
31
|
um_cancel_and_wait(async_op->machine, async_op->op);
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
RAISE_IF_EXCEPTION(ret);
|
|
34
|
+
RB_GC_GUARD(ret);
|
|
34
35
|
return INT2NUM(async_op->op->result.res);
|
|
35
36
|
}
|
|
36
37
|
|
data/ext/um/um_class.c
CHANGED
|
@@ -327,6 +327,11 @@ VALUE UM_open(VALUE self, VALUE pathname, VALUE flags) {
|
|
|
327
327
|
return fd;
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
+
VALUE UM_poll(VALUE self, VALUE fd, VALUE mask) {
|
|
331
|
+
struct um *machine = um_get_machine(self);
|
|
332
|
+
return um_poll(machine, NUM2INT(fd), NUM2UINT(mask));
|
|
333
|
+
}
|
|
334
|
+
|
|
330
335
|
VALUE UM_waitpid(VALUE self, VALUE pid, VALUE options) {
|
|
331
336
|
struct um *machine = um_get_machine(self);
|
|
332
337
|
return um_waitpid(machine, NUM2INT(pid), NUM2INT(options));
|
|
@@ -382,6 +387,7 @@ void Init_UM(void) {
|
|
|
382
387
|
rb_define_method(cUM, "write_async", UM_write_async, 2);
|
|
383
388
|
rb_define_method(cUM, "statx", UM_statx, 4);
|
|
384
389
|
|
|
390
|
+
rb_define_method(cUM, "poll", UM_poll, 2);
|
|
385
391
|
rb_define_method(cUM, "waitpid", UM_waitpid, 2);
|
|
386
392
|
|
|
387
393
|
rb_define_method(cUM, "accept", UM_accept, 1);
|
data/ext/um/um_const.c
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
#include <netinet/udp.h>
|
|
13
13
|
#include <netdb.h>
|
|
14
14
|
#include <net/if.h>
|
|
15
|
-
|
|
15
|
+
#include <poll.h>
|
|
16
16
|
|
|
17
17
|
#define DEF_CONST_INT(mod, v) rb_define_const(mod, #v, INT2NUM(v))
|
|
18
18
|
|
|
@@ -258,6 +258,10 @@ void um_define_net_constants(VALUE mod) {
|
|
|
258
258
|
|
|
259
259
|
DEF_CONST_INT(mod, SOMAXCONN);
|
|
260
260
|
|
|
261
|
+
DEF_CONST_INT(mod, POLLIN);
|
|
262
|
+
DEF_CONST_INT(mod, POLLOUT);
|
|
263
|
+
DEF_CONST_INT(mod, POLLERR);
|
|
264
|
+
|
|
261
265
|
DEF_CONST_INT(mod, EPERM);
|
|
262
266
|
DEF_CONST_INT(mod, ENOENT);
|
|
263
267
|
DEF_CONST_INT(mod, ESRCH);
|
|
@@ -387,5 +391,4 @@ void um_define_net_constants(VALUE mod) {
|
|
|
387
391
|
DEF_CONST_INT(mod, EKEYREJECTED);
|
|
388
392
|
DEF_CONST_INT(mod, EOWNERDEAD);
|
|
389
393
|
DEF_CONST_INT(mod, ENOTRECOVERABLE);
|
|
390
|
-
|
|
391
394
|
}
|
data/ext/um/um_stream.c
CHANGED
|
@@ -26,7 +26,7 @@ int stream_read_more(struct um_stream *stream) {
|
|
|
26
26
|
size_t capa = rb_str_capacity(stream->buffer);
|
|
27
27
|
if (capa - stream->pos < maxlen)
|
|
28
28
|
rb_str_modify_expand(stream->buffer, maxlen - (capa - stream->pos));
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
rb_str_modify(stream->buffer);
|
|
31
31
|
char *ptr = RSTRING_PTR(stream->buffer) + stream->pos;
|
|
32
32
|
size_t ret = um_read_raw(stream->machine, stream->fd, ptr, maxlen);
|
|
@@ -94,7 +94,7 @@ VALUE stream_get_string(struct um_stream *stream, VALUE buf, ssize_t len) {
|
|
|
94
94
|
|
|
95
95
|
abslen = stream->len - stream->pos;
|
|
96
96
|
}
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
char *start = RSTRING_PTR(stream->buffer) + stream->pos;
|
|
99
99
|
stream->pos += abslen;
|
|
100
100
|
|
|
@@ -137,7 +137,7 @@ VALUE resp_get_string(struct um_stream *stream, ulong len, VALUE out_buffer) {
|
|
|
137
137
|
|
|
138
138
|
while (stream->len - stream->pos < read_len)
|
|
139
139
|
if (!stream_read_more(stream)) return Qnil;
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
char *start = RSTRING_PTR(stream->buffer) + stream->pos;
|
|
142
142
|
stream->pos += read_len;
|
|
143
143
|
|
|
@@ -198,7 +198,7 @@ static inline VALUE resp_decode_string_with_encoding(struct um_stream *stream, V
|
|
|
198
198
|
|
|
199
199
|
static inline VALUE resp_decode_integer(char *ptr) {
|
|
200
200
|
long value = strtol(ptr + 1, NULL, 10);
|
|
201
|
-
return LONG2NUM(value);
|
|
201
|
+
return LONG2NUM(value);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
static inline VALUE resp_decode_float(char *ptr) {
|
|
@@ -234,13 +234,13 @@ VALUE resp_decode(struct um_stream *stream, VALUE out_buffer) {
|
|
|
234
234
|
ulong len = RSTRING_LEN(msg);
|
|
235
235
|
ulong data_len;
|
|
236
236
|
if (len == 0) return Qnil;
|
|
237
|
-
|
|
237
|
+
|
|
238
238
|
switch (ptr[0]) {
|
|
239
239
|
case '%': // hash
|
|
240
240
|
case '|': // attributes hash
|
|
241
241
|
data_len = resp_parse_length_field(ptr, len);
|
|
242
242
|
return resp_decode_hash(stream, out_buffer, data_len);
|
|
243
|
-
|
|
243
|
+
|
|
244
244
|
case '*': // array
|
|
245
245
|
case '~': // set
|
|
246
246
|
case '>': // pub/sub push
|
|
@@ -276,7 +276,7 @@ VALUE resp_decode(struct um_stream *stream, VALUE out_buffer) {
|
|
|
276
276
|
default:
|
|
277
277
|
rb_raise(rb_eRuntimeError, "Invalid character encountered");
|
|
278
278
|
}
|
|
279
|
-
|
|
279
|
+
|
|
280
280
|
RB_GC_GUARD(msg);
|
|
281
281
|
}
|
|
282
282
|
|
|
@@ -284,7 +284,7 @@ void write_buffer_init(struct um_write_buffer *buf, VALUE str) {
|
|
|
284
284
|
size_t capa = 1 << 12;
|
|
285
285
|
size_t len = RSTRING_LEN(str);
|
|
286
286
|
while (capa < len) capa += 1 << 12;
|
|
287
|
-
|
|
287
|
+
|
|
288
288
|
rb_str_resize(str, capa);
|
|
289
289
|
rb_str_set_len(str, len);
|
|
290
290
|
buf->str = str;
|
data/ext/um/um_stream_class.c
CHANGED
|
@@ -88,7 +88,7 @@ void Init_Stream(void) {
|
|
|
88
88
|
rb_define_alloc_func(cStream, Stream_allocate);
|
|
89
89
|
|
|
90
90
|
rb_define_method(cStream, "initialize", Stream_initialize, 2);
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
rb_define_method(cStream, "get_line", Stream_get_line, 2);
|
|
93
93
|
rb_define_method(cStream, "get_string", Stream_get_string, 2);
|
|
94
94
|
|
data/ext/um/um_sync.c
CHANGED
|
@@ -21,8 +21,8 @@ void um_futex_wait(struct um *machine, uint32_t *futex, uint32_t expect) {
|
|
|
21
21
|
um_raise_on_error_result(op.result.res);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
RAISE_IF_EXCEPTION(ret);
|
|
24
25
|
RB_GC_GUARD(ret);
|
|
25
|
-
raise_if_exception(ret);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
void um_futex_wake(struct um *machine, uint32_t *futex, uint32_t num_waiters) {
|
|
@@ -38,8 +38,8 @@ void um_futex_wake(struct um *machine, uint32_t *futex, uint32_t num_waiters) {
|
|
|
38
38
|
VALUE ret = um_fiber_switch(machine);
|
|
39
39
|
um_check_completion(machine, &op);
|
|
40
40
|
|
|
41
|
+
RAISE_IF_EXCEPTION(ret);
|
|
41
42
|
RB_GC_GUARD(ret);
|
|
42
|
-
raise_if_exception(ret);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
void um_futex_wake_transient(struct um *machine, uint32_t *futex, uint32_t num_waiters) {
|
data/ext/um/um_utils.c
CHANGED
|
@@ -129,7 +129,7 @@ inline void um_add_strings_to_buffer_ring(struct um *machine, int bgid, VALUE st
|
|
|
129
129
|
ulong count = RARRAY_LEN(strings);
|
|
130
130
|
VALUE str = Qnil;
|
|
131
131
|
VALUE converted = Qnil;
|
|
132
|
-
|
|
132
|
+
|
|
133
133
|
for (ulong i = 0; i < count; i++) {
|
|
134
134
|
str = rb_ary_entry(strings, i);
|
|
135
135
|
if (TYPE(str) != T_STRING) {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class UringMachine
|
|
4
|
+
class FiberScheduler
|
|
5
|
+
def initialize(machine)
|
|
6
|
+
@machine = machine
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def p(o)
|
|
10
|
+
@machine.write(1, "#{o.inspect}\n")
|
|
11
|
+
rescue Errno::EINTR
|
|
12
|
+
retry
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def join(*)
|
|
16
|
+
@machine.join(*)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def block(blocker, timeout)
|
|
20
|
+
p block: [blocker, timeout]
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def unblock(blocker, fiber)
|
|
25
|
+
p unblock: [blocker, fiber]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def kernel_sleep(duration = nil)
|
|
29
|
+
# p sleep: [duration]
|
|
30
|
+
if duration
|
|
31
|
+
@machine.sleep(duration)
|
|
32
|
+
else
|
|
33
|
+
@machine.yield
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def io_wait(io, events, timeout = nil)
|
|
38
|
+
timeout ||= io.timeout
|
|
39
|
+
p timeout: timeout
|
|
40
|
+
if timeout
|
|
41
|
+
p 1
|
|
42
|
+
@machine.timeout(timeout, Timeout::Error) {
|
|
43
|
+
p 2
|
|
44
|
+
@machine.poll(io.fileno, events).tap { p 3 }
|
|
45
|
+
}.tap { p 4 }
|
|
46
|
+
else
|
|
47
|
+
p 5
|
|
48
|
+
@machine.poll(io.fileno, events).tap { p 6 }
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
rescue => e
|
|
52
|
+
p e: e
|
|
53
|
+
raise
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def fiber(&block)
|
|
57
|
+
f = @machine.spin(&block)
|
|
58
|
+
@machine.snooze
|
|
59
|
+
f
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def io_write(io, buffer, length, offset)
|
|
63
|
+
p io_write: [io, buffer.get_string, length, offset]
|
|
64
|
+
@machine.write(io.fileno, buffer.get_string)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def io_read(io, buffer, length, offset)
|
|
68
|
+
# p io_read: [io, buffer, length, offset]
|
|
69
|
+
s = +''
|
|
70
|
+
length = buffer.size if length == 0
|
|
71
|
+
bytes = @machine.read(io.fileno, s, length)
|
|
72
|
+
buffer.set_string(s)
|
|
73
|
+
bytes
|
|
74
|
+
rescue SystemCallError => e
|
|
75
|
+
-e.errno
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def io_pwrite(io, buffer, from, length, offset)
|
|
79
|
+
p io_pwrite: [io, buffer, from, length, offset]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def io_pread(io, buffer, from, length, offset)
|
|
83
|
+
p io_pread: [io, buffer, from, length, offset]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# def fiber(&block)
|
|
87
|
+
# fiber = Fiber.new(blocking: false, &block)
|
|
88
|
+
# unblock(nil, fiber)
|
|
89
|
+
# # fiber.resume
|
|
90
|
+
# return fiber
|
|
91
|
+
# end
|
|
92
|
+
|
|
93
|
+
# def kernel_sleep(duration = nil)
|
|
94
|
+
# block(:sleep, duration)
|
|
95
|
+
# end
|
|
96
|
+
|
|
97
|
+
# def process_wait(pid, flags)
|
|
98
|
+
# # This is a very simple way to implement a non-blocking wait:
|
|
99
|
+
# Thread.new do
|
|
100
|
+
# Process::Status.wait(pid, flags)
|
|
101
|
+
# end.value
|
|
102
|
+
# end
|
|
103
|
+
end
|
|
104
|
+
end
|
data/lib/uringmachine/version.rb
CHANGED
data/test/test_stream.rb
CHANGED
|
@@ -7,7 +7,7 @@ class StreamBaseTest < UMBaseTest
|
|
|
7
7
|
super
|
|
8
8
|
@rfd, @wfd = UM.pipe
|
|
9
9
|
@stream = UM::Stream.new(@machine, @rfd)
|
|
10
|
-
end
|
|
10
|
+
end
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
class StreamTest < StreamBaseTest
|
|
@@ -166,7 +166,7 @@ class StreamRespTest < StreamBaseTest
|
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
def test_resp_encode
|
|
169
|
-
s = UM::Stream
|
|
169
|
+
s = UM::Stream
|
|
170
170
|
assert_equal "_\r\n", s.resp_encode(+'', nil)
|
|
171
171
|
assert_equal "#t\r\n", s.resp_encode(+'', true)
|
|
172
172
|
assert_equal "#f\r\n", s.resp_encode(+'', false)
|
|
@@ -174,7 +174,7 @@ class StreamRespTest < StreamBaseTest
|
|
|
174
174
|
assert_equal ",42.1\r\n", s.resp_encode(+'', 42.1)
|
|
175
175
|
assert_equal "$6\r\nfoobar\r\n", s.resp_encode(+'', 'foobar')
|
|
176
176
|
assert_equal "$10\r\nפובאר\r\n", s.resp_encode(+'', 'פובאר')
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
assert_equal "*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",
|
|
179
179
|
s.resp_encode(+'', ['foo', 'bar'])
|
|
180
180
|
|
data/test/test_um.rb
CHANGED
|
@@ -142,6 +142,31 @@ class ScheduleTest < UMBaseTest
|
|
|
142
142
|
assert_kind_of TOError, e
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
+
def test_timeout_stress
|
|
146
|
+
skip
|
|
147
|
+
# GC.stress = true
|
|
148
|
+
c = 0
|
|
149
|
+
fs = 100.times.map {
|
|
150
|
+
machine.spin {
|
|
151
|
+
q = UM::Queue.new
|
|
152
|
+
1000.times {
|
|
153
|
+
machine.sleep rand(0.001..0.005)
|
|
154
|
+
begin
|
|
155
|
+
machine.timeout(rand(0.001..0.06), TOError) do
|
|
156
|
+
machine.shift(q)
|
|
157
|
+
end
|
|
158
|
+
rescue => _e
|
|
159
|
+
c += 1
|
|
160
|
+
STDOUT << '*' if c % 1000 == 0
|
|
161
|
+
end
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
machine.join(*fs)
|
|
166
|
+
ensure
|
|
167
|
+
GC.stress = false
|
|
168
|
+
end
|
|
169
|
+
|
|
145
170
|
def test_timeout_with_raising_block
|
|
146
171
|
e = nil
|
|
147
172
|
begin
|
|
@@ -169,6 +194,17 @@ class ScheduleTest < UMBaseTest
|
|
|
169
194
|
assert_equal 0, machine.pending_count
|
|
170
195
|
end
|
|
171
196
|
|
|
197
|
+
def test_timeout_with_no_timeout
|
|
198
|
+
_r, w = UM.pipe
|
|
199
|
+
v = machine.timeout(0.1, TOError) { machine.write(w, 'foo') }
|
|
200
|
+
|
|
201
|
+
assert_equal 3, v
|
|
202
|
+
|
|
203
|
+
assert_equal 1, machine.pending_count
|
|
204
|
+
machine.sleep 0.01 # wait for cancelled CQE
|
|
205
|
+
assert_equal 0, machine.pending_count
|
|
206
|
+
end
|
|
207
|
+
|
|
172
208
|
class TO2Error < RuntimeError; end
|
|
173
209
|
class TO3Error < RuntimeError; end
|
|
174
210
|
|
|
@@ -1299,6 +1335,37 @@ class PipeTest < UMBaseTest
|
|
|
1299
1335
|
end
|
|
1300
1336
|
end
|
|
1301
1337
|
|
|
1338
|
+
class PollTest < UMBaseTest
|
|
1339
|
+
def test_poll
|
|
1340
|
+
rfd, wfd = UM.pipe
|
|
1341
|
+
|
|
1342
|
+
events = []
|
|
1343
|
+
f1 = machine.spin do
|
|
1344
|
+
events << :pre
|
|
1345
|
+
events << machine.poll(rfd, UM::POLLIN)
|
|
1346
|
+
events << :post
|
|
1347
|
+
end
|
|
1348
|
+
|
|
1349
|
+
machine.snooze
|
|
1350
|
+
assert_equal [:pre], events
|
|
1351
|
+
|
|
1352
|
+
machine.write(wfd, 'foo')
|
|
1353
|
+
machine.snooze
|
|
1354
|
+
assert_equal [:pre, UM::POLLIN, :post], events
|
|
1355
|
+
|
|
1356
|
+
ret = machine.poll(wfd, UM::POLLOUT)
|
|
1357
|
+
assert_equal UM::POLLOUT, ret
|
|
1358
|
+
|
|
1359
|
+
machine.close(rfd)
|
|
1360
|
+
ret = machine.poll(wfd, UM::POLLOUT | UM::POLLERR)
|
|
1361
|
+
assert_equal UM::POLLOUT | UM::POLLERR, ret
|
|
1362
|
+
end
|
|
1363
|
+
|
|
1364
|
+
def test_poll_bad_fd
|
|
1365
|
+
assert_raises(Errno::EBADF) { machine.poll(9876, POLLIN) }
|
|
1366
|
+
end
|
|
1367
|
+
end
|
|
1368
|
+
|
|
1302
1369
|
class WaitTest < UMBaseTest
|
|
1303
1370
|
def test_waitpid
|
|
1304
1371
|
skip if UM.kernel_version < 607
|
|
@@ -1396,6 +1463,17 @@ class StatxTest < UMBaseTest
|
|
|
1396
1463
|
io.close
|
|
1397
1464
|
end
|
|
1398
1465
|
|
|
1466
|
+
def test_statx_mask
|
|
1467
|
+
fd = @machine.open(__FILE__, UM::O_RDONLY)
|
|
1468
|
+
ustat = machine.statx(fd, nil, UM::AT_EMPTY_PATH, UM::STATX_MTIME | UM::STATX_SIZE)
|
|
1469
|
+
rstat = File.stat(__FILE__)
|
|
1470
|
+
|
|
1471
|
+
assert_equal rstat.size, ustat[:size]
|
|
1472
|
+
assert_equal rstat.mtime.to_i, ustat[:mtime].to_i
|
|
1473
|
+
ensure
|
|
1474
|
+
@machine.close_async(fd)
|
|
1475
|
+
end
|
|
1476
|
+
|
|
1399
1477
|
def test_statx_bad_path
|
|
1400
1478
|
assert_raises(Errno::ENOENT) { machine.statx(UM::AT_FDCWD, 'foobar', 0, UM::STATX_ALL) }
|
|
1401
1479
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: uringmachine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 0.19.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sharon Rosner
|
|
@@ -92,6 +92,7 @@ files:
|
|
|
92
92
|
- examples/bm_write.rb
|
|
93
93
|
- examples/dns_client.rb
|
|
94
94
|
- examples/echo_server.rb
|
|
95
|
+
- examples/fiber_scheduler_demo.rb
|
|
95
96
|
- examples/http_server.rb
|
|
96
97
|
- examples/inout.rb
|
|
97
98
|
- examples/nc.rb
|
|
@@ -119,6 +120,7 @@ files:
|
|
|
119
120
|
- lib/uringmachine.rb
|
|
120
121
|
- lib/uringmachine/actor.rb
|
|
121
122
|
- lib/uringmachine/dns_resolver.rb
|
|
123
|
+
- lib/uringmachine/fiber_scheduler.rb
|
|
122
124
|
- lib/uringmachine/version.rb
|
|
123
125
|
- supressions/ruby.supp
|
|
124
126
|
- test/helper.rb
|
|
@@ -673,7 +675,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
673
675
|
- !ruby/object:Gem::Version
|
|
674
676
|
version: '0'
|
|
675
677
|
requirements: []
|
|
676
|
-
rubygems_version: 3.
|
|
678
|
+
rubygems_version: 3.7.0.dev
|
|
677
679
|
specification_version: 4
|
|
678
680
|
summary: A lean, mean io_uring machine
|
|
679
681
|
test_files: []
|