Skip to content

Instantly share code, notes, and snippets.

@zipizap
Last active December 20, 2015 00:19
Show Gist options
  • Save zipizap/6040947 to your computer and use it in GitHub Desktop.
Save zipizap/6040947 to your computer and use it in GitHub Desktop.
Pass a file-descriptor between 2 processes, using UnixSockets - (MRI vs Jruby limitations)
require 'socket'
require 'spoon'
# I made this code to experiment with 2 things:
# 1) Use the 'spoon' gem and Spoon.spawnp as a poor-man-substitute of a normal fork
# 2) pass a file-descriptor over unix-sockets, from a parent process to a child process
#
# Jruby cannot .fork() as MRI Ruby... it seems the alternative is 'spoon' gem and its
# spawn/spawnp methods.
# Fork is not the same as spawning: basically fork will copy to a child process all
# file-descriptors and continue to execute ruby code. Spawn is fork+exec = start executing
# another external program (not internal code) with file-descriptors independence.
# To make a poor-man-fork with spawn, the idea is to spawn the same program in a child-process,
# but that child-process does not have access to parent's file descriptors. To pass the
# file-descriptor from parent-process to child-process, a unix socket is created by the parent,
# and given as argument to the child-process when spawning it. The child-process will then read
# the unix-socket and receive from it the file-descriptor of the parent (unix sockets CAN do that,
# google "unix socket pass file-descriptor").
#
# After joining these pieces, I've made this code and saw that the unix-socket communication was
# good, that 'spoon' was working as expected and that the file-descriptor is passed correctly
# with UNIXSocket#send_io/recv_i. All works in MRI Ruby, but in Jruby unfortunately, neither
# fork() (because of jruby limitation) nor file-descriptor-passing-in-unix-sockets works
# (no errors, ?maybe a bug?).
#
# So, dear reader, I hope this helps you for whatever reason, and leave it published in the internets
#
# zipizap 19/07/2013
#
#.
#
#
if ARGV[0].nil?
#parent: "ruby this-file.rb"
puts "p> Im parent"
fd = File.open("/tmp/a.tmp",'w')
fd.puts "Im parent"
#create unix socket
uskt_file = "/tmp/unix_socket"
%x(rm -f #{uskt_file} &>/dev/null)
uskt_server = UNIXServer.new(uskt_file)
#spawn child process
puts "p> pre spawning"
# child_pid = Spoon.spawnp(*%W(java -jar deployment/jruby.jar #{$0} #{uskt_file}))
child_pid = Spoon.spawnp(*%W(ruby #{$0} #{uskt_file}))
Process.detach(child_pid)
puts "p> pos spawning"
#accept unix socket connections
puts "p> pre accept"
uskt_server_with_client = uskt_server.accept
puts "p> pos accept"
#send fd into unix socket
puts "p> going to send fd=#{fd.inspect}"
uskt_server_with_client.send_io fd
#close unix socket
uskt_server_with_client.close
fd.close
#child will rm unix socket
#parent process exit
puts "p> bye"
exit 0
elsif File.socket?(ARGV[0]||"")
#child: "ruby this-file.rb /tmp/unix_socket"
puts "c> Im child"
uskt_file = ARGV[0]
#open unix socket of arg[0]
puts "c> going to open unix socket"
uskt = UNIXSocket.open(uskt_file)
#read fd from unix socket
puts "c> pre read "
fd = uskt.recv_io
puts "c> pos read fd=#{fd.inspect}"
# #write into fd
fd.puts "Im child"
#close unix socket
uskt.close
fd.close
#rm unix socket file
%x(rm -f #{uskt_file} &>/dev/null)
#child process exit
puts "c> bye"
exit 0
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment