/// <summary> /// Processes an incoming acknowledgement and returns a new buffer with only unacknowledged elements remaining. /// </summary> /// <param name="ack">The received acknowledgement</param> /// <exception cref="ResendUnfulfillableException">Thrown if we couldn't fit all of the nacks stored inside <see cref="Ack"/> onto the buffer.</exception> /// <returns>An updated buffer containing the remaining unacknowledged messages</returns> public AckedSendBuffer <T> Acknowledge(Ack ack) { if (ack.CumulativeAck > MaxSeq) { throw new ArgumentException(nameof(ack), $"Highest SEQ so far was {MaxSeq} but cumulative ACK is {ack.CumulativeAck}"); } var newNacked = ack.Nacks.Count == 0 ? ImmutableList <T> .Empty : Nacked.AddRange(NonAcked).Where(x => ack.Nacks.Contains(x.Seq)).ToImmutableList(); if (newNacked.Count < ack.Nacks.Count) { throw new ResendUnfulfillableException(); } else { return(Copy(nonAcked: NonAcked.Where(x => x.Seq > ack.CumulativeAck).ToImmutableList(), nacked: newNacked)); } }
/// <summary> /// Extract all messages that could be safely delivered, an updated ack to be sent to the sender(), and /// an updated buffer that has the messages removed that can be delivered. /// </summary> /// <returns>Triplet of the updated buffer, messages that can be delivered, and the updated acknowledgement.</returns> public AckReceiveDeliverable <T> ExtractDeliverable() { var deliver = new List <T>(); var ack = new Ack(CumulativeAck); var updatedLastDelivered = LastDelivered; var prev = LastDelivered; foreach (var bufferedMessage in Buf) { if (bufferedMessage.Seq.IsSuccessor(updatedLastDelivered)) { deliver.Add(bufferedMessage); updatedLastDelivered = updatedLastDelivered.Inc(); } else if (!bufferedMessage.Seq.IsSuccessor(prev)) { var nacks = new HashSet <SeqNo>(); unchecked //in Java, there are no overflow / underflow exceptions so the value rolls over. We have to explicitly squelch those errors in .NET { var diff = Math.Abs(bufferedMessage.Seq.RawValue - prev.RawValue - 1); // Collect all missing sequence numbers (gaps) while (diff > 0) { nacks.Add(prev.RawValue + diff); diff--; } } ack = new Ack(CumulativeAck, ack.Nacks.Concat(nacks)); } prev = bufferedMessage.Seq; } var newBuf = !deliver.Any() ? Buf : Buf.Except(deliver); return(new AckReceiveDeliverable <T>(Copy(lastDelivered: updatedLastDelivered, buffer: newBuf), deliver, ack)); }
/// <summary> /// TBD /// </summary> /// <param name="buffer">TBD</param> /// <param name="deliverables">TBD</param> /// <param name="ack">TBD</param> public AckReceiveDeliverable(AckedReceiveBuffer <T> buffer, List <T> deliverables, Ack ack) { Ack = ack; Deliverables = deliverables; Buffer = buffer; }