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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b063db46fac29472c42866eb1864c731b24fc0846fe424eec06213bba8fde0a
4
- data.tar.gz: a535fabf5d16107de8d3823766d9ff0d7d22d0702c5401e263eb925eb222dd5d
3
+ metadata.gz: dc849d9467e24fa296a104e5b1caf71907495a3ed9cc68370bc6eb134a474cbf
4
+ data.tar.gz: 4fcdf5cd6055321f37375094aa11b56696c894d6a2c47ed9f756fb9a40d79753
5
5
  SHA512:
6
- metadata.gz: 94ad9e942e84e87f0acbd75007962c3df5ea259de77ef4e590ff91dc4ea614dcd22e7db3da1954be05b11951e621f0cf58858391b677e109e97590bb0f8e3dee
7
- data.tar.gz: 4c77e826fb33fc879de748b877eec9a6d49f6927b8167377b4191c30b9aa2b638dbd0cc76a77ec0a17daec648d2587ca4357f367501391aad06c7532606e85b2
6
+ metadata.gz: 3d83d7419000bd434bac101d72c6850274a71dd0324bd1732043cbdb08ef659b683e1010dc31093fe7a77e37368698db56592685bf7fe8a2f76ee4cd70cee52f
7
+ data.tar.gz: dcb79682bbdc20b99bd312aad22145642c4479009250f01f2e0c83f506026218d973b5c6e8b0aecff4e993acbe3998822bccbdf1de7c77a025b7560b5860ca77
@@ -13,7 +13,7 @@ jobs:
13
13
  matrix:
14
14
  # macos-latest uses arm64, macos-13 uses x86
15
15
  os: [ubuntu-latest]
16
- ruby: ['3.3', 'head']
16
+ ruby: ['3.3', '3.4', 'head']
17
17
 
18
18
  name: ${{matrix.os}}, ${{matrix.ruby}}
19
19
 
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 2025-04-23 Version 0.6
2
+
3
+ - Add `#periodically` for multishot timeout
4
+ - Add `UM::Actor` class
1
5
  - Add `#prep_timeout` and `AsyncOp`
2
6
 
3
7
  # 2024-11-14 Version 0.5
data/TODO.md CHANGED
@@ -1,3 +1,7 @@
1
+ - [ ] multishot timeout
2
+ - [v] machine.periodically(interval) { ... }
3
+ - [ ] machine.prep_timeout_multishot(interval)
4
+
1
5
  - splice / - tee
2
6
  - sendto
3
7
  - recvfrom
@@ -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
@@ -90,7 +90,7 @@ def get_config
90
90
  }
91
91
  end
92
92
 
93
- config_ssl
93
+ # config_ssl
94
94
 
95
95
  config = get_config
96
96
  puts "Building UringMachine (\n#{config.map { |(k, v)| " #{k}: #{v}\n"}.join})"
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 (!(op->flags & OP_F_ASYNC))
92
- um_runqueue_push(machine, op);
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UringMachine
4
- VERSION = '0.5.1'
4
+ VERSION = '0.6'
5
5
  end
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 = Fiber.new do |resume_value|
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}"
@@ -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.8'
24
- s.add_development_dependency 'minitest', '5.25.1'
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.5.1
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: 2024-12-11 00:00:00.000000000 Z
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.8
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.8
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.1
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.1
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.5.16
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: []