@@ -22,7 +22,7 @@ modern Linux machines. UringMachine provides a rich API for performing I/O using
22
22
- High performance (needs to be proved).
23
23
- (Eventually) I/O class with buffered reads and an intuitive API.
24
24
25
- ## Prior art
25
+ ## Design
26
26
27
27
UringMachine is based on my experience marrying Ruby and io_uring:
28
28
@@ -32,6 +32,47 @@ UringMachine is based on my experience marrying Ruby and io_uring:
32
32
- [ IOU] ( https://github.com/digital-fabric/iou ) - a low-level asynchronous API
33
33
for using io_uring from Ruby.
34
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
+
35
76
## Example
36
77
37
78
``` ruby
0 commit comments