uringmachine 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +3 -4
  3. data/.rubocop.yml +2 -0
  4. data/CHANGELOG.md +34 -0
  5. data/TODO.md +132 -26
  6. data/benchmark/README.md +173 -0
  7. data/benchmark/bm_io_pipe.rb +70 -0
  8. data/benchmark/bm_io_socketpair.rb +71 -0
  9. data/benchmark/bm_mutex_cpu.rb +57 -0
  10. data/benchmark/bm_mutex_io.rb +64 -0
  11. data/benchmark/bm_pg_client.rb +109 -0
  12. data/benchmark/bm_queue.rb +76 -0
  13. data/benchmark/chart.png +0 -0
  14. data/benchmark/common.rb +135 -0
  15. data/benchmark/dns_client.rb +47 -0
  16. data/{examples/bm_http_parse.rb → benchmark/http_parse.rb} +1 -1
  17. data/benchmark/run_bm.rb +8 -0
  18. data/benchmark/sqlite.rb +108 -0
  19. data/{examples/bm_write.rb → benchmark/write.rb} +6 -3
  20. data/ext/um/extconf.rb +1 -1
  21. data/ext/um/um.c +404 -95
  22. data/ext/um/um.h +77 -24
  23. data/ext/um/um_async_op.c +2 -2
  24. data/ext/um/um_class.c +168 -18
  25. data/ext/um/um_op.c +43 -0
  26. data/ext/um/um_sync.c +10 -16
  27. data/ext/um/um_utils.c +16 -0
  28. data/grant-2025/journal.md +242 -1
  29. data/grant-2025/tasks.md +136 -41
  30. data/lib/uringmachine/actor.rb +8 -0
  31. data/lib/uringmachine/dns_resolver.rb +1 -2
  32. data/lib/uringmachine/fiber_scheduler.rb +283 -110
  33. data/lib/uringmachine/version.rb +1 -1
  34. data/lib/uringmachine.rb +32 -3
  35. data/test/helper.rb +7 -18
  36. data/test/test_actor.rb +12 -3
  37. data/test/test_async_op.rb +10 -10
  38. data/test/test_fiber.rb +84 -1
  39. data/test/test_fiber_scheduler.rb +1425 -20
  40. data/test/test_um.rb +565 -113
  41. data/uringmachine.gemspec +6 -5
  42. data/vendor/liburing/src/include/liburing/io_uring.h +1 -0
  43. data/vendor/liburing/src/include/liburing.h +13 -0
  44. data/vendor/liburing/src/liburing-ffi.map +1 -0
  45. data/vendor/liburing/test/bind-listen.c +175 -13
  46. data/vendor/liburing/test/read-write.c +4 -4
  47. data/vendor/liburing/test/ringbuf-read.c +4 -4
  48. data/vendor/liburing/test/send_recv.c +8 -7
  49. metadata +50 -28
  50. data/examples/bm_fileno.rb +0 -33
  51. data/examples/bm_queue.rb +0 -110
  52. data/examples/bm_side_running.rb +0 -83
  53. data/examples/bm_sqlite.rb +0 -89
  54. data/examples/dns_client.rb +0 -12
  55. /data/{examples/bm_mutex.rb → benchmark/mutex.rb} +0 -0
  56. /data/{examples/bm_mutex_single.rb → benchmark/mutex_single.rb} +0 -0
  57. /data/{examples/bm_send.rb → benchmark/send.rb} +0 -0
  58. /data/{examples/bm_snooze.rb → benchmark/snooze.rb} +0 -0
