### Concurrent Execution with UringMachine (Ruby) Source: https://github.com/digital-fabric/uringmachine/blob/main/README.md Illustrates concurrent execution using UringMachine's `#spin` method, which creates fibers. This example uses pipes for inter-fiber communication and a buffer ring for efficient reading. It requires the 'uringmachine' gem. ```ruby machine = UringMachine.new rfd, wfd = machine.pipe f1 = machine.spin do machine.write(wfd, 'hello') machine.write(wfd, 'world') machine.close(wfd) end bgid = machine.setup_buffer_ring(4096, 1024) f2 = machine.spin do machine.read_each(rfd, bgid) do |str| puts str end end ``` -------------------------------- ### Echo Server Example using Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt A complete TCP echo server implementation using Uringmachine. It demonstrates setting up a server socket, binding to an address, listening for connections, and handling client requests asynchronously. It also includes graceful shutdown handling. ```ruby require 'uringmachine' machine = UM.new bgid = machine.setup_buffer_ring(4096, 1024) def handle_client(machine, fd, bgid) machine.read_each(fd, bgid) do |data| machine.write(fd, data) end rescue => e puts "Client error: #{e.message}" ensure machine.close(fd) rescue nil end # Create server server_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0) machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true) machine.bind(server_fd, '0.0.0.0', 8080) machine.listen(server_fd, UM::SOMAXCONN) puts "Echo server listening on port 8080" # Handle graceful shutdown main = Fiber.current trap('SIGINT') { machine.schedule(main, nil) } trap('SIGTERM') { machine.schedule(main, nil) } # Accept loop in background fiber machine.spin do machine.accept_each(server_fd) do |client_fd| machine.spin(client_fd) { |fd| handle_client(machine, fd, bgid) } end end # Wait for shutdown signal machine.yield puts "Shutting down..." machine.close(server_fd) rescue nil ``` -------------------------------- ### Accept Network Connections with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Details methods for handling incoming network connections: `accept` for a single connection, `accept_each` for iterating over connections, and `accept_into_queue` for placing connections into a queue for asynchronous processing. Examples include setting up a server, accepting clients, and processing connections concurrently. ```ruby machine = UM.new # Setup server server_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0) machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true) machine.bind(server_fd, '127.0.0.1', 8080) machine.listen(server_fd, UM::SOMAXCONN) # Accept single connection client_fd = machine.accept(server_fd) machine.write(client_fd, "Welcome!\n") machine.close(client_fd) # Accept in a loop (multishot) machine.accept_each(server_fd) do |fd| machine.spin(fd) do |client_fd| machine.write(client_fd, "Hello!\n") machine.close(client_fd) end end # Accept into a queue for custom processing queue = UM::Queue.new machine.spin do machine.accept_into_queue(server_fd, queue) end machine.spin do while (fd = machine.shift(queue)) machine.write(fd, "Queued response\n") machine.close(fd) end end ``` -------------------------------- ### Read from File Descriptors (Ruby) Source: https://context7.com/digital-fabric/uringmachine/llms.txt Illustrates reading data from file descriptors into String or IO::Buffer objects. Covers reading with optional length, buffer offset, and file offset parameters, including examples for pipes and files. ```ruby machine = UM.new # Read from a pipe r, w = UM.pipe machine.write(w, 'Hello, World!') buffer = +'' bytes_read = machine.read(r, buffer, 8192) puts buffer # => "Hello, World!" puts bytes_read # => 13 # Read with buffer offset (append to existing string) buffer = +'Prefix: ' machine.read(r, buffer, 100, buffer.bytesize) # Read with file offset (for files) fd = machine.open('/tmp/test.txt', UM::O_RDONLY) buffer = +'' machine.read(fd, buffer, 100, 0, 10) # Read 100 bytes starting at file offset 10 machine.close(fd) # Read into IO::Buffer read_buffer = IO::Buffer.new(1024) bytes = machine.read(r, read_buffer, 1024) data = read_buffer.get_string(0, bytes) ``` -------------------------------- ### Create and Run HTTP Server with UringMachine (Ruby) Source: https://context7.com/digital-fabric/uringmachine/llms.txt This Ruby code snippet sets up a simple HTTP server using UringMachine. It binds to port 8080, accepts incoming connections, and handles each request by reading data, parsing it with http_parser, and sending a 'Hello, World!' response. It demonstrates socket setup, listening, accepting, and request handling within UringMachine's fiber-based concurrency model. ```ruby require 'uringmachine' require 'http/parser' machine = UM.new bgid = machine.setup_buffer_ring(4096, 1024) def handle_request(machine, fd, bgid) parser = Http::Parser.new request_complete = false parser.on_message_complete = -> { request_complete = true } machine.read_each(fd, bgid) do |data| parser << data break if request_complete end # Send response body = "Hello, World!\n" response = "HTTP/1.1 200 OK\r\n" \ "Content-Type: text/plain\r\n" \ "Content-Length: #{body.bytesize}\r\n" \ "Connection: close\r\n" \ "\r\n#{body}" machine.write(fd, response) ensure machine.close(fd) rescue nil end server_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0) machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true) machine.bind(server_fd, '0.0.0.0', 8080) machine.listen(server_fd, UM::SOMAXCONN) puts "HTTP server listening on port 8080" machine.accept_each(server_fd) do |fd| machine.spin(fd) { |client_fd| handle_request(machine, client_fd, bgid) } end ``` -------------------------------- ### Control Fiber Execution Flow with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Explains how to control fiber execution using `schedule`, `snooze`, `yield`, and `switch`. The example shows scheduling fibers with values, yielding execution with `machine.yield`, and cooperative yielding with `machine.snooze`. It also demonstrates interrupting a fiber with an exception. ```ruby machine = UM.new # Schedule a fiber with a value fiber = Fiber.new do |value| puts "Received: #{value}" new_value = machine.yield # Wait for next schedule puts "Then received: #{new_value}" machine.switch # Yield without expecting to be scheduled end machine.schedule(fiber, 'hello') machine.snooze # Process runqueue, yield to other fibers machine.schedule(fiber, 'world') machine.snooze # Snooze to allow other fibers to run machine.spin do 10.times do |i| puts "Fiber iteration #{i}" machine.snooze # Cooperative yield end end machine.sleep(0.1) # Let fiber run # Interrupt a fiber with an exception fiber = machine.spin { machine.sleep(10) } machine.schedule(fiber, RuntimeError.new('interrupted')) machine.snooze ``` -------------------------------- ### Multishot Read Operations with Buffer Rings (Ruby) Source: https://context7.com/digital-fabric/uringmachine/llms.txt Explains how to perform multishot reads using `read_each` and io_uring's buffer ring feature for efficient data streaming. Includes setup of the buffer ring, spawning a writer fiber, and processing incoming data chunks. ```ruby machine = UM.new r, w = UM.pipe # Setup buffer ring (returns buffer group ID) bgid = machine.setup_buffer_ring(4096, 1024) # 4KB buffers, 1024 count # Spawn a writer fiber machine.spin do machine.write(w, 'chunk1') machine.sleep(0.01) machine.write(w, 'chunk2') machine.sleep(0.01) machine.write(w, 'chunk3') machine.close(w) end # Read each chunk as it arrives chunks = [] machine.read_each(r, bgid) do |data| chunks << data puts "Received: #{data}" end puts chunks # => ["chunk1", "chunk2", "chunk3"] ``` -------------------------------- ### Send and Receive Data over Sockets with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Provides an overview of methods for socket input/output operations: `send`, `recv`, and `recv_each`. The example demonstrates creating a connected Unix domain socket pair and then using these methods to exchange data between the two ends of the pair. ```ruby machine = UM.new # Create connected socket pair client_fd, server_fd = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0) ``` -------------------------------- ### Create UringMachine Instance (Ruby) Source: https://context7.com/digital-fabric/uringmachine/llms.txt Demonstrates creating a UringMachine instance with default and custom settings, including queue size, SQPOLL mode, and sidecar mode. Also shows how to check the kernel version. ```ruby require 'uringmachine' # Create with default settings (4096 SQ entries) machine = UringMachine.new # Create with custom size machine = UM.new(size: 256) # Create with SQPOLL mode for reduced syscall overhead machine = UM.new(sqpoll: true) machine = UM.new(sqpoll: 0.05) # With 50ms idle timeout # Create with sidecar mode machine = UM.new(sidecar: true) # Check kernel version (returns integer, e.g., 607 for kernel 6.7) puts UM.kernel_version # => 612 ``` -------------------------------- ### Pipe and Socketpair Creation with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Demonstrates the creation and usage of pipes and Unix domain socket pairs using UM.pipe and UM.socketpair. Shows bidirectional communication over socket pairs and data transfer through pipes. ```ruby machine = UM.new # Create a pipe read_fd, write_fd = UM.pipe machine.write(write_fd, 'through the pipe') machine.close(write_fd) buffer = +'' machine.read(read_fd, buffer, 8192) puts buffer # => "through the pipe" machine.close(read_fd) # Create a Unix socket pair fd1, fd2 = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0) # Bidirectional communication machine.write(fd1, 'Hello from fd1') buf = +'' machine.read(fd2, buf, 100) puts buf # => "Hello from fd1" machine.write(fd2, 'Hello from fd2') buf = +'' machine.read(fd1, buf, 100) puts buf # => "Hello from fd2" machine.close(fd1) machine.close(fd2) ``` -------------------------------- ### Execute Periodic Operations with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Describes the `periodically` method, which executes a given block at regular intervals in an infinite loop. The example shows how to use `periodically` and how to break out of the loop. It also demonstrates combining `periodically` with `timeout` to limit its execution duration. ```ruby machine = UM.new # Run periodically (break to exit) counter = 0 machine.periodically(0.1) do counter += 1 puts "Tick #{counter}" break if counter >= 5 end # Periodic with timeout begin machine.timeout(1.0, RuntimeError) do machine.periodically(0.1) do puts "Running..." end end rescue RuntimeError puts "Periodic operation timed out" end ``` -------------------------------- ### Basic I/O with UringMachine (Ruby) Source: https://github.com/digital-fabric/uringmachine/blob/main/README.md Demonstrates basic reading from stdin and writing to stdout using UringMachine. It requires the 'uringmachine' gem. The code reads user input and echoes it back until the user signals termination. ```ruby require 'uringmachine' machine = UringMachine.new stdout_fd = STDOUT.fileno stdin_fd = STDIN.fileno machine.write(stdout_fd, "Hello, world!\n") loop do machine.write(stdout_fd, "Say something: ") buf = +'' res = machine.read(stdin_fd, buf, 8192) if res > 0 machine.write(stdout_fd, "You said: #{buf}") else break end end ``` -------------------------------- ### UringMachine Instance Methods Source: https://github.com/digital-fabric/uringmachine/blob/main/docs/um_api.md These methods operate on an instance of UringMachine and provide functionalities for managing network connections, file operations, and asynchronous tasks. ```APIDOC ## UringMachine Instance Methods ### Description These methods are called on a UringMachine instance to perform various I/O operations, manage connections, and control asynchronous tasks. ### Methods - **Connection Handling**: - `accept_each(sockfd) { |fd| ... }`: Accepts incoming connections in a loop, yielding each to a block. - `accept_into_queue(sockfd, queue)`: Accepts incoming connections and pushes them to a queue. - `accept(sockfd)`: Accepts a single incoming connection, returning its file descriptor. - `bind(sockfd, host, port)`: Binds a socket to a specified address and port. - `connect(sockfd, host, port)`: Connects a socket to a specified address and port. - `listen(sockfd)`: Starts listening for incoming connections on a socket. - `socket(domain, type, protocol, flags)`: Creates a socket and returns its file descriptor. - `shutdown_async(fd, how)`: Shuts down a socket asynchronously. - `shutdown(fd, how)`: Shuts down a socket. - **File I/O**: - `close_async(fd)`: Closes a file descriptor asynchronously. - `close(fd)`: Closes a file descriptor. - `open(pathname, flags)`: Opens a file and returns its file descriptor. - `read_each(fd, bgid) { |data| ... }`: Reads repeatedly from a file descriptor, yielding data chunks. - `read(fd, buffer[, maxlen[, buffer_offset[, file_offset]]])`: Reads data from a file descriptor into a buffer. - `write_async(fd, buffer[, len[, offset]])`: Writes data to a file descriptor asynchronously. - `write(fd, buffer[, len[, offset]])`: Writes data to a file descriptor. - `writev(fd, *buffers[, file_offset])`: Writes multiple buffers to a file descriptor. - `statx(dirfd, path, flags, mask)`: Retrieves file status information. - **Socket Options & Data Transfer**: - `getsockopt(sockfd, level, opt)`: Retrieves a socket option value. - `setsockopt(fd, level, opt, value)`: Sets a socket option. - `recv_each(fd, bgid, flags)`: Receives data repeatedly from a socket. - `recv(fd, buffer, maxlen, flags)`: Receives data from a socket into a buffer. - `send_bundle(fd, bgid, *strings)`: Sends a bundle of buffers over a socket. - `send(fd, buffer, len, flags)`: Sends data over a socket. - `sendv(fd, *buffers)`: Sends multiple buffers over a socket. - **Concurrency & Queues**: - `pending_fibers`: Returns the set of pending fibers. - `periodically(interval) { ... }`: Executes a block at regular intervals. - `poll(fd, mask)`: Waits for a file descriptor to become ready. - `pop(queue)`: Removes and returns an item from the end of a queue. - `prep_timeout(interval)`: Prepares a timeout operation. - `push(queue, value)`: Adds a value to the end of a queue. - `schedule(fiber, value)`: Schedules a fiber to run with a given value. - `select(rfds, wfds, efds)`: Selects ready file descriptors from given sets. - `shift(queue)`: Removes and returns an item from the head of a queue. - `sleep(duration)`: Pauses execution for a specified duration. - `snooze`: Adds the current fiber to the runqueue and yields control. - `switch`: Switches to the next fiber in the runqueue. - `synchronize(mutex)`: Synchronizes access using a mutex. - `timeout(interval, exception_class) { ... }`: Executes a block with a timeout. - `unshift(queue, value)`: Adds a value to the beginning of a queue. - `waitid_status(idtype, id, options)`: Waits for a process and returns its status. - `waitid(idtype, id, options)`: Waits for a process and returns its PID and status. - `wakeup`: Wakes up a machine waiting for completions. - `yield()`: Yields control to the next fiber in the runqueue. - **Utilities**: - `metrics`: Returns machine metrics. - `setup_buffer_ring(size, count)`: Sets up a buffer ring. - `size`: Returns the number of entries in the submission queue. ``` -------------------------------- ### Accepting Connections Source: https://context7.com/digital-fabric/uringmachine/llms.txt Details methods for accepting incoming network connections, including `accept`, `accept_each`, and `accept_into_queue`, for handling client requests. ```APIDOC ## Accepting Connections ### Description Methods for accepting incoming connections: `accept`, `accept_each`, `accept_into_queue`. Used to handle client requests on server sockets. ### Method `accept`, `accept_each`, `accept_into_queue` ### Endpoint N/A (In-process operations) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby machine = UM.new # Setup server server_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0) machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true) machine.bind(server_fd, '127.0.0.1', 8080) machine.listen(server_fd, UM::SOMAXCONN) # Accept single connection client_fd = machine.accept(server_fd) machine.write(client_fd, "Welcome!\n") machine.close(client_fd) # Accept in a loop (multishot) machine.accept_each(server_fd) do |fd| machine.spin(fd) do |client_fd| machine.write(client_fd, "Hello!\n") machine.close(client_fd) end end # Accept into a queue for custom processing queue = UM::Queue.new machine.spin do machine.accept_into_queue(server_fd, queue) end machine.spin do while (fd = machine.shift(queue)) machine.write(fd, "Queued response\n") machine.close(fd) end end ``` ### Response #### Success Response (200) - **client_fd** (Integer) - File descriptor for the accepted client connection. - **fd** (Integer) - File descriptor for the accepted client connection (in `accept_each` block). #### Response Example ```json { "client_fd": 5 } ``` ``` -------------------------------- ### Add UM.pidfd_open and UM.pidfd_send_signal APIs Source: https://github.com/digital-fabric/uringmachine/blob/main/grant-2025/journal.md New low-level APIs `UM.pidfd_open` and `UM.pidfd_send_signal` are added for advanced process management using file descriptors associated with PIDs. `pidfd_open` creates a file descriptor for a process, and `pidfd_send_signal` sends a signal to that process via its PID file descriptor. ```ruby require 'uringmachine' child_pid = fork do # Child process logic sleep 5 end # Open a file descriptor for the child process fd = UM.pidfd_open(child_pid) # Send a signal (e.g., SIGUSR1) to the child process UM.pidfd_send_signal(fd, UM::SIGUSR1) # Wait for the child process to exit using the PID file descriptor # Assuming 'machine' is an instance of UM::FiberScheduler or similar pid, status = machine.waitid(P_PIDFD, fd, UM::WEXITED) puts "Child process #{pid} exited with status: #{status}" # Close the file descriptor # (Implicitly handled by Ruby's GC or explicit close needed depending on implementation) ``` -------------------------------- ### Create Network Sockets with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Covers methods for creating various types of sockets: TCP server (`socket`, `bind`, `listen`), TCP client (`socket`, `connect`), UDP (`socket`, `bind`), and Unix domain sockets (`socketpair`). It demonstrates setting socket options like `SO_REUSEADDR` and performing basic operations like writing and reading through socket pairs. ```ruby machine = UM.new # Create a TCP server socket server_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0) machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true) machine.bind(server_fd, '127.0.0.1', 8080) machine.listen(server_fd, UM::SOMAXCONN) # Create a client socket and connect client_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0) machine.connect(client_fd, '127.0.0.1', 8080) # UDP socket udp_fd = machine.socket(UM::AF_INET, UM::SOCK_DGRAM, 0, 0) machine.bind(udp_fd, '0.0.0.0', 5000) # Unix socket pair fd1, fd2 = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0) machine.write(fd1, 'hello') buf = +'' machine.read(fd2, buf, 100) puts buf # => "hello" ``` -------------------------------- ### Read Multiple Files Concurrently (Ruby) Source: https://github.com/digital-fabric/uringmachine/blob/main/TODO.md Illustrates how to read the content of multiple files efficiently. It provides two syntaxes: one using a block to process each file's data as it's read, and another that returns a hash containing all file contents after all reads are complete. ```ruby # with a block machine.read_files(*fns) { |fn, data| ... } # without a block machine.read_files(*fns) #=> { fn1:, fn2:, fn3:, ...} ``` -------------------------------- ### Creating Sockets Source: https://context7.com/digital-fabric/uringmachine/llms.txt Covers methods for creating various types of sockets, including TCP, UDP, and Unix domain sockets, using `socket`, `bind`, `listen`, and `connect`. ```APIDOC ## Creating Sockets ### Description Methods for creating sockets: `socket`, `bind`, `listen`, `connect`. Supports TCP, UDP, and Unix domain sockets. ### Method `socket`, `bind`, `listen`, `connect`, `socketpair` ### Endpoint N/A (In-process operations) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby machine = UM.new # Create a TCP server socket server_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0) machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true) machine.bind(server_fd, '127.0.0.1', 8080) machine.listen(server_fd, UM::SOMAXCONN) # Create a client socket and connect client_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0) machine.connect(client_fd, '127.0.0.1', 8080) # UDP socket udp_fd = machine.socket(UM::AF_INET, UM::SOCK_DGRAM, 0, 0) machine.bind(udp_fd, '0.0.0.0', 5000) # Unix socket pair fd1, fd2 = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0) machine.write(fd1, 'hello') buf = +'' machine.read(fd2, buf, 100) puts buf # => "hello" ``` ### Response #### Success Response (200) - **server_fd**, **client_fd**, **udp_fd**, **fd1**, **fd2** (Integer) - File descriptors representing the created sockets. #### Response Example ```json { "server_fd": 3, "client_fd": 4 } ``` ``` -------------------------------- ### Read Data with Automatic Buffer Management (Ruby) Source: https://github.com/digital-fabric/uringmachine/blob/main/TODO.md Demonstrates two ways to read data using the machine.read_each method. The first reads data as strings, while the second allows retrieval of IO::Buffer objects for more direct memory access. This feature simplifies data handling by automatically managing buffers. ```ruby # completely hands off machine.read_each(fd) { |str| ... } # what if we want to get IO::Buffer? machine.read_each(fd, io_buffer: true) { |iobuff, len| ... } ``` -------------------------------- ### Actor Pattern with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Demonstrates the actor pattern using `spin_actor` for creating concurrent processes with message passing. It shows how to define an actor, spawn it, send messages asynchronously (`cast`), and send messages synchronously (`call`) to retrieve results. ```ruby require 'uringmachine' require 'uringmachine/actor' machine = UM.new # Define an actor module module Counter def setup(initial = 0) @count = initial end def increment @count += 1 end def get_count @count end def teardown puts "Counter actor shutting down with count: #{@count}" end end # Spawn the actor actor = machine.spin_actor(Counter, 10) # Cast (async, no return value) actor.cast(:increment) actor.cast(:increment) # Call (sync, returns value) response_queue = Fiber.current.mailbox count = actor.call(response_queue, :get_count) puts count # => 12 # Stop the actor actor.stop ``` -------------------------------- ### Fiber-Aware Mutex Synchronization with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Shows how to use UM::Mutex for fiber-aware mutual exclusion, ensuring safe access to shared resources across concurrent fibers. Demonstrates locking and unlocking around critical sections. ```ruby machine = UM.new mutex = UM::Mutex.new shared_resource = [] # Synchronize access to shared resource 10.times do |i| machine.spin do machine.synchronize(mutex) do shared_resource << i machine.sleep(0.01) # Simulate work shared_resource << i * 10 end end end machine.sleep(0.2) # Wait for all fibers puts shared_resource.inspect # Pairs are always consecutive ``` -------------------------------- ### Write to File Descriptors (Ruby) Source: https://context7.com/digital-fabric/uringmachine/llms.txt Shows how to write data from a buffer to a file descriptor, including basic writes, writes with explicit length, and writes with file offsets. Also covers asynchronous writes using `write_async` and writing `IO::Buffer` objects. ```ruby machine = UM.new r, w = UM.pipe # Basic write bytes_written = machine.write(w, 'Hello, World!') puts bytes_written # => 13 # Write with explicit length machine.write(w, 'foobar', 3) # Writes only 'foo' # Write with file offset (for files) fd = machine.open('/tmp/test.txt', UM::O_WRONLY | UM::O_CREAT) machine.write(fd, 'inserted', -1, 10) # Write at file offset 10 machine.close(fd) # Async write (fire-and-forget, doesn't block) machine.write_async(w, 'async data') machine.snooze # Process pending completions # Write IO::Buffer buffer = IO::Buffer.new(11) buffer.set_string('Hello world') machine.write(w, buffer) ``` -------------------------------- ### Optimize UM Mutex Synchronization Source: https://github.com/digital-fabric/uringmachine/blob/main/grant-2025/journal.md This code addresses a performance issue in UM#synchronize by avoiding unnecessary futex wake calls when no fibers are waiting. It introduces a `num_waiters` field to `struct um_mutex` to track waiting fibers, improving efficiency. ```c #include #include "uringmachine.h" // ... (struct um_mutex definition with num_waiters field) int um_mutex_lock(struct um_mutex *mutex) { // ... (existing lock logic) if (mutex->num_waiters == 0) { // Avoid futex wake if no one is waiting } else { um_futex_wake(mutex->futex, 1); } // ... return 0; } ``` -------------------------------- ### Create and Manage Fibers with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Demonstrates spawning new fibers for concurrent execution using `spin` and waiting for their completion with `join`. It also shows how to spawn fibers with initial values and join multiple fibers simultaneously. The `await_fibers` method is used to wait for a collection of fibers without retrieving their results. ```ruby machine = UM.new # Spawn a fiber fiber = machine.spin do machine.sleep(0.1) 42 # Return value end # Spawn with initial value fiber = machine.spin(:initial_value) do |value| puts "Started with: #{value}" value.to_s.upcase end # Wait for fiber to complete and get result result = machine.join(fiber) puts result # => "INITIAL_VALUE" # Join multiple fibers f1 = machine.spin { machine.sleep(0.1); :a } f2 = machine.spin { machine.sleep(0.2); :b } results = machine.join(f1, f2) puts results # => [:a, :b] # Await fibers without getting results fibers = 5.times.map { machine.spin { machine.sleep(rand * 0.1) } } machine.await_fibers(fibers) ``` -------------------------------- ### UringMachine Class Methods Source: https://github.com/digital-fabric/uringmachine/blob/main/docs/um_api.md These methods are associated with the UringMachine class itself and are used for initialization, debugging, and system-level operations. ```APIDOC ## UringMachine Class Methods ### Description Provides access to class-level functionalities of UringMachine, including initialization, debugging, and process management. ### Methods - `debug(msg)`: Prints a string message to STDERR. - `kernel_version`: Returns the Linux kernel version as an integer. - `new(size)`: Creates a new UringMachine instance with a specified size. - `pidfd_open(pid)`: Creates and returns a file descriptor for a given process ID. - `pidfd_send_signal(pidfd, sig, flags)`: Sends a signal to a process identified by a PID file descriptor. - `pipe`: Creates a pipe and returns read and write file descriptors. - `socketpair(domain, type, protocol)`: Creates a socket pair and returns the two file descriptors. ``` -------------------------------- ### Asynchronous DNS Resolution Source: https://context7.com/digital-fabric/uringmachine/llms.txt Demonstrates asynchronous DNS resolution using the `resolve` method. It shows how to resolve a hostname to its IP addresses and how to specify the record type. ```ruby machine = UM.new # Resolve hostname to IP addresses addresses = machine.resolve('example.com') puts addresses # => ["93.184.216.34"] # Resolve with type (default is :A) addresses = machine.resolve('example.com', :A) ``` -------------------------------- ### Add waitid APIs for Process Monitoring Source: https://github.com/digital-fabric/uringmachine/blob/main/grant-2025/journal.md Introduces two new low-level APIs, `UM#waitpid` and `UM#waitid_status`, for waiting on processes using the io_uring `waitid` system call. `UM#waitid` returns process PID and exit status, while `UM#waitid_status` returns a `Process::Status` object. These replace the older `waitpid` method. ```c #include #include #include "uringmachine.h" // Function to get Process::Status object (if available) VALUE rb_process_status_new(int pid, int status); int um_waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options, int *pid_out, int *status_out) { // ... implementation using io_uring waitid ... return 0; } VALUE um_waitid_status(int fd, int options) { siginfo_t info; int pid, status; if (um_waitid(P_PIDFD, fd, &info, options, &pid, &status) == 0) { // Check if rb_process_status_new is available #ifdef HAVE_RB_PROCESS_STATUS_NEW return rb_process_status_new(pid, status); #else // Fallback or error handling if function is not available return Qnil; #endif } return Qnil; } ``` -------------------------------- ### Creating and Managing Fibers Source: https://context7.com/digital-fabric/uringmachine/llms.txt This section covers the creation and management of fibers using the `spin` and `join` methods. Fibers are scheduled for concurrent execution, and `join` is used to wait for their completion and retrieve results. ```APIDOC ## Creating and Managing Fibers ### Description Creates and schedules a new fiber for concurrent execution. Use `join` to wait for fiber completion. ### Method `spin` (creates fiber), `join` (waits for completion) ### Endpoint N/A (In-process operations) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby machine = UM.new # Spawn a fiber fiber = machine.spin do machine.sleep(0.1) 42 # Return value end # Wait for fiber to complete and get result result = machine.join(fiber) puts result # => 42 # Join multiple fibers f1 = machine.spin { machine.sleep(0.1); :a } f2 = machine.spin { machine.sleep(0.2); :b } results = machine.join(f1, f2) puts results # => [:a, :b] ``` ### Response #### Success Response (200) - **result** (any) - The return value of the fiber. - **results** (Array) - An array of return values from joined fibers. #### Response Example ```json { "result": 42 } ``` ```json { "results": ["a", "b"] } ``` ``` -------------------------------- ### Add IO::Buffer Support to Low-Level I/O APIs Source: https://github.com/digital-fabric/uringmachine/blob/main/grant-2025/journal.md This update integrates `IO::Buffer` support across all low-level I/O APIs within UringMachine. This enhancement eliminates the need for the fiber scheduler to convert `IO::Buffer` objects to strings before invoking UringMachine APIs, leading to more efficient data handling. ```c #include #include "uringmachine.h" // Example of a low-level I/O API accepting IO::Buffer int um_read(int fd, IO_Buffer *buffer, size_t len) { // ... (implementation using IO::Buffer directly) return 0; } int um_write(int fd, const char *data, size_t len) { // ... (implementation potentially accepting IO::Buffer) return 0; } ``` -------------------------------- ### Address File I/O Assumptions in Fiber Scheduler Source: https://github.com/digital-fabric/uringmachine/blob/main/grant-2025/journal.md This section discusses how UringMachine's io_uring capabilities challenge Ruby's assumptions about file I/O. Ruby traditionally treats regular file I/O as blocking and uses a separate thread via `#blocking_operation_wait`. With io_uring, file I/O is inherently asynchronous, requiring adjustments in how the fiber scheduler interacts with file operations. ```ruby # Conceptual interaction between Ruby core and UM::FiberScheduler # Ruby's assumption for regular files: def regular_file_io(file_descriptor, data) if Fiber.current_scheduler # Ruby invokes this hook, assuming blocking I/O Fiber.current_scheduler.blocking_operation_wait do # Perform blocking I/O in a separate thread perform_blocking_write(file_descriptor, data) end else # Fallback to standard blocking I/O perform_blocking_write(file_descriptor, data) end end # UringMachine's approach (conceptual): # The UM::FiberScheduler would ideally intercept file I/O # and submit io_uring operations directly, bypassing the # need for #blocking_operation_wait for async file operations. ``` -------------------------------- ### Process Management with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Details process control functionalities including forking, waiting for child processes using waitid and waitid_status, and using pidfd for process management. Shows how to retrieve exit status and PIDs. ```ruby machine = UM.new # Fork and wait for child process child_pid = fork do sleep 0.1 exit 42 end pid, status = machine.waitid(UM::P_PID, child_pid, UM::WEXITED) puts "Child #{pid} exited with status #{status}" # => 42 # Wait for any child child_pid = fork { exit 0 } pid, status = machine.waitid(UM::P_ALL, 0, UM::WEXITED) # Get Process::Status object child_pid = fork { exit 13 } status = machine.waitid_status(UM::P_PID, child_pid, UM::WEXITED) puts status.exitstatus # => 13 puts status.pid # => child_pid # Use pidfd for process management pid = fork { sleep 10 } pidfd = UM.pidfd_open(pid) ``` -------------------------------- ### File Operations with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Covers file input/output operations including opening, closing (synchronously and asynchronously), reading, writing, and retrieving file statistics using statx. Supports opening files with block syntax for automatic closing. ```ruby machine = UM.new # Open and write to file fd = machine.open('/tmp/test.txt', UM::O_CREAT | UM::O_WRONLY) machine.write(fd, 'Hello, File!') machine.close(fd) # Open with block (auto-closes) machine.open('/tmp/test.txt', UM::O_RDONLY) do |fd| buffer = +'' machine.read(fd, buffer, 8192) puts buffer # => "Hello, File!" end # Async close (fire-and-forget) fd = machine.open('/tmp/test.txt', UM::O_RDONLY) machine.close_async(fd) machine.snooze # Process the close # Get file stats fd = machine.open('/tmp/test.txt', UM::O_RDONLY) stats = machine.statx(fd, nil, UM::AT_EMPTY_PATH, UM::STATX_ALL) puts stats[:size] # File size puts stats[:mtime] # Modification time machine.close(fd) # Stat by path stats = machine.statx(UM::AT_FDCWD, '/tmp/test.txt', 0, UM::STATX_SIZE | UM::STATX_MTIME) ``` -------------------------------- ### Thread-Safe Queue Operations with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Illustrates the use of UM::Queue for thread-safe First-In, First-Out (FIFO) and Last-In, First-Out (LIFO) queue operations. Includes blocking shifts and cross-thread communication patterns. ```ruby machine = UM.new queue = UM::Queue.new # Push and shift (FIFO) machine.push(queue, :first) machine.push(queue, :second) machine.push(queue, :third) puts machine.shift(queue) # => :first puts machine.shift(queue) # => :second # Push and pop (LIFO) machine.push(queue, :a) machine.push(queue, :b) puts machine.pop(queue) # => :b # Unshift (add to front) machine.unshift(queue, :front) puts machine.shift(queue) # => :front # Blocking shift (waits for data) machine.spin do machine.sleep(0.1) machine.push(queue, :delayed_value) end value = machine.shift(queue) # Blocks until value arrives puts value # => :delayed_value # Cross-thread communication worker_queue = UM::Queue.new Thread.new do m = UM.new while (job = m.shift(worker_queue)) break if job == :stop puts "Processing: #{job}" end end machine.push(worker_queue, 'task1') machine.push(worker_queue, 'task2') machine.push(worker_queue, :stop) ``` -------------------------------- ### Socket Shutdown Operations with Uringmachine Source: https://context7.com/digital-fabric/uringmachine/llms.txt Explains how to shut down socket connections using UM.shutdown and UM.shutdown_async. Demonstrates shutting down write, read, or both directions of a socket and the implications for subsequent operations. ```ruby machine = UM.new fd1, fd2 = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0) # Shutdown write side machine.shutdown(fd1, UM::SHUT_WR) # Attempting to write after shutdown raises error begin machine.send(fd1, 'test', 4, 0) rescue Errno::EPIPE puts "Cannot write after shutdown" end # Async shutdown machine.shutdown_async(fd2, UM::SHUT_RDWR) machine.sleep(0.01) # Let it complete machine.close(fd1) machine.close(fd2) ``` -------------------------------- ### Adapt Socket I/O Handling in Fiber Scheduler Source: https://github.com/digital-fabric/uringmachine/blob/main/grant-2025/journal.md Unlike regular files, Ruby's fiber scheduler handles socket I/O by making file descriptors non-blocking and using the `#io_wait` hook. This update notes that UringMachine's io_uring implementation fundamentally changes these I/O paradigms, potentially offering more efficient asynchronous operations for sockets without relying solely on `#io_wait`. ```ruby # Conceptual interaction for socket I/O # Ruby's current approach for sockets: def socket_send(socket_fd, data) # Make socket non-blocking (if not already) # ... if Fiber.current_scheduler # Ruby uses #io_wait to poll for readiness Fiber.current_scheduler.io_wait(socket_fd, :writable) do # Attempt to send data bytes_sent = attempt_socket_send(socket_fd, data) # Handle partial sends, etc. end else # Fallback to standard non-blocking socket send attempt_socket_send(socket_fd, data) end end # UringMachine's potential improvement: # By using io_uring, the scheduler could directly submit send requests # and be notified upon completion, potentially optimizing the wait/send cycle. ``` -------------------------------- ### Send Signal and Wait using pidfd Source: https://context7.com/digital-fabric/uringmachine/llms.txt Demonstrates sending a signal to a process using its pidfd and then waiting for the process to exit using the same pidfd. It also shows closing the pidfd after use. ```ruby UM.pidfd_send_signal(pidfd, UM::SIGTERM) machine.waitid(UM::P_PIDFD, pidfd, UM::WEXITED) machine.close(pidfd) ``` -------------------------------- ### Fiber Scheduler Integration with UM Source: https://context7.com/digital-fabric/uringmachine/llms.txt Explains how to integrate Uringmachine with Ruby's `Fiber::Scheduler` interface using `UM::FiberScheduler`. This allows standard Ruby I/O operations and concurrency primitives like `sleep` and `Mutex` to be fiber-aware and non-blocking. ```ruby require 'uringmachine' require 'uringmachine/fiber_scheduler' machine = UM.new scheduler = UM::FiberScheduler.new(machine) Fiber.set_scheduler(scheduler) # Now standard Ruby I/O is fiber-aware r, w = IO.pipe Fiber.schedule do w.write('Hello from fiber!') w.close end Fiber.schedule do data = r.read puts data # => "Hello from fiber!" end # Standard sleep is fiber-aware Fiber.schedule do sleep 0.1 # Doesn't block other fibers puts "Woke up!" end # Mutex works with fibers mutex = Mutex.new Fiber.schedule do mutex.synchronize do sleep 0.05 puts "In critical section" end end ``` -------------------------------- ### Stream Receive with UringMachine Source: https://github.com/digital-fabric/uringmachine/blob/main/docs/design/buffer_pool.md Demonstrates how to use the `UM#stream_recv` API to process incoming data from a file descriptor. It shows a loop that continuously receives data, parses line lengths, reads specified amounts of data, and processes it. This API is suitable for long-running connections and line-oriented protocols. ```ruby machine.stream_recv(fd) do |stream| loop do line = stream.get_line(max: 60) if (size = parse_size(line)) data = stream.read(size) process_data(data) else raise "Protocol error!" end end end ```