/// <summary> /// Temporarily disconnects the inbound message stream and drops /// all the messages on the fly. Causes 'hiccuped' event to be generated in the peer. /// </summary> public void Hiccup() { // If termination is already under way do nothing. if (m_state != State.Active) { return; } // We'll drop the pointer to the in-pipe. From now on, the peer is // responsible for deallocating it. m_inboundPipe = null; // Create new in-pipe. m_inboundPipe = new YPipe <Msg>(Config.MessagePipeGranularity, "inpipe"); m_inActive = true; // Notify the peer about the hiccup. SendHiccup(m_peer, m_inboundPipe); }
/// <summary> /// Handles the delimiter read from the pipe. /// </summary> private void Delimit() { if (m_state == State.Active) { m_state = State.Delimited; return; } if (m_state == State.Pending) { m_outboundPipe = null; SendPipeTermAck(m_peer); m_state = State.Terminating; return; } // Delimiter in any other state is invalid. Debug.Assert(false); }
public static Pipe[] PipePair([NotNull] ZObject[] parents, [NotNull] int[] highWaterMarks, [NotNull] int[] lowWaterMarks, [NotNull] bool[] delays) { // Creates two pipe objects. These objects are connected by two ypipes, // each to pass messages in one direction. var upipe1 = new YPipe <Msg>(Config.MessagePipeGranularity, "upipe1"); var upipe2 = new YPipe <Msg>(Config.MessagePipeGranularity, "upipe2"); var pipes = new[] { new Pipe(parents[0], upipe1, upipe2, highWaterMarks[1], highWaterMarks[0], lowWaterMarks[1], delays[0]), new Pipe(parents[1], upipe2, upipe1, highWaterMarks[0], highWaterMarks[1], lowWaterMarks[0], delays[1]) }; pipes[0].SetPeer(pipes[1]); pipes[1].SetPeer(pipes[0]); return(pipes); }
/// <summary> /// Create a new Pipe object with the given parent, and inbound and outbound YPipes. /// </summary> /// <remarks> /// Constructor is private as pipe can only be created using <see cref="PipePair"/> method. /// </remarks> private Pipe( [NotNull] ZObject parent, [NotNull] YPipe <Msg> inboundPipe, [NotNull] YPipe <Msg> outboundPipe, int inHighWatermark, int outHighWatermark, int predefinedLowWatermark) : base(parent) { m_parent = parent; m_inboundPipe = inboundPipe; m_outboundPipe = outboundPipe; m_inActive = true; m_outActive = true; m_highWatermark = outHighWatermark; m_lowWatermark = ComputeLowWatermark(inHighWatermark, predefinedLowWatermark); m_numberOfMessagesRead = 0; m_numberOfMessagesWritten = 0; m_peersMsgsRead = 0; m_peer = null; m_sink = null; m_state = State.Active; m_delay = true; }
/// <summary> /// Process the pipe-termination ack. /// </summary> protected override void ProcessPipeTermAck() { // Notify the user that all the references to the pipe should be dropped. Assumes.NotNull(m_sink); m_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 (m_state == State.Terminated) { m_outboundPipe = null; Assumes.NotNull(m_peer); SendPipeTermAck(m_peer); } else { Debug.Assert(m_state == State.Terminating || m_state == State.DoubleTerminated); } Assumes.NotNull(m_inboundPipe); // 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. var msg = new Msg(); while (m_inboundPipe.TryRead(out msg)) { msg.Close(); } m_inboundPipe = null; }
/// <summary> /// This method is called to assign the specified pipe as a replacement for the outbound pipe that was being used. /// </summary> /// <param name="pipe">the pipe to use for writing</param> /// <remarks> /// A "Hiccup" occurs when an outbound pipe experiences something like a transient disconnect or for whatever other reason /// is no longer available for writing to. /// </remarks> protected override void ProcessHiccup(object pipe) { // Destroy old out-pipe. Note that the read end of the pipe was already // migrated to this thread. Debug.Assert(m_outboundPipe != null); m_outboundPipe.Flush(); var msg = new Msg(); while (m_outboundPipe.TryRead(out msg)) { msg.Close(); } // Plug in the new out-pipe. Debug.Assert(pipe != null); m_outboundPipe = (YPipe <Msg>)pipe; m_outActive = true; // If appropriate, notify the user about the hiccup. if (m_state == State.Active) { m_sink.Hiccuped(this); } }
/// <summary> /// Ask pipe to terminate. The termination will happen asynchronously /// and user will be notified about actual deallocation by 'terminated' /// event. /// </summary> /// <param name="delay">if set to <c>true</c>, the pending messages will be processed /// before actual shutdown. </param> public void Terminate(bool delay) { // Overload the value specified at pipe creation. m_delay = delay; // If terminate was already called, we can ignore the duplicate invocation. if (m_state == State.Terminated || m_state == State.DoubleTerminated) { return; } // If the pipe is in the phase of async termination, it's going to // closed anyway. No need to do anything special here. if (m_state == State.Terminating) { return; } if (m_state == State.Active) { // The simple sync termination case. Ask the peer to terminate and wait // for the ack. SendPipeTerm(m_peer); m_state = State.Terminated; } else if (m_state == State.Pending && !m_delay) { // There are still pending messages available, but the user calls // 'terminate'. We can act as if all the pending messages were read. m_outboundPipe = null; SendPipeTermAck(m_peer); m_state = State.Terminating; } else if (m_state == State.Pending) { // If there are pending messages still available, do nothing. } else if (m_state == State.Delimited) { // 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. SendPipeTerm(m_peer); m_state = State.Terminated; } else { // There are no other states. Debug.Assert(false); } // Stop outbound flow of messages. m_outActive = false; if (m_outboundPipe != 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. var msg = new Msg(); msg.InitDelimiter(); m_outboundPipe.Write(ref msg, false); Flush(); } }