uringmachine 0.3 → 0.4

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +85 -0
  4. data/TODO.md +5 -0
  5. data/examples/echo_server.rb +18 -40
  6. data/examples/inout.rb +19 -0
  7. data/examples/nc.rb +36 -0
  8. data/ext/um/extconf.rb +6 -15
  9. data/ext/um/um.c +245 -53
  10. data/ext/um/um.h +21 -9
  11. data/ext/um/um_class.c +74 -87
  12. data/ext/um/um_const.c +184 -0
  13. data/ext/um/um_op.c +10 -13
  14. data/ext/um/um_utils.c +48 -3
  15. data/lib/uringmachine/version.rb +1 -1
  16. data/lib/uringmachine.rb +12 -0
  17. data/test/helper.rb +8 -0
  18. data/test/test_um.rb +227 -7
  19. data/vendor/liburing/.github/workflows/build.yml +29 -1
  20. data/vendor/liburing/.gitignore +1 -0
  21. data/vendor/liburing/CHANGELOG +15 -0
  22. data/vendor/liburing/CONTRIBUTING.md +165 -0
  23. data/vendor/liburing/configure +32 -0
  24. data/vendor/liburing/examples/Makefile +8 -1
  25. data/vendor/liburing/examples/kdigest.c +405 -0
  26. data/vendor/liburing/examples/proxy.c +75 -8
  27. data/vendor/liburing/liburing.pc.in +1 -1
  28. data/vendor/liburing/src/Makefile +16 -2
  29. data/vendor/liburing/src/include/liburing/io_uring.h +31 -0
  30. data/vendor/liburing/src/include/liburing/sanitize.h +39 -0
  31. data/vendor/liburing/src/include/liburing.h +31 -4
  32. data/vendor/liburing/src/liburing-ffi.map +5 -0
  33. data/vendor/liburing/src/liburing.map +1 -0
  34. data/vendor/liburing/src/queue.c +3 -0
  35. data/vendor/liburing/src/register.c +36 -0
  36. data/vendor/liburing/src/sanitize.c +176 -0
  37. data/vendor/liburing/src/setup.c +1 -1
  38. data/vendor/liburing/test/35fa71a030ca.c +7 -0
  39. data/vendor/liburing/test/500f9fbadef8.c +2 -0
  40. data/vendor/liburing/test/7ad0e4b2f83c.c +0 -25
  41. data/vendor/liburing/test/917257daa0fe.c +7 -0
  42. data/vendor/liburing/test/Makefile +31 -4
  43. data/vendor/liburing/test/a0908ae19763.c +7 -0
  44. data/vendor/liburing/test/a4c0b3decb33.c +7 -0
  45. data/vendor/liburing/test/accept.c +14 -4
  46. data/vendor/liburing/test/b19062a56726.c +7 -0
  47. data/vendor/liburing/test/bind-listen.c +2 -2
  48. data/vendor/liburing/test/buf-ring-nommap.c +10 -3
  49. data/vendor/liburing/test/buf-ring.c +2 -0
  50. data/vendor/liburing/test/coredump.c +7 -0
  51. data/vendor/liburing/test/cq-overflow.c +13 -1
  52. data/vendor/liburing/test/d4ae271dfaae.c +11 -3
  53. data/vendor/liburing/test/defer-taskrun.c +2 -2
  54. data/vendor/liburing/test/defer-tw-timeout.c +4 -1
  55. data/vendor/liburing/test/defer.c +2 -2
  56. data/vendor/liburing/test/double-poll-crash.c +1 -1
  57. data/vendor/liburing/test/eeed8b54e0df.c +2 -0
  58. data/vendor/liburing/test/eventfd.c +0 -1
  59. data/vendor/liburing/test/exit-no-cleanup.c +11 -0
  60. data/vendor/liburing/test/fadvise.c +9 -26
  61. data/vendor/liburing/test/fdinfo.c +9 -1
  62. data/vendor/liburing/test/file-register.c +14 -2
  63. data/vendor/liburing/test/file-update.c +1 -1
  64. data/vendor/liburing/test/file-verify.c +27 -16
  65. data/vendor/liburing/test/files-exit-hang-timeout.c +1 -2
  66. data/vendor/liburing/test/fixed-buf-iter.c +3 -1
  67. data/vendor/liburing/test/fixed-hugepage.c +12 -1
  68. data/vendor/liburing/test/fsnotify.c +1 -0
  69. data/vendor/liburing/test/futex.c +16 -4
  70. data/vendor/liburing/test/helpers.c +47 -0
  71. data/vendor/liburing/test/helpers.h +6 -0
  72. data/vendor/liburing/test/init-mem.c +5 -3
  73. data/vendor/liburing/test/io-cancel.c +0 -24
  74. data/vendor/liburing/test/io_uring_passthrough.c +2 -0
  75. data/vendor/liburing/test/io_uring_register.c +25 -6
  76. data/vendor/liburing/test/iopoll-leak.c +4 -0
  77. data/vendor/liburing/test/iopoll-overflow.c +1 -1
  78. data/vendor/liburing/test/iopoll.c +3 -3
  79. data/vendor/liburing/test/kallsyms.c +203 -0
  80. data/vendor/liburing/test/link-timeout.c +159 -0
  81. data/vendor/liburing/test/linked-defer-close.c +224 -0
  82. data/vendor/liburing/test/madvise.c +12 -25
  83. data/vendor/liburing/test/min-timeout-wait.c +0 -25
  84. data/vendor/liburing/test/min-timeout.c +0 -25
  85. data/vendor/liburing/test/mkdir.c +6 -0
  86. data/vendor/liburing/test/msg-ring.c +8 -2
  87. data/vendor/liburing/test/napi-test.c +15 -2
  88. data/vendor/liburing/test/no-mmap-inval.c +2 -0
  89. data/vendor/liburing/test/nop.c +44 -0
  90. data/vendor/liburing/test/ooo-file-unreg.c +1 -1
  91. data/vendor/liburing/test/open-close.c +40 -0
  92. data/vendor/liburing/test/openat2.c +37 -14
  93. data/vendor/liburing/test/poll-many.c +13 -7
  94. data/vendor/liburing/test/poll-mshot-update.c +17 -10
  95. data/vendor/liburing/test/poll-v-poll.c +6 -3
  96. data/vendor/liburing/test/pollfree.c +148 -0
  97. data/vendor/liburing/test/read-mshot-empty.c +156 -153
  98. data/vendor/liburing/test/read-mshot.c +276 -27
  99. data/vendor/liburing/test/read-write.c +78 -13
  100. data/vendor/liburing/test/recv-msgall-stream.c +3 -0
  101. data/vendor/liburing/test/recv-msgall.c +5 -0
  102. data/vendor/liburing/test/recvsend_bundle-inc.c +680 -0
  103. data/vendor/liburing/test/recvsend_bundle.c +92 -29
  104. data/vendor/liburing/test/reg-fd-only.c +14 -4
  105. data/vendor/liburing/test/regbuf-clone.c +187 -0
  106. data/vendor/liburing/test/regbuf-merge.c +7 -0
  107. data/vendor/liburing/test/register-restrictions.c +86 -85
  108. data/vendor/liburing/test/rename.c +59 -1
  109. data/vendor/liburing/test/ringbuf-read.c +5 -0
  110. data/vendor/liburing/test/ringbuf-status.c +5 -1
  111. data/vendor/liburing/test/runtests.sh +16 -1
  112. data/vendor/liburing/test/send-zerocopy.c +59 -0
  113. data/vendor/liburing/test/short-read.c +1 -0
  114. data/vendor/liburing/test/socket.c +43 -0
  115. data/vendor/liburing/test/splice.c +3 -1
  116. data/vendor/liburing/test/sq-poll-dup.c +1 -1
  117. data/vendor/liburing/test/sq-poll-share.c +2 -0
  118. data/vendor/liburing/test/sqpoll-disable-exit.c +8 -0
  119. data/vendor/liburing/test/sqpoll-exit-hang.c +1 -25
  120. data/vendor/liburing/test/sqpoll-sleep.c +1 -25
  121. data/vendor/liburing/test/statx.c +89 -0
  122. data/vendor/liburing/test/stdout.c +2 -0
  123. data/vendor/liburing/test/submit-and-wait.c +1 -25
  124. data/vendor/liburing/test/submit-reuse.c +4 -26
  125. data/vendor/liburing/test/symlink.c +12 -1
  126. data/vendor/liburing/test/sync-cancel.c +48 -21
  127. data/vendor/liburing/test/thread-exit.c +5 -0
  128. data/vendor/liburing/test/timeout-new.c +1 -26
  129. data/vendor/liburing/test/timeout.c +12 -26
  130. data/vendor/liburing/test/unlink.c +94 -1
  131. data/vendor/liburing/test/uring_cmd_ublk.c +1252 -0
  132. data/vendor/liburing/test/waitid.c +62 -8
  133. data/vendor/liburing/test/wq-aff.c +35 -0
  134. data/vendor/liburing/test/xfail_prep_link_timeout_out_of_scope.c +46 -0
  135. data/vendor/liburing/test/xfail_register_buffers_out_of_scope.c +51 -0
  136. metadata +17 -4
  137. data/examples/event_loop.rb +0 -69
  138. data/examples/fibers.rb +0 -105
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7129b3c8605d5734f7152bddb4fa1b6f034fd1de26262eabfbfc2190846967bd
4
- data.tar.gz: 902e6663bac65d56e45d67383e2cd3eb0601534ed2b451f4f76b2812ce6125b1
3
+ metadata.gz: 9a9efd4af7de9fdb8db6888b9f6347e9dbc63aae9e26fa0f81d43984d265bd90
4
+ data.tar.gz: 5289699ce1e3e173d3766e0211fc77b57f3c123e2a6e041e9e751a3bf5ce07b2
5
5
  SHA512:
