fix race condition in session init

Signed-off-by: Dhammika Pathirana <dhammika@gmail.com>
This commit is contained in:
Dhammika Pathirana 2010-12-17 15:30:56 +01:00 committed by Martin Sustrik
parent 27e83cc525
commit b19ee99bb1
4 changed files with 53 additions and 20 deletions

View File

@ -40,6 +40,7 @@ zmq::zmq_engine_t::zmq_engine_t (fd_t fd_, const options_t &options_) :
outsize (0), outsize (0),
encoder (out_batch_size), encoder (out_batch_size),
inout (NULL), inout (NULL),
ephemeral_inout (NULL),
options (options_), options (options_),
plugged (false) plugged (false)
{ {
@ -57,8 +58,9 @@ void zmq::zmq_engine_t::plug (io_thread_t *io_thread_, i_inout *inout_)
{ {
zmq_assert (!plugged); zmq_assert (!plugged);
plugged = true; plugged = true;
ephemeral_inout = NULL;
// Conncet to session/init object. // Connect to session/init object.
zmq_assert (!inout); zmq_assert (!inout);
zmq_assert (inout_); zmq_assert (inout_);
encoder.set_inout (inout_); encoder.set_inout (inout_);
@ -89,6 +91,7 @@ void zmq::zmq_engine_t::unplug ()
// Disconnect from init/session object. // Disconnect from init/session object.
encoder.set_inout (NULL); encoder.set_inout (NULL);
decoder.set_inout (NULL); decoder.set_inout (NULL);
ephemeral_inout = inout;
inout = NULL; inout = NULL;
} }
@ -139,7 +142,13 @@ void zmq::zmq_engine_t::in_event ()
} }
// Flush all messages the decoder may have produced. // Flush all messages the decoder may have produced.
inout->flush (); // If IO handler has unplugged engine, flush transient IO handler.
if (unlikely (!plugged)) {
zmq_assert (ephemeral_inout);
ephemeral_inout->flush ();
} else {
inout->flush ();
}
if (disconnection) if (disconnection)
error (); error ();
@ -152,7 +161,14 @@ void zmq::zmq_engine_t::out_event ()
outpos = NULL; outpos = NULL;
encoder.get_data (&outpos, &outsize); encoder.get_data (&outpos, &outsize);
// If IO handler has unplugged engine, flush transient IO handler.
if (unlikely (!plugged)) {
zmq_assert (ephemeral_inout);
ephemeral_inout->flush ();
return;
}
// If there is no data to send, stop polling for output. // If there is no data to send, stop polling for output.
if (outsize == 0) { if (outsize == 0) {
reset_pollout (handle); reset_pollout (handle);

View File

@ -70,6 +70,9 @@ namespace zmq
i_inout *inout; i_inout *inout;
// Detached transient inout handler.
i_inout *ephemeral_inout;
options_t options; options_t options;
bool plugged; bool plugged;

View File

@ -34,6 +34,7 @@ zmq::zmq_init_t::zmq_init_t (io_thread_t *io_thread_,
socket_base_t *socket_, session_t *session_, fd_t fd_, socket_base_t *socket_, session_t *session_, fd_t fd_,
const options_t &options_) : const options_t &options_) :
own_t (io_thread_, options_), own_t (io_thread_, options_),
ephemeral_engine (NULL),
sent (false), sent (false),
received (false), received (false),
socket (socket_), socket (socket_),
@ -64,8 +65,7 @@ bool zmq::zmq_init_t::read (::zmq_msg_t *msg_)
options.identity.size ()); options.identity.size ());
sent = true; sent = true;
// If initialisation is done, pass the engine to the session and // Try finalize initialization.
// destroy the init object.
finalise_initialisation (); finalise_initialisation ();
return true; return true;
@ -92,6 +92,9 @@ bool zmq::zmq_init_t::write (::zmq_msg_t *msg_)
received = true; received = true;
// Try finalize initialization.
finalise_initialisation ();
return true; return true;
} }
@ -101,9 +104,9 @@ void zmq::zmq_init_t::flush ()
if (!received) if (!received)
return; return;
// If initialisation is done, pass the engine to the session and // Initialization is done, dispatch engine.
// destroy the init object. if (ephemeral_engine)
finalise_initialisation (); dispatch_engine ();
} }
void zmq::zmq_init_t::detach () void zmq::zmq_init_t::detach ()
@ -134,18 +137,31 @@ void zmq::zmq_init_t::process_unplug ()
} }
void zmq::zmq_init_t::finalise_initialisation () void zmq::zmq_init_t::finalise_initialisation ()
{
// Unplug and prepare to dispatch engine.
if (sent && received) {
ephemeral_engine = engine;
engine = NULL;
ephemeral_engine->unplug ();
return;
}
}
void zmq::zmq_init_t::dispatch_engine ()
{ {
if (sent && received) { if (sent && received) {
// Engine must be detached.
zmq_assert (!engine);
zmq_assert (ephemeral_engine);
// If we know what session we belong to, it's easy, just send the // If we know what session we belong to, it's easy, just send the
// engine to that session and destroy the init object. Note that we // engine to that session and destroy the init object. Note that we
// know about the session only if this object is owned by it. Thus, // know about the session only if this object is owned by it. Thus,
// lifetime of this object in contained in the lifetime of the session // lifetime of this object in contained in the lifetime of the session
// so the pointer cannot become invalid without notice. // so the pointer cannot become invalid without notice.
if (session) { if (session) {
engine->unplug (); send_attach (session, ephemeral_engine, peer_identity, true);
send_attach (session, engine, peer_identity, true);
engine = NULL;
terminate (); terminate ();
return; return;
} }
@ -165,9 +181,7 @@ void zmq::zmq_init_t::finalise_initialisation ()
zmq_assert (session); zmq_assert (session);
session->inc_seqnum (); session->inc_seqnum ();
launch_sibling (session); launch_sibling (session);
engine->unplug (); send_attach (session, ephemeral_engine, peer_identity, false);
send_attach (session, engine, peer_identity, false);
engine = NULL;
terminate (); terminate ();
return; return;
} }
@ -178,9 +192,7 @@ void zmq::zmq_init_t::finalise_initialisation ()
// than by send_attach. // than by send_attach.
session = socket->find_session (peer_identity); session = socket->find_session (peer_identity);
if (session) { if (session) {
engine->unplug (); send_attach (session, ephemeral_engine, peer_identity, false);
send_attach (session, engine, peer_identity, false);
engine = NULL;
terminate (); terminate ();
return; return;
} }
@ -194,9 +206,7 @@ void zmq::zmq_init_t::finalise_initialisation ()
zmq_assert (session); zmq_assert (session);
session->inc_seqnum (); session->inc_seqnum ();
launch_sibling (session); launch_sibling (session);
engine->unplug (); send_attach (session, ephemeral_engine, peer_identity, false);
send_attach (session, engine, peer_identity, false);
engine = NULL;
terminate (); terminate ();
return; return;
} }

View File

@ -44,6 +44,7 @@ namespace zmq
private: private:
void finalise_initialisation (); void finalise_initialisation ();
void dispatch_engine ();
// i_inout interface implementation. // i_inout interface implementation.
bool read (::zmq_msg_t *msg_); bool read (::zmq_msg_t *msg_);
@ -58,6 +59,9 @@ namespace zmq
// Associated wire-protocol engine. // Associated wire-protocol engine.
i_engine *engine; i_engine *engine;
// Detached transient engine.
i_engine *ephemeral_engine;
// True if our own identity was already sent to the peer. // True if our own identity was already sent to the peer.
bool sent; bool sent;