libzmq: Inconsistencies with ZMQ_POLLOUT and ZMQ_ROUTER in ZMQ_MANDATORY mode

(Related to PR #2622; partial fix, API and ABI compatible)

I am copying the description of the problem here from #2622:

If ZMQ_ROUTER_MANDATORY is set, and zmq_poll() waits for ZMQ_POLLOUT events, zmq_poll() will immediately wake up if only 1 pipe has room to send, regardless of the peer, creating a busy loop of zmq_poll() wake-up, zmq_send() (EAGAIN). There is no way for the application to selectively wait for ZMQ_POLLOUT for specific peer(s), which seems somehow necessary in ZMQ_ROUTER_MANDATORY.

Some possible solutions come to my mind now (obviously, all of them breaking API and ABI unless implemented in parallel):

  1. Change zmq_poll_item_t to accept a list of ZMQ_IDENTITY to ZMQ_POLLOUT as an optional parameter. If none is specified, then current behaviour should be maintained.

  2. Create zmq_pollable peer zmq_sock_t (fake), and open APIs to get those zmq_sock_t given ZMQ_IDENTITY to be placed as regular zmq_poll_item_t.

I tend to think 1) is a bit easier to implement and simpler to use. I would like to know the opinions. We could try to work a bit on it, either on zmq_poll() itself or a parallel version. But I want to make sure there are chances to be accepted before committing any efforts.

Thoughts?

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Reactions: 1
  • Comments: 27 (27 by maintainers)

Commits related to this issue

Most upvoted comments

Sorry but we can’t break zmq_poll compatibility, as its API is finalised.

But we have in development a new polling implementation, you can find it as zmq_poller - I think that would be a good place to implement this functionality. That API has not yet been declared STABLE, so it can be changed if needed.

https://github.com/zeromq/libzmq/blob/master/include/zmq.h#L585 https://github.com/zeromq/libzmq/blob/master/src/zmq.cpp#L754 https://github.com/zeromq/libzmq/blob/master/src/socket_poller.cpp

So,

You can find some preliminary, incomplete work here, but I think sufficient to trigger discussion. I will continue tomorrow, but I would appreciate early feedback before going any much further.

This branch is frozen for discussion:

https://github.com/msune/libzmq/commits/poller_peer_support_discuss

In particular, this commit:

https://github.com/msune/libzmq/commit/1c386bf4eb3a1d8ab24bd96006b17f0df123b412

Short summary

Key 4 additional methods are:

 +ZMQ_EXPORT int  zmq_poller_add_peer (void *poller, void *socket, void *zid,
 +                                                            int zid_len,
 +                                                            void *user_data,
 +                                                            short events);
 +ZMQ_EXPORT int  zmq_poller_modify_peer (void *poller, void *socket, void *zid,
 +                                                            int zid_len,
 +                                                            short events);
 +ZMQ_EXPORT int  zmq_poller_remove_peer (void *poller, void *socket, void *zid,
 +                                                            int zid_len);

 +ZMQ_EXPORT int zmq_recv_peer (void *s, void *zid, int zid_len, void *buf,
 +                                                               size_t len, 
 +                                                               int flags);

I think the usage is clear. One can use regular poller methods to add the entire socket or (or in addition) use _peer to add specific ZMQ identity poller items.

To read from a specific ZMQ identity, zmq_recv_peer()

There is probably a better name than peer (identity?). Open to suggestions.

Missing

A lot:

  • Validation in all poller_peer() methods to check that socket is multi-peer
  • Implementation of zmq_recv_peer() and support for ZMQ_PEER_EVENTS in getsockopt() on sockets with multi-peer support
  • Tests and validation
  • Documentation

What I don’t like

I dislike using getsockopt() with an additional struct zmq_gso_peer_events_t for ZMQ_PEER_EVENTS. I was following the current approach.

I would very much prefer opening an API to the socket base to get that, and expose it through regular C bindings. Comments?

Some other stuff, unrelated to this feature

Thoughts?

I think I might have a simple solution, right now we have zmq_poller_add which accept events to listen to. We can have an overload which accept a predicate (function pointer) instead of the events. The predicate will be run when the socket is signaled and return the events for the socket. This will work as zeromq socket is edge triggered.

Inside the predicate user can implement any logic, including checking if specific identity is ready.

we should also had a API function or socket option to retrieve the status of specific identity inside the router socket.