uringmachine 0.5.1 → 0.6
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/.github/workflows/test.yml +1 -1
- data/CHANGELOG.md +4 -0
- data/TODO.md +4 -0
- data/examples/bm_http_parse.rb +149 -0
- data/examples/bm_queue.rb +111 -0
- data/ext/um/extconf.rb +1 -1
- data/ext/um/um.c +73 -2
- data/ext/um/um.h +9 -6
- data/ext/um/um_class.c +6 -0
- data/lib/uringmachine/actor.rb +52 -0
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +2 -2
- data/test/test_actor.rb +63 -0
- data/test/test_um.rb +71 -0
- data/uringmachine.gemspec +2 -2
- metadata +11 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc849d9467e24fa296a104e5b1caf71907495a3ed9cc68370bc6eb134a474cbf
|
4
|
+
data.tar.gz: 4fcdf5cd6055321f37375094aa11b56696c894d6a2c47ed9f756fb9a40d79753
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d83d7419000bd434bac101d72c6850274a71dd0324bd1732043cbdb08ef659b683e1010dc31093fe7a77e37368698db56592685bf7fe8a2f76ee4cd70cee52f
|
7
|
+
data.tar.gz: dcb79682bbdc20b99bd312aad22145642c4479009250f01f2e0c83f506026218d973b5c6e8b0aecff4e993acbe3998822bccbdf1de7c77a025b7560b5860ca77
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/TODO.md
CHANGED
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/inline'
|
4
|
+
|
5
|
+
gemfile do
|
6
|
+
source 'https://rubygems.org'
|
7
|
+
gem 'uringmachine', path: '..'
|
8
|
+
gem 'benchmark-ips'
|
9
|
+
gem 'http_parser.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'benchmark/ips'
|
13
|
+
require 'uringmachine'
|
14
|
+
require 'http/parser'
|
15
|
+
|
16
|
+
$machine = UM.new
|
17
|
+
|
18
|
+
HTTP_MSG = "GET /foo/bar HTTP/1.1\r\nServer: foobar.com\r\nFoo: bar\r\n\r\n"
|
19
|
+
|
20
|
+
$count = 0
|
21
|
+
|
22
|
+
def parse_http_parser
|
23
|
+
current_fiber = Fiber.current
|
24
|
+
$count += 1
|
25
|
+
r, w = IO.pipe
|
26
|
+
parser = Http::Parser.new
|
27
|
+
$machine.spin do
|
28
|
+
buffer = +''
|
29
|
+
loop do
|
30
|
+
res = $machine.read(r.fileno, buffer, 512)
|
31
|
+
break if res == 0
|
32
|
+
parser << buffer
|
33
|
+
end
|
34
|
+
rescue Exception => e
|
35
|
+
$machine.schedule(current_fiber, e)
|
36
|
+
# puts e.backtrace.join("\n")
|
37
|
+
# exit!
|
38
|
+
end
|
39
|
+
parser.on_message_complete = -> do
|
40
|
+
headers = parser.headers
|
41
|
+
headers['method'] = parser.http_method.downcase
|
42
|
+
headers['path'] = parser.request_url
|
43
|
+
headers['protocol'] = parser.http_version
|
44
|
+
$machine.schedule(current_fiber, headers)
|
45
|
+
end
|
46
|
+
|
47
|
+
$machine.write(w.fileno, HTTP_MSG)
|
48
|
+
$machine.yield
|
49
|
+
ensure
|
50
|
+
$machine.close(r.fileno)
|
51
|
+
$machine.close(w.fileno)
|
52
|
+
end
|
53
|
+
|
54
|
+
require 'stringio'
|
55
|
+
|
56
|
+
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
57
|
+
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
58
|
+
|
59
|
+
def get_line(fd, sio, buffer)
|
60
|
+
while true
|
61
|
+
line = sio.gets(chomp: true)
|
62
|
+
return line if line
|
63
|
+
|
64
|
+
res = $machine.read(fd, buffer, 1024, -1)
|
65
|
+
return nil if res == 0
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_request_line(fd, sio, buffer)
|
70
|
+
line = get_line(fd, sio, buffer)
|
71
|
+
|
72
|
+
m = line.match(RE_REQUEST_LINE)
|
73
|
+
return nil if !m
|
74
|
+
|
75
|
+
{
|
76
|
+
'method' => m[1].downcase,
|
77
|
+
'path' => m[2],
|
78
|
+
'protocol' => m[3].downcase
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def parse_headers(fd)
|
83
|
+
buffer = String.new('', capacity: 4096)
|
84
|
+
sio = StringIO.new(buffer)
|
85
|
+
|
86
|
+
headers = get_request_line(fd, sio, buffer)
|
87
|
+
return nil if !headers
|
88
|
+
|
89
|
+
while true
|
90
|
+
line = get_line(fd, sio, buffer)
|
91
|
+
break if line.empty?
|
92
|
+
|
93
|
+
m = line.match(RE_HEADER_LINE)
|
94
|
+
raise "Invalid header" if !m
|
95
|
+
|
96
|
+
headers[m[1]] = m[2]
|
97
|
+
end
|
98
|
+
|
99
|
+
headers
|
100
|
+
end
|
101
|
+
|
102
|
+
def parse_http_stringio
|
103
|
+
current_fiber = Fiber.current
|
104
|
+
r, w = IO.pipe
|
105
|
+
|
106
|
+
$machine.spin do
|
107
|
+
headers = parse_headers(r.fileno)
|
108
|
+
$machine.schedule(current_fiber, headers)
|
109
|
+
rescue Exception => e
|
110
|
+
p e
|
111
|
+
puts e.backtrace.join("\n")
|
112
|
+
exit!
|
113
|
+
end
|
114
|
+
|
115
|
+
$machine.write(w.fileno, HTTP_MSG)
|
116
|
+
$machine.yield
|
117
|
+
ensure
|
118
|
+
$machine.close(r.fileno)
|
119
|
+
$machine.close(w.fileno)
|
120
|
+
end
|
121
|
+
|
122
|
+
# p parse_http_parser
|
123
|
+
# p parse_http_stringio
|
124
|
+
# exit
|
125
|
+
|
126
|
+
GC.disable
|
127
|
+
|
128
|
+
def alloc_count
|
129
|
+
count0 = ObjectSpace.count_objects[:TOTAL]
|
130
|
+
yield
|
131
|
+
count1 = ObjectSpace.count_objects[:TOTAL]
|
132
|
+
count1 - count0
|
133
|
+
end
|
134
|
+
|
135
|
+
X = 100
|
136
|
+
p(
|
137
|
+
alloc_http_parser: alloc_count { X.times { parse_http_parser } },
|
138
|
+
alloc_stringio: alloc_count { X.times { parse_http_stringio } }
|
139
|
+
)
|
140
|
+
exit
|
141
|
+
|
142
|
+
Benchmark.ips do |x|
|
143
|
+
x.config(:time => 5, :warmup => 3)
|
144
|
+
|
145
|
+
x.report("http_parser") { parse_http_parser }
|
146
|
+
x.report("homegrown") { parse_http_stringio }
|
147
|
+
|
148
|
+
x.compare!
|
149
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/inline'
|
4
|
+
|
5
|
+
gemfile do
|
6
|
+
source 'https://rubygems.org'
|
7
|
+
gem 'uringmachine', path: '..'
|
8
|
+
gem 'benchmark-ips'
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'benchmark/ips'
|
12
|
+
require 'uringmachine'
|
13
|
+
|
14
|
+
COUNT = 1000
|
15
|
+
NUM_PRODUCERS = 2
|
16
|
+
NUM_CONSUMERS = 10
|
17
|
+
|
18
|
+
def run_threads
|
19
|
+
queue = Queue.new
|
20
|
+
done = Queue.new
|
21
|
+
|
22
|
+
NUM_PRODUCERS.times do
|
23
|
+
Thread.new do
|
24
|
+
COUNT.times { queue << rand(1000) }
|
25
|
+
done << true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
total = 0
|
30
|
+
NUM_CONSUMERS.times do
|
31
|
+
Thread.new do
|
32
|
+
loop do
|
33
|
+
item = queue.shift
|
34
|
+
break if item.nil?
|
35
|
+
|
36
|
+
total += item
|
37
|
+
end
|
38
|
+
done << true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# wait for producers
|
43
|
+
NUM_PRODUCERS.times { done.shift }
|
44
|
+
|
45
|
+
# stop and wait for consumers
|
46
|
+
NUM_CONSUMERS.times do
|
47
|
+
queue << nil
|
48
|
+
done.shift
|
49
|
+
end
|
50
|
+
|
51
|
+
total
|
52
|
+
end
|
53
|
+
|
54
|
+
def run_um
|
55
|
+
machine = UM.new
|
56
|
+
queue = UM::Queue.new
|
57
|
+
done = UM::Queue.new
|
58
|
+
|
59
|
+
NUM_PRODUCERS.times do
|
60
|
+
machine.spin do
|
61
|
+
COUNT.times { machine.push(queue, rand(1000)) }
|
62
|
+
machine.push(done, true)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
total = 0
|
67
|
+
NUM_CONSUMERS.times do
|
68
|
+
machine.spin do
|
69
|
+
loop do
|
70
|
+
item = machine.shift(queue)
|
71
|
+
break if item.nil?
|
72
|
+
|
73
|
+
total += item
|
74
|
+
end
|
75
|
+
machine.push(done, true)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# wait for producers
|
80
|
+
NUM_PRODUCERS.times { machine.shift(done) }
|
81
|
+
|
82
|
+
# stop and wait for consumers
|
83
|
+
NUM_CONSUMERS.times do
|
84
|
+
machine.push(queue, nil)
|
85
|
+
machine.shift(done)
|
86
|
+
end
|
87
|
+
|
88
|
+
total
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# puts "running"
|
93
|
+
# res = run_threads
|
94
|
+
# p threads: res
|
95
|
+
|
96
|
+
# 100.times {
|
97
|
+
# res = run_um
|
98
|
+
# p fibers: res
|
99
|
+
# }
|
100
|
+
|
101
|
+
|
102
|
+
# __END__
|
103
|
+
|
104
|
+
Benchmark.ips do |x|
|
105
|
+
x.config(:time => 5, :warmup => 2)
|
106
|
+
|
107
|
+
x.report("threads") { run_threads }
|
108
|
+
x.report("UM") { run_um }
|
109
|
+
|
110
|
+
x.compare!
|
111
|
+
end
|
data/ext/um/extconf.rb
CHANGED
data/ext/um/um.c
CHANGED
@@ -88,8 +88,9 @@ static inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe)
|
|
88
88
|
op->result.flags = cqe->flags;
|
89
89
|
}
|
90
90
|
|
91
|
-
if (
|
92
|
-
|
91
|
+
if (op->flags & OP_F_ASYNC) return;
|
92
|
+
|
93
|
+
um_runqueue_push(machine, op);
|
93
94
|
}
|
94
95
|
|
95
96
|
// copied from liburing/queue.c
|
@@ -216,6 +217,8 @@ inline void um_prep_op(struct um *machine, struct um_op *op, enum op_kind kind)
|
|
216
217
|
case OP_ACCEPT_MULTISHOT:
|
217
218
|
case OP_READ_MULTISHOT:
|
218
219
|
case OP_RECV_MULTISHOT:
|
220
|
+
case OP_TIMEOUT_MULTISHOT:
|
221
|
+
case OP_SLEEP_MULTISHOT:
|
219
222
|
op->flags |= OP_F_MULTISHOT;
|
220
223
|
default:
|
221
224
|
}
|
@@ -297,6 +300,33 @@ VALUE um_sleep(struct um *machine, double duration) {
|
|
297
300
|
return raise_if_exception(ret);
|
298
301
|
}
|
299
302
|
|
303
|
+
// VALUE um_periodically(struct um *machine, double interval) {
|
304
|
+
// struct um_op op;
|
305
|
+
// VALUE ret = Qnil;
|
306
|
+
// um_prep_op(machine, &op, OP_SLEEP_MULTISHOT);
|
307
|
+
// op.ts = um_double_to_timespec(interval);
|
308
|
+
// op.flags |= OP_F_MULTISHOT;
|
309
|
+
// struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
310
|
+
// io_uring_prep_timeout(sqe, &op.ts, 0, IORING_TIMEOUT_MULTISHOT);
|
311
|
+
|
312
|
+
// while (true) {
|
313
|
+
// ret = um_fiber_switch(machine);
|
314
|
+
|
315
|
+
// if (!um_op_completed_p(&op)) {
|
316
|
+
// um_cancel_and_wait(machine, &op);
|
317
|
+
// break;
|
318
|
+
// }
|
319
|
+
// else {
|
320
|
+
// if (op.result.res != -ETIME) um_raise_on_error_result(op.result.res);
|
321
|
+
// ret = DBL2NUM(interval);
|
322
|
+
// }
|
323
|
+
// }
|
324
|
+
|
325
|
+
// RB_GC_GUARD(ret);
|
326
|
+
// return raise_if_exception(ret);
|
327
|
+
|
328
|
+
// }
|
329
|
+
|
300
330
|
inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset) {
|
301
331
|
struct um_op op;
|
302
332
|
um_prep_op(machine, &op, OP_READ);
|
@@ -701,3 +731,44 @@ VALUE um_recv_each(struct um *machine, int fd, int bgid, int flags) {
|
|
701
731
|
struct op_ctx ctx = { .machine = machine, .op = &op, .fd = fd, .bgid = bgid, .read_buf = NULL, .flags = flags };
|
702
732
|
return rb_ensure(read_recv_each_begin, (VALUE)&ctx, multishot_ensure, (VALUE)&ctx);
|
703
733
|
}
|
734
|
+
|
735
|
+
VALUE periodically_begin(VALUE arg) {
|
736
|
+
struct op_ctx *ctx = (struct op_ctx *)arg;
|
737
|
+
struct io_uring_sqe *sqe = um_get_sqe(ctx->machine, ctx->op);
|
738
|
+
io_uring_prep_timeout(sqe, &ctx->ts, 0, IORING_TIMEOUT_MULTISHOT);
|
739
|
+
|
740
|
+
while (true) {
|
741
|
+
VALUE ret = um_fiber_switch(ctx->machine);
|
742
|
+
if (!um_op_completed_p(ctx->op))
|
743
|
+
return raise_if_exception(ret);
|
744
|
+
|
745
|
+
int more = false;
|
746
|
+
struct um_op_result *result = &ctx->op->result;
|
747
|
+
while (result) {
|
748
|
+
more = (result->flags & IORING_CQE_F_MORE);
|
749
|
+
if (result->res < 0 && result->res != -ETIME) {
|
750
|
+
um_op_multishot_results_clear(ctx->machine, ctx->op);
|
751
|
+
return Qnil;
|
752
|
+
}
|
753
|
+
rb_yield(Qnil);
|
754
|
+
result = result->next;
|
755
|
+
}
|
756
|
+
um_op_multishot_results_clear(ctx->machine, ctx->op);
|
757
|
+
if (more)
|
758
|
+
ctx->op->flags &= ~OP_F_COMPLETED;
|
759
|
+
else
|
760
|
+
break;
|
761
|
+
}
|
762
|
+
|
763
|
+
return Qnil;
|
764
|
+
}
|
765
|
+
|
766
|
+
VALUE um_periodically(struct um *machine, double interval) {
|
767
|
+
struct um_op op;
|
768
|
+
um_prep_op(machine, &op, OP_SLEEP_MULTISHOT);
|
769
|
+
op.ts = um_double_to_timespec(interval);
|
770
|
+
|
771
|
+
struct op_ctx ctx = { .machine = machine, .op = &op, .ts = op.ts, .read_buf = NULL };
|
772
|
+
return rb_ensure(periodically_begin, (VALUE)&ctx, multishot_ensure, (VALUE)&ctx);
|
773
|
+
}
|
774
|
+
|
data/ext/um/um.h
CHANGED
@@ -43,14 +43,16 @@ enum op_kind {
|
|
43
43
|
|
44
44
|
OP_ACCEPT_MULTISHOT,
|
45
45
|
OP_READ_MULTISHOT,
|
46
|
-
OP_RECV_MULTISHOT
|
46
|
+
OP_RECV_MULTISHOT,
|
47
|
+
OP_TIMEOUT_MULTISHOT,
|
48
|
+
OP_SLEEP_MULTISHOT
|
47
49
|
};
|
48
50
|
|
49
|
-
#define OP_F_COMPLETED (1U << 0)
|
50
|
-
#define OP_F_TRANSIENT (1U << 1)
|
51
|
-
#define OP_F_ASYNC (1U << 2)
|
52
|
-
#define OP_F_IGNORE_CANCELED (1U << 3)
|
53
|
-
#define OP_F_MULTISHOT (1U << 4)
|
51
|
+
#define OP_F_COMPLETED (1U << 0) // op is completed (set on each CQE for multishot ops)
|
52
|
+
#define OP_F_TRANSIENT (1U << 1) // op is heap allocated
|
53
|
+
#define OP_F_ASYNC (1U << 2) // op belongs to an AsyncOp
|
54
|
+
#define OP_F_IGNORE_CANCELED (1U << 3) // CQE with -ECANCEL should be ignored
|
55
|
+
#define OP_F_MULTISHOT (1U << 4) // op is multishot
|
54
56
|
|
55
57
|
struct um_op_result {
|
56
58
|
__s32 res;
|
@@ -199,6 +201,7 @@ void um_schedule(struct um *machine, VALUE fiber, VALUE value);
|
|
199
201
|
VALUE um_timeout(struct um *machine, VALUE interval, VALUE class);
|
200
202
|
|
201
203
|
VALUE um_sleep(struct um *machine, double duration);
|
204
|
+
VALUE um_periodically(struct um *machine, double interval);
|
202
205
|
VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset);
|
203
206
|
VALUE um_read_each(struct um *machine, int fd, int bgid);
|
204
207
|
VALUE um_write(struct um *machine, int fd, VALUE str, int len);
|
data/ext/um/um_class.c
CHANGED
@@ -91,6 +91,11 @@ VALUE UM_sleep(VALUE self, VALUE duration) {
|
|
91
91
|
return um_sleep(machine, NUM2DBL(duration));
|
92
92
|
}
|
93
93
|
|
94
|
+
VALUE UM_periodically(VALUE self, VALUE interval) {
|
95
|
+
struct um *machine = um_get_machine(self);
|
96
|
+
return um_periodically(machine, NUM2DBL(interval));
|
97
|
+
}
|
98
|
+
|
94
99
|
VALUE UM_read(int argc, VALUE *argv, VALUE self) {
|
95
100
|
struct um *machine = um_get_machine(self);
|
96
101
|
VALUE fd;
|
@@ -329,6 +334,7 @@ void Init_UM(void) {
|
|
329
334
|
rb_define_method(cUM, "read", UM_read, -1);
|
330
335
|
rb_define_method(cUM, "read_each", UM_read_each, 2);
|
331
336
|
rb_define_method(cUM, "sleep", UM_sleep, 1);
|
337
|
+
rb_define_method(cUM, "periodically", UM_periodically, 1);
|
332
338
|
rb_define_method(cUM, "write", UM_write, -1);
|
333
339
|
|
334
340
|
rb_define_method(cUM, "waitpid", UM_waitpid, 2);
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UringMachine
|
4
|
+
def spin_actor(mod, *a, **k)
|
5
|
+
target = Object.new.extend(mod)
|
6
|
+
mailbox = UM::Queue.new
|
7
|
+
actor = spin(nil, Actor) { actor.run(self, target, mailbox) }
|
8
|
+
target.setup(*a, **k)
|
9
|
+
snooze
|
10
|
+
actor
|
11
|
+
end
|
12
|
+
|
13
|
+
class Actor < Fiber
|
14
|
+
def run(machine, target, mailbox)
|
15
|
+
@machine = machine
|
16
|
+
@target = target
|
17
|
+
@mailbox = mailbox
|
18
|
+
while (msg = machine.shift(mailbox))
|
19
|
+
process_message(msg)
|
20
|
+
end
|
21
|
+
ensure
|
22
|
+
@target.teardown if @target.respond_to?(:teardown)
|
23
|
+
end
|
24
|
+
|
25
|
+
def cast(sym, *a, **k)
|
26
|
+
self << [:cast, nil, sym, a, k]
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(sym, *a, **k)
|
31
|
+
self << [:call, Fiber.current, sym, a, k]
|
32
|
+
@machine.yield
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def process_message(msg)
|
38
|
+
type, fiber, sym, args, kwargs = msg
|
39
|
+
case type
|
40
|
+
when :cast
|
41
|
+
@target.send(sym, *args, **kwargs)
|
42
|
+
when :call
|
43
|
+
res = @target.send(sym, *args, **kwargs)
|
44
|
+
@machine.schedule(fiber, res)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def <<(msg)
|
49
|
+
@machine.push(@mailbox, msg)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/uringmachine/version.rb
CHANGED
data/lib/uringmachine.rb
CHANGED
@@ -12,8 +12,8 @@ class UringMachine
|
|
12
12
|
@@fiber_map
|
13
13
|
end
|
14
14
|
|
15
|
-
def spin(value = nil, &block)
|
16
|
-
f =
|
15
|
+
def spin(value = nil, fiber_class = Fiber, &block)
|
16
|
+
f = fiber_class.new do |resume_value|
|
17
17
|
block.(resume_value)
|
18
18
|
rescue Exception => e
|
19
19
|
STDERR.puts "Unhandled fiber exception: #{e.inspect}"
|
data/test/test_actor.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
require 'socket'
|
5
|
+
require 'uringmachine/actor'
|
6
|
+
|
7
|
+
class ActorTest < UMBaseTest
|
8
|
+
module Counter
|
9
|
+
def setup
|
10
|
+
@count = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def incr
|
14
|
+
@count += 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def get
|
18
|
+
@count
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset
|
22
|
+
@count = 0
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_basic_actor_functionality
|
27
|
+
actor = @machine.spin_actor(Counter)
|
28
|
+
|
29
|
+
assert_kind_of Fiber, actor
|
30
|
+
|
31
|
+
assert_equal 0, actor.call(:get)
|
32
|
+
assert_equal 1, actor.call(:incr)
|
33
|
+
assert_equal actor, actor.cast(:incr)
|
34
|
+
assert_equal 2, actor.call(:get)
|
35
|
+
assert_equal actor, actor.cast(:reset)
|
36
|
+
assert_equal 0, actor.call(:get)
|
37
|
+
end
|
38
|
+
|
39
|
+
module Counter2
|
40
|
+
def setup(count)
|
41
|
+
@count = count
|
42
|
+
end
|
43
|
+
|
44
|
+
def incr
|
45
|
+
@count += 1
|
46
|
+
end
|
47
|
+
|
48
|
+
def get
|
49
|
+
@count
|
50
|
+
end
|
51
|
+
|
52
|
+
def reset
|
53
|
+
@count = 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def test_actor_with_args
|
59
|
+
actor = @machine.spin_actor(Counter2, 43)
|
60
|
+
|
61
|
+
assert_equal 43, actor.call(:get)
|
62
|
+
end
|
63
|
+
end
|
data/test/test_um.rb
CHANGED
@@ -212,6 +212,57 @@ class SleepTest < UMBaseTest
|
|
212
212
|
end
|
213
213
|
end
|
214
214
|
|
215
|
+
class PeriodicallyTest < UMBaseTest
|
216
|
+
class Cancel < StandardError; end
|
217
|
+
|
218
|
+
def test_periodically
|
219
|
+
count = 0
|
220
|
+
cancel = 0
|
221
|
+
|
222
|
+
t0 = monotonic_clock
|
223
|
+
assert_equal 0, machine.pending_count
|
224
|
+
begin
|
225
|
+
machine.periodically(0.01) do
|
226
|
+
count += 1
|
227
|
+
raise Cancel if count >= 5
|
228
|
+
end
|
229
|
+
rescue Cancel
|
230
|
+
cancel = 1
|
231
|
+
end
|
232
|
+
machine.snooze
|
233
|
+
assert_equal 0, machine.pending_count
|
234
|
+
t1 = monotonic_clock
|
235
|
+
assert_in_range 0.05..0.09, t1 - t0
|
236
|
+
assert_equal 5, count
|
237
|
+
assert_equal 1, cancel
|
238
|
+
end
|
239
|
+
|
240
|
+
def test_periodically_with_timeout
|
241
|
+
count = 0
|
242
|
+
cancel = 0
|
243
|
+
|
244
|
+
t0 = monotonic_clock
|
245
|
+
assert_equal 0, machine.pending_count
|
246
|
+
begin
|
247
|
+
machine.timeout(0.05, Cancel) do
|
248
|
+
machine.periodically(0.01) do
|
249
|
+
count += 1
|
250
|
+
raise Cancel if count >= 5
|
251
|
+
end
|
252
|
+
end
|
253
|
+
rescue Cancel
|
254
|
+
cancel = 1
|
255
|
+
end
|
256
|
+
machine.snooze
|
257
|
+
assert_equal 0, machine.pending_count
|
258
|
+
t1 = monotonic_clock
|
259
|
+
assert_in_range 0.05..0.08, t1 - t0
|
260
|
+
assert_in_range 4..6, count
|
261
|
+
assert_equal 1, cancel
|
262
|
+
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
215
266
|
class ReadTest < UMBaseTest
|
216
267
|
def test_read
|
217
268
|
r, w = IO.pipe
|
@@ -274,6 +325,26 @@ class ReadTest < UMBaseTest
|
|
274
325
|
assert_equal 3, result
|
275
326
|
assert_equal 'foobar', buffer
|
276
327
|
end
|
328
|
+
|
329
|
+
def test_read_with_string_io
|
330
|
+
require 'stringio'
|
331
|
+
|
332
|
+
buffer = +'foo'
|
333
|
+
sio = StringIO.new(buffer)
|
334
|
+
|
335
|
+
r, w = IO.pipe
|
336
|
+
w << 'bar'
|
337
|
+
|
338
|
+
result = machine.read(r.fileno, buffer, 100, -1)
|
339
|
+
assert_equal 3, result
|
340
|
+
assert_equal 'foobar', sio.read
|
341
|
+
|
342
|
+
w << 'baz'
|
343
|
+
|
344
|
+
result = machine.read(r.fileno, buffer, 100, -1)
|
345
|
+
assert_equal 3, result
|
346
|
+
assert_equal 'baz', sio.read
|
347
|
+
end
|
277
348
|
end
|
278
349
|
|
279
350
|
class ReadEachTest < UMBaseTest
|
data/uringmachine.gemspec
CHANGED
@@ -20,8 +20,8 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
s.required_ruby_version = '>= 3.3'
|
22
22
|
|
23
|
-
s.add_development_dependency 'rake-compiler', '1.2.
|
24
|
-
s.add_development_dependency 'minitest', '5.25.
|
23
|
+
s.add_development_dependency 'rake-compiler', '1.2.9'
|
24
|
+
s.add_development_dependency 'minitest', '5.25.4'
|
25
25
|
s.add_development_dependency 'http_parser.rb', '0.8.0'
|
26
26
|
s.add_development_dependency 'benchmark-ips', '2.14.0'
|
27
27
|
s.add_development_dependency 'localhost', '1.3.1'
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uringmachine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.6'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-04-23 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: rake-compiler
|
@@ -16,28 +15,28 @@ dependencies:
|
|
16
15
|
requirements:
|
17
16
|
- - '='
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.2.
|
18
|
+
version: 1.2.9
|
20
19
|
type: :development
|
21
20
|
prerelease: false
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
23
22
|
requirements:
|
24
23
|
- - '='
|
25
24
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.2.
|
25
|
+
version: 1.2.9
|
27
26
|
- !ruby/object:Gem::Dependency
|
28
27
|
name: minitest
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
30
29
|
requirements:
|
31
30
|
- - '='
|
32
31
|
- !ruby/object:Gem::Version
|
33
|
-
version: 5.25.
|
32
|
+
version: 5.25.4
|
34
33
|
type: :development
|
35
34
|
prerelease: false
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
37
36
|
requirements:
|
38
37
|
- - '='
|
39
38
|
- !ruby/object:Gem::Version
|
40
|
-
version: 5.25.
|
39
|
+
version: 5.25.4
|
41
40
|
- !ruby/object:Gem::Dependency
|
42
41
|
name: http_parser.rb
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,7 +79,6 @@ dependencies:
|
|
80
79
|
- - '='
|
81
80
|
- !ruby/object:Gem::Version
|
82
81
|
version: 1.3.1
|
83
|
-
description:
|
84
82
|
email: sharon@noteflakes.com
|
85
83
|
executables: []
|
86
84
|
extensions:
|
@@ -98,6 +96,8 @@ files:
|
|
98
96
|
- README.md
|
99
97
|
- Rakefile
|
100
98
|
- TODO.md
|
99
|
+
- examples/bm_http_parse.rb
|
100
|
+
- examples/bm_queue.rb
|
101
101
|
- examples/bm_snooze.rb
|
102
102
|
- examples/bm_sqlite.rb
|
103
103
|
- examples/bm_write.rb
|
@@ -129,12 +129,14 @@ files:
|
|
129
129
|
- ext/um/um_sync.c
|
130
130
|
- ext/um/um_utils.c
|
131
131
|
- lib/uringmachine.rb
|
132
|
+
- lib/uringmachine/actor.rb
|
132
133
|
- lib/uringmachine/dns_resolver.rb
|
133
134
|
- lib/uringmachine/ssl.rb
|
134
135
|
- lib/uringmachine/ssl/context_builder.rb
|
135
136
|
- lib/uringmachine/version.rb
|
136
137
|
- supressions/ruby.supp
|
137
138
|
- test/helper.rb
|
139
|
+
- test/test_actor.rb
|
138
140
|
- test/test_async_op.rb
|
139
141
|
- test/test_ssl.rb
|
140
142
|
- test/test_um.rb
|
@@ -439,7 +441,6 @@ metadata:
|
|
439
441
|
source_code_uri: https://github.com/digital-fabric/uringmachine
|
440
442
|
documentation_uri: https://www.rubydoc.info/gems/uringmachine
|
441
443
|
changelog_uri: https://github.com/digital-fabric/uringmachine/blob/master/CHANGELOG.md
|
442
|
-
post_install_message:
|
443
444
|
rdoc_options:
|
444
445
|
- "--title"
|
445
446
|
- UringMachine
|
@@ -458,8 +459,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
458
459
|
- !ruby/object:Gem::Version
|
459
460
|
version: '0'
|
460
461
|
requirements: []
|
461
|
-
rubygems_version: 3.
|
462
|
-
signing_key:
|
462
|
+
rubygems_version: 3.6.2
|
463
463
|
specification_version: 4
|
464
464
|
summary: A lean, mean io_uring machine
|
465
465
|
test_files: []
|