data/examples/bm_queue.rb DELETED
@@ -1,110 +0,0 @@
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 = 8
16
- NUM_CONSUMERS = 8
17
-
18
- @queue = Queue.new
19
- @done = Queue.new
20
-
21
- def run_threads
22
-
23
- NUM_PRODUCERS.times do
24
- Thread.new do
25
- COUNT.times { @queue << rand(1000) }
26
- @done << true
27
- end
28
- end
29
-
30
- total = 0
31
- NUM_CONSUMERS.times do
32
- Thread.new do
33
- loop do
34
- item = @queue.shift
35
- break if item.nil?
36
-
37
- total += item
38
- end
39
- @done << true
40
- end
41
- end
42
-
43
- # wait for producers
44
- NUM_PRODUCERS.times { @done.shift }
45
-
46
- # stop and wait for consumers
47
- NUM_CONSUMERS.times do
48
- @queue << nil
49
- @done.shift
50
- end
51
-
52
- total
53
- end
54
-
55
- @machine = UM.new
56
- @um_queue = UM::Queue.new
57
- @um_done = UM::Queue.new
58
-
59
- def run_um
60
- NUM_PRODUCERS.times do
61
- @machine.spin do
62
- COUNT.times { @machine.push(@um_queue, rand(1000)) }
63
- @machine.push(@um_done, true)
64
- end
65
- end
66
-
67
- total = 0
68
- NUM_CONSUMERS.times do
69
- @machine.spin do
70
- loop do
71
- item = @machine.shift(@um_queue)
72
- break if item.nil?
73
-
74
- total += item
75
- end
76
- @machine.push(@um_done, true)
77
- end
78
- end
79
-
80
- # wait for producers
81
- NUM_PRODUCERS.times { @machine.shift(@um_done) }
82
-
83
- # stop and wait for consumers
84
- NUM_CONSUMERS.times do
85
- @machine.push(@um_queue, nil)
86
- @machine.shift(@um_done)
87
- end
88
-
89
- total
90
- end
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
- # __END__
102
-
103
- Benchmark.ips do |x|
104
- x.config(:time => 10, :warmup => 3)
105
-
106
- x.report("threads") { run_threads }
107
- x.report("UM") { run_um }
108
-
109
- x.compare!(order: :baseline)
110
- end
@@ -1,83 +0,0 @@
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
- def consume_from_queue(queue)
15
- m = UM.new
16
- while true
17
- response_mailbox, closure = m.shift(queue)
18
- result = closure.call
19
- m.push(response_mailbox, result)
20
- end
21
- # rescue UM::Terminate
22
- # # We can also add a timeout here
23
- # t0 = Time.now
24
- # while !queue.empty? && (Time.now - t0) < 10
25
- # response_mailbox, closure = m.shift(queue)
26
- # result = closure.call
27
- # m.push(response_mailbox, result)
28
- # end
29
- end
30
-
31
- N = 8
32
-
33
- $machine = UM.new
34
- @queue = UM::Queue.new
35
- @threads = N.times.map { Thread.new { consume_from_queue(@queue) } }
36
-
37
- def side_run(&block)
38
- mailbox = Fiber.current.mailbox
39
- $machine.push(@queue, [mailbox, block])
40
- Thread.pass
41
- $machine.shift(mailbox)
42
- end
43
-
44
- @rqueue = Queue.new
45
- @rthreads = N.times.map { Thread.new { r_consume_from_queue(@rqueue) }}
46
-
47
- def r_consume_from_queue(queue)
48
- m = UM.new
49
- while true
50
- response_mailbox, closure = @rqueue.shift
51
- result = closure.call
52
- m.push(response_mailbox, result)
53
- end
54
- # rescue UM::Terminate
55
- # # We can also add a timeout here
56
- # t0 = Time.now
57
- # while !queue.empty? && (Time.now - t0) < 10
58
- # response_mailbox, closure = m.shift(queue)
59
- # result = closure.call
60
- # m.push(response_mailbox, result)
61
- # end
62
- end
63
-
64
- def r_side_run(&block)
65
- mailbox = Fiber.current.mailbox
66
- @rqueue.push([mailbox, block])
67
- Thread.pass
68
- $machine.shift(mailbox)
69
- end
70
-
71
- # puts '*' * 40
72
- # p r_side_run { }
73
- # exit!
74
-
75
- Benchmark.ips do |x|
76
- x.config(:time => 5, :warmup => 2)
77
-
78
- x.report("side-run") { side_run { } }
79
- x.report("r-side-run") { r_side_run { } }
80
- # x.report("snoozing") { $machine.snooze }
81
-
82
- x.compare!
83
- end
@@ -1,89 +0,0 @@
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 'extralite'
9
- gem 'benchmark-ips'
10
- end
11
-
12
- require 'uringmachine'
13
- require 'extralite'
14
-
15
- class UM::Actor < Fiber
16
- def initialize(machine, target)
17
- @machine = machine
18
- @target = target
19
- @mailbox = UM::Queue.new
20
- super { act }
21
- end
22
-
23
- def act
24
- while (sym, a, k, peer = @machine.shift(@mailbox))
25
-
26
- begin
27
- ret = @target.send(sym, *a, **k)
28
- @machine.schedule(peer, ret)
29
- rescue => e
30
- @machine.schedule(peer, e)
31
- end
32
- end
33
- rescue Exception => e
34
- # handle unhandled exceptions
35
- ensure
36
- @machine.fiber_map.delete(self)
37
- @machine.yield
38
- end
39
-
40
- def method_missing(sym, *a, **k)
41
- @machine.push(@mailbox, [sym, a, k, Fiber.current])
42
- ret = @machine.yield
43
- raise(ret) if ret.is_a?(Exception)
44
- ret
45
- end
46
- end
47
-
48
- class UM
49
- def spin_actor(target)
50
- f = UM::Actor.new(self, target)
51
- schedule(f, nil)
52
- @@fiber_map[f] = f
53
- f
54
- end
55
- end
56
-
57
- class Locker
58
- def initialize(machine, target)
59
- @machine = machine
60
- @target = target
61
- @mutex = UM::Mutex.new
62
- end
63
-
64
- def method_missing(sym, *a, **k)
65
- @machine.synchronize(@mutex) { @target.send(sym, *a, **k) }
66
- end
67
- end
68
-
69
-
70
- PATH = '/tmp/foo'
71
-
72
- $machine = UM.new
73
- $raw_db = Extralite::Database.new(PATH)
74
- $actor_db = $machine.spin_actor(Extralite::Database.new(PATH))
75
- $locker_db = Locker.new($machine, Extralite::Database.new(PATH))
76
-
77
- [$raw_db, $actor_db, $locker_db].each do |db|
78
- p db.query('select 1')
79
- end
80
-
81
- bm = Benchmark.ips do |x|
82
- x.config(:time => 5, :warmup => 2)
83
-
84
- x.report("raw") { $raw_db.query('select 1') }
85
- x.report("actor") { $actor_db.query('select 1') }
86
- x.report("locker") { $locker_db.query('select 1') }
87
-
88
- x.compare!
89
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../lib/uringmachine'
4
- require 'resolv'
5
-
6
- machine = UM.new
7
-
8
- addrs = machine.resolve('status.realiteq.net')
9
-
10
- puts '*' * 40
11
- puts addrs.join("\n")
12
- puts
File without changes
File without changes
File without changes