mirror of
https://github.com/zeromq/libzmq.git
synced 2025-10-30 13:47:13 +01:00
O(1) fair-queueing in XREP implemented
Up to now the complexity of fair-queueing in XREP was O(n). Signed-off-by: Martin Sustrik <sustrik@250bpm.com>
This commit is contained in:
13
src/pipe.cpp
13
src/pipe.cpp
@@ -63,7 +63,8 @@ zmq::pipe_t::pipe_t (object_t *parent_, upipe_t *inpipe_, upipe_t *outpipe_,
|
|||||||
peer (NULL),
|
peer (NULL),
|
||||||
sink (NULL),
|
sink (NULL),
|
||||||
state (active),
|
state (active),
|
||||||
delay (delay_)
|
delay (delay_),
|
||||||
|
pipe_id (0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +86,16 @@ void zmq::pipe_t::set_event_sink (i_pipe_events *sink_)
|
|||||||
sink = sink_;
|
sink = sink_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void zmq::pipe_t::set_pipe_id (uint32_t id_)
|
||||||
|
{
|
||||||
|
pipe_id = id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t zmq::pipe_t::get_pipe_id ()
|
||||||
|
{
|
||||||
|
return pipe_id;
|
||||||
|
}
|
||||||
|
|
||||||
bool zmq::pipe_t::check_read ()
|
bool zmq::pipe_t::check_read ()
|
||||||
{
|
{
|
||||||
if (unlikely (!in_active || (state != active && state != pending)))
|
if (unlikely (!in_active || (state != active && state != pending)))
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "ypipe.hpp"
|
#include "ypipe.hpp"
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "object.hpp"
|
#include "object.hpp"
|
||||||
|
#include "stdint.hpp"
|
||||||
#include "array.hpp"
|
#include "array.hpp"
|
||||||
|
|
||||||
namespace zmq
|
namespace zmq
|
||||||
@@ -67,6 +68,10 @@ namespace zmq
|
|||||||
// Specifies the object to send events to.
|
// Specifies the object to send events to.
|
||||||
void set_event_sink (i_pipe_events *sink_);
|
void set_event_sink (i_pipe_events *sink_);
|
||||||
|
|
||||||
|
// Pipe endpoint can store an opaque ID to be used by its clients.
|
||||||
|
void set_pipe_id (uint32_t id_);
|
||||||
|
uint32_t get_pipe_id ();
|
||||||
|
|
||||||
// Returns true if there is at least one message to read in the pipe.
|
// Returns true if there is at least one message to read in the pipe.
|
||||||
bool check_read ();
|
bool check_read ();
|
||||||
|
|
||||||
@@ -176,6 +181,9 @@ namespace zmq
|
|||||||
// asks us to.
|
// asks us to.
|
||||||
bool delay;
|
bool delay;
|
||||||
|
|
||||||
|
// Opaque ID. To be used by the clients, not the pipe itself.
|
||||||
|
uint32_t pipe_id;
|
||||||
|
|
||||||
// Returns true if the message is delimiter; false otherwise.
|
// Returns true if the message is delimiter; false otherwise.
|
||||||
static bool is_delimiter (msg_t &msg_);
|
static bool is_delimiter (msg_t &msg_);
|
||||||
|
|
||||||
|
|||||||
111
src/xrep.cpp
111
src/xrep.cpp
@@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
zmq::xrep_t::xrep_t (class ctx_t *parent_, uint32_t tid_) :
|
zmq::xrep_t::xrep_t (class ctx_t *parent_, uint32_t tid_) :
|
||||||
socket_base_t (parent_, tid_),
|
socket_base_t (parent_, tid_),
|
||||||
current_in (0),
|
|
||||||
prefetched (false),
|
prefetched (false),
|
||||||
more_in (false),
|
more_in (false),
|
||||||
current_out (NULL),
|
current_out (NULL),
|
||||||
@@ -34,14 +33,16 @@ zmq::xrep_t::xrep_t (class ctx_t *parent_, uint32_t tid_) :
|
|||||||
{
|
{
|
||||||
options.type = ZMQ_XREP;
|
options.type = ZMQ_XREP;
|
||||||
|
|
||||||
|
prefetched_msg.init ();
|
||||||
|
|
||||||
// Start the peer ID sequence from a random point.
|
// Start the peer ID sequence from a random point.
|
||||||
generate_random (&next_peer_id, sizeof (next_peer_id));
|
generate_random (&next_peer_id, sizeof (next_peer_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
zmq::xrep_t::~xrep_t ()
|
zmq::xrep_t::~xrep_t ()
|
||||||
{
|
{
|
||||||
zmq_assert (inpipes.empty ());
|
|
||||||
zmq_assert (outpipes.empty ());
|
zmq_assert (outpipes.empty ());
|
||||||
|
prefetched_msg.close ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void zmq::xrep_t::xattach_pipe (pipe_t *pipe_, const blob_t &peer_identity_)
|
void zmq::xrep_t::xattach_pipe (pipe_t *pipe_, const blob_t &peer_identity_)
|
||||||
@@ -68,8 +69,8 @@ void zmq::xrep_t::xattach_pipe (pipe_t *pipe_, const blob_t &peer_identity_)
|
|||||||
zmq_assert (ok);
|
zmq_assert (ok);
|
||||||
|
|
||||||
// Add the pipe to the list of inbound pipes.
|
// Add the pipe to the list of inbound pipes.
|
||||||
inpipe_t inpipe = {pipe_, next_peer_id, true};
|
pipe_->set_pipe_id (next_peer_id);
|
||||||
inpipes.push_back (inpipe);
|
fq.attach (pipe_);
|
||||||
|
|
||||||
// Advance next peer ID so that if new connection is dropped shortly after
|
// Advance next peer ID so that if new connection is dropped shortly after
|
||||||
// its creation we don't accidentally get two subsequent peers with
|
// its creation we don't accidentally get two subsequent peers with
|
||||||
@@ -79,20 +80,8 @@ void zmq::xrep_t::xattach_pipe (pipe_t *pipe_, const blob_t &peer_identity_)
|
|||||||
|
|
||||||
void zmq::xrep_t::xterminated (pipe_t *pipe_)
|
void zmq::xrep_t::xterminated (pipe_t *pipe_)
|
||||||
{
|
{
|
||||||
for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end ();
|
fq.terminated (pipe_);
|
||||||
++it) {
|
|
||||||
if (it->pipe == pipe_) {
|
|
||||||
if ((inpipes_t::size_type) (it - inpipes.begin ()) < current_in)
|
|
||||||
current_in--;
|
|
||||||
inpipes.erase (it);
|
|
||||||
if (current_in >= inpipes.size ())
|
|
||||||
current_in = 0;
|
|
||||||
goto clean_outpipes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zmq_assert (false);
|
|
||||||
|
|
||||||
clean_outpipes:
|
|
||||||
for (outpipes_t::iterator it = outpipes.begin ();
|
for (outpipes_t::iterator it = outpipes.begin ();
|
||||||
it != outpipes.end (); ++it) {
|
it != outpipes.end (); ++it) {
|
||||||
if (it->second.pipe == pipe_) {
|
if (it->second.pipe == pipe_) {
|
||||||
@@ -107,15 +96,7 @@ clean_outpipes:
|
|||||||
|
|
||||||
void zmq::xrep_t::xread_activated (pipe_t *pipe_)
|
void zmq::xrep_t::xread_activated (pipe_t *pipe_)
|
||||||
{
|
{
|
||||||
for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end ();
|
fq.activated (pipe_);
|
||||||
++it) {
|
|
||||||
if (it->pipe == pipe_) {
|
|
||||||
zmq_assert (!it->active);
|
|
||||||
it->active = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zmq_assert (false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void zmq::xrep_t::xwrite_activated (pipe_t *pipe_)
|
void zmq::xrep_t::xwrite_activated (pipe_t *pipe_)
|
||||||
@@ -212,55 +193,30 @@ int zmq::xrep_t::xrecv (msg_t *msg_, int flags_)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deallocate old content of the message.
|
// Get next message part.
|
||||||
int rc = msg_->close ();
|
pipe_t *pipe;
|
||||||
errno_assert (rc == 0);
|
int rc = fq.recvpipe (msg_, flags_, &pipe);
|
||||||
|
if (rc != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
// If we are in the middle of reading a message, just grab next part of it.
|
// If we are in the middle of reading a message, just return the next part.
|
||||||
if (more_in) {
|
if (more_in) {
|
||||||
zmq_assert (inpipes [current_in].active);
|
|
||||||
bool fetched = inpipes [current_in].pipe->read (msg_);
|
|
||||||
zmq_assert (fetched);
|
|
||||||
more_in = msg_->flags () & (msg_t::more | msg_t::label);
|
more_in = msg_->flags () & (msg_t::more | msg_t::label);
|
||||||
if (!more_in) {
|
|
||||||
current_in++;
|
|
||||||
if (current_in >= inpipes.size ())
|
|
||||||
current_in = 0;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Round-robin over the pipes to get the next message.
|
// We are at the beginning of a new message. Move the message part we
|
||||||
for (inpipes_t::size_type count = inpipes.size (); count != 0; count--) {
|
// have to the prefetched and return the ID of the peer instead.
|
||||||
|
rc = prefetched_msg.move (*msg_);
|
||||||
// Try to fetch new message.
|
|
||||||
if (inpipes [current_in].active)
|
|
||||||
prefetched = inpipes [current_in].pipe->read (&prefetched_msg);
|
|
||||||
|
|
||||||
// If we have a message, create a prefix and return it to the caller.
|
|
||||||
if (prefetched) {
|
|
||||||
int rc = msg_->init_size (4);
|
|
||||||
errno_assert (rc == 0);
|
errno_assert (rc == 0);
|
||||||
put_uint32 ((unsigned char*) msg_->data (),
|
prefetched = true;
|
||||||
inpipes [current_in].peer_id);
|
rc = msg_->close ();
|
||||||
|
errno_assert (rc == 0);
|
||||||
|
rc = msg_->init_size (4);
|
||||||
|
errno_assert (rc == 0);
|
||||||
|
put_uint32 ((unsigned char*) msg_->data (), pipe->get_pipe_id ());
|
||||||
msg_->set_flags (msg_t::label);
|
msg_->set_flags (msg_t::label);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
// If me don't have a message, mark the pipe as passive and
|
|
||||||
// move to next pipe.
|
|
||||||
inpipes [current_in].active = false;
|
|
||||||
current_in++;
|
|
||||||
if (current_in >= inpipes.size ())
|
|
||||||
current_in = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No message is available. Initialise the output parameter
|
|
||||||
// to be a 0-byte message.
|
|
||||||
rc = msg_->init ();
|
|
||||||
errno_assert (rc == 0);
|
|
||||||
errno = EAGAIN;
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int zmq::xrep_t::rollback (void)
|
int zmq::xrep_t::rollback (void)
|
||||||
@@ -275,28 +231,9 @@ int zmq::xrep_t::rollback (void)
|
|||||||
|
|
||||||
bool zmq::xrep_t::xhas_in ()
|
bool zmq::xrep_t::xhas_in ()
|
||||||
{
|
{
|
||||||
// There are subsequent parts of the partly-read message available.
|
if (prefetched)
|
||||||
if (prefetched || more_in)
|
|
||||||
return true;
|
return true;
|
||||||
|
return fq.has_in ();
|
||||||
// Note that messing with current doesn't break the fairness of fair
|
|
||||||
// queueing algorithm. If there are no messages available current will
|
|
||||||
// get back to its original value. Otherwise it'll point to the first
|
|
||||||
// pipe holding messages, skipping only pipes with no messages available.
|
|
||||||
for (inpipes_t::size_type count = inpipes.size (); count != 0; count--) {
|
|
||||||
if (inpipes [current_in].active &&
|
|
||||||
inpipes [current_in].pipe->check_read ())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// If me don't have a message, mark the pipe as passive and
|
|
||||||
// move to next pipe.
|
|
||||||
inpipes [current_in].active = false;
|
|
||||||
current_in++;
|
|
||||||
if (current_in >= inpipes.size ())
|
|
||||||
current_in = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool zmq::xrep_t::xhas_out ()
|
bool zmq::xrep_t::xhas_out ()
|
||||||
|
|||||||
17
src/xrep.hpp
17
src/xrep.hpp
@@ -22,11 +22,11 @@
|
|||||||
#define __ZMQ_XREP_HPP_INCLUDED__
|
#define __ZMQ_XREP_HPP_INCLUDED__
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "socket_base.hpp"
|
#include "socket_base.hpp"
|
||||||
#include "stdint.hpp"
|
#include "stdint.hpp"
|
||||||
#include "msg.hpp"
|
#include "msg.hpp"
|
||||||
|
#include "fq.hpp"
|
||||||
|
|
||||||
namespace zmq
|
namespace zmq
|
||||||
{
|
{
|
||||||
@@ -58,19 +58,8 @@ namespace zmq
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct inpipe_t
|
// Fair queueing object for inbound pipes.
|
||||||
{
|
fq_t fq;
|
||||||
class pipe_t *pipe;
|
|
||||||
uint32_t peer_id;
|
|
||||||
bool active;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Inbound pipes with the names of corresponging peers.
|
|
||||||
typedef std::vector <inpipe_t> inpipes_t;
|
|
||||||
inpipes_t inpipes;
|
|
||||||
|
|
||||||
// The pipe we are currently reading from.
|
|
||||||
inpipes_t::size_type current_in;
|
|
||||||
|
|
||||||
// Have we prefetched a message.
|
// Have we prefetched a message.
|
||||||
bool prefetched;
|
bool prefetched;
|
||||||
|
|||||||
Reference in New Issue
Block a user