zmq.Pipe Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jeromq Show documentation
Show all versions of jeromq Show documentation
Pure Java implementation of libzmq
/*
Copyright (c) 2009-2011 250bpm s.r.o.
Copyright (c) 2007-2009 iMatix Corporation
Copyright (c) 2011 VMware, Inc.
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 .
*/
package zmq;
// Note that pipe can be stored in three different arrays.
// The array of inbound pipes (1), the array of outbound pipes (2) and
// the generic array of pipes to deallocate (3).
public class Pipe extends ZObject {
public interface IPipeEvents {
void read_activated(Pipe pipe);
void write_activated(Pipe pipe);
void hiccuped(Pipe pipe);
void terminated(Pipe pipe);
}
// Underlying pipes for both directions.
private YPipe inpipe;
private YPipe outpipe;
// Can the pipe be read from / written to?
private boolean in_active;
private boolean out_active;
// High watermark for the outbound pipe.
private int hwm;
// Low watermark for the inbound pipe.
private int lwm;
// Number of messages read and written so far.
private long msgs_read;
private long msgs_written;
// Last received peer's msgs_read. The actual number in the peer
// can be higher at the moment.
private long peers_msgs_read;
// The pipe object on the other side of the pipepair.
private Pipe peer;
// Sink to send events to.
private IPipeEvents sink;
// State of the pipe endpoint. Active is common state before any
// termination begins. Delimited means that delimiter was read from
// pipe before term command was received. Pending means that term
// command was already received from the peer but there are still
// pending messages to read. Terminating means that all pending
// messages were already read and all we are waiting for is ack from
// the peer. Terminated means that 'terminate' was explicitly called
// by the user. Double_terminated means that user called 'terminate'
// and then we've got term command from the peer as well.
enum State {
ACTIVE,
DELIMITED,
PENDING,
TERMINATING,
TERMINATED,
DOUBLE_TERMINATED
}
private State state;
// If true, we receive all the pending inbound messages before
// terminating. If false, we terminate immediately when the peer
// asks us to.
private boolean delay;
// Identity of the writer. Used uniquely by the reader side.
private Blob identity;
// JeroMQ only
private ZObject parent;
// Constructor is private. Pipe can only be created using
// pipepair function.
private Pipe (ZObject parent_, YPipe inpipe_, YPipe outpipe_,
int inhwm_, int outhwm_, boolean delay_) {
super(parent_);
inpipe = inpipe_;
outpipe = outpipe_;
in_active = true;
out_active = true;
hwm = outhwm_;
lwm = compute_lwm (inhwm_);
msgs_read = 0;
msgs_written = 0;
peers_msgs_read = 0;
peer = null ;
sink = null ;
state = State.ACTIVE;
delay = delay_;
parent = parent_;
}
// Create a pipepair for bi-directional transfer of messages.
// First HWM is for messages passed from first pipe to the second pipe.
// Second HWM is for messages passed from second pipe to the first pipe.
// Delay specifies how the pipe behaves when the peer terminates. If true
// pipe receives all the pending messages before terminating, otherwise it
// terminates straight away.
public static void pipepair(ZObject[] parents_, Pipe[] pipes_, int[] hwms_,
boolean[] delays_) {
// Creates two pipe objects. These objects are connected by two ypipes,
// each to pass messages in one direction.
YPipe upipe1 = new YPipe(Config.MESSAGE_PIPE_GRANULARITY.getValue());
YPipe upipe2 = new YPipe(Config.MESSAGE_PIPE_GRANULARITY.getValue());
pipes_ [0] = new Pipe(parents_ [0], upipe1, upipe2,
hwms_ [1], hwms_ [0], delays_ [0]);
pipes_ [1] = new Pipe(parents_ [1], upipe2, upipe1,
hwms_ [0], hwms_ [1], delays_ [1]);
pipes_ [0].set_peer (pipes_ [1]);
pipes_ [1].set_peer (pipes_ [0]);
}
// Pipepair uses this function to let us know about
// the peer pipe object.
private void set_peer (Pipe peer_)
{
// Peer can be set once only.
assert (peer_ != null);
peer = peer_;
}
// Specifies the object to send events to.
public void set_event_sink(IPipeEvents sink_) {
assert (sink == null);
sink = sink_;
}
// Pipe endpoint can store an opaque ID to be used by its clients.
public void set_identity(Blob identity_) {
identity = identity_;
}
public Blob get_identity() {
return identity;
}
// Returns true if there is at least one message to read in the pipe.
public boolean check_read() {
if (!in_active || (state != State.ACTIVE && state != State.PENDING))
return false;
// Check if there's an item in the pipe.
if (!inpipe.check_read ()) {
in_active = false;
return false;
}
// If the next item in the pipe is message delimiter,
// initiate termination process.
if (is_delimiter(inpipe.probe ())) {
Msg msg = inpipe.read ();
assert (msg != null);
delimit ();
return false;
}
return true;
}
// Reads a message to the underlying pipe.
public Msg read()
{
if (!in_active || (state != State.ACTIVE && state != State.PENDING))
return null;
Msg msg_ = inpipe.read();
if (msg_ == null) {
in_active = false;
return null;
}
// If delimiter was read, start termination process of the pipe.
if (msg_.isDelimiter()) {
delimit ();
return null;
}
if (!msg_.hasMore())
msgs_read++;
if (lwm > 0 && msgs_read % lwm == 0)
send_activate_write(peer, msgs_read);
return msg_;
}
// Checks whether messages can be written to the pipe. If writing
// the message would cause high watermark the function returns false.
public boolean check_write ()
{
if (!out_active || state != State.ACTIVE)
return false;
boolean full = hwm > 0 && msgs_written - peers_msgs_read == (long) (hwm);
if (full) {
out_active = false;
return false;
}
return true;
}
// Writes a message to the underlying pipe. Returns false if the
// message cannot be written because high watermark was reached.
public boolean write (Msg msg_)
{
if (!check_write ())
return false;
boolean more = msg_.hasMore();
outpipe.write (msg_, more);
if (!more)
msgs_written++;
return true;
}
// Remove unfinished parts of the outbound message from the pipe.
public void rollback ()
{
// Remove incomplete message from the outbound pipe.
Msg msg;
if (outpipe!= null) {
while ((msg = outpipe.unwrite ()) != null) {
assert ((msg.flags () & Msg.MORE) > 0);
//msg.close ();
}
}
}
// Flush the messages downsteam.
public void flush ()
{
// The peer does not exist anymore at this point.
if (state == State.TERMINATING)
return;
if (outpipe != null && !outpipe.flush ()) {
send_activate_read (peer);
}
}
@Override
protected void process_activate_read ()
{
if (!in_active && (state == State.ACTIVE || state == State.PENDING)) {
in_active = true;
sink.read_activated (this);
}
}
@Override
protected void process_activate_write (long msgs_read_)
{
// Remember the peers's message sequence number.
peers_msgs_read = msgs_read_;
if (!out_active && state == State.ACTIVE) {
out_active = true;
sink.write_activated (this);
}
}
@SuppressWarnings("unchecked")
@Override
protected void process_hiccup (Object pipe_) {
// Destroy old outpipe. Note that the read end of the pipe was already
// migrated to this thread.
assert (outpipe != null);
outpipe.flush ();
while (outpipe.read () !=null) {
}
// Plug in the new outpipe.
assert (pipe_ != null);
outpipe = (YPipe) pipe_;
out_active = true;
// If appropriate, notify the user about the hiccup.
if (state == State.ACTIVE)
sink.hiccuped (this);
}
@Override
protected void process_pipe_term ()
{
// This is the simple case of peer-induced termination. If there are no
// more pending messages to read, or if the pipe was configured to drop
// pending messages, we can move directly to the terminating state.
// Otherwise we'll hang up in pending state till all the pending messages
// are sent.
if (state == State.ACTIVE) {
if (!delay) {
state = State.TERMINATING;
outpipe = null;
send_pipe_term_ack (peer);
}
else
state = State.PENDING;
return;
}
// Delimiter happened to arrive before the term command. Now we have the
// term command as well, so we can move straight to terminating state.
if (state == State.DELIMITED) {
state = State.TERMINATING;
outpipe = null;
send_pipe_term_ack (peer);
return;
}
// This is the case where both ends of the pipe are closed in parallel.
// We simply reply to the request by ack and continue waiting for our
// own ack.
if (state == State.TERMINATED) {
state = State.DOUBLE_TERMINATED;
outpipe = null;
send_pipe_term_ack (peer);
return;
}
// pipe_term is invalid in other states.
assert (false);
}
@Override
protected void process_pipe_term_ack ()
{
// Notify the user that all the references to the pipe should be dropped.
assert (sink!=null);
sink.terminated (this);
// In terminating and double_terminated states there's nothing to do.
// Simply deallocate the pipe. In terminated state we have to ack the
// peer before deallocating this side of the pipe. All the other states
// are invalid.
if (state == State.TERMINATED) {
outpipe = null;
send_pipe_term_ack (peer);
}
else
assert (state == State.TERMINATING || state == State.DOUBLE_TERMINATED);
// We'll deallocate the inbound pipe, the peer will deallocate the outbound
// pipe (which is an inbound pipe from its point of view).
// First, delete all the unread messages in the pipe. We have to do it by
// hand because msg_t doesn't have automatic destructor. Then deallocate
// the ypipe itself.
while (inpipe.read () != null) {
}
inpipe = null;
// Deallocate the pipe object
}
// Ask pipe to terminate. The termination will happen asynchronously
// and user will be notified about actual deallocation by 'terminated'
// event. If delay is true, the pending messages will be processed
// before actual shutdown.
public void terminate (boolean delay_)
{
// Overload the value specified at pipe creation.
delay = delay_;
// If terminate was already called, we can ignore the duplicit invocation.
if (state == State.TERMINATED || state == State.DOUBLE_TERMINATED)
return;
// If the pipe is in the final phase of async termination, it's going to
// closed anyway. No need to do anything special here.
else if (state == State.TERMINATING)
return;
// The simple sync termination case. Ask the peer to terminate and wait
// for the ack.
else if (state == State.ACTIVE) {
send_pipe_term (peer);
state = State.TERMINATED;
}
// There are still pending messages available, but the user calls
// 'terminate'. We can act as if all the pending messages were read.
else if (state == State.PENDING && !delay) {
outpipe = null;
send_pipe_term_ack (peer);
state = State.TERMINATING;
}
// If there are pending messages still availabe, do nothing.
else if (state == State.PENDING) {
}
// We've already got delimiter, but not term command yet. We can ignore
// the delimiter and ack synchronously terminate as if we were in
// active state.
else if (state == State.DELIMITED) {
send_pipe_term (peer);
state = State.TERMINATED;
}
// There are no other states.
else
assert (false);
// Stop outbound flow of messages.
out_active = false;
if (outpipe != null) {
// Drop any unfinished outbound messages.
rollback ();
// Write the delimiter into the pipe. Note that watermarks are not
// checked; thus the delimiter can be written even when the pipe is full.
Msg msg = new Msg();
msg.initDelimiter ();
outpipe.write (msg, false);
flush ();
}
}
// Returns true if the message is delimiter; false otherwise.
private static boolean is_delimiter(Msg msg_) {
return msg_.isDelimiter ();
}
// Computes appropriate low watermark from the given high watermark.
private static int compute_lwm (int hwm_)
{
// Compute the low water mark. Following point should be taken
// into consideration:
//
// 1. LWM has to be less than HWM.
// 2. LWM cannot be set to very low value (such as zero) as after filling
// the queue it would start to refill only after all the messages are
// read from it and thus unnecessarily hold the progress back.
// 3. LWM cannot be set to very high value (such as HWM-1) as it would
// result in lock-step filling of the queue - if a single message is
// read from a full queue, writer thread is resumed to write exactly one
// message to the queue and go back to sleep immediately. This would
// result in low performance.
//
// Given the 3. it would be good to keep HWM and LWM as far apart as
// possible to reduce the thread switching overhead to almost zero,
// say HWM-LWM should be max_wm_delta.
//
// That done, we still we have to account for the cases where
// HWM < max_wm_delta thus driving LWM to negative numbers.
// Let's make LWM 1/2 of HWM in such cases.
int result = (hwm_ > Config.MAX_WM_DELTA.getValue() * 2) ?
hwm_ - Config.MAX_WM_DELTA.getValue() : (hwm_ + 1) / 2;
return result;
}
// Handler for delimiter read from the pipe.
private void delimit ()
{
if (state == State.ACTIVE) {
state = State.DELIMITED;
return;
}
if (state == State.PENDING) {
outpipe = null;
send_pipe_term_ack (peer);
state = State.TERMINATING;
return;
}
// Delimiter in any other state is invalid.
assert (false);
}
// Temporaraily disconnects the inbound message stream and drops
// all the messages on the fly. Causes 'hiccuped' event to be generated
// in the peer.
public void hiccup() {
// If termination is already under way do nothing.
if (state != State.ACTIVE)
return;
// We'll drop the pointer to the inpipe. From now on, the peer is
// responsible for deallocating it.
inpipe = null;
// Create new inpipe.
inpipe = new YPipe(Config.MESSAGE_PIPE_GRANULARITY.getValue());
in_active = true;
// Notify the peer about the hiccup.
send_hiccup (peer, inpipe);
}
@Override
public String toString() {
return super.toString() + "[" + parent + "]";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy