From 4bcbb3055ebf6753a2135fb3132311603d6ea930 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 1 Feb 2016 12:00:45 +0000 Subject: [PATCH] Problem: cannot use pre-allocated FDs. Fixes #777 Solution: add new [set|get]sockopt ZMQ_PRE_ALLOCATED_FD to allow users to let ZMQ use a pre-allocated file descriptor instead of allocating a new one. Update [set|get]sockopt documentation and test accordingly. The main use case for this feature is a socket-activated systemd service. For more information about this feature see: http://0pointer.de/blog/projects/socket-activation.html --- doc/zmq_getsockopt.txt | 13 +++++++++++++ doc/zmq_setsockopt.txt | 22 ++++++++++++++++++++++ include/zmq.h | 1 + src/options.cpp | 17 ++++++++++++++++- src/options.hpp | 5 +++++ tests/test_setsockopt.cpp | 28 ++++++++++++++++++++++++++++ 6 files changed, 85 insertions(+), 1 deletion(-) diff --git a/doc/zmq_getsockopt.txt b/doc/zmq_getsockopt.txt index 2ffd95b0..0589ceee 100644 --- a/doc/zmq_getsockopt.txt +++ b/doc/zmq_getsockopt.txt @@ -464,6 +464,19 @@ Default value:: null string Applicable socket types:: all, when using TCP or IPC transports +ZMQ_PRE_ALLOCATED_FD: Retrieve the pre-allocated socket file descriptor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The 'ZMQ_PRE_ALLOCATED_FD' option shall retrieve the pre-allocated file +descriptor that has been assigned to a ZMQ socket, if any. -1 shall be +returned if a pre-allocated file descriptor was not set for the socket. + +[horizontal] +Option value type:: int +Option value unit:: file descriptor +Default value:: -1 +Applicable socket types:: all bound sockets, when using IPC or TCP transport + + ZMQ_RATE: Retrieve multicast data rate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The 'ZMQ_RATE' option shall retrieve the maximum send or receive data rate for diff --git a/doc/zmq_setsockopt.txt b/doc/zmq_setsockopt.txt index 220a54c2..7948f526 100644 --- a/doc/zmq_setsockopt.txt +++ b/doc/zmq_setsockopt.txt @@ -492,6 +492,28 @@ Default value:: not set Applicable socket types:: all, when using TCP transport +ZMQ_PRE_ALLOCATED_FD: Set the pre-allocated socket file descriptor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When set to a positive integer value before zmq_bind is called on the socket, +the socket shall use the corresponding file descriptor for connections over +TCP or IPC instead of allocating a new file descriptor. +Useful for writing systemd socket activated services. If set to -1 (default), +a new file descriptor will be allocated instead (default behaviour). + +NOTE: if set after calling zmq_bind, this option shall have no effect. +NOTE: the file descriptor passed through MUST have been ran through the "bind" + and "listen" system calls beforehand. Also, socket option that would + normally be passed through zmq_setsockopt like TCP buffers length, + IP_TOS or SO_REUSEADDR MUST be set beforehand by the caller, as they + must be set before the socket is bound. + +[horizontal] +Option value type:: int +Option value unit:: file descriptor +Default value:: -1 +Applicable socket types:: all bound sockets, when using IPC or TCP transport + + ZMQ_PROBE_ROUTER: bootstrap connections to ROUTER sockets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When set to 1, the socket will automatically send an empty message when a diff --git a/include/zmq.h b/include/zmq.h index 96bbe1d4..8ac82b06 100644 --- a/include/zmq.h +++ b/include/zmq.h @@ -344,6 +344,7 @@ ZMQ_EXPORT const char *zmq_msg_group (zmq_msg_t *msg); #define ZMQ_VMCI_BUFFER_MIN_SIZE 86 #define ZMQ_VMCI_BUFFER_MAX_SIZE 87 #define ZMQ_VMCI_CONNECT_TIMEOUT 88 +#define ZMQ_PRE_ALLOCATED_FD 89 /* Message options */ #define ZMQ_MORE 1 diff --git a/src/options.cpp b/src/options.cpp index f13b187c..1acabae1 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -77,7 +77,8 @@ zmq::options_t::options_t () : connected (false), heartbeat_ttl (0), heartbeat_interval (0), - heartbeat_timeout (-1) + heartbeat_timeout (-1), + pre_allocated_fd (-1) { #if defined ZMQ_HAVE_VMCI vmci_buffer_size = 0; @@ -621,6 +622,13 @@ int zmq::options_t::setsockopt (int option_, const void *optval_, break; # endif + case ZMQ_PRE_ALLOCATED_FD: + if (is_int && value >= -1) { + pre_allocated_fd = value; + return 0; + } + break; + default: #if defined (ZMQ_ACT_MILITANT) // There are valid scenarios for probing with unknown socket option @@ -1031,6 +1039,13 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_) } break; + case ZMQ_PRE_ALLOCATED_FD: + if (is_int) { + *value = pre_allocated_fd; + return 0; + } + break; + default: #if defined (ZMQ_ACT_MILITANT) malformed = false; diff --git a/src/options.hpp b/src/options.hpp index 54a8a301..7116461c 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -231,6 +231,11 @@ namespace zmq uint64_t vmci_buffer_max_size; int vmci_connect_timeout; # endif + + // When creating a new ZMQ socket, if this option is set the value + // will be used as the File Descriptor instead of allocating a new + // one via the socket () system call. + int pre_allocated_fd; }; } diff --git a/tests/test_setsockopt.cpp b/tests/test_setsockopt.cpp index 2a91df61..3b092fef 100644 --- a/tests/test_setsockopt.cpp +++ b/tests/test_setsockopt.cpp @@ -70,9 +70,37 @@ void test_setsockopt_tcp_send_buffer() zmq_ctx_term(ctx); } +void test_setsockopt_pre_allocated_fd() +{ + int rc; + void *ctx = zmq_ctx_new(); + void *socket = zmq_socket(ctx, ZMQ_PUSH); + + int val = 0; + size_t placeholder = sizeof(val); + + rc = zmq_getsockopt(socket, ZMQ_PRE_ALLOCATED_FD, &val, &placeholder); + assert(rc == 0); + assert(val == -1); + + val = 3; + + rc = zmq_setsockopt(socket, ZMQ_PRE_ALLOCATED_FD, &val, sizeof(val)); + assert(rc == 0); + assert(val == 3); + + rc = zmq_getsockopt(socket, ZMQ_PRE_ALLOCATED_FD, &val, &placeholder); + assert(rc == 0); + assert(val == 3); + + zmq_close(socket); + zmq_ctx_term(ctx); +} + int main() { test_setsockopt_tcp_recv_buffer(); test_setsockopt_tcp_send_buffer(); + test_setsockopt_pre_allocated_fd(); }