6
- metadata.gz: 683db63642ddb5d98c9eb137f8121a8cb8cb0fe44456928fca78cf8e322cebc01078efa71379abcceae1a1889140bf5b39f6ac16348db0b77d9c8a5bb9cf5cd0
7
- data.tar.gz: 850dce780030b6102371861805f831299d3ea113f8c02141515afe167b0e49f43a51812a6159a91b6335085b41fcb226713eeb963ae69d208818cb1f3cb303b3
6
+ metadata.gz: 3cf02bbaae5a9e19b9ea994d1c2bf4b327a96121b083ca6756eca70629738f6c24aca10096bb49a0400678542232062969889ed3f0773591f5dc71f718707fef
7
+ data.tar.gz: 67b368097ddecd6c9ca9ce360cc918d8e7de91ec83a00d617765c2026fbe08f16f5009b78323e9f314cd1df63a1baaa39e8887b4a90fff0cb8010d810276df7d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 2024-10-06 Version 0.4
2
+
3
+ - Add socket constants
4
+ - Add `#bind`, `#listen`
5
+ - Add `#spin`
6
+ - Fix bugs in multishot read and other ops
7
+ - Add `#recv`, `#send`
8
+ - Add `#socket`, `#connect`
9
+
1
10
  # 2024-10-04 Version 0.3
2
11
 
3
12
  - Fix race condition affecting `#timeout` and `#sleep`.
data/README.md CHANGED
@@ -9,3 +9,88 @@
9
9
  <a href="https://github.com/digital-fabric/uringmachine/blob/master/LICENSE">
10
10
  <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
11
11
  </a>
12
+
13
+ UringMachine is a fiber-based library for creating concurrent apps in Ruby on
14
+ modern Linux machines. UringMachine provides a rich API for performing I/O using
15
+ [io_uring](https://en.wikipedia.org/wiki/Io_uring).
16
+
17
+ ## Features
18
+
19
+ - Automatic fiber switching when performing blocking operations.
20
+ - Automatic cancellation using of ongoing operations with Ruby exceptions.
21
+ - General-purpose API for cancelling any operation on timeout.
22
+ - High performance (needs to be proved).
23
+ - (Eventually) I/O class with buffered reads and an intuitive API.
24
+
25
+ ## Design
26
+
27
+ UringMachine is based on my experience marrying Ruby and io_uring:
28
+
29
+ - [Polyphony](https://github.com/digital-fabric/polyphony) - a comprehensive gem
30
+ providing io_uring functionality, structured concurrency, and monkey-patching
31
+ for the Ruby standard library.
32
+ - [IOU](https://github.com/digital-fabric/iou) - a low-level asynchronous API
33
+ for using io_uring from Ruby.
34
+
35
+ Some important learnings from those two projects, in no particular order:
36
+
37
+ - Monkey-patching is not a good solution, long term. You need to deal with
38
+ changing APIs (Ruby is evolving quite rapidly these days!), and anyways you're
39
+ always going to get stuck with some standard Ruby API that's implemented as a
40
+ C extension and just won't play nice with whatever you're trying to do.
41
+ - The design of the Polyphony io_uring backend was an evolution of something
42
+ that was originally based on libev as an event loop. In hindsight, adapting
43
+ the design for how io_uring worked led to code that was too complex and even
44
+ somewhat brittle.
45
+ - IOU showed me that even if we embrace callbacks, the developer experience is
46
+ substantially inferior to what you can do with a sequential coding style. Even
47
+ just in terms of line count - with callbacks you end up with roughly double
48
+ the number of lines of code.
49
+ - Implementing fiber switching on top of IOU was disappointing in terms of
50
+ performance. In order for a fiber-based solution to be performed it had to be
51
+ baked in - hence UringMachine.
52
+ - Working with fibers has the very important benefit that you can keep stuff on
53
+ the stack, instead of passing around all kinds of references to the heap. In
54
+ addition, you mostly don't need to worry about marking Ruby objects used in
55
+ operations, since normally they'll already be on the stack as method call
56
+ parameters.
57
+ - Polyphony was designed as an all-in-one solution that did everything: turning
58
+ stock APIs into fiber-aware ones, providing a solid structured-concurrency
59
+ implementation for controlling fiber life times, extensions providing
60
+ additional features such as compressing streaming data between two fds, other
61
+ APIs based on splicing etc. Perhaps a more cautious approach would be better.
62
+ - Pending operation lifetime management in Polyphony was based a complex
63
+ reference counting scheme that proved problematic, especially for multishot
64
+ operations.
65
+
66
+ So, based on those two projects, I wanted to design a Ruby API for io_uring
67
+ based on the following principles:
68
+
69
+ - Automatic fiber switching.
70
+ - No monkey-patching. Instead, provide a simple custom API, as a replacement for
71
+ the stock Ruby `IO` and `Socket` classes.
72
+ - Simpler management of pending operation lifetime.
73
+ - Do not insist on structured concurrency, just provide the APIs necessary to
74
+ create actors and to supervise the execution of fibers.
75
+
76
+ ## Example
77
+
78
+ ```ruby
79
+ require 'uringmachine'
80
+
81
+ machine = UringMachine.new
82
+ stdout_fd = STDOUT.fileno
83
+ stdin_fd = STDIN.fileno
84
+ machine.write(stdout_fd, "Hello, world!\n")
85
+
86
+ loop do
87
+ machine.write(stdout_fd, "Say something: ")
88
+ buf = +''
89
+ res = machine.read(stdin_fd, buf, 8192)
90
+ if res > 0
91
+ machine.write(stdout_fd, "You said: #{buf}")
92
+ else
93
+ break
94
+ end
95
+ end
96
+ ```
data/TODO.md CHANGED
@@ -0,0 +1,5 @@
1
+ - splice
2
+ - sendto
3
+ - recvfrom
4
+
5
+ - queues
@@ -1,52 +1,30 @@
1
- require_relative '../lib/iou'
1
+ require_relative '../lib/uringmachine'
2
2
  require 'socket'
3
3
 
4
4
  socket = TCPServer.open('127.0.0.1', 1234)
5
5
  puts 'Listening on port 1234...'
6
6
 
7
- @ring = IOU::Ring.new
7
+ $machine = UringMachine.new
8
+ $bgid = $machine.setup_buffer_ring(4096, 1024)
8
9
 
9
- @ring.prep_accept(fd: socket.fileno, multishot: true) do |c|
10
- setup_connection(c[:result]) if c[:result] > 0
11
- end
12
-
13
- def setup_connection(fd)
14
- puts "Connection accepted fd #{fd}"
15
-
16
- buffer = +''
17
- echo_prep_read(fd, buffer)
18
- end
19
-
20
- def echo_prep_read(fd, buffer)
21
- @ring.prep_read(fd: fd, buffer: buffer, len: 4096, buffer_offset: -1) do |c|
22
- if c[:result] > 0
23
- echo_lines(fd, buffer)
24
- echo_prep_read(fd, buffer)
25
- elsif c[:result] == 0
26
- puts "Connection closed by client on fd #{fd}"
27
- else
28
- puts "Got error #{c[:result]} on fd #{fd}, closing connection..."
29
- @ring.prep_close(fd: fd) do |c|
30
- puts "Connection closed on fd #{fd}, result #{c[:result]}"
31
- end
32
- end
10
+ def handle_connection(fd)
11
+ $machine.read_each(fd, $bgid) do |buf|
12
+ $machine.write(fd, buf)
33
13
  end
14
+ puts "Connection closed by client fd #{fd}"
15
+ rescue Exception => e
16
+ puts "Got error #{e.inspect}, closing connection"
17
+ $machine.close(fd) rescue nil
34
18
  end
35
19
 
36
- def echo_lines(fd, buffer)
37
- sep = $/
38
- sep_size = sep.bytesize
39
-
40
- while true
41
- idx = buffer.index(sep)
42
- if idx
43
- line = buffer.slice!(0, idx + sep_size)
44
- @ring.prep_write(fd: fd, buffer: line)
45
- else
46
- break
47
- end
20
+ $machine.spin do
21
+ loop do
22
+ $machine.sleep 5
23
+ puts "pending: #{$machine.pending_count}"
48
24
  end
49
25
  end
50
26
 
51
- trap('SIGINT') { exit! }
52
- @ring.process_completions_loop
27
+ $machine.accept_each(socket.fileno) do |fd|
28
+ puts "Connection accepted fd #{fd}"
29
+ $machine.spin(fd) { handle_connection(_1) }
30
+ end
data/examples/inout.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/uringmachine'
4
+
5
+ machine = UringMachine.new
6
+ stdout_fd = STDOUT.fileno
7
+ stdin_fd = STDIN.fileno
8
+ machine.write(stdout_fd, "Hello, world!\n")
9
+
10
+ loop do
11
+ machine.write(stdout_fd, "Say something: ")
12
+ buf = +''
13
+ res = machine.read(stdin_fd, buf, 8192)
14
+ if res > 0
15
+ machine.write(stdout_fd, "You said: #{buf}")
16
+ else
17
+ break
18
+ end
19
+ end
data/examples/nc.rb ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/uringmachine'
4
+ require 'socket'
5
+
6
+ HOST = ARGV[0]
7
+ PORT = ARGV[1].to_i
8
+
9
+ machine = UringMachine.new
10
+
11
+ conn_fd = machine.socket(Socket::AF_INET, Socket::SOCK_STREAM, 0, 0);
12
+ machine.connect(conn_fd, HOST, PORT)
13
+
14
+ stdin_fd = STDIN.fileno
15
+ stdout_fd = STDOUT.fileno
16
+
17
+ f_writer = Fiber.new do
18
+ bgidw = machine.setup_buffer_ring(4096, 1024)
19
+ machine.read_each(stdin_fd, bgidw) do |buf|
20
+ machine.write(conn_fd, buf)
21
+ end
22
+ end
23
+ machine.schedule(f_writer, nil)
24
+
25
+ f_reader = Fiber.new do
26
+ bgidr = machine.setup_buffer_ring(4096, 1024)
27
+ machine.read_each(conn_fd, bgidr) do |buf|
28
+ machine.write(stdout_fd, buf)
29
+ end
30
+ end
31
+ machine.schedule(f_reader, nil)
32
+
33
+ trap('SIGINT') { exit! }
34
+ loop do
35
+ machine.sleep(60)
36
+ end
data/ext/um/extconf.rb CHANGED
@@ -21,14 +21,11 @@ def get_config
21
21
  raise "UringMachine requires kernel version 6.4 or newer!" if combined_version < 604
22
22
 
23
23
  config[:kernel_version] = combined_version
24
- config[:pidfd_open] = combined_version > 503
25
- config[:multishot_accept] = combined_version >= 519
26
- config[:multishot_recv] = combined_version >= 600
27
- config[:multishot_recvmsg] = combined_version >= 600
28
- config[:multishot_timeout] = combined_version >= 604
29
24
  config[:submit_all_flag] = combined_version >= 518
30
25
  config[:coop_taskrun_flag] = combined_version >= 519
31
26
  config[:single_issuer_flag] = combined_version >= 600
27
+ config[:prep_bind] = combined_version >= 611
28
+ config[:prep_listen] = combined_version >= 611
32
29
 
33
30
  config
34
31
  end
@@ -54,16 +51,10 @@ if !find_library('uring', nil, File.join(liburing_path, 'src'))
54
51
  raise "Couldn't find liburing.a"
55
52
  end
56
53
 
57
- def define_bool(name, value)
58
- $defs << "-D#{name}=#{value ? 1 : 0 }"
59
- end
60
-
61
- $defs << '-DHAVE_IO_URING_PREP_MULTISHOT_ACCEPT' if config[:multishot_accept]
62
- $defs << '-DHAVE_IO_URING_PREP_RECV_MULTISHOT' if config[:multishot_recv]
63
- $defs << '-DHAVE_IO_URING_PREP_RECVMSG_MULTISHOT' if config[:multishot_recvmsg]
64
- $defs << '-DHAVE_IO_URING_TIMEOUT_MULTISHOT' if config[:multishot_timeout]
65
- $defs << '-DHAVE_IORING_SETUP_SUBMIT_ALL' if config[:submit_all_flag]
66
- $defs << '-DHAVE_IORING_SETUP_COOP_TASKRUN' if config[:coop_taskrun_flag]
54
+ $defs << '-DHAVE_IORING_SETUP_SUBMIT_ALL' if config[:submit_all_flag]
55
+ $defs << '-DHAVE_IORING_SETUP_COOP_TASKRUN' if config[:coop_taskrun_flag]
56
+ $defs << '-DHAVE_IO_URING_PREP_BIND' if config[:prep_bind]
57
+ $defs << '-DHAVE_IO_URING_PREP_LISTEN' if config[:prep_listen]
67
58
  $CFLAGS << ' -Wno-pointer-arith'
68
59
 
69
60
  CONFIG['optflags'] << ' -fno-strict-aliasing'