-
-
Save peterc/2582673 to your computer and use it in GitHub Desktop.
# 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 |
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.
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.. :-)
A corrected version to work on ruby 2.6.3
https://gist.github.com/unplugandplay/a66068239f621790c87e0e017b064a92
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.