uringmachine 0.2 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -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 +340 -53
  10. data/ext/um/um.h +33 -11
  11. data/ext/um/um_class.c +101 -119
  12. data/ext/um/um_const.c +184 -0
  13. data/ext/um/um_op.c +39 -18
  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 +13 -12
  18. data/test/test_um.rb +301 -3
  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: 52ccd2dc6b2caf48fd03405d4536cb9a0a9640470fb14a5f105da84029857471
4
- data.tar.gz: cf1c06469be27a93089a805cfd7552978f8f84d1a6f6d225289954d908ec981e
3
+ metadata.gz: 9a9efd4af7de9fdb8db6888b9f6347e9dbc63aae9e26fa0f81d43984d265bd90
4
+ data.tar.gz: 5289699ce1e3e173d3766e0211fc77b57f3c123e2a6e041e9e751a3bf5ce07b2
5
5
  SHA512:
6
- metadata.gz: e79d481864b8be758efc1f67924b491118c4d00a038fc24ee2a6078cc34edbfab9602166836e155fb74dcd423f5bcee2da47dd89d24fb23c68b3412481704f98
7
- data.tar.gz: 00f2867826b20a9ed0198dc683f5f26d81d59a94bacfa2320b991c89c0dcb0cb327cb821b33d6c501fb2f67d73c2ff0784841cb01d973d58584081d621ff4563
6
+ metadata.gz: 3cf02bbaae5a9e19b9ea994d1c2bf4b327a96121b083ca6756eca70629738f6c24aca10096bb49a0400678542232062969889ed3f0773591f5dc71f718707fef
7
+ data.tar.gz: 67b368097ddecd6c9ca9ce360cc918d8e7de91ec83a00d617765c2026fbe08f16f5009b78323e9f314cd1df63a1baaa39e8887b4a90fff0cb8010d810276df7d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
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
+
10
+ # 2024-10-04 Version 0.3
11
+
12
+ - Fix race condition affecting `#timeout` and `#sleep`.
13
+ - Add `#accept_each`
14
+ - Add `#accept`
15
+
1
16
  # 2024-10-03 Version 0.2
2
17
 
3
18
  - Remove old IOU code.
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'