Skip to content

Instantly share code, notes, and snippets.

@peterc
Created May 3, 2012 02:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save peterc/2582673 to your computer and use it in GitHub Desktop.
Save peterc/2582673 to your computer and use it in GitHub Desktop.
Sys V message queues in Ruby on OS X (take one)
# Lightweight library to access the System V message queue functionality on Mac OS X (32 and 64 bit)
# Still quite scrappy and needs to be packaged up properly but.. it works!
require 'fiddle'
class MsgQ
LIBC = DL.dlopen('libc.dylib')
IPC_CREAT = 001000
IPC_EXCL = 002000
IPC_NOWAIT = 004000
IPC_R = 000400
IPC_W = 000200
IPC_M = 010000
SIZEOF_LONG = [0].pack('L_').size
def initialize(path, id)
@id = self.class.get(path, id)
end
def self.ftok(path, id)
id = id.class == String ? id.ord : id.to_i
Fiddle::Function.new(LIBC['ftok'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT], Fiddle::TYPE_INT).call(path, id)
end
def self.get(path, id, msgflag = IPC_CREAT | IPC_R | IPC_W | IPC_M )
Fiddle::Function.new(LIBC['msgget'], [Fiddle::TYPE_INT, Fiddle::TYPE_INT], Fiddle::TYPE_INT)
.call(ftok(path, id), msgflag)
end
def send(msg = {}, flags = 0)
msg = { msg: msg } if msg.is_a? String
msg = { type: 1, msg: '' }.merge(msg)
r = Fiddle::Function.new(LIBC['msgsnd'], [Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_INT], Fiddle::TYPE_INT)
.call(@id, [msg[:type]].pack('Q') + msg[:msg], msg[:msg].length + 1, flags)
r == -1 ? false : r
end
def receive(size, type, flags)
ptr = DL::CPtr.malloc(size + SIZEOF_LONG)
r = Fiddle::Function.new(LIBC['msgrcv'], [Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_INT, Fiddle::TYPE_INT], Fiddle::TYPE_INT)
.call(@id, ptr.to_i, size, type, flags)
msg_type = ptr[0, SIZEOF_LONG].unpack('Q')[0]
msg = (ptr + SIZEOF_LONG).to_s
ptr.free
r == -1 ? false : { type: msg_type, msg: msg }
end
end
###
if __FILE__ == $0
queue = MsgQ.new('/tmp', 'A')
queue.send type: 2, msg: "test"
queue.send type: 3, msg: "test"
queue.send type: 4, msg: "test"
queue.send "test"
while m = queue.receive(5, 0, MsgQ::IPC_NOWAIT)
p m
end
end
@peterc
Copy link
Author

peterc commented May 3, 2012

Had to do a ton of diving around in arcane Ruby code and Japanese documentation to get this working..! This is an area that's very poorly documented, but I hope to write an article about it.

@jstorimer
Copy link

This is pretty awesome. So if you're familiar with the C api is it a pretty straightforward translation to Ruby + Fiddle? I suspect the corresponding C code would look pretty similar, just swap some boilerplate code but keep the constants.

Reminds me that I recently discovered /usr/include/sys/syscall.h on OSX and had a fun night of hacking with Kernel#syscall. Have you seen http://bogomips.org/ruby_posix_mq/? Similar API for POSIX message queues. I haven't had any luck getting it to work on anything but Linux.

@peterc
Copy link
Author

peterc commented May 4, 2012

Reasonably straightforward. I had to do quite a bit of "spelunking" to work out various DL trivialities and issues though. I'm still semi-stuck on one - how to get access to "errno"! However, it seems FFI.errno will do the trick, but introduces another dependency. Man, DL documentation is SO hard to come by.. you end up having to read weird Japanese forums.

I've seen the POSIX mq stuff but.. it's entirely a no-go on OS X. Unless things have changed recently, last I read was OS X has zero support for it and only does SysV (and even then, it's not that good..)

I hope to write up this spelunking for an article on Ruby Inside. I did learn a bit of stuff and as DL/Fiddle documentation is so short.. :-)

@unplugandplay
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment