/// <summary> /// Does an early check to see if this queue empty callback is already /// running, then asynchronously firing the event /// </summary> /// <param name="categories">Throttle categories to fire the callback for</param> private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) { // if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) if (!m_isQueueEmptyRunning && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) { m_isQueueEmptyRunning = true; int start = Environment.TickCount & Int32.MaxValue; const int MIN_CALLBACK_MS = 30; m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; if (m_nextOnQueueEmpty == 0) { m_nextOnQueueEmpty = 1; } // Use a value of 0 to signal that FireQueueEmpty is running // m_nextOnQueueEmpty = 0; m_categories = categories; if (HasUpdates(m_categories)) { // Asynchronously run the callback Util.FireAndForget(FireQueueEmpty, categories); } else { m_isQueueEmptyRunning = false; } } }
public bool QueueRequest(LLUDPClient client, ThrottleOutPacketTypeFlags categories) { if (m_requestQueue.Count < m_requestQueue.BoundedCapacity) { // m_log.DebugFormat( // "[OUTGOING QUEUE REFILL ENGINE]: Adding request for categories {0} for {1} in {2}", // categories, client.AgentID, m_udpServer.Scene.Name); m_requestQueue.Add(new RefillRequest(client, categories)); if (!m_warnOverMaxQueue) { m_warnOverMaxQueue = true; } return(true); } else { if (m_warnOverMaxQueue) { m_log.WarnFormat( "[OUTGOING QUEUE REFILL ENGINE]: Request queue at maximum capacity, not recording request from {0} in {1}", client.AgentID, m_udpServer.Scene.Name); m_warnOverMaxQueue = false; } return(false); } }
/// <summary> /// Fires the OnQueueEmpty callback and sets the minimum time that it /// can be called again /// </summary> /// <param name="o">Throttle categories to fire the callback for, /// stored as an object to match the WaitCallback delegate /// signature</param> private void FireQueueEmpty(object o) { // int start = Environment.TickCount & Int32.MaxValue; // const int MIN_CALLBACK_MS = 30; // if (m_udpServer.IsRunningOutbound) // { ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; QueueEmpty callback = OnQueueEmpty; if (callback != null) { // if (m_udpServer.IsRunningOutbound) // { try { callback(categories); } catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } // } } // } // m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; // if (m_nextOnQueueEmpty == 0) // m_nextOnQueueEmpty = 1; // } m_isQueueEmptyRunning = false; }
/// <summary> /// Does an early check to see if this queue empty callback is already /// running, then asynchronously firing the event /// </summary> /// <param name="categories">Throttle categories to fire the callback for</param> private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) { if (!m_isQueueEmptyRunning) { if (!HasUpdates(categories)) { return; } double start = Util.GetTimeStampMS(); if (start < m_nextOnQueueEmpty) { return; } m_isQueueEmptyRunning = true; m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; // Asynchronously run the callback if (m_udpServer.OqrEngine.IsRunning) { m_udpServer.OqrEngine.QueueJob(AgentID.ToString(), () => FireQueueEmpty(categories)); } else { Util.FireAndForget(FireQueueEmpty, categories, "LLUDPClient.BeginFireQueueEmpty"); } } }
/// <summary> /// Does an early check to see if this queue empty callback is already /// running, then asynchronously firing the event /// </summary> /// <param name="categories">Throttle categories to fire the callback for</param> private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) { int start = Environment.TickCount; if (!m_isQueueEmptyRunning && start - m_nextOnQueueEmpty >= 0) { m_isQueueEmptyRunning = true; const int MIN_CALLBACK_MS = 30; m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; m_categories = categories; if (HasUpdates(m_categories)) { // Asynchronously run the callback Util.FireAndForget(FireQueueEmpty, categories); } else { m_isQueueEmptyRunning = false; } } }
/// <summary> /// Does an early check to see if this queue empty callback is already /// running, then asynchronously firing the event /// </summary> /// <param name="throttleIndex">Throttle category to fire the callback /// for</param> private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) { if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) { // Use a value of 0 to signal that FireQueueEmpty is running m_nextOnQueueEmpty = 0; // Asynchronously run the callback Util.FireAndForget(FireQueueEmpty, categories); } }
/// <summary> /// Fires the OnQueueEmpty callback and sets the minimum time that it /// can be called again /// </summary> /// <param name="o">Throttle categories to fire the callback for, /// stored as an object to match the WaitCallback delegate /// signature</param> private void FireQueueEmpty(object o) { ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; QueueEmpty callback = OnQueueEmpty; if (callback != null) { try { callback(categories); } catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } } m_isQueueEmptyRunning = false; }
/// <summary> /// Does an early check to see if this queue empty callback is already /// running, then asynchronously firing the event /// </summary> /// <param name="categories">Throttle categories to fire the callback for</param> private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) { if (!QueueEmptyRunning && HasUpdates(categories) && OnQueueEmpty != null) { double start = Util.GetTimeStampMS(); if (start < m_nextOnQueueEmpty) { return; } QueueEmptyRunning = true; m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; // Asynchronously run the callback if (m_udpServer.OqrEngine.IsRunning) { LLUDPClient udpcli = this; ThrottleOutPacketTypeFlags cats = categories; Action <LLUDPClient, ThrottleOutPacketTypeFlags> act = delegate { QueueEmpty callback = udpcli.OnQueueEmpty; if (callback != null) { try { callback(cats); } catch { } if (callback != null) { udpcli.QueueEmptyRunning = false; } } udpcli = null; callback = null; }; m_udpServer.OqrEngine.QueueJob(AgentID.ToString(), () => act(udpcli, cats)); } else { Util.FireAndForget(FireQueueEmpty, categories, "LLUDPClient.BeginFireQueueEmpty"); } } }
/// <summary> /// Fires the OnQueueEmpty callback and sets the minimum time that it /// can be called again /// </summary> /// <param name="o">Throttle categories to fire the callback for, /// stored as an object to match the WaitCallback delegate /// signature</param> private void FireQueueEmpty(object o) { const int MIN_CALLBACK_MS = 30; ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; QueueEmpty callback = OnQueueEmpty; int start = Environment.TickCount & Int32.MaxValue; if (callback != null) { try { callback(categories); } catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } } m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; if (m_nextOnQueueEmpty == 0) m_nextOnQueueEmpty = 1; }
/// <summary> /// Does an early check to see if this queue empty callback is already /// running, then asynchronously firing the event /// </summary> /// <param name="categories">Throttle categories to fire the callback for</param> private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) { if (!m_isQueueEmptyRunning) { int start = Environment.TickCount & Int32.MaxValue; if (start < m_nextOnQueueEmpty) { return; } m_isQueueEmptyRunning = true; m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; if (m_nextOnQueueEmpty == 0) { m_nextOnQueueEmpty = 1; } if (HasUpdates(categories)) { if (!m_udpServer.OqrEngine.IsRunning) { // Asynchronously run the callback Util.FireAndForget(FireQueueEmpty, categories, "LLUDPClient.BeginFireQueueEmpty"); } else { m_udpServer.OqrEngine.QueueJob(AgentID.ToString(), () => FireQueueEmpty(categories)); } } else { m_isQueueEmptyRunning = false; } } }
/// <summary> /// Loops through all of the packet queues for this client and tries to send /// an outgoing packet from each, obeying the throttling bucket limits /// </summary> /// /// <remarks> /// Packet queues are inspected in ascending numerical order starting from 0. Therefore, queues with a lower /// ThrottleOutPacketType number will see their packet get sent first (e.g. if both Land and Wind queues have /// packets, then the packet at the front of the Land queue will be sent before the packet at the front of the /// wind queue). /// /// This function is only called from a synchronous loop in the /// UDPServer so we don't need to bother making this thread safe /// </remarks> /// /// <returns>True if any packets were sent, otherwise false</returns> public bool DequeueOutgoing() { // if (m_deliverPackets == false) return false; OutgoingPacket packet = null; DoubleLocklessQueue <OutgoingPacket> queue; TokenBucket bucket; bool packetSent = false; ThrottleOutPacketTypeFlags emptyCategories = 0; //string queueDebugOutput = String.Empty; // Serious debug business for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { bucket = m_throttleCategories[i]; //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business if (m_nextPackets[i] != null) { // This bucket was empty the last time we tried to send a packet, // leaving a dequeued packet still waiting to be sent out. Try to // send it again OutgoingPacket nextPacket = m_nextPackets[i]; if (nextPacket.Buffer == null) { if (m_packetOutboxes[i].Count < 5) { emptyCategories |= CategoryToFlag(i); } continue; } if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(nextPacket); m_nextPackets[i] = null; packetSent = true; if (m_packetOutboxes[i].Count < 5) { emptyCategories |= CategoryToFlag(i); } } } else { // No dequeued packet waiting to be sent, try to pull one off // this queue queue = m_packetOutboxes[i]; if (queue != null) { bool success = false; try { success = queue.Dequeue(out packet); } catch { m_packetOutboxes[i] = new DoubleLocklessQueue <OutgoingPacket>(); } if (success) { // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (packet.Buffer == null) { // packet canceled elsewhere (by a ack for example) if (queue.Count < 5) { emptyCategories |= CategoryToFlag(i); } } else { if (bucket.RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; if (queue.Count < 5) { emptyCategories |= CategoryToFlag(i); } } else { // Save the dequeued packet for the next iteration m_nextPackets[i] = packet; } } } else { // No packets in this queue. Fire the queue empty callback // if it has not been called recently emptyCategories |= CategoryToFlag(i); } } else { m_packetOutboxes[i] = new DoubleLocklessQueue <OutgoingPacket>(); emptyCategories |= CategoryToFlag(i); } } } if (emptyCategories != 0) { BeginFireQueueEmpty(emptyCategories); } //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business return(packetSent); }
public bool QueueRequest(LLUDPClient client, ThrottleOutPacketTypeFlags categories) { if (m_requestQueue.Count < m_requestQueue.BoundedCapacity) { // m_log.DebugFormat( // "[OUTGOING QUEUE REFILL ENGINE]: Adding request for categories {0} for {1} in {2}", // categories, client.AgentID, m_udpServer.Scene.Name); m_requestQueue.Add(new RefillRequest(client, categories)); if (!m_warnOverMaxQueue) m_warnOverMaxQueue = true; return true; } else { if (m_warnOverMaxQueue) { m_log.WarnFormat( "[OUTGOING QUEUE REFILL ENGINE]: Request queue at maximum capacity, not recording request from {0} in {1}", client.AgentID, m_udpServer.Scene.Name); m_warnOverMaxQueue = false; } return false; } }
/// <summary> /// Loops through all of the packet queues for this client and tries to send /// an outgoing packet from each, obeying the throttling bucket limits /// </summary> /// /// <remarks> /// Packet queues are inspected in ascending numerical order starting from 0. Therefore, queues with a lower /// ThrottleOutPacketType number will see their packet get sent first (e.g. if both Land and Wind queues have /// packets, then the packet at the front of the Land queue will be sent before the packet at the front of the /// wind queue). /// /// This function is only called from a synchronous loop in the /// UDPServer so we don't need to bother making this thread safe /// </remarks> /// /// <returns>True if any packets were sent, otherwise false</returns> public bool DequeueOutgoing() { OutgoingPacket packet; OpenSim.Framework.LocklessQueue <OutgoingPacket> queue; TokenBucket bucket; bool packetSent = false; ThrottleOutPacketTypeFlags emptyCategories = 0; //string queueDebugOutput = String.Empty; // Serious debug business for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { bucket = m_throttleCategories[i]; //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business if (m_nextPackets[i] != null) { // This bucket was empty the last time we tried to send a packet, // leaving a dequeued packet still waiting to be sent out. Try to // send it again OutgoingPacket nextPacket = m_nextPackets[i]; if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(nextPacket); m_nextPackets[i] = null; packetSent = true; } } else { // No dequeued packet waiting to be sent, try to pull one off // this queue queue = m_packetOutboxes[i]; if (queue.Dequeue(out packet)) { // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (bucket.RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; } else { // Save the dequeued packet for the next iteration m_nextPackets[i] = packet; } // If the queue is empty after this dequeue, fire the queue // empty callback now so it has a chance to fill before we // get back here if (queue.Count == 0) { emptyCategories |= CategoryToFlag(i); } } else { // No packets in this queue. Fire the queue empty callback // if it has not been called recently emptyCategories |= CategoryToFlag(i); } } } if (emptyCategories != 0) { BeginFireQueueEmpty(emptyCategories); } //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business return(packetSent); }
/// <summary> /// Does an early check to see if this queue empty callback is already /// running, then asynchronously firing the event /// </summary> /// <param name="categories">Throttle categories to fire the callback for</param> private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) { // if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) if (!m_isQueueEmptyRunning && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) { m_isQueueEmptyRunning = true; int start = Environment.TickCount & Int32.MaxValue; const int MIN_CALLBACK_MS = 30; m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; if (m_nextOnQueueEmpty == 0) m_nextOnQueueEmpty = 1; // Use a value of 0 to signal that FireQueueEmpty is running // m_nextOnQueueEmpty = 0; m_categories = categories; if (HasUpdates(m_categories)) { if (!m_udpServer.OqrEngine.IsRunning) { // Asynchronously run the callback Util.FireAndForget(FireQueueEmpty, categories, "LLUDPClient.BeginFireQueueEmpty"); } else { m_udpServer.OqrEngine.QueueJob(AgentID.ToString(), () => FireQueueEmpty(categories)); } } else { m_isQueueEmptyRunning = false; } } }
public RefillRequest(LLUDPClient client, ThrottleOutPacketTypeFlags categories) { Client = client; Categories = categories; }
/// <summary> /// Loops through all of the packet queues for this client and tries to send /// an outgoing packet from each, obeying the throttling bucket limits /// </summary> /// /// Packet queues are inspected in ascending numerical order starting from 0. Therefore, queues with a lower /// ThrottleOutPacketType number will see their packet get sent first (e.g. if both Land and Wind queues have /// packets, then the packet at the front of the Land queue will be sent before the packet at the front of the /// wind queue). /// /// <remarks>This function is only called from a synchronous loop in the /// UDPServer so we don't need to bother making this thread safe</remarks> /// <returns>True if any packets were sent, otherwise false</returns> public bool DequeueOutgoing (int MaxNPacks) { OutgoingPacket packet; bool packetSent = false; ThrottleOutPacketTypeFlags emptyCategories = 0; if (m_nextOutPackets != null) { OutgoingPacket nextPacket = m_nextOutPackets; if (m_throttle.RemoveTokens (nextPacket.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal (nextPacket); m_nextOutPackets = null; packetSent = true; this.PacketsSent++; } } else { // No dequeued packet waiting to be sent, try to pull one off // this queue if (m_outbox.Dequeue (out packet)) { // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (packet.Category == ThrottleOutPacketType.OutBand || m_throttle.RemoveTokens (packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal (packet); packetSent = true; if (m_throttle.MaxBurst < TotalRateRequested) { float tmp = (float)m_throttle.MaxBurst * 1.005f; m_throttle.DripRate = (int)tmp; m_throttle.MaxBurst = (int)tmp; } this.PacketsSent++; } else { m_nextOutPackets = packet; } } else { emptyCategories = (ThrottleOutPacketTypeFlags)0xffff; } } if (m_outbox.count < 100) { emptyCategories = (ThrottleOutPacketTypeFlags)0xffff; BeginFireQueueEmpty (emptyCategories); } /* if (emptyCategories != 0) BeginFireQueueEmpty(emptyCategories); else { int i = MapCatsToPriority[(int)ThrottleOutPacketType.Texture]; // hack to keep textures flowing for now if (m_outbox.queues[i].Count < 30) { emptyCategories |= ThrottleOutPacketTypeFlags.Texture; } } */ //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business return packetSent; /* OutgoingPacket packet; OpenSim.Framework.LocklessQueue<OutgoingPacket> queue; TokenBucket bucket; bool packetSent = false; ThrottleOutPacketTypeFlags emptyCategories = 0; //string queueDebugOutput = String.Empty; // Serious debug business int npacksTosent = MaxNPacks; int i = m_lastthrottleCategoryChecked; for (int j = 0; j < (int)ThrottleOutPacketType.OutBand; j++) // don't check OutBand { bucket = m_throttleCategories[i]; //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business if (m_nextPackets[i] != null) { // This bucket was empty the last time we tried to send a packet, // leaving a dequeued packet still waiting to be sent out. Try to // send it again OutgoingPacket nextPacket = m_nextPackets[i]; if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(nextPacket); m_nextPackets[i] = null; packetSent = true; this.PacketsSent++; } } else { // No dequeued packet waiting to be sent, try to pull one off // this queue queue = m_packetOutboxes[i]; if (queue.Dequeue(out packet)) { // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (bucket.RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; this.PacketsSent++; } else { // Save the dequeued packet for the next iteration m_nextPackets[i] = packet; } // If the queue is empty after this dequeue, fire the queue // empty callback now so it has a chance to fill before we // get back here if (queue.Count == 0) emptyCategories |= CategoryToFlag(i); } else { // No packets in this queue. Fire the queue empty callback // if it has not been called recently emptyCategories |= CategoryToFlag(i); } } if (++i >= (int)ThrottleOutPacketType.OutBand) i = 0; if (--npacksTosent <= 0) break; } m_lastthrottleCategoryChecked = i; // send at least one outband packet if (npacksTosent <= 0) npacksTosent = 1; i = (int)ThrottleOutPacketType.OutBand; while (npacksTosent > 0) { //outband has no tokens checking and no throttle // No dequeued packet waiting to be sent, try to pull one off // this queue queue = m_packetOutboxes[i]; if (queue.Dequeue(out packet)) { // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out // if (bucket.RemoveTokens(packet.Buffer.DataLength)) // { // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; this.PacketsSent++; // } // else // { // Save the dequeued packet for the next iteration // m_nextPackets[i] = packet; // } // If the queue is empty after this dequeue, fire the queue // empty callback now so it has a chance to fill before we // get back here if (queue.Count == 0) emptyCategories |= CategoryToFlag(i); } else { // No packets in this queue. Fire the queue empty callback // if it has not been called recently emptyCategories |= CategoryToFlag(i); break; } npacksTosent--; } if (emptyCategories != 0) BeginFireQueueEmpty(emptyCategories); //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business return packetSent; */ }
/// <summary> /// Loops through all of the packet queues for this client and tries to send /// an outgoing packet from each, obeying the throttling bucket limits /// </summary> /// /// <remarks> /// Packet queues are inspected in ascending numerical order starting from 0. Therefore, queues with a lower /// ThrottleOutPacketType number will see their packet get sent first (e.g. if both Land and Wind queues have /// packets, then the packet at the front of the Land queue will be sent before the packet at the front of the /// wind queue). /// /// This function is only called from a synchronous loop in the /// UDPServer so we don't need to bother making this thread safe /// </remarks> /// /// <returns>True if any packets were sent, otherwise false</returns> public bool DequeueOutgoing() { // if (m_deliverPackets == false) return false; OutgoingPacket packet = null; DoubleLocklessQueue <OutgoingPacket> queue; bool packetSent = false; ThrottleOutPacketTypeFlags emptyCategories = 0; //string queueDebugOutput = String.Empty; // Serious debug business // do resends packet = m_nextPackets[0]; if (packet != null) { if (packet.Buffer != null) { if (m_throttleCategories[0].RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; m_nextPackets[0] = null; } } else { m_nextPackets[0] = null; } } else { queue = m_packetOutboxes[0]; if (queue != null) { if (queue.Dequeue(out packet)) { // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (packet.Buffer != null) { if (m_throttleCategories[0].RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; } else { // Save the dequeued packet for the next iteration m_nextPackets[0] = packet; } } } } else { m_packetOutboxes[0] = new DoubleLocklessQueue <OutgoingPacket>(); } } if (NeedAcks.Count() > 50) { Interlocked.Increment(ref AckStalls); return(true); } for (int i = 1; i < THROTTLE_CATEGORY_COUNT; i++) { //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business packet = m_nextPackets[i]; if (packet != null) { if (packet.Buffer == null) { if (m_packetOutboxes[i].Count < 5) { emptyCategories |= CategoryToFlag(i); } m_nextPackets[i] = null; continue; } if (m_throttleCategories[i].RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); m_nextPackets[i] = null; packetSent = true; if (m_packetOutboxes[i].Count < 5) { emptyCategories |= CategoryToFlag(i); } } } else { // No dequeued packet waiting to be sent, try to pull one off // this queue queue = m_packetOutboxes[i]; if (queue.Dequeue(out packet)) { if (packet.Buffer == null) { // packet canceled elsewhere (by a ack for example) if (queue.Count < 5) { emptyCategories |= CategoryToFlag(i); } continue; } if (m_throttleCategories[i].RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; if (queue.Count < 5) { emptyCategories |= CategoryToFlag(i); } } else { // Save the dequeued packet for the next iteration m_nextPackets[i] = packet; } } else { // No packets in this queue. Fire the queue empty callback // if it has not been called recently emptyCategories |= CategoryToFlag(i); } } } if (emptyCategories != 0) { BeginFireQueueEmpty(emptyCategories); } //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business return(packetSent); }