Upgrading from libzmq 2.x to 3.2

Compatible Changes

These changes don't impact existing application code directly:

  • Pubsub filtering is now done at the publisher side instead of subscriber side.
  • Many new API methods (disconnect, unbind, monitor, ctx_set, etc.)

Areas of Impact

These are the main areas of impact on applications or bindings that want to run on libzmq 3.2 as well as 2.x:

  • Renamed methods: zmq_recv is now zmq_recvmsgzmq_msg_recv, zmq_send is now zmq_sendmsg zmq_msg_send. Symptom: compile failures on zmq_send and zmq_recv. Advice: use zmq_msg_send and zmq_msg_recv now, with help of the macros below.
  • These two methods return positive values on success, and -1 on error. In 2.x they always returned zero on success. Symptom: apparent errors when things actually work fine. Advice: test strictly for return code = -1, not non-zero.
  • zmq_poll now waits for milliseconds, not microseconds. Symptom: application stops responding (in fact responds 1000 times slower). Advice: use the ZMQ_POLL_MSEC macro defined below, in all zmq_poll calls.
  • ZMQ_NOBLOCK is now called ZMQ_DONTWAIT. Symptom: compile failures on ZMQ_NOBLOCK macro. Advice: use ZMQ_DONTWAIT now, with help of the macro below.
  • The ZMQ_HWM socket option is now broken into ZMQ_SNDHWM and ZMQ_RCVHWM. Symptom: compile failures on ZMQ_HWM. Advice for binding authors: create wrapper function that sets both send & recv HWM in one go.
  • Most but not all socket options are now ints. See this summary. Symptom: runtime error returns on zmq_setsockopt and zmq_getsockopt. Advice for binding authors: create wrapper functions for all options, and always check return codes on these calls.
  • The ZMQ_SWAP option has gone. There is no documented replacement yet but the plan is to build a swap device. Symptom: compile failures on ZMQ_SWAP. Advice: as soon as there is a replacement, this should be used in existing applications to make them forwards compatible with 3.1.

Suggested Shim Macros

Our advice is to emulate 3.2 as far as possible, so you can start to use that API today, but with the benefits of being compatible with 2.x at the same time. Here are C macro definitions that help your C/C++ code to work across both versions (taken from CZMQ):

#include <zmq.h>

#ifndef ZMQ_DONTWAIT
#   define ZMQ_DONTWAIT   ZMQ_NOBLOCK
#endif
#ifndef ZMQ_RCVHWM
#   define ZMQ_RCVHWM     ZMQ_HWM
#endif
#ifndef ZMQ_SNDHWM
#   define ZMQ_SNDHWM     ZMQ_HWM
#endif
#if ZMQ_VERSION_MAJOR == 2
#   define more_t int64_t
#   define zmq_ctx_destroy(context) zmq_term(context)
#   define zmq_msg_send(msg,sock,opt) zmq_send (sock, msg, opt)
#   define zmq_msg_recv(msg,sock,opt) zmq_recv (sock, msg, opt)
#   define ZMQ_POLL_MSEC    1000        //  zmq_poll is usec
#elif ZMQ_VERSION_MAJOR == 3
#   define more_t int
#   define ZMQ_POLL_MSEC    1           //  zmq_poll is msec
#endif

Worked Example

Here is a fragment of code (from the Guide) running on 2.x:

if (zmq_recv (socket, &self->frame [frame_nbr], 0)) {
    kvmsg_destroy (&self);
    break;
}
//  Verify multipart framing
more_t more;
size_t more_size = sizeof (more_t);
zmq_getsockopt (socket, ZMQ_RCVMORE, &more, &more_size);
int expected = (frame_nbr < KVMSG_FRAMES - 1)? 1: 0;
if (more != expected) {
    kvmsg_destroy (&self);
    break;
}

These are the problems:

  • zmq_recv method will fail at compile time with 'too few arguments'.
  • zmq_recv method tests for non-zero return code, must test for -1 explicitly.
  • zmq_getsockopt uses wrong integer type for argument, will fail with an error.

Here is the same code ported onto CZMQ, and compatible with 3.2:

if (zmq_msg_recv (&self->frame [frame_nbr], socket, 0) == -1) {
    kvmsg_destroy (&self);
    break;
}
//  Verify multipart framing
int rcvmore = (frame_nbr < KVMSG_FRAMES - 1)? 1: 0;
if (zsockopt_rcvmore (socket) != rcvmore) {
    kvmsg_destroy (&self);
    break;
}