lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


I might have found a bug with ffi.copy() when used without the third 'len' 
parameter.  Attached is a simplified version of the ZeroMQ FFI bindings that 
shows the bug (see the ffi.copy() call at line 81).  If you disable the JIT 
then this bug doesn't happen, or if you only send about 50 messages.  So it 
looks like the JIT is incorrectly optimizing the ffi.copy() call.  Also if you 
pass "msg_len" as the third parameter to ffi.copy() (see line 83) then it 
works.

To run the script you have to start two instances (client & server):
start the server first:
luajit-2 ffi_copy_bug.lua server 20 100000

then the client:
luajit-2 ffi_copy_bug.lua client 20 100000

The server binds to port 5555 on 127.0.0.1.

-- 
Robert G. Jakabosky
local ffi=require"ffi"

ffi.cdef[[
typedef struct zmq_msg_t
{
    void *content;
    unsigned char flags;
    unsigned char vsm_size;
    unsigned char vsm_data [30];
} zmq_msg_t;

int zmq_msg_init (zmq_msg_t *msg);
int zmq_msg_init_size (zmq_msg_t *msg, size_t size);
int zmq_msg_close (zmq_msg_t *msg);
void *zmq_msg_data (zmq_msg_t *msg);
size_t zmq_msg_size (zmq_msg_t *msg);

void *zmq_init (int io_threads);
int zmq_term (void *context);

void *zmq_socket (void *context, int type);
int zmq_close (void *s);
int zmq_bind (void *s, const char *addr);
int zmq_connect (void *s, const char *addr);
int zmq_send (void *s, zmq_msg_t *msg, int flags);
int zmq_recv (void *s, zmq_msg_t *msg, int flags);

]]

local zmq = {
-- socket types
PAIR = 0,
PUB = 1,
SUB = 2,
REQ = 3,
REP = 4,
XREQ = 5,
XREP = 6,
PULL = 7,
PUSH = 8,

-- socket options
HWM = 1,
SWAP = 3,
AFFINITY = 4,
IDENTITY = 5,
SUBSCRIBE = 6,
UNSUBSCRIBE = 7,
RATE = 8,
RECOVERY_IVL = 9,
MCAST_LOOP = 10,
SNDBUF = 11,
RCVBUF = 12,
RCVMORE = 13,
FD = 14,
EVENTS = 15,
TYPE = 16,
LINGER = 17,
RECONNECT_IVL = 18,
BACKLOG = 19,

-- send/recv flags
NOBLOCK = 1,
SNDMORE = 2,
}

local C = ffi.load"zmq"
setmetatable(_G, { __index=C })

local tmp_msg = ffi.new('zmq_msg_t')
local function zmq_send(sock, data, flags)
	local msg = tmp_msg
	local msg_len = #data + 1
	-- initialize message
	if C.zmq_msg_init_size(msg, msg_len) < 0 then
		return error("some zmq_*() function returned an error")
	end
	-- copy data into message.
	-- BROKEN: ffi.copy
	ffi.copy(C.zmq_msg_data(msg), data)
	-- WORKING: ffi.copy
	--ffi.copy(C.zmq_msg_data(msg), data, msg_len)

	-- send message
	local ret = C.zmq_send(sock, msg, flags or 0)
	-- close message before processing return code
	if C.zmq_msg_close(msg) ~= 0 then
		return error("some zmq_*() function returned an error")
	end
	-- now process send return code
	if ret ~= 0 then
		return error("some zmq_*() function returned an error")
	end
	return true
end

local function zmq_recv(sock, flags)
	local msg = tmp_msg
	-- initialize blank message.
	if C.zmq_msg_init(msg) < 0 then
		return error("some zmq_*() function returned an error")
	end

	-- receive message
	local ret = C.zmq_recv(sock, msg, flags or 0)
	if ret ~= 0 then
		local data, err = error("some zmq_*() function returned an error")
		C.zmq_msg_close(msg)
		return data, err
	end
	local data = ffi.string(C.zmq_msg_data(msg), C.zmq_msg_size(msg))
	-- close message
	if C.zmq_msg_close(msg) ~= 0 then
		return error("some zmq_*() function returned an error")
	end
	return data
end

local ctx=zmq_init(1)

local is_client = arg[1]
local message_size = tonumber(arg[2])
local roundtrip_count = tonumber(arg[3])

local msg
local sock
if arg[1] == 'client' then
	sock = zmq_socket(ctx, zmq.REQ)
	zmq_connect(sock, "tcp://localhost:5555")

	msg = string.rep('0', message_size)
	for i=1,roundtrip_count do
		zmq_send(sock, msg, 0)
		msg = zmq_recv(sock, 0)
	end
else
	sock = zmq_socket(ctx, zmq.REP)
	zmq_bind(sock, "tcp://lo:5555")

	for i=1,roundtrip_count do
		msg = zmq_recv(sock, 0)
		zmq_send(sock, msg, 0)
	end
end
print(is_client, "end")
print('sock close:', zmq_close(sock))
print('zmq_term:',zmq_term(ctx))