Skip to content

Commit

Permalink
Fix race condition and support multiple socket connects before bind.
Browse files Browse the repository at this point in the history
  • Loading branch information
ricnewton committed Sep 12, 2013
1 parent 6fefa41 commit 7c3496a
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 66 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ tests/test_req_request_ids
tests/test_req_strict
tests/test_fork
tests/test_conflate
tests/test_inproc_connect_before_bind
tests/test_linger
tests/test_security_null
tests/test_security_null.opp
Expand Down
1 change: 1 addition & 0 deletions src/command.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ namespace zmq
term_ack,
reap,
reaped,
inproc_connected,
done
} type;

Expand Down
43 changes: 32 additions & 11 deletions src/ctx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,28 +396,49 @@ void zmq::ctx_t::pend_connection (const char *addr_, const pending_connection_t
{
endpoints_sync.lock ();

// Todo, use multimap to support multiple pending connections
pending_connections[addr_] = pending_connection_;
endpoints_t::iterator it = endpoints.find (addr_);
if (it == endpoints.end ())
{
// Still no bind.
pending_connection_.socket->inc_seqnum ();
pending_connections.insert (pending_connections_t::value_type (std::string (addr_), pending_connection_));
}
else
{
// Bind has happened in the mean time, connect directly
pending_connection_t copy = pending_connection_;
it->second.socket->inc_seqnum();
copy.pipe->set_tid(it->second.socket->get_tid());
command_t cmd;
cmd.type = command_t::bind;
cmd.args.bind.pipe = copy.pipe;
it->second.socket->process_command(cmd);
}

endpoints_sync.unlock ();
}

zmq::pending_connection_t zmq::ctx_t::next_pending_connection(const char *addr_)
void zmq::ctx_t::connect_pending (const char *addr_, zmq::socket_base_t *bind_socket_)
{
endpoints_sync.lock ();

pending_connections_t::iterator it = pending_connections.find (addr_);
if (it == pending_connections.end ()) {
std::pair<pending_connections_t::iterator, pending_connections_t::iterator> pending = pending_connections.equal_range(addr_);

endpoints_sync.unlock ();
pending_connection_t empty = {NULL, NULL};
return empty;
for (pending_connections_t::iterator p = pending.first; p != pending.second; ++p)
{
bind_socket_->inc_seqnum();
p->second.pipe->set_tid(bind_socket_->get_tid());
command_t cmd;
cmd.type = command_t::bind;
cmd.args.bind.pipe = p->second.pipe;
bind_socket_->process_command(cmd);

bind_socket_->send_inproc_connected(p->second.socket);
}
pending_connection_t pending_connection = it->second;
pending_connections.erase(it);

pending_connections.erase(pending.first, pending.second);

endpoints_sync.unlock ();
return pending_connection;
}

// The last used socket ID, or 0 if no socket was used so far. Note that this
Expand Down
4 changes: 2 additions & 2 deletions src/ctx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ namespace zmq
void unregister_endpoints (zmq::socket_base_t *socket_);
endpoint_t find_endpoint (const char *addr_);
void pend_connection (const char *addr_, const pending_connection_t &pending_connection_);
pending_connection_t next_pending_connection (const char *addr_);
void connect_pending (const char *addr_, zmq::socket_base_t *bind_socket_);

enum {
term_tid = 0,
Expand Down Expand Up @@ -166,7 +166,7 @@ namespace zmq
endpoints_t endpoints;

// List of inproc connection endpoints pending a bind
typedef std::map <std::string, pending_connection_t> pending_connections_t;
typedef std::multimap <std::string, pending_connection_t> pending_connections_t;
pending_connections_t pending_connections;

// Synchronisation of access to the list of inproc endpoints.
Expand Down
16 changes: 14 additions & 2 deletions src/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ void zmq::object_t::process_command (command_t &cmd_)
process_reaped ();
break;

case command_t::inproc_connected:
process_seqnum ();
break;

case command_t::done:
default:
zmq_assert (false);
Expand All @@ -153,9 +157,9 @@ void zmq::object_t::pend_connection (const char *addr_, const pending_connection
ctx->pend_connection (addr_, pending_connection_);
}

zmq::pending_connection_t zmq::object_t::next_pending_connection (const char *addr_)
void zmq::object_t::connect_pending (const char *addr_, zmq::socket_base_t *bind_socket_)
{
return ctx->next_pending_connection(addr_);
return ctx->connect_pending(addr_, bind_socket_);
}

void zmq::object_t::destroy_socket (socket_base_t *socket_)
Expand Down Expand Up @@ -312,6 +316,14 @@ void zmq::object_t::send_reaped ()
send_command (cmd);
}

void zmq::object_t::send_inproc_connected (zmq::socket_base_t *socket_)
{
command_t cmd;
cmd.destination = socket_;
cmd.type = command_t::inproc_connected;
send_command (cmd);
}

void zmq::object_t::send_done ()
{
command_t cmd;
Expand Down
3 changes: 2 additions & 1 deletion src/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ namespace zmq
void set_tid(uint32_t id);
ctx_t *get_ctx ();
void process_command (zmq::command_t &cmd_);
void send_inproc_connected (zmq::socket_base_t *socket_);

protected:

Expand All @@ -60,7 +61,7 @@ namespace zmq
void unregister_endpoints (zmq::socket_base_t *socket_);
zmq::endpoint_t find_endpoint (const char *addr_);
void pend_connection (const char *addr_, const pending_connection_t &pending_connection_);
zmq::pending_connection_t next_pending_connection (const char *addr_);
void connect_pending (const char *addr_, zmq::socket_base_t *bind_socket_);

void destroy_socket (zmq::socket_base_t *socket_);

Expand Down
46 changes: 1 addition & 45 deletions src/socket_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,52 +342,8 @@ int zmq::socket_base_t::bind (const char *addr_)
endpoint_t endpoint = {this, options};
int rc = register_endpoint (addr_, endpoint);
if (rc == 0) {
// Save last endpoint URI
connect_pending(addr_, this);
last_endpoint.assign (addr_);

pending_connection_t pending_connection = next_pending_connection(addr_);
while (pending_connection.pipe != NULL)
{
inc_seqnum();
//// If required, send the identity of the local socket to the peer.
//if (peer.options.recv_identity) {
// msg_t id;
// rc = id.init_size (options.identity_size);
// errno_assert (rc == 0);
// memcpy (id.data (), options.identity, options.identity_size);
// id.set_flags (msg_t::identity);
// bool written = new_pipes [0]->write (&id);
// zmq_assert (written);
// new_pipes [0]->flush ();
//}

//// If required, send the identity of the peer to the local socket.
//if (options.recv_identity) {
// msg_t id;
// rc = id.init_size (peer.options.identity_size);
// errno_assert (rc == 0);
// memcpy (id.data (), peer.options.identity, peer.options.identity_size);
// id.set_flags (msg_t::identity);
// bool written = new_pipes [1]->write (&id);
// zmq_assert (written);
// new_pipes [1]->flush ();
//}

//// Attach remote end of the pipe to the peer socket. Note that peer's
//// seqnum was incremented in find_endpoint function. We don't need it
//// increased here.
//send_bind (peer.socket, new_pipes [1], false);

pending_connection.pipe->set_tid(get_tid());

command_t cmd;
cmd.type = command_t::bind;
cmd.args.bind.pipe = pending_connection.pipe;
process_command(cmd);


pending_connection = next_pending_connection(addr_);
}
}
return rc;
}
Expand Down
4 changes: 3 additions & 1 deletion tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ noinst_PROGRAMS = test_system \
test_spec_pushpull \
test_req_request_ids \
test_req_strict \
test_conflate
test_conflate \
test_inproc_connect_before_bind

if !ON_MINGW
noinst_PROGRAMS += test_shutdown_stress \
Expand Down Expand Up @@ -80,6 +81,7 @@ test_spec_pushpull_SOURCES = test_spec_pushpull.cpp
test_req_request_ids_SOURCES = test_req_request_ids.cpp
test_req_strict_SOURCES = test_req_strict.cpp
test_conflate_SOURCES = test_conflate.cpp
test_inproc_connect_before_bind_SOURCES = test_inproc_connect_before_bind.cpp
if !ON_MINGW
test_shutdown_stress_SOURCES = test_shutdown_stress.cpp
test_pair_ipc_SOURCES = test_pair_ipc.cpp testutil.hpp
Expand Down
Loading

0 comments on commit 7c3496a

Please sign in to comment.