The zmq_getsockopt man page says,
Option value type int on POSIX systems, SOCKET on Windows
Question
On windows x64 passing the address of a local variable to zmq_getsockopt for ZMQ_FD consistently results in EINVAL. The code below is the smallest possible to reproduce the problem.
#include <zmq.h>
#include <stdio.h>
void zmq_perror(const char*);
int main(void)
{
const char *endpoint = "tcp://127.0.0.1:7100";
void *ctx = zmq_ctx_new();
if (ctx == NULL) { zmq_perror("zmq_ctx_new"); }
void *socket = zmq_socket(ctx, ZMQ_DEALER);
if (socket == NULL) { zmq_perror("zmq_socket"); }
int rc;
rc = zmq_connect(socket, endpoint);
if ( rc == -1 ) { zmq_perror("zmq_connect"); }
/*** This results in EINVAL ***/
int fd;
size_t fd_size = sizeof (fd);
rc = zmq_getsockopt(socket, ZMQ_FD, &fd, &fd_size);
if (rc == -1) { zmq_perror("zmq_getsockopt"); }
/*** This works without issue ***/
/*
int *fd = malloc(sizeof(int));
size_t fd_size = sizeof (fd);
rc = zmq_getsockopt(socket, ZMQ_FD, fd, &fd_size);
if (rc == -1) { zmq_perror("zmq_getsockopt"); }
*/
}
void zmq_perror(const char *f)
{
fprintf(stderr, "%s: %s\n", f, zmq_strerror(zmq_errno()));
abort();
}
Running the above using the first (manpage) form always produces:
zmq_getsockopt: Invalid argument
However the second, commented out form using malloc has no issues. This makes zero sense to me since passing the address of the local variable to zmq_getsockopt is perfectly legal.
This problem only manifests with 64 bit binaries on windows; 32 bit binaries on windows or 64 bit binaries on linux have no issue.
It also seems to only be an issue with the ZMQ_FD socket option. ZMQ_TYPE and ZMQ_SNDHWM worked without issue.
Is there some weird behavior related to ZMQ_FD on windows x64 that I'm not aware of?
Update
So I just noticed that my "working" code is actually erroneous.
sizeof(fd)
is taking the sizeof a pointer in the second form. In fact, it has nothing to do with malloc, as once I change it to sizeof(int) as it should be I get EINVAL again:
/* Fail */
int *fd = malloc(sizeof(int));
size_t fd_size = sizeof(int);
rc = zmq_getsockopt(socket, ZMQ_FD, fd, &fd_size);
if (rc == -1) { zmq_perror("zmq_getsockopt"); }
It turns out I apparently need to use a 64bit integer type with ZMQ_FD on windows x64
/* Success! */
uint64_t fd;
size_t fd_size = sizeof(uint64_t);
rc = zmq_getsockopt(socket, ZMQ_FD, &fd, &fd_size);
if (rc == -1) { zmq_perror("zmq_getsockopt"); }
This is very confusing since the api for zmq_getsockopt is int. Is this a bug? A windows eccentricity? Me being dense?
Relevant addendum:
zmq version: 3.2.3
compiler: cross compiled using mingw-w64, rubenvb-4.8.0 build for both 64bit and 32bit binaries
os: windows 7
Solution
The zmq_getsockopt man page says,
Option value type int on POSIX systems, SOCKET on Windows