From 00d25b78734b2253bd7914121188e0f6343a23ba Mon Sep 17 00:00:00 2001 From: Simon Giesecke Date: Sun, 13 May 2018 18:06:05 +0200 Subject: [PATCH 1/5] Problem: inconsistent behaviour of zmq_poller_add and zmq_poller_add_fd in case of memory exhaustion Solution: always return -1 with errno == ENOMEM --- src/socket_poller.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/socket_poller.cpp b/src/socket_poller.cpp index 570e4b8dbd..eed1bfae91 100644 --- a/src/socket_poller.cpp +++ b/src/socket_poller.cpp @@ -137,7 +137,13 @@ int zmq::socket_poller_t::add (socket_base_t *socket_, -1 #endif }; - items.push_back (item); + try { + items.push_back (item); + } + catch (const std::bad_alloc &) { + errno = ENOMEM; + return -1; + } need_rebuild = true; return 0; @@ -162,7 +168,13 @@ int zmq::socket_poller_t::add_fd (fd_t fd_, void *user_data_, short events_) -1 #endif }; - items.push_back (item); + try { + items.push_back (item); + } + catch (const std::bad_alloc &) { + errno = ENOMEM; + return -1; + } need_rebuild = true; return 0; From 2cd147e5ff935e95a0153b175510b89741f37391 Mon Sep 17 00:00:00 2001 From: Simon Giesecke Date: Sun, 13 May 2018 18:09:01 +0200 Subject: [PATCH 2/5] Problem: misleading comment for calling zmq_poller_add with events == 0 Solution: change comment, and add test case for zmq_poller_modify with events == 0 --- tests/test_poller.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/test_poller.cpp b/tests/test_poller.cpp index 2414fd7369..a1fe989ba7 100644 --- a/tests/test_poller.cpp +++ b/tests/test_poller.cpp @@ -339,9 +339,19 @@ void call_poller_modify_unregistered_fails (void *poller, void *socket) void call_poller_add_no_events (void *poller, void *socket) { - // add a socket with no events - // TODO should this really be legal? it does not make any sense... + // add a socket with no events initially (may be activated later with + // zmq_poller_modify) TEST_ASSERT_SUCCESS_ERRNO (zmq_poller_add (poller, socket, NULL, 0)); + // TODO test that no events are signalled +} + +void call_poller_modify_no_events (void *poller, void *socket) +{ + // deactivates all events for a socket temporarily (may be activated again + // later with zmq_poller_modify) + zmq_poller_add (poller, socket, NULL, ZMQ_POLLIN); + TEST_ASSERT_SUCCESS_ERRNO (zmq_poller_modify (poller, socket, 0)); + // TODO test that no events are signalled } void call_poller_add_fd_twice_fails (void *poller, void * /*zeromq_socket*/) @@ -389,6 +399,7 @@ TEST_CASE_FUNC_PARAM (call_poller_remove_unregistered_fails, TEST_CASE_FUNC_PARAM (call_poller_modify_unregistered_fails, test_with_empty_poller) TEST_CASE_FUNC_PARAM (call_poller_add_no_events, test_with_empty_poller) +TEST_CASE_FUNC_PARAM (call_poller_modify_no_events, test_with_empty_poller) TEST_CASE_FUNC_PARAM (call_poller_add_fd_twice_fails, test_with_empty_poller) TEST_CASE_FUNC_PARAM (call_poller_remove_fd_unregistered_fails, test_with_empty_poller) @@ -603,6 +614,7 @@ int main (void) RUN_TEST (test_call_poller_remove_unregistered_fails); RUN_TEST (test_call_poller_modify_unregistered_fails); RUN_TEST (test_call_poller_add_no_events); + RUN_TEST (test_call_poller_modify_no_events); RUN_TEST (test_call_poller_add_fd_twice_fails); RUN_TEST (test_call_poller_remove_fd_unregistered_fails); RUN_TEST (test_call_poller_modify_fd_unregistered_fails); From 213254cca5846a9fe5b2283dd5792694a1c1bba8 Mon Sep 17 00:00:00 2001 From: Simon Giesecke Date: Sun, 13 May 2018 18:10:34 +0200 Subject: [PATCH 3/5] Problem: inconsistent behaviour of zmq_poller_new in case of memory exhaustion Solution: return NULL and set errno to ENOMEM --- src/zmq.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/zmq.cpp b/src/zmq.cpp index 832eb772dd..6301cdc800 100644 --- a/src/zmq.cpp +++ b/src/zmq.cpp @@ -1167,7 +1167,9 @@ int zmq_poll (zmq_pollitem_t *items_, int nitems_, long timeout_) void *zmq_poller_new (void) { zmq::socket_poller_t *poller = new (std::nothrow) zmq::socket_poller_t; - alloc_assert (poller); + if (!poller) { + errno = ENOMEM; + } return poller; } @@ -1295,7 +1297,6 @@ int zmq_poller_remove_fd (void *poller_, int fd_) return ((zmq::socket_poller_t *) poller_)->remove_fd (fd_); } - int zmq_poller_wait (void *poller_, zmq_poller_event_t *event_, long timeout_) { if (!poller_ || !((zmq::socket_poller_t *) poller_)->check_tag ()) { From f4b1cae082075f92bda8cd52ba1886621b692ec5 Mon Sep 17 00:00:00 2001 From: Simon Giesecke Date: Sun, 13 May 2018 18:11:43 +0200 Subject: [PATCH 4/5] Problem: no documentation for zmq_poller_* Solution: add initial documentation --- doc/zmq.txt | 5 +- doc/zmq_poller.txt | 269 +++++++++++++++++++++++++++++++++++++++++ doc/zmq_setsockopt.txt | 2 +- 3 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 doc/zmq_poller.txt diff --git a/doc/zmq.txt b/doc/zmq.txt index 1f929ffcce..02f2f4909f 100644 --- a/doc/zmq.txt +++ b/doc/zmq.txt @@ -142,7 +142,10 @@ Monitoring socket events:: 0MQ provides a mechanism for applications to multiplex input/output events over a set containing both 0MQ sockets and standard sockets. This mechanism mirrors the standard _poll()_ system call, and is described in detail in -linkzmq:zmq_poll[3]. +linkzmq:zmq_poll[3]. This API is deprecated, however. + +There is a new DRAFT API with multiple zmq_poller_* function, which is described +in linkzmq:zmq_poller[3]. Transports diff --git a/doc/zmq_poller.txt b/doc/zmq_poller.txt new file mode 100644 index 0000000000..70934f1e9a --- /dev/null +++ b/doc/zmq_poller.txt @@ -0,0 +1,269 @@ +zmq_poller(3) +=========== + + +NAME +---- +zmq_poller - input/output multiplexing + + +SYNOPSIS +-------- + +*void *zmq_poller_new (void);* +*int zmq_poller_destroy (void **'poller_p');* + +*int zmq_poller_add (void *'poller', void *'socket', void *'user_data', short 'events'); +*int zmq_poller_modify (void *'poller', void *'socket', short 'events');* +*int zmq_poller_remove (void *'poller', void *'socket');* + +*int zmq_poller_add_fd (void *'poller', int 'fd', void *'user_data', short 'events');* +*int zmq_poller_modify_fd (void *'poller', int 'fd', short 'events');* +*int zmq_poller_remove_fd (void *'poller', int 'fd');* + +*int zmq_poller_wait_all (void *'poller', + zmq_poller_event_t *'events', + int 'n_events', + long 'timeout');* + +DESCRIPTION +----------- +The _zmq_poller_*_ functions provide a mechanism for applications to multiplex +input/output events in a level-triggered fashion over a set of sockets. + +_zmq_poller_new_ and _zmq_poller_destroy_ manage the lifetime of a poller +instance. _zmq_poller_new_ creates and returns a new poller instance, while +_zmq_poller_destroy_ destroys it. A pointer to a valid poller must be passed +as the _poller_p_ argument of _zmq_poller_destroy_. In particular, +_zmq_poller_destroy_ may not be called multiple times for the same poller +instance. _zmq_poller_destroy_ sets the passed pointer to NULL in case of a +successful execution. _zmq_poller_destroy_ implicitly unregisters all +registered sockets and file descriptors. + +_zmq_poller_add_, _zmq_poller_modify_ and _zmq_poller_remove_ manage the 0MQ +sockets registered with a poller. + +_zmq_poller_add_ registers a new _socket_ with a given _poller_. Both _poller_ +and _socket_ must point to valid 0MQ objects. The _events_ parameter specifies +which event types the client wants to subscribe to. It is legal to specify no +events (i.e. 0), and activate them later with _zmq_poller_modify_. +In addition, _user_data_ may be specified, which is not used by the poller, but +passed back to the caller when an event was signalled in a call to +_zmq_poller_wait_ or _zmq_poller_wait_all_. _user_data_ may be NULL. If it is +not NULL, it must be a valid pointer. Otherwise, behaviour is undefined. +_zmq_poller_add_ may not be called multiple times for the same socket +(unless _zmq_poller_remove_ has been called for that socket). + +_zmq_poller_modify_ modifies the subscribed events for a socket. It is +legal to specify no events (i.e. 0) to disable events temporarily, and +reactivate them later with another call to _zmq_poller_modify_. + +_zmq_poller_remove_ removes a socket registration completely. +_zmq_poller_remove_ must be called before a socket is closed with _zmq_close_. + +Note that it is not necessary to call _zmq_poller_remove_ for any socket +before calling _zmq_poller_destroy_. + +Also note that calling _zmq_poller_remove_ is not equivalent to calling +_zmq_poller_modify_ with no events. _zmq_poller_modify_ does not free resources +associated with the socket registration, and requires that the _socket_ +remains valid. + +_zmq_poller_add_fd_, _zmq_poller_modify_fd_ and _zmq_poller_remove_fd_ are +analogous to the previous functions but manage regular file descriptiors +registered with a poller. On Windows, these functions can only be used with +WinSock sockets. + +In the following, 0MQ sockets added with _zmq_poller_add_ and file descriptors +added with _zmq_poller_add_fd_ are referred to as 'registered objects'. + +The *zmq_poller_event_t* structure is defined as follows: + +["literal", subs="quotes"] +typedef struct +{ + void *socket; +#if defined _WIN32 + SOCKET fd; +#else + int fd; +#endif + void *user_data; + short events; +} zmq_poller_event_t; + +For each registered object, _zmq_poller_wait_all()_ shall examine the +registered objects for the event(s) currently registered. + +If none of the registered events have occurred, _zmq_poller_wait_all_ shall +wait 'timeout' milliseconds for an event to occur on any of the registered +objects. If the value of 'timeout' is `0`, _zmq_poller_wait_all_ shall +return immediately. If the value of 'timeout' is `-1`, _zmq_poller_wait_all_ +shall block indefinitely until one event has occurred on any of the +registered objects. + +The 'events' argument _zmq_poller_wait_all_ must be a pointer to an array of +at least 'n_events' elements. Behaviour is undefined if 'events' does not point +to an array of at least 'n_events' elements. + +_zmq_poller_wait_all_ returns at most 'n_events' events. If more than +'n_events' events were signalled, only an unspecified subset of the signalled +events is returned through 'events'. + +A caller is advised to ensure that 'n_events' is equal to the number of +registered objects. Otherwise, a livelock situation may result: If more than +'n_events' registered objects have an active event on each call to +_zmq_poller_wait_all_, it might happen that the same subset of registered +objects is always returned, and the caller never notices the events on the +others. + +_zmq_poller_wait_all_ returns the number of valid elements. The valid elements +are placed in positions '0' to 'n_events - 1' in the 'events' array. All +members of a valid element are set to valid values by _zmq_poller_wait_all_. +The client does therefore not need to initialize the contents of the events +array before a call to _zmq_poller_wait_all_. It is unspecified whether the +the remaining elements of 'events' are written to by _zmq_poller_wait_all_. + +EVENT TYPES +----------- + +The 'events' parameter of _zmq_poller_add_ and _zmq_poller_modify_, and the +'events' member of the zmq_poller_event_t structure are bit masks constructed +by OR'ing a combination of the following event flags: + +*ZMQ_POLLIN*:: +For 0MQ sockets, at least one message may be received from the 'socket' without +blocking. For standard sockets this is equivalent to the 'POLLIN' flag of the +_poll()_ system call and generally means that at least one byte of data may be +read from 'fd' without blocking. + +*ZMQ_POLLOUT*:: +For 0MQ sockets, at least one message may be sent to the 'socket' without +blocking. For standard sockets this is equivalent to the 'POLLOUT' flag of the +_poll()_ system call and generally means that at least one byte of data may be +written to 'fd' without blocking. + +*ZMQ_POLLERR*:: +For 0MQ sockets this flag has no effect on the _zmq_poller_add_ and +_zmq_poller_modify_ functions, and is never set in the +'events' member of the zmq_poller_event_t structure. +For standard sockets, this flag is passed through _zmq_poller_wait_all_ to the +underlying _poll()_ system call and generally means that some sort of error +condition is present on the socket specified by 'fd'. + +*ZMQ_POLLPRI*:: +For 0MQ sockets this flag has no effect on the _zmq_poller_add_ and +_zmq_poller_modify_ functions, and is never set in the +'events' member of the zmq_poller_event_t structure. +For standard sockets this means there +is urgent data to read. Refer to the POLLPRI flag for more informations. +For a file descriptor, refer to your OS documentation: as an example, GPIO +interrupts are signaled through a POLLPRI event. +This flag has no effect on Windows. + +NOTE: The _zmq_poller_*_ functions may be implemented or emulated using operating +system interfaces other than _poll()_, and as such may be subject to the limits +of those interfaces in ways not defined in this documentation. + +THREAD SAFETY +------------- +Like most other 0MQ objects, a poller is not thread-safe. All operations must +be called from the same thread. Otherwise, behaviour is undefined. + +RETURN VALUE +------------ +_zmq_poller_new_ always returns a valid pointer to a poller. + +All functions that return an int, return -1 in case of a failure. In that case, +zmq_errno() can be used to query the type of the error as described below. + +_zmq_poller_wait_all_ returns the number of events signalled and returned in +the events array. It never returns 0. + +All other functions return 0 in case of a successful execution. + +ERRORS +------ +On _zmq_poller_new_: +*ENOMEM*:: +A new poller could not be allocated successfully. + +On _zmq_poller_destroy_: +*EFAULT*:: +_poller_p_ did not point to a valid poller. Note that passing an invalid pointer (e.g. +pointer to deallocated memory) may cause undefined behaviour (e.g. an access violation). + +On _zmq_poller_add_, _zmq_poller_modify_ and _zmq_poller_remove_: +*EFAULT*:: +_poller_ did not point to a valid poller. Note that passing an +invalid pointer (e.g. pointer to deallocated memory) may cause undefined +behaviour (e.g. an access violation). +*ENOTSOCK*:: +_socket_ did not point to a valid socket. Note that passing an +invalid pointer (e.g. pointer to deallocated memory) may cause undefined +behaviour (e.g. an access violation). + +On _zmq_poller_add_: +*EMFILE*:: +TODO + +On _zmq_poller_add_ or _zmq_poller_add_fd_: +*ENOMEM*:: +Necessary resources could not be allocated. +*EINVAL*:: +_socket_ resp. _fd_ was already registered with the poller. + +On _zmq_poller_modify_, _zmq_poller_modify_fd_, _zmq_poller_remove_ or +_zmq_poller_remove_fd_: +*EINVAL*:: +_socket_ resp. _fd_ was not registered with the poller. + +On _zmq_poller_add_fd_, _zmq_poller_modify_fd_ and _zmq_poller_remove_fd_: +*EBADF**: +The _fd_ specified was the retired fd. + +On _zmq_poller_wait_ and _zmq_poller_wait_all_: +*ETERM*:: +At least one of the registered objects is a 'socket' whose associated 0MQ +'context' was terminated. +*EFAULT*:: +The provided 'events' was NULL, or 'poller' did not point to a valid poller, +or there are no registered objects and 'timeout' was negative. +*EINTR*:: +The operation was interrupted by delivery of a signal before any events were +available. +*EAGAIN*:: +No registered event was signalled before the timeout was reached. + + +EXAMPLE +------- +.Polling indefinitely for input events on both a 0MQ socket and a standard socket. +---- +void *poller = zmq_poller_new (); + +zmq_poller_event_t events [2]; +/* First item refers to 0MQ socket 'socket' */ +zmq_poller_add (poller, socket, ZMQ_POLLIN, NULL); +/* Second item refers to standard socket 'fd' */ +zmq_poller_add_fd (poller, fd, ZMQ_POLLIN, NULL); +/* Poll for events indefinitely */ +int rc = zmq_poller_wait_all (items, events, 2, -1); +assert (rc >= 0); +/* Returned events will be stored in 'events' */ +zmq_poller_destroy (&poller); +---- + + +SEE ALSO +-------- +linkzmq:zmq_socket[3] +linkzmq:zmq_send[3] +linkzmq:zmq_recv[3] +linkzmq:zmq[7] + + +AUTHORS +------- +This page was written by the 0MQ community. To make a change please +read the 0MQ Contribution Policy at . diff --git a/doc/zmq_setsockopt.txt b/doc/zmq_setsockopt.txt index 480a21e938..a7ab3ab60b 100644 --- a/doc/zmq_setsockopt.txt +++ b/doc/zmq_setsockopt.txt @@ -774,7 +774,7 @@ up. When ZMQ_ROUTER_MANDATORY is set to `1`, 'ZMQ_POLLOUT' events will be generated if one or more messages can be sent to at least one of the peers. If ZMQ_ROUTER_MANDATORY is set to `0`, the socket will generate a 'ZMQ_POLLOUT' -event on every call to 'zmq_poll'. +event on every call to 'zmq_poll' resp. 'zmq_poller_wait_all'. [horizontal] Option value type:: int From 4fea7184dc2ea19d28dcb6f6be943a42d1a04d0c Mon Sep 17 00:00:00 2001 From: Simon Giesecke Date: Mon, 14 May 2018 09:46:39 +0200 Subject: [PATCH 5/5] Problem: man zmq_poller_* not working Solution: add symlinks to zmq_poller.txt --- doc/zmq_poller_add.txt | 1 + doc/zmq_poller_add_fd.txt | 1 + doc/zmq_poller_destroy.txt | 1 + doc/zmq_poller_modify.txt | 1 + doc/zmq_poller_modify_fd.txt | 1 + doc/zmq_poller_new.txt | 1 + doc/zmq_poller_remove.txt | 1 + doc/zmq_poller_remove_fd.txt | 1 + doc/zmq_poller_wait_all.txt | 1 + 9 files changed, 9 insertions(+) create mode 120000 doc/zmq_poller_add.txt create mode 120000 doc/zmq_poller_add_fd.txt create mode 120000 doc/zmq_poller_destroy.txt create mode 120000 doc/zmq_poller_modify.txt create mode 120000 doc/zmq_poller_modify_fd.txt create mode 120000 doc/zmq_poller_new.txt create mode 120000 doc/zmq_poller_remove.txt create mode 120000 doc/zmq_poller_remove_fd.txt create mode 120000 doc/zmq_poller_wait_all.txt diff --git a/doc/zmq_poller_add.txt b/doc/zmq_poller_add.txt new file mode 120000 index 0000000000..6133b29209 --- /dev/null +++ b/doc/zmq_poller_add.txt @@ -0,0 +1 @@ +zmq_poller.txt \ No newline at end of file diff --git a/doc/zmq_poller_add_fd.txt b/doc/zmq_poller_add_fd.txt new file mode 120000 index 0000000000..6133b29209 --- /dev/null +++ b/doc/zmq_poller_add_fd.txt @@ -0,0 +1 @@ +zmq_poller.txt \ No newline at end of file diff --git a/doc/zmq_poller_destroy.txt b/doc/zmq_poller_destroy.txt new file mode 120000 index 0000000000..6133b29209 --- /dev/null +++ b/doc/zmq_poller_destroy.txt @@ -0,0 +1 @@ +zmq_poller.txt \ No newline at end of file diff --git a/doc/zmq_poller_modify.txt b/doc/zmq_poller_modify.txt new file mode 120000 index 0000000000..6133b29209 --- /dev/null +++ b/doc/zmq_poller_modify.txt @@ -0,0 +1 @@ +zmq_poller.txt \ No newline at end of file diff --git a/doc/zmq_poller_modify_fd.txt b/doc/zmq_poller_modify_fd.txt new file mode 120000 index 0000000000..6133b29209 --- /dev/null +++ b/doc/zmq_poller_modify_fd.txt @@ -0,0 +1 @@ +zmq_poller.txt \ No newline at end of file diff --git a/doc/zmq_poller_new.txt b/doc/zmq_poller_new.txt new file mode 120000 index 0000000000..6133b29209 --- /dev/null +++ b/doc/zmq_poller_new.txt @@ -0,0 +1 @@ +zmq_poller.txt \ No newline at end of file diff --git a/doc/zmq_poller_remove.txt b/doc/zmq_poller_remove.txt new file mode 120000 index 0000000000..6133b29209 --- /dev/null +++ b/doc/zmq_poller_remove.txt @@ -0,0 +1 @@ +zmq_poller.txt \ No newline at end of file diff --git a/doc/zmq_poller_remove_fd.txt b/doc/zmq_poller_remove_fd.txt new file mode 120000 index 0000000000..6133b29209 --- /dev/null +++ b/doc/zmq_poller_remove_fd.txt @@ -0,0 +1 @@ +zmq_poller.txt \ No newline at end of file diff --git a/doc/zmq_poller_wait_all.txt b/doc/zmq_poller_wait_all.txt new file mode 120000 index 0000000000..6133b29209 --- /dev/null +++ b/doc/zmq_poller_wait_all.txt @@ -0,0 +1 @@ +zmq_poller.txt \ No newline at end of file