/// <summary> /// Process the next packet to be sent on <see cref="channelId"/>. Return true /// if there was a packet to be processed on the channel, or false otherwise. /// </summary> /// <param name="channelId">the channel to process</param> /// <param name="csme">accumulated errors/exceptions</param> /// <returns>true if a packet was processed on the channel, or false if there are no packets /// to process or some error/exception arose</returns> protected virtual bool ProcessNextPacket(byte channelId, CannotSendMessagesError csme) { ChannelSendingState cs = default(ChannelSendingState); if (!FindNextPacket(channelId, csme, ref cs)) { return false; } Debug.Assert(cs.MarshalledForm != null && cs.MarshalledForm.HasPackets); // we could do something here to check if the transport was backlogged... TransportPacket tp; if(!packetsInProgress.TryGetValue(cs.Transport, out tp) || tp == null) { tp = packetsInProgress[cs.Transport] = new TransportPacket(); } if (!sentMessages.ContainsKey(cs.Transport)) { sentMessages[cs.Transport] = new List<Message>(); } if (!messagesInProgress.ContainsKey(cs.Transport)) { messagesInProgress[cs.Transport] = new List<Message>(); } TransportPacket next = cs.MarshalledForm.RemovePacket(); if (tp.Length + next.Length >= cs.Transport.MaximumPacketSize) { try { cnx.SendPacket(cs.Transport, tp); } catch(TransportError e) { csme.AddAll(e, sentMessages[cs.Transport]); csme.AddAll(e, messagesInProgress[cs.Transport]); sentMessages[cs.Transport].Clear(); messagesInProgress[cs.Transport].Clear(); packetsInProgress.Remove(cs.Transport); return true; } NotifyMessagesSent(sentMessages[cs.Transport], cs.Transport); sentMessages[cs.Transport].Clear(); packetsInProgress[cs.Transport] = tp = next; } else { tp.Append(next, 0, next.Length); next.Dispose(); } if (cs.MarshalledForm.Finished) { messagesInProgress[cs.Transport].Remove(cs.PendingMessage.Message); sentMessages[cs.Transport].Add(cs.PendingMessage.Message); pmPool.Return(cs.PendingMessage); cs.PendingMessage = null; cs.MarshalledForm.Dispose(); cs.MarshalledForm = null; } else { messagesInProgress[cs.Transport].Add(cs.PendingMessage.Message); } return true; }
protected virtual void FlushPendingPackets(CannotSendMessagesError csme) { // And send any pending stuff foreach (ITransport t in packetsInProgress.Keys) { TransportPacket tp; if(!packetsInProgress.TryGetValue(t, out tp) || tp == null || tp.Length == 0) { continue; } try { cnx.SendPacket(t, tp); NotifyMessagesSent(sentMessages[t], t); sentMessages[t].Clear(); } catch(TransportError e) { csme.AddAll(e, sentMessages[t]); csme.AddAll(e, messagesInProgress[t]); } if (disposed) { return; } } packetsInProgress.Clear(); }
public override void FlushChannelMessages(byte channelId) { int channelIndex; if(!channelIndices.TryGetValue(channelId, out channelIndex)) { return; } CannotSendMessagesError csme = new CannotSendMessagesError(cnx); // Process all the packets on the channel while (ProcessNextPacket(channelId, csme)) { /* keep going */ } FlushPendingPackets(csme); if (csme.IsApplicable) { NotifyError(new ErrorSummary(Severity.Warning, SummaryErrorCode.MessagesCannotBeSent, "Unable to send messages", csme)); } //Debug.Assert(messagesInProgress.Count == 0); Debug.Assert(packetsInProgress.Count == 0); }
/// <summary> /// Find the next packet to be processed; return true if there are packets to /// process on channelId. The channel sending state is returned in <see cref="cs"/>. /// </summary> /// <param name="channelId"></param> /// <param name="csme"></param> /// <param name="cs"></param> /// <returns></returns> protected virtual bool FindNextPacket(byte channelId, CannotSendMessagesError csme, ref ChannelSendingState cs) { if (channelSendingStates.TryGetValue(channelId, out cs)) { if (cs.MarshalledForm != null) { if(!cs.MarshalledForm.Finished) { return true; } pmPool.Return(cs.PendingMessage); cs.PendingMessage = null; cs.MarshalledForm.Dispose(); cs.MarshalledForm = null; } } else { channelSendingStates[channelId] = cs = new ChannelSendingState(); } PendingMessage pm; while (DetermineNextPendingMessage(channelId, out pm)) { try { ITransport transport = cnx.FindTransport(pm.MDR, pm.CDR); IMarshalledResult mr = cnx.Marshal(pm.Message, transport); Debug.Assert(!mr.Finished, "Marshallers shouldn't produce an empty result"); if (mr.Finished) { // this shouldn't happen mr.Dispose(); pmPool.Return(pm); continue; } cs.MarshalledForm = mr; cs.PendingMessage = pm; cs.Transport = transport; return true; } catch(NoMatchingTransport e) { csme.Add(e, pm); continue; } catch (MarshallingException e) { csme.Add(e, pm); continue; } } // There were no messages for channelId. So remove it from contention // and advance the nextChannelIndex to the next channel (actually, by // virtue of removing this channel, nextChannelIndex does point to the // next channel, so we only need to check for wrapping) channels.Remove(channelId); channelIndices.Remove(channelId); channelSendingStates.Remove(channelId); cs = null; if (nextChannelIndex >= channels.Count) { nextChannelIndex = 0; } return false; }
public override void Flush() { CannotSendMessagesError csme = new CannotSendMessagesError(cnx); while (channels.Count > 0) { byte channelId = channels[nextChannelIndex]; if(ProcessNextPacket(channelId, csme) && channels.Count > 0) { nextChannelIndex = (nextChannelIndex + 1) % channels.Count; } } FlushPendingPackets(csme); if (csme.IsApplicable) { NotifyError(new ErrorSummary(Severity.Warning, SummaryErrorCode.MessagesCannotBeSent, "Unable to send messages", csme)); } Debug.Assert(channels.Count == 0); Debug.Assert(pending.Count == 0); //Debug.Assert(messagesInProgress.Count == 0); Debug.Assert(packetsInProgress.Count == 0); }