summaryrefslogtreecommitdiff
path: root/src/pgm_receiver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pgm_receiver.cpp')
-rw-r--r--src/pgm_receiver.cpp283
1 files changed, 283 insertions, 0 deletions
diff --git a/src/pgm_receiver.cpp b/src/pgm_receiver.cpp
new file mode 100644
index 0000000..030c293
--- /dev/null
+++ b/src/pgm_receiver.cpp
@@ -0,0 +1,283 @@
+/*
+ Copyright (c) 2009-2011 250bpm s.r.o.
+ Copyright (c) 2007-2009 iMatix Corporation
+ Copyright (c) 2010-2011 Miru Limited
+ Copyright (c) 2007-2011 Other contributors as noted in the AUTHORS file
+
+ This file is part of 0MQ.
+
+ 0MQ is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ 0MQ is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "platform.hpp"
+
+#if defined ZMQ_HAVE_OPENPGM
+
+#include <new>
+
+#ifdef ZMQ_HAVE_WINDOWS
+#include "windows.hpp"
+#endif
+
+#include "pgm_receiver.hpp"
+#include "session_base.hpp"
+#include "stdint.hpp"
+#include "wire.hpp"
+#include "err.hpp"
+
+zmq::pgm_receiver_t::pgm_receiver_t (class io_thread_t *parent_,
+ const options_t &options_) :
+ io_object_t (parent_),
+ has_rx_timer (false),
+ pgm_socket (true, options_),
+ options (options_),
+ session (NULL),
+ mru_decoder (NULL),
+ pending_bytes (0)
+{
+}
+
+zmq::pgm_receiver_t::~pgm_receiver_t ()
+{
+ // Destructor should not be called before unplug.
+ zmq_assert (peers.empty ());
+}
+
+int zmq::pgm_receiver_t::init (bool udp_encapsulation_, const char *network_)
+{
+ return pgm_socket.init (udp_encapsulation_, network_);
+}
+
+void zmq::pgm_receiver_t::plug (io_thread_t *io_thread_,
+ session_base_t *session_)
+{
+ // Retrieve PGM fds and start polling.
+ fd_t socket_fd = retired_fd;
+ fd_t waiting_pipe_fd = retired_fd;
+ pgm_socket.get_receiver_fds (&socket_fd, &waiting_pipe_fd);
+ socket_handle = add_fd (socket_fd);
+ pipe_handle = add_fd (waiting_pipe_fd);
+ set_pollin (pipe_handle);
+ set_pollin (socket_handle);
+
+ session = session_;
+
+ // If there are any subscriptions already queued in the session, drop them.
+ drop_subscriptions ();
+}
+
+void zmq::pgm_receiver_t::unplug ()
+{
+ // Delete decoders.
+ for (peers_t::iterator it = peers.begin (); it != peers.end (); ++it) {
+ if (it->second.decoder != NULL)
+ delete it->second.decoder;
+ }
+ peers.clear ();
+
+ mru_decoder = NULL;
+ pending_bytes = 0;
+
+ if (has_rx_timer) {
+ cancel_timer (rx_timer_id);
+ has_rx_timer = false;
+ }
+
+ rm_fd (socket_handle);
+ rm_fd (pipe_handle);
+
+ session = NULL;
+}
+
+void zmq::pgm_receiver_t::terminate ()
+{
+ unplug ();
+ delete this;
+}
+
+void zmq::pgm_receiver_t::activate_out ()
+{
+ drop_subscriptions ();
+}
+
+void zmq::pgm_receiver_t::activate_in ()
+{
+ // It is possible that the most recently used decoder
+ // processed the whole buffer but failed to write
+ // the last message into the pipe.
+ if (pending_bytes == 0) {
+ if (mru_decoder != NULL) {
+ mru_decoder->process_buffer (NULL, 0);
+ session->flush ();
+ }
+
+ // Resume polling.
+ set_pollin (pipe_handle);
+ set_pollin (socket_handle);
+
+ return;
+ }
+
+ zmq_assert (mru_decoder != NULL);
+ zmq_assert (pending_ptr != NULL);
+
+ // Ask the decoder to process remaining data.
+ size_t n = mru_decoder->process_buffer (pending_ptr, pending_bytes);
+ pending_bytes -= n;
+ session->flush ();
+
+ if (pending_bytes > 0)
+ return;
+
+ // Resume polling.
+ set_pollin (pipe_handle);
+ set_pollin (socket_handle);
+
+ in_event ();
+}
+
+void zmq::pgm_receiver_t::in_event ()
+{
+ // Read data from the underlying pgm_socket.
+ unsigned char *data = NULL;
+ const pgm_tsi_t *tsi = NULL;
+
+ if (pending_bytes > 0)
+ return;
+
+ if (has_rx_timer) {
+ cancel_timer (rx_timer_id);
+ has_rx_timer = false;
+ }
+
+ // TODO: This loop can effectively block other engines in the same I/O
+ // thread in the case of high load.
+ while (true) {
+
+ // Get new batch of data.
+ // Note the workaround made not to break strict-aliasing rules.
+ void *tmp = NULL;
+ ssize_t received = pgm_socket.receive (&tmp, &tsi);
+ data = (unsigned char*) tmp;
+
+ // No data to process. This may happen if the packet received is
+ // neither ODATA nor ODATA.
+ if (received == 0) {
+ if (errno == ENOMEM || errno == EBUSY) {
+ const long timeout = pgm_socket.get_rx_timeout ();
+ add_timer (timeout, rx_timer_id);
+ has_rx_timer = true;
+ }
+ break;
+ }
+
+ // Find the peer based on its TSI.
+ peers_t::iterator it = peers.find (*tsi);
+
+ // Data loss. Delete decoder and mark the peer as disjoint.
+ if (received == -1) {
+ if (it != peers.end ()) {
+ it->second.joined = false;
+ if (it->second.decoder == mru_decoder)
+ mru_decoder = NULL;
+ if (it->second.decoder != NULL) {
+ delete it->second.decoder;
+ it->second.decoder = NULL;
+ }
+ }
+ break;
+ }
+
+ // New peer. Add it to the list of know but unjoint peers.
+ if (it == peers.end ()) {
+ peer_info_t peer_info = {false, NULL};
+ it = peers.insert (peers_t::value_type (*tsi, peer_info)).first;
+ }
+
+ // Read the offset of the fist message in the current packet.
+ zmq_assert ((size_t) received >= sizeof (uint16_t));
+ uint16_t offset = get_uint16 (data);
+ data += sizeof (uint16_t);
+ received -= sizeof (uint16_t);
+
+ // Join the stream if needed.
+ if (!it->second.joined) {
+
+ // There is no beginning of the message in current packet.
+ // Ignore the data.
+ if (offset == 0xffff)
+ continue;
+
+ zmq_assert (offset <= received);
+ zmq_assert (it->second.decoder == NULL);
+
+ // We have to move data to the begining of the first message.
+ data += offset;
+ received -= offset;
+
+ // Mark the stream as joined.
+ it->second.joined = true;
+
+ // Create and connect decoder for the peer.
+ it->second.decoder = new (std::nothrow) decoder_t (0,
+ options.maxmsgsize);
+ alloc_assert (it->second.decoder);
+ it->second.decoder->set_msg_sink (session);
+ }
+
+ mru_decoder = it->second.decoder;
+
+ // Push all the data to the decoder.
+ ssize_t processed = it->second.decoder->process_buffer (data, received);
+ if (processed < received) {
+ // Save some state so we can resume the decoding process later.
+ pending_bytes = received - processed;
+ pending_ptr = data + processed;
+ // Stop polling.
+ reset_pollin (pipe_handle);
+ reset_pollin (socket_handle);
+
+ // Reset outstanding timer.
+ if (has_rx_timer) {
+ cancel_timer (rx_timer_id);
+ has_rx_timer = false;
+ }
+
+ break;
+ }
+ }
+
+ // Flush any messages decoder may have produced.
+ session->flush ();
+}
+
+void zmq::pgm_receiver_t::timer_event (int token)
+{
+ zmq_assert (token == rx_timer_id);
+
+ // Timer cancels on return by poller_base.
+ has_rx_timer = false;
+ in_event ();
+}
+
+void zmq::pgm_receiver_t::drop_subscriptions ()
+{
+ msg_t msg;
+ msg.init ();
+ while (session->pull_msg (&msg))
+ msg.close ();
+}
+
+#endif
+