From 10694373e34c0dba4cd6e64a3bd2209cd1cd9913 Mon Sep 17 00:00:00 2001
From: Marc Sune <marc@voltanet.io>
Date: Fri, 14 Jul 2017 15:35:32 +0200
Subject: [PATCH] Fix ROUTER's xhas_out() in MANDATORY mode

Before this commit, xhas_out() was returning true regardless. This
was correct before the ZMQ_ROUTER_MANDATORY flag as introduced.
However, ZMQ_POLLOUT.

With this commit, _if_ ZMQ_ROUTER_MANDATORY is set, xhas_out() will
return false if ALL peer's outgoing pipes are full.

There is an outstanding high-level design question:

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.

This discussion will be addressed in a separate issue.

Signed-off-by: Marc Sune <marc@voltanet.io>
Signed-off-by: Fredi Raspall <fredi@voltanet.io>
---
 src/router.cpp | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/src/router.cpp b/src/router.cpp
index fc46ae19..45a4f51c 100644
--- a/src/router.cpp
+++ b/src/router.cpp
@@ -385,10 +385,19 @@ bool zmq::router_t::xhas_in ()
 
 bool zmq::router_t::xhas_out ()
 {
-    //  In theory, ROUTER socket is always ready for writing. Whether actual
-    //  attempt to write succeeds depends on whitch pipe the message is going
-    //  to be routed to.
-    return true;
+    //  In theory, ROUTER socket is always ready for writing (except when
+    //  MANDATORY is set). Whether actual attempt to write succeeds depends
+    //  on whitch pipe the message is going to be routed to.
+
+    if(!mandatory)
+        return true;
+
+    bool has_out = false;
+    outpipes_t::iterator it;
+    for (it = outpipes.begin (); it != outpipes.end (); ++it)
+        has_out |= it->second.pipe->check_hwm();
+
+    return has_out;
 }
 
 zmq::blob_t zmq::router_t::get_credential () const