Esempio n. 1
0
static int zio_zloop_writer_cb (zloop_t *zl, zmq_pollitem_t *zp, zio_t *zio)
{
    int rc;
    zio_handler_start (zio);
    rc = zio_writer_cb (zio);
    if (!zio_write_pending (zio))
        zloop_poller_end (zl, zp);
    zio_handler_end (zio);
    return (rc);
}
Esempio n. 2
0
File: vtx_tcp.c Progetto: imatix/vtx
static void
peering_poller (peering_t *self, int events)
{
    driver_t *driver = self->driver;
    if (self->events != events) {
        zmq_pollitem_t item = { NULL, self->handle, events, 0 };
        zloop_poller_end (driver->loop, &item);
        if (events)
            zloop_poller (driver->loop, &item, s_peering_activity, self);
        self->events = events;
    }
}
Esempio n. 3
0
static int zio_zloop_read_cb (zloop_t *zl, zmq_pollitem_t *zp, zio_t *zio)
{
    int rc;
    zio_handler_start (zio);
    rc = zio_read_cb_common (zio);
    if (rc >= 0 && zio_eof_sent (zio)) {
        zio_debug (zio, "reader detaching from zloop\n");
        zloop_poller_end (zl, zp);
        rc = zio_close (zio);
    }
    zio_handler_end (zio);
    return (rc);
}
Esempio n. 4
0
// Handle input from client, on frontend
int s_handle_frontend(zloop_t *loop, zmq_pollitem_t *poller, void *arg)
{
	lbbroker_t *self = (lbbroker_t *)arg;
	zmsg_t *msg = zmsg_recv(self->frontend);
	if (msg) {
		zmsg_wrap(msg, (zframe_t *)zlist_pop(self->workers));
		zmsg_send(&msg, self->backend);

		// Cancel reader on frontend if we went from 1 to 0 workers
		if (zlist_size(self->workers) == 0) {
			zmq_pollitem_t poller = { self->frontend, 0, ZMQ_POLLIN };
			zloop_poller_end(loop, &poller);
		}
	}
	return 0;
}
Esempio n. 5
0
static void
peering_lower (peering_t *self)
{
    vocket_t *vocket = self->vocket;
    driver_t *driver = self->driver;
    if (driver->verbose)
        zclock_log ("I: (tcp) take down peering to %s", self->address);
    if (self->alive) {
        self->alive = FALSE;
        zlist_remove (vocket->live_peerings, self);
        if (zlist_size (vocket->live_peerings) < vocket->min_peerings) {
            //  Ask reactor to stop monitoring vocket's msgpipe pipe
            zmq_pollitem_t item = { vocket->msgpipe, 0, ZMQ_POLLIN, 0 };
            zloop_poller_end (driver->loop, &item);
        }
    }
}
Esempio n. 6
0
File: vtx_tcp.c Progetto: imatix/vtx
static void
vocket_destroy (vocket_t **self_p)
{
    assert (self_p);
    if (*self_p) {
        vocket_t *self = *self_p;
        driver_t *driver = self->driver;

        if (self->min_peerings == 0) {
            //  Ask reactor to stop monitoring vocket's msgpipe
            zmq_pollitem_t item = { self->msgpipe, 0, ZMQ_POLLIN, 0 };
            zloop_poller_end (driver->loop, &item);
        }

        //  Close message msgpipe socket
        zsocket_destroy (driver->ctx, self->msgpipe);

        //  Destroy all bindings for this vocket
        zhash_destroy (&self->binding_hash);

        //  Destroy all peerings for this vocket
        zhash_destroy (&self->peering_hash);
        zlist_destroy (&self->peering_list);
        zlist_destroy (&self->live_peerings);

        //  Remove vocket from driver list of vockets
        zlist_remove (driver->vockets, self);

#ifdef VOCKET_STATS
        char *type_name [] = {
            "PAIR", "PUB", "SUB", "REQ", "REP",
            "DEALER", "ROUTER", "PULL", "PUSH",
            "XPUB", "XSUB"
        };
        printf ("I: type=%s sent=%d recd=%d outp=%d inp=%d drop=%d\n",
            type_name [self->socktype],
            self->outgoing, self->incoming,
            self->outpiped, self->inpiped,
            self->dropped);
#endif
        free (self->vtxname);
        free (self);
        *self_p = NULL;
    }
}
Esempio n. 7
0
static int
s_new_master (zloop_t *loop, zmq_pollitem_t *unused, void *args)
{
    clonesrv_t *self = (clonesrv_t *) args;

    self->master = TRUE;
    self->slave = FALSE;
    zmq_pollitem_t poller = { self->subscriber, 0, ZMQ_POLLIN };
    zloop_poller_end (bstar_zloop (self->bstar), &poller);

    //  Apply pending list to own hash table
    while (zlist_size (self->pending)) {
        kvmsg_t *kvmsg = (kvmsg_t *) zlist_pop (self->pending);
        kvmsg_set_sequence (kvmsg, ++self->sequence);
        kvmsg_send (kvmsg, self->publisher);
        kvmsg_store (&kvmsg, self->kvmap);
        zclock_log ("I: publishing pending=%d", (int) self->sequence);
    }
    return 0;
}
Esempio n. 8
0
File: vtx_tcp.c Progetto: imatix/vtx
static void
peering_lower (peering_t *self)
{
    vocket_t *vocket = self->vocket;
    driver_t *driver = self->driver;
    if (driver->verbose)
        zclock_log ("I: (tcp) take down peering to %s", self->address);
    if (self->alive) {
        self->alive = FALSE;
        zlist_remove (vocket->live_peerings, self);
        if (zlist_size (vocket->live_peerings) < vocket->min_peerings) {
            //  Ask reactor to stop monitoring vocket's msgpipe pipe
            zmq_pollitem_t item = { vocket->msgpipe, 0, ZMQ_POLLIN, 0 };
            zloop_poller_end (driver->loop, &item);
        }
        //  current_peering cannot receive messages any more
        if (vocket->current_peering == self)
            vocket->current_peering = NULL;
        //  Make sure we are not subscribed anymore (makes sense for SUB
        //  socket only)
        self->subscribed = FALSE;
    }
}
Esempio n. 9
0
int
zloop_start (zloop_t *self)
{
    assert (self);
    int rc = 0;

    //  Main reactor loop
    while (!zsys_interrupted) {
        if (self->need_rebuild) {
            //  If s_rebuild_pollset() fails, break out of the loop and
            //  return its error
            rc = s_rebuild_pollset (self);
            if (rc)
                break;
        }
        rc = zmq_poll (self->pollset, (int) self->poll_size, s_tickless (self));
        if (rc == -1 || zsys_interrupted) {
            if (self->verbose)
                zsys_debug ("zloop: interrupted");
            rc = 0;
            break;              //  Context has been shut down
        }
        
        //  Handle any timers that have now expired
        int64_t time_now = zclock_mono ();
        s_timer_t *timer = (s_timer_t *) zlistx_first (self->timers);
        while (timer) {
            if (time_now >= timer->when) {
                if (self->verbose)
                    zsys_debug ("zloop: call timer handler id=%d", timer->timer_id);
                rc = timer->handler (self, timer->timer_id, timer->arg);
                if (rc == -1)
                    break;      //  Timer handler signaled break
                if (timer->times && --timer->times == 0)
                    zlistx_delete (self->timers, timer->list_handle);
                else
                    timer->when += timer->delay;
            }
            timer = (s_timer_t *) zlistx_next (self->timers);
        }
        
        //  Handle any tickets that have now expired
        s_ticket_t *ticket = (s_ticket_t *) zlistx_first (self->tickets);
        while (ticket && time_now >= ticket->when) {
            if (self->verbose)
                zsys_debug ("zloop: call ticket handler");
            rc = ticket->handler (self, 0, ticket->arg);
            if (rc == -1)
                break;      //  Timer handler signaled break
            zlistx_delete (self->tickets, ticket->list_handle);
            ticket = (s_ticket_t *) zlistx_next (self->tickets);
        }
        
        //  Handle any readers and pollers that are ready
        size_t item_nbr;
        for (item_nbr = 0; item_nbr < self->poll_size && rc >= 0; item_nbr++) {
            s_reader_t *reader = &self->readact [item_nbr];
            if (reader->handler) {
                if ((self->pollset [item_nbr].revents & ZMQ_POLLERR)
                && !reader->tolerant) {
                    if (self->verbose)
                        zsys_warning ("zloop: can't read %s socket: %s",
                                      zsock_type_str (reader->sock),
                                      zmq_strerror (zmq_errno ()));
                    //  Give handler one chance to handle error, then kill
                    //  reader because it'll disrupt the reactor otherwise.
                    if (reader->errors++) {
                        zloop_reader_end (self, reader->sock);
                        self->pollset [item_nbr].revents = 0;
                    }
                }
                else
                    reader->errors = 0;     //  A non-error happened

                if (self->pollset [item_nbr].revents) {
                    if (self->verbose)
                        zsys_debug ("zloop: call %s socket handler",
                                    zsock_type_str (reader->sock));
                    rc = reader->handler (self, reader->sock, reader->arg);
                    if (rc == -1 || self->need_rebuild)
                        break;
                }
            }
            else {
                s_poller_t *poller = &self->pollact [item_nbr];
                assert (self->pollset [item_nbr].socket == poller->item.socket);

                if ((self->pollset [item_nbr].revents & ZMQ_POLLERR)
                && !poller->tolerant) {
                    if (self->verbose)
                        zsys_warning ("zloop: can't poll %s socket (%p, %d): %s",
                                      poller->item.socket ?
                                      zsys_sockname (zsock_type (poller->item.socket)) : "FD",
                                      poller->item.socket, poller->item.fd,
                                      zmq_strerror (zmq_errno ()));
                    //  Give handler one chance to handle error, then kill
                    //  poller because it'll disrupt the reactor otherwise.
                    if (poller->errors++) {
                        zloop_poller_end (self, &poller->item);
                        self->pollset [item_nbr].revents = 0;
                    }
                }
                else
                    poller->errors = 0;     //  A non-error happened

                if (self->pollset [item_nbr].revents) {
                    if (self->verbose)
                        zsys_debug ("zloop: call %s socket handler (%p, %d)",
                                    poller->item.socket ?
                                    zsys_sockname (zsock_type (poller->item.socket)) : "FD",
                                    poller->item.socket, poller->item.fd);
                    rc = poller->handler (self, &self->pollset [item_nbr], poller->arg);
                    if (rc == -1 || self->need_rebuild)
                        break;
                }
            }
        }
        //  Now handle any timer zombies
        //  This is going to be slow if we have many timers; we might use
        //  a faster lookup on the timer list.
        while (zlistx_first (self->zombies)) {
            //  Get timer_id back from pointer
            int timer_id = (byte *) zlistx_detach (self->zombies, NULL) - (byte *) NULL;
            s_timer_remove (self, timer_id);
        }
        if (rc == -1)
            break;
    }
    self->terminated = true;
    return rc;
}
Esempio n. 10
0
///
//  Cancel a pollitem from the reactor, specified by socket or FD. If both
//  are specified, uses only socket. If multiple poll items exist for same
//  socket/FD, cancels ALL of them.                                       
void QmlZloop::pollerEnd (zmq_pollitem_t *item) {
    zloop_poller_end (self, item);
};
Esempio n. 11
0
// This function is called when a message is ready to be read on the specified
//connection. The connection is registered with the URI if needed.
int maltcp_ctx_socket_receive(zloop_t *loop, zmq_pollitem_t *poller, void *arg) {
  clog_debug(maltcp_logger, "maltcp_ctx_socket_receive\n");
  maltcp_ctx_t *self = (maltcp_ctx_t *) arg;

  struct sockaddr src_addr;
  socklen_t addr_size = sizeof(struct sockaddr_in);
  if (getpeername(poller->fd, (struct sockaddr *)&src_addr, &addr_size) != 0) {
    clog_error(maltcp_logger, "maltcp_ctx_socket_receive: getpeername failed\n");
    return -1;
  }

  char src_ipstr[INET6_ADDRSTRLEN];
  int src_port;

  if (src_addr.sa_family == AF_INET) {
      struct sockaddr_in *s = (struct sockaddr_in *)&src_addr;
      src_port = ntohs(s->sin_port);
      inet_ntop(AF_INET, &s->sin_addr, src_ipstr, sizeof src_ipstr);
  } else { // AF_INET6
      struct sockaddr_in6 *s = (struct sockaddr_in6 *)&src_addr;
      src_port = ntohs(s->sin6_port);
      inet_ntop(AF_INET6, &s->sin6_addr, src_ipstr, sizeof src_ipstr);
  }
  mal_uri_t peer_uri[strlen(src_ipstr) + 10 + 10 + 1];
  sprintf((char*) peer_uri, "%s%s:%d", MALTCP_URI, src_ipstr, src_port);
  clog_debug(maltcp_logger, "maltcp_ctx: TCP server socket receive from URI: %s\n", peer_uri);

  char header[FIXED_HEADER_LENGTH];
  int nb = 0;
  while (nb < FIXED_HEADER_LENGTH) {
    int nbread = recv(poller->fd, &header[nb], FIXED_HEADER_LENGTH-nb, 0);
    if (nbread == 0) {
      clog_warning(maltcp_logger, "maltcp_ctx_socket_receive: TCP connection (%d) closed\n", poller->fd);
      maltcp_ctx_connection_remove_socket(self, poller->fd);
      zloop_poller_end(loop, poller);
      close(poller->fd);
      return 0;
    }

    if (nbread <0) {
      clog_error(maltcp_logger, "maltcp_ctx_socket_receive: Error reading header on TCP connection\n");
      maltcp_ctx_connection_remove_socket(self, poller->fd);
      zloop_poller_end(loop, poller);
      close(poller->fd);
      return -1;
    }
    nb += nbread;
  }

  // First read, get the message size in the header
  int msg_size = maltcp_decode_variable_length(header, nb) + FIXED_HEADER_LENGTH;
  clog_debug(maltcp_logger, "maltcp_ctx_socket_receive: should read = %d bytes\n", msg_size);

  // Allocate the message and copy header
  char* msg = (char*) malloc(msg_size);
  memcpy(msg, header, FIXED_HEADER_LENGTH);

  // Read the whole message
  while (nb < msg_size) {
    int nbread = recv(poller->fd, &msg[nb], msg_size-nb, 0);
    if (nbread <=0) {
      clog_error(maltcp_logger, "maltcp_ctx_socket_receive: Error reading message on TCP connection\n");
      maltcp_ctx_connection_remove_socket(self, poller->fd);
      zloop_poller_end(loop, poller);
      close(poller->fd);
      free(msg);
      return -1;
    }
    nb += nbread;
  }

  clog_debug(maltcp_logger, "maltcp_ctx_socket_receive: message received, size = %d\n", msg_size);

  // Get URIs from message and register connection if needed
  mal_uri_t *uri_to;
  mal_uri_t *uri_from;
  if (maltcp_decode_uris(self->maltcp_header,
      self->decoder, msg, msg_size, &uri_to, &uri_from) != 0) {
    clog_error(maltcp_logger, "maltcp_ctx_socket_receive, could not decode uri_to\n");
    return -1;
  }
  clog_debug(maltcp_logger, "maltcp_ctx_socket_receive: uri_to=%s uri_from=%s\n", uri_to, uri_from);

  if ((uri_from != NULL) && (strncmp(uri_from, MALTCP_URI, sizeof MALTCP_URI -1) == 0)) {
    // The complete URI is registered in the message header, use it.
    int len = strlen(uri_from);
    char *ptr = strchr(uri_from +sizeof MALTCP_URI, '/');
    if (ptr != NULL)
      len = ptr-uri_from;
    char uri[len+1];
    strncpy(uri, uri_from, len);
    uri[len] = '\0';
    clog_debug(maltcp_logger, "maltcp_ctx_socket_receive: register#1 %s\n", uri);
    // Register the connection if needed
    maltcp_ctx_connection_register_incoming(self, poller->fd, uri);
  } else {
    clog_debug(maltcp_logger, "maltcp_ctx_socket_receive: register#2 %s\n", peer_uri);
    // Register the connection if needed
    maltcp_ctx_connection_register_incoming(self, poller->fd, peer_uri);
  }

  // Forward the message to the appropriate endpoint.

  // Note: Normally the message will be deleted by the appropriate endpoint.
  // What happens if no endpoint reads this message? It seems that Router socket
  // discard messages if there are no readers.

  assert(uri_to != NULL);

  // Get the service part of URI needed to route the message.
  mal_uri_t *short_uri_to = maltcp_get_service_from_uri(uri_to);
  int rc = zmq_send(self->endpoints_socket, short_uri_to, strlen(short_uri_to), ZMQ_SNDMORE);
  assert(rc == strlen(short_uri_to));
  clog_debug(maltcp_logger, "maltcp_ctx_socket_receive: send identity (%d bytes) to endpoint %s\n", rc, short_uri_to);

  // Send the peer URI
  rc = zmq_send(self->endpoints_socket, peer_uri, strlen(peer_uri), ZMQ_SNDMORE);
  assert(rc == strlen(peer_uri));
  clog_debug(maltcp_logger, "maltcp_ctx_socket_receive: send peer URI (%d bytes) to endpoint %s\n", rc, peer_uri);

  // Send the message
  clog_debug(maltcp_logger, "maltcp_ctx_socket_receive: send message (%d bytes) to endpoint %s\n", msg_size, short_uri_to);
  rc = zmq_send(self->endpoints_socket, msg, msg_size, ZMQ_DONTWAIT);
  assert(rc == msg_size);

  clog_debug(maltcp_logger, "maltcp_ctx: message handled.\n");

  // Destroy URIs: Be careful, short_uri_to is simply a pointer in uri_to.
  mal_uri_destroy(&uri_to);
  mal_uri_destroy(&uri_from);

  return 0;
}
Esempio n. 12
0
File: zloop.c Progetto: fars/czmq
int
zloop_start (zloop_t *self)
{
    assert (self);
    int rc = 0;

    //  Recalculate all timers now
    s_timer_t *timer = (s_timer_t *) zlist_first (self->timers);
    while (timer) {
        timer->when = timer->delay + zclock_time ();
        timer = (s_timer_t *) zlist_next (self->timers);
    }
    //  Main reactor loop
    while (!zctx_interrupted) {
        if (self->need_rebuild) {
            // If s_rebuild_pollset() fails, break out of the loop and
            // return its error
            rc = s_rebuild_pollset (self);
            if (rc)
                break;
        }
        rc = zmq_poll (self->pollset, (int) self->poll_size,
                       s_tickless_timer (self) * ZMQ_POLL_MSEC);
        if (rc == -1 || zctx_interrupted) {
            if (self->verbose)
                zclock_log ("I: zloop: interrupted (%d) - %s", rc, 
                            zmq_strerror (zmq_errno ()));
            rc = 0;
            break;              //  Context has been shut down
        }
        //  Handle any timers that have now expired
        timer = (s_timer_t *) zlist_first (self->timers);
        while (timer) {
            if (zclock_time () >= timer->when && timer->when != -1) {
                if (self->verbose)
                    zclock_log ("I: zloop: call timer id=%d handler", timer->timer_id);
                rc = timer->handler (self, timer->timer_id, timer->arg);
                if (rc == -1)
                    break;      //  Timer handler signaled break
                if (timer->times && --timer->times == 0) {
                    zlist_remove (self->timers, timer);
                    free (timer);
                }
                else
                    timer->when = timer->delay + zclock_time ();
            }
            timer = (s_timer_t *) zlist_next (self->timers);
        }
        //  Handle any pollers that are ready
        size_t item_nbr;
        for (item_nbr = 0; item_nbr < self->poll_size && rc >= 0; item_nbr++) {
            s_poller_t *poller = &self->pollact [item_nbr];
            assert (self->pollset [item_nbr].socket == poller->item.socket);
            
            if ((self->pollset [item_nbr].revents & ZMQ_POLLERR)
            && !poller->tolerant) {
                if (self->verbose)
                    zclock_log ("W: zloop: can't poll %s socket (%p, %d): %s",
                        poller->item.socket?
                            zsocket_type_str (poller->item.socket): "FD",
                        poller->item.socket, poller->item.fd,
                        zmq_strerror (zmq_errno ()));
                //  Give handler one chance to handle error, then kill
                //  poller because it'll disrupt the reactor otherwise.
                if (poller->errors++) {
                    zloop_poller_end (self, &poller->item);
                    self->pollset [item_nbr].revents = 0;
                }
            }
            else
                poller->errors = 0;     //  A non-error happened

            if (self->pollset [item_nbr].revents) {
                if (self->verbose)
                    zclock_log ("I: zloop: call %s socket handler (%p, %d)",
                        poller->item.socket?
                            zsocket_type_str (poller->item.socket): "FD",
                        poller->item.socket, poller->item.fd);
                rc = poller->handler (self, &self->pollset [item_nbr], poller->arg);
                if (rc == -1 || self->need_rebuild)
                    break; 
            }
        }
        //  Now handle any timer zombies
        //  This is going to be slow if we have many timers; we might use
        //  a faster lookup on the timer list.
        while (zlist_size (self->zombies)) {
            //  This hack lets us convert our pointer back into an integer timer_id
            int timer_id = (byte *) zlist_pop (self->zombies) - (byte *) NULL;
            timer = (s_timer_t *) zlist_first (self->timers);
            while (timer) {
                if (timer->timer_id == timer_id) {
                    zlist_remove (self->timers, timer);
                    free (timer);
                }
                timer = (s_timer_t *) zlist_next (self->timers);
            }
        }
        if (rc == -1)
            break;
    }
    return rc;
}
Esempio n. 13
0
File: zloop.c Progetto: dnaeon/czmq
int
zloop_start (zloop_t *self)
{
    assert (self);
    int rc = 0;

    //  Recalculate all timers now
    s_timer_t *timer = (s_timer_t *) zlist_first (self->timers);
    while (timer) {
        timer->when = timer->delay + zclock_time ();
        timer = (s_timer_t *) zlist_next (self->timers);
    }
    //  Main reactor loop
    while (!zctx_interrupted) {
        if (self->dirty) {
            // If s_rebuild_pollset() fails, break out of the loop and
            // return its error
            rc = s_rebuild_pollset (self);
            if (rc)
                break;
        }
        rc = zmq_poll (self->pollset, (int) self->poll_size,
                       s_tickless_timer (self) * ZMQ_POLL_MSEC);
        if (rc == -1 || zctx_interrupted) {
            if (self->verbose)
                zclock_log ("I: zloop: interrupted (%d) - %s", rc, 
                            zmq_strerror (zmq_errno ()));
            rc = 0;
            break;              //  Context has been shut down
        }
        //  Handle any timers that have now expired
        timer = (s_timer_t *) zlist_first (self->timers);
        while (timer) {
            if (zclock_time () >= timer->when && timer->when != -1) {
                if (self->verbose)
                    zclock_log ("I: zloop: call timer handler");
                rc = timer->handler (self, NULL, timer->arg);
                if (rc == -1)
                    break;      //  Timer handler signaled break
                if (timer->times && --timer->times == 0) {
                    zlist_remove (self->timers, timer);
                    free (timer);
                }
                else
                    timer->when = timer->delay + zclock_time ();
            }
            timer = (s_timer_t *) zlist_next (self->timers);
        }
        //  Handle any pollers that are ready
        size_t item_nbr;
        for (item_nbr = 0; item_nbr < self->poll_size && rc >= 0; item_nbr++) {
            s_poller_t *poller = &self->pollact [item_nbr];
            assert (self->pollset [item_nbr].socket == poller->item.socket);
            
            if ((self->pollset [item_nbr].revents & ZMQ_POLLERR)
            && !poller->ignore_errors) {
                if (self->verbose)
                    zclock_log ("I: zloop: can't poll %s socket (%p, %d): %s",
                        poller->item.socket?
                            zsocket_type_str (poller->item.socket): "FD",
                        poller->item.socket, poller->item.fd,
                        zmq_strerror (zmq_errno ()));
                //  Give handler one chance to handle error, then kill
                //  poller because it'll disrupt the reactor otherwise.
                if (poller->errors++) {
                    zloop_poller_end (self, &poller->item);
                    self->pollset [item_nbr].revents = 0;
                }
            }
            else
                poller->errors = 0;     //  A non-error happened

            if (self->pollset [item_nbr].revents) {
                if (self->verbose)
                    zclock_log ("I: zloop: call %s socket handler (%p, %d)",
                        poller->item.socket?
                            zsocket_type_str (poller->item.socket): "FD",
                        poller->item.socket, poller->item.fd);
                rc = poller->handler (self, &self->pollset [item_nbr], poller->arg);
                if (rc == -1)
                    break;      //  Poller handler signaled break
                // If the poller handler calls zloop_poller_end on poller other than itself
                // we need to force rebuild in order to avoid reading from freed memory in the handler
                if (self->dirty) {
                    if (self->verbose)
                        zclock_log ("I: zloop: pollers canceled, forcing rebuild");
                    break;
                }
            }
        }
        //  Now handle any timer zombies
        //  This is going to be slow if we have many zombies
        while (zlist_size (self->zombies)) {
            void *arg = zlist_pop (self->zombies);
            timer = (s_timer_t *) zlist_first (self->timers);
            while (timer) {
                if (timer->arg == arg) {
                    zlist_remove (self->timers, timer);
                    free (timer);
                }
                timer = (s_timer_t *) zlist_next (self->timers);
            }
        }
        if (rc == -1)
            break;
    }
    return rc;
}