Skip to content

Commit 5dca347

Browse files
Add test for cross-process memory usage.
1 parent cc478a3 commit 5dca347

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

test/io/memory.rb

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
require "sus"
77
require "io/memory"
88
require "io/memory/a_memory_buffer"
9+
require "socket"
910

1011
# Try to require all implementations for testing.
1112
begin
@@ -85,4 +86,119 @@
8586
end
8687
end
8788
end
89+
end
90+
91+
# Test Unix domain socket file descriptor passing for shared memory
92+
describe IO::Memory do
93+
with "Unix domain socket file descriptor passing" do
94+
it "can share memory buffers across process boundaries via socket" do
95+
skip "Fork not supported" unless Process.respond_to?(:fork)
96+
97+
# Create a Unix domain socket pair
98+
parent_socket, child_socket = UNIXSocket.socketpair
99+
100+
# Create shared memory buffer
101+
handle = IO::Memory.new(1024)
102+
buffer = handle.map
103+
104+
# Write test data from parent
105+
test_message = "Hello from parent process!"
106+
buffer.set_string(test_message, 0)
107+
108+
pid = fork do
109+
# Child process
110+
parent_socket.close
111+
112+
begin
113+
# Receive file descriptor from parent
114+
received_io = child_socket.recv_io
115+
116+
# Map the received memory
117+
child_buffer = ::IO::Buffer.map(received_io, 1024)
118+
119+
# Read what parent wrote
120+
received_message = child_buffer.get_string(0, test_message.length)
121+
expect(received_message).to be == test_message
122+
123+
# Write response that parent can see
124+
response = "Hello from child process!"
125+
child_buffer.set_string(response, 100)
126+
127+
# Signal completion
128+
child_socket.write("OK")
129+
130+
received_io.close
131+
child_socket.close
132+
rescue => error
133+
child_socket.write("ERROR: #{error}")
134+
exit 1
135+
end
136+
137+
exit 0
138+
end
139+
140+
# Parent process
141+
child_socket.close
142+
143+
# Send file descriptor to child
144+
parent_socket.send_io(handle.io)
145+
146+
# Wait for child response
147+
response = parent_socket.read(100) # Read more to get full error message
148+
if response.start_with?("ERROR:")
149+
raise "Child process error: #{response}"
150+
end
151+
expect(response[0, 2]).to be == "OK"
152+
153+
# Verify child's write is visible to parent
154+
child_response = buffer.get_string(100, 25)
155+
expect(child_response).to be == "Hello from child process!"
156+
157+
# Clean up
158+
Process.wait(pid)
159+
parent_socket.close
160+
handle.close
161+
end
162+
163+
it "can share memory between threads using file descriptor duplication" do
164+
# Create shared memory buffer
165+
handle = IO::Memory.new(512)
166+
buffer = handle.map
167+
168+
# Write initial data
169+
initial_data = "Thread communication test"
170+
buffer.set_string(initial_data, 0)
171+
172+
# Share file descriptor with another thread
173+
shared_fd = handle.io.dup
174+
175+
# Data to be written by the other thread
176+
thread_message = "Updated by thread"
177+
178+
thread = Thread.new do
179+
# Map the same memory in the thread
180+
thread_io = IO.for_fd(shared_fd.fileno, autoclose: false)
181+
thread_buffer = ::IO::Buffer.map(thread_io, 512)
182+
183+
# Verify initial data is visible
184+
read_data = thread_buffer.get_string(0, initial_data.length)
185+
expect(read_data).to be == initial_data
186+
187+
# Write new data
188+
thread_buffer.set_string(thread_message, 100)
189+
190+
thread_io = nil # Don't close, let the dup handle it
191+
end
192+
193+
thread.join
194+
195+
# Verify thread's write is visible in main thread
196+
result = buffer.get_string(100, thread_message.length)
197+
expect(result).to be == thread_message
198+
199+
# Clean up
200+
shared_fd.close
201+
handle.close
202+
end
203+
end
88204
end

0 commit comments

Comments
 (0)