aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs11
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs33
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs143
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs147
4 files changed, 126 insertions, 208 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 38bbce0..997f38c 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -1228,10 +1228,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1228 StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck); 1228 StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck);
1229 pc.Header.Reliable = false; 1229 pc.Header.Reliable = false;
1230 1230
1231 OutgoingPacket oldestPacket = m_udpClient.NeedAcks.GetOldest();
1232
1233 pc.PingID.PingID = seq; 1231 pc.PingID.PingID = seq;
1234 pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0; 1232 // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit
1233 pc.PingID.OldestUnacked = 0;
1235 1234
1236 OutPacket(pc, ThrottleOutPacketType.Unknown); 1235 OutPacket(pc, ThrottleOutPacketType.Unknown);
1237 } 1236 }
@@ -3320,8 +3319,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3320 // If we received an update about our own avatar, process the avatar update priority queue immediately 3319 // If we received an update about our own avatar, process the avatar update priority queue immediately
3321 if (data.AgentID == m_agentId) 3320 if (data.AgentID == m_agentId)
3322 ProcessAvatarTerseUpdates(); 3321 ProcessAvatarTerseUpdates();
3323 else
3324 m_udpServer.SignalOutgoingPacketHandler();
3325 } 3322 }
3326 3323
3327 private void ProcessAvatarTerseUpdates() 3324 private void ProcessAvatarTerseUpdates()
@@ -3409,8 +3406,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3409 3406
3410 lock (m_primFullUpdates.SyncRoot) 3407 lock (m_primFullUpdates.SyncRoot)
3411 m_primFullUpdates.Enqueue(data.priority, objectData, data.localID); 3408 m_primFullUpdates.Enqueue(data.priority, objectData, data.localID);
3412
3413 m_udpServer.SignalOutgoingPacketHandler();
3414 } 3409 }
3415 3410
3416 void ProcessPrimFullUpdates() 3411 void ProcessPrimFullUpdates()
@@ -3454,8 +3449,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3454 3449
3455 lock (m_primTerseUpdates.SyncRoot) 3450 lock (m_primTerseUpdates.SyncRoot)
3456 m_primTerseUpdates.Enqueue(data.Priority, objectData, data.LocalID); 3451 m_primTerseUpdates.Enqueue(data.Priority, objectData, data.LocalID);
3457
3458 m_udpServer.SignalOutgoingPacketHandler();
3459 } 3452 }
3460 3453
3461 void ProcessPrimTerseUpdates() 3454 void ProcessPrimTerseUpdates()
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index ca9925c..458e78d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -202,7 +202,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
202 public void Shutdown() 202 public void Shutdown()
203 { 203 {
204 IsConnected = false; 204 IsConnected = false;
205 NeedAcks.Clear();
206 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 205 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
207 { 206 {
208 m_packetOutboxes[i].Clear(); 207 m_packetOutboxes[i].Clear();
@@ -394,7 +393,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
394 { 393 {
395 // Not enough tokens in the bucket, queue this packet 394 // Not enough tokens in the bucket, queue this packet
396 queue.Enqueue(packet); 395 queue.Enqueue(packet);
397 m_udpServer.SignalOutgoingPacketHandler();
398 return true; 396 return true;
399 } 397 }
400 } 398 }
@@ -411,15 +409,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
411 /// </summary> 409 /// </summary>
412 /// <remarks>This function is only called from a synchronous loop in the 410 /// <remarks>This function is only called from a synchronous loop in the
413 /// UDPServer so we don't need to bother making this thread safe</remarks> 411 /// UDPServer so we don't need to bother making this thread safe</remarks>
414 /// <returns>The minimum amount of time before the next packet 412 /// <returns>True if any packets were sent, otherwise false</returns>
415 /// can be sent to this client</returns> 413 public bool DequeueOutgoing()
416 public int DequeueOutgoing()
417 { 414 {
418 OutgoingPacket packet; 415 OutgoingPacket packet;
419 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue; 416 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
420 TokenBucket bucket; 417 TokenBucket bucket;
421 int dataLength; 418 bool packetSent = false;
422 int minTimeout = Int32.MaxValue;
423 419
424 //string queueDebugOutput = String.Empty; // Serious debug business 420 //string queueDebugOutput = String.Empty; // Serious debug business
425 421
@@ -434,18 +430,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
434 // leaving a dequeued packet still waiting to be sent out. Try to 430 // leaving a dequeued packet still waiting to be sent out. Try to
435 // send it again 431 // send it again
436 OutgoingPacket nextPacket = m_nextPackets[i]; 432 OutgoingPacket nextPacket = m_nextPackets[i];
437 dataLength = nextPacket.Buffer.DataLength; 433 if (bucket.RemoveTokens(nextPacket.Buffer.DataLength))
438 if (bucket.RemoveTokens(dataLength))
439 { 434 {
440 // Send the packet 435 // Send the packet
441 m_udpServer.SendPacketFinal(nextPacket); 436 m_udpServer.SendPacketFinal(nextPacket);
442 m_nextPackets[i] = null; 437 m_nextPackets[i] = null;
443 minTimeout = 0; 438 packetSent = true;
444 }
445 else if (minTimeout != 0)
446 {
447 // Check the minimum amount of time we would have to wait before this packet can be sent out
448 minTimeout = Math.Min(minTimeout, ((dataLength - bucket.Content) / bucket.DripPerMS) + 1);
449 } 439 }
450 } 440 }
451 else 441 else
@@ -457,23 +447,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
457 { 447 {
458 // A packet was pulled off the queue. See if we have 448 // A packet was pulled off the queue. See if we have
459 // enough tokens in the bucket to send it out 449 // enough tokens in the bucket to send it out
460 dataLength = packet.Buffer.DataLength; 450 if (bucket.RemoveTokens(packet.Buffer.DataLength))
461 if (bucket.RemoveTokens(dataLength))
462 { 451 {
463 // Send the packet 452 // Send the packet
464 m_udpServer.SendPacketFinal(packet); 453 m_udpServer.SendPacketFinal(packet);
465 minTimeout = 0; 454 packetSent = true;
466 } 455 }
467 else 456 else
468 { 457 {
469 // Save the dequeued packet for the next iteration 458 // Save the dequeued packet for the next iteration
470 m_nextPackets[i] = packet; 459 m_nextPackets[i] = packet;
471
472 if (minTimeout != 0)
473 {
474 // Check the minimum amount of time we would have to wait before this packet can be sent out
475 minTimeout = Math.Min(minTimeout, ((dataLength - bucket.Content) / bucket.DripPerMS) + 1);
476 }
477 } 460 }
478 461
479 // If the queue is empty after this dequeue, fire the queue 462 // If the queue is empty after this dequeue, fire the queue
@@ -492,7 +475,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
492 } 475 }
493 476
494 //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business 477 //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business
495 return minTimeout; 478 return packetSent;
496 } 479 }
497 480
498 /// <summary> 481 /// <summary>
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index 7d5c11e..a8ce102 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -121,12 +121,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
121 private int m_recvBufferSize; 121 private int m_recvBufferSize;
122 /// <summary>Flag to process packets asynchronously or synchronously</summary> 122 /// <summary>Flag to process packets asynchronously or synchronously</summary>
123 private bool m_asyncPacketHandling; 123 private bool m_asyncPacketHandling;
124 /// <summary>Track the minimum amount of time needed to send the next packet in the 124 /// <summary>Tracks whether or not a packet was sent each round so we know
125 /// OutgoingPacketHandler loop so we know when to sleep</summary> 125 /// whether or not to sleep</summary>
126 private int m_minTimeout = Int32.MaxValue; 126 private bool m_packetSent;
127 /// <summary>EventWaitHandle to signify the outgoing packet handler thread that
128 /// there is more work to do</summary>
129 private EventWaitHandle m_outgoingWaitHandle;
130 127
131 public Socket Server { get { return null; } } 128 public Socket Server { get { return null; } }
132 129
@@ -174,8 +171,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
174 171
175 base.Start(m_recvBufferSize, m_asyncPacketHandling); 172 base.Start(m_recvBufferSize, m_asyncPacketHandling);
176 173
177 m_outgoingWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
178
179 // Start the incoming packet processing thread 174 // Start the incoming packet processing thread
180 Thread incomingThread = new Thread(IncomingPacketHandler); 175 Thread incomingThread = new Thread(IncomingPacketHandler);
181 incomingThread.Name = "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")"; 176 incomingThread.Name = "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")";
@@ -190,8 +185,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
190 { 185 {
191 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); 186 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
192 base.Stop(); 187 base.Stop();
193
194 m_outgoingWaitHandle.Close();
195 } 188 }
196 189
197 public void AddScene(IScene scene) 190 public void AddScene(IScene scene)
@@ -374,10 +367,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
374 StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck); 367 StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck);
375 pc.Header.Reliable = false; 368 pc.Header.Reliable = false;
376 369
377 OutgoingPacket oldestPacket = udpClient.NeedAcks.GetOldest();
378
379 pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++; 370 pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++;
380 pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0; 371 // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit
372 pc.PingID.OldestUnacked = 0;
381 373
382 SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); 374 SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false);
383 } 375 }
@@ -397,39 +389,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
397 return; 389 return;
398 } 390 }
399 391
400 if (udpClient.NeedAcks.Count > 0) 392 // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO
393 List<OutgoingPacket> expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO);
394
395 if (expiredPackets != null)
401 { 396 {
402 // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO 397 m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO);
403 List<OutgoingPacket> expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO);
404 398
405 if (expiredPackets != null) 399 // Resend packets
400 for (int i = 0; i < expiredPackets.Count; i++)
406 { 401 {
407 m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID); 402 OutgoingPacket outgoingPacket = expiredPackets[i];
408 403
409 // Resend packets 404 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
410 for (int i = 0; i < expiredPackets.Count; i++) 405 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
411 {
412 OutgoingPacket outgoingPacket = expiredPackets[i];
413
414 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
415 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
416 406
417 // Set the resent flag 407 // Set the resent flag
418 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); 408 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
419 outgoingPacket.Category = ThrottleOutPacketType.Resend; 409 outgoingPacket.Category = ThrottleOutPacketType.Resend;
420 410
421 // The TickCount will be set to the current time when the packet 411 // The TickCount will be set to the current time when the packet
422 // is actually sent out again 412 // is actually sent out again
423 outgoingPacket.TickCount = 0; 413 outgoingPacket.TickCount = 0;
424 414
425 // Bump up the resend count on this packet 415 // Bump up the resend count on this packet
426 Interlocked.Increment(ref outgoingPacket.ResendCount); 416 Interlocked.Increment(ref outgoingPacket.ResendCount);
427 //Interlocked.Increment(ref Stats.ResentPackets); 417 //Interlocked.Increment(ref Stats.ResentPackets);
428 418
429 // Requeue or resend the packet 419 // Requeue or resend the packet
430 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) 420 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
431 SendPacketFinal(outgoingPacket); 421 SendPacketFinal(outgoingPacket);
432 }
433 } 422 }
434 } 423 }
435 } 424 }
@@ -577,11 +566,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
577 // Handle appended ACKs 566 // Handle appended ACKs
578 if (packet.Header.AppendedAcks && packet.Header.AckList != null) 567 if (packet.Header.AppendedAcks && packet.Header.AckList != null)
579 { 568 {
580 lock (udpClient.NeedAcks.SyncRoot) 569 for (int i = 0; i < packet.Header.AckList.Length; i++)
581 { 570 udpClient.NeedAcks.Remove(packet.Header.AckList[i], now, packet.Header.Resent);
582 for (int i = 0; i < packet.Header.AckList.Length; i++)
583 AcknowledgePacket(udpClient, packet.Header.AckList[i], now, packet.Header.Resent);
584 }
585 } 571 }
586 572
587 // Handle PacketAck packets 573 // Handle PacketAck packets
@@ -589,11 +575,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
589 { 575 {
590 PacketAckPacket ackPacket = (PacketAckPacket)packet; 576 PacketAckPacket ackPacket = (PacketAckPacket)packet;
591 577
592 lock (udpClient.NeedAcks.SyncRoot) 578 for (int i = 0; i < ackPacket.Packets.Length; i++)
593 { 579 udpClient.NeedAcks.Remove(ackPacket.Packets[i].ID, now, packet.Header.Resent);
594 for (int i = 0; i < ackPacket.Packets.Length; i++)
595 AcknowledgePacket(udpClient, ackPacket.Packets[i].ID, now, packet.Header.Resent);
596 }
597 580
598 // We don't need to do anything else with PacketAck packets 581 // We don't need to do anything else with PacketAck packets
599 return; 582 return;
@@ -734,21 +717,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
734 client.Close(); 717 client.Close();
735 } 718 }
736 719
737 private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend)
738 {
739 OutgoingPacket ackedPacket;
740 if (client.NeedAcks.RemoveUnsafe(ack, out ackedPacket) && !fromResend)
741 {
742 // Update stats
743 Interlocked.Add(ref client.UnackedBytes, -ackedPacket.Buffer.DataLength);
744
745 // Calculate the round-trip time for this packet and its ACK
746 int rtt = currentTime - ackedPacket.TickCount;
747 if (rtt > 0)
748 client.UpdateRoundTrip(rtt);
749 }
750 }
751
752 private void IncomingPacketHandler() 720 private void IncomingPacketHandler()
753 { 721 {
754 // Set this culture for the thread that incoming packets are received 722 // Set this culture for the thread that incoming packets are received
@@ -775,11 +743,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
775 packetInbox.Clear(); 743 packetInbox.Clear();
776 } 744 }
777 745
778 public bool SignalOutgoingPacketHandler()
779 {
780 return m_outgoingWaitHandle.Set();
781 }
782
783 private void OutgoingPacketHandler() 746 private void OutgoingPacketHandler()
784 { 747 {
785 // Set this culture for the thread that outgoing packets are sent 748 // Set this culture for the thread that outgoing packets are sent
@@ -790,28 +753,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
790 { 753 {
791 try 754 try
792 { 755 {
793 m_minTimeout = Int32.MaxValue; 756 m_packetSent = false;
794 757
795 // Handle outgoing packets, resends, acknowledgements, and pings for each 758 // Handle outgoing packets, resends, acknowledgements, and pings for each
796 // client. m_minTimeout will be set to 0 if more packets are waiting in the 759 // client. m_packetSent will be set to true if a packet is sent
797 // queues with bandwidth to spare, or the number of milliseconds we need to
798 // wait before at least one packet can be sent to a client
799 m_scene.ClientManager.ForEachSync(ClientOutgoingPacketHandler); 760 m_scene.ClientManager.ForEachSync(ClientOutgoingPacketHandler);
800 761
801 // Can't wait for a negative amount of time, and put a 100ms ceiling on our 762 // If a packet was sent, only do a minimum length context switch to allow
802 // maximum wait time 763 // other parts of the code to do work. If nothing was sent, sleep for the
803 m_minTimeout = Utils.Clamp(m_minTimeout, 0, 100); 764 // minimum amount of time before a token bucket could get more tokens
804 765 if (m_packetSent)
805 if (m_minTimeout > 0) 766 Thread.Sleep(0);
806 { 767 else
807 // Don't bother waiting for a shorter interval than our TickCountResolution 768 Thread.Sleep((int)TickCountResolution);
808 // since the token buckets wouldn't update anyways
809 m_minTimeout = Math.Max(m_minTimeout, (int)TickCountResolution);
810
811 // Wait for someone to signal that packets are ready to be sent, or for our
812 // sleep interval to expire
813 m_outgoingWaitHandle.WaitOne(m_minTimeout);
814 }
815 } 769 }
816 catch (Exception ex) 770 catch (Exception ex)
817 { 771 {
@@ -841,7 +795,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
841 if (udpClient.ElapsedMSOutgoingPacketHandler >= 100) 795 if (udpClient.ElapsedMSOutgoingPacketHandler >= 100)
842 { 796 {
843 ResendUnacked(udpClient); 797 ResendUnacked(udpClient);
844 udpClient.ElapsedMSOutgoingPacketHandler -= 100; 798 udpClient.ElapsedMSOutgoingPacketHandler = 0;
845 udpClient.Elapsed100MSOutgoingPacketHandler += 1; 799 udpClient.Elapsed100MSOutgoingPacketHandler += 1;
846 } 800 }
847 801
@@ -849,7 +803,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
849 if (udpClient.Elapsed100MSOutgoingPacketHandler >= 5) 803 if (udpClient.Elapsed100MSOutgoingPacketHandler >= 5)
850 { 804 {
851 SendAcks(udpClient); 805 SendAcks(udpClient);
852 udpClient.Elapsed100MSOutgoingPacketHandler -= 5; 806 udpClient.Elapsed100MSOutgoingPacketHandler = 0;
853 udpClient.Elapsed500MSOutgoingPacketHandler += 1; 807 udpClient.Elapsed500MSOutgoingPacketHandler += 1;
854 } 808 }
855 809
@@ -857,19 +811,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
857 if (udpClient.Elapsed500MSOutgoingPacketHandler >= 10) 811 if (udpClient.Elapsed500MSOutgoingPacketHandler >= 10)
858 { 812 {
859 SendPing(udpClient); 813 SendPing(udpClient);
860 udpClient.Elapsed500MSOutgoingPacketHandler -= 10; 814 udpClient.Elapsed500MSOutgoingPacketHandler = 0;
861 } 815 }
862 816
863 // Dequeue any outgoing packets that are within the throttle limits 817 // Dequeue any outgoing packets that are within the throttle limits
864 // and get the minimum time we would have to sleep before this client 818 if (udpClient.DequeueOutgoing())
865 // could send a packet out 819 m_packetSent = true;
866 int minTimeoutThisLoop = udpClient.DequeueOutgoing();
867
868 // Although this is not thread safe, it is cheaper than locking and the
869 // worst that will happen is we sleep for slightly longer than the
870 // minimum necessary interval
871 if (minTimeoutThisLoop < m_minTimeout)
872 m_minTimeout = minTimeoutThisLoop;
873 } 820 }
874 821
875 udpClient.TickLastOutgoingPacketHandler = thisTick; 822 udpClient.TickLastOutgoingPacketHandler = thisTick;
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
index 87c7df4..12f0c0a 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
@@ -37,97 +37,57 @@ namespace OpenSim.Region.ClientStack.LindenUDP
37 /// </summary> 37 /// </summary>
38 public sealed class UnackedPacketCollection 38 public sealed class UnackedPacketCollection
39 { 39 {
40 /// <summary>Synchronization primitive. A lock must be acquired on this
41 /// object before calling any of the unsafe methods</summary>
42 public object SyncRoot = new object();
43
44 /// <summary>Holds the actual unacked packet data, sorted by sequence number</summary>
45 private SortedDictionary<uint, OutgoingPacket> packets = new SortedDictionary<uint, OutgoingPacket>();
46
47 /// <summary>Gets the total number of unacked packets</summary>
48 public int Count { get { return packets.Count; } }
49
50 /// <summary> 40 /// <summary>
51 /// Default constructor 41 /// Holds information about a pending acknowledgement
52 /// </summary> 42 /// </summary>
53 public UnackedPacketCollection() 43 private struct PendingAck
54 { 44 {
55 } 45 /// <summary>Sequence number of the packet to remove</summary>
46 public uint SequenceNumber;
47 /// <summary>Environment.TickCount value when the remove was queued.
48 /// This is used to update round-trip times for packets</summary>
49 public int RemoveTime;
50 /// <summary>Whether or not this acknowledgement was attached to a
51 /// resent packet. If so, round-trip time will not be calculated</summary>
52 public bool FromResend;
56 53
57 /// <summary> 54 public PendingAck(uint sequenceNumber, int currentTime, bool fromResend)
58 /// Add an unacked packet to the collection
59 /// </summary>
60 /// <param name="packet">Packet that is awaiting acknowledgement</param>
61 /// <returns>True if the packet was successfully added, false if the
62 /// packet already existed in the collection</returns>
63 public bool Add(OutgoingPacket packet)
64 {
65 lock (SyncRoot)
66 { 55 {
67 if (!packets.ContainsKey(packet.SequenceNumber)) 56 SequenceNumber = sequenceNumber;
68 { 57 RemoveTime = currentTime;
69 packets.Add(packet.SequenceNumber, packet); 58 FromResend = fromResend;
70 return true;
71 }
72 return false;
73 } 59 }
74 } 60 }
75 61
76 /// <summary> 62 /// <summary>Holds the actual unacked packet data, sorted by sequence number</summary>
77 /// Removes a packet from the collection without attempting to obtain a 63 private SortedDictionary<uint, OutgoingPacket> m_packets = new SortedDictionary<uint, OutgoingPacket>();
78 /// lock first 64 /// <summary>Holds packets that need to be added to the unacknowledged list</summary>
79 /// </summary> 65 private LocklessQueue<OutgoingPacket> m_pendingAdds = new LocklessQueue<OutgoingPacket>();
80 /// <param name="sequenceNumber">Sequence number of the packet to remove</param> 66 /// <summary>Holds information about pending acknowledgements</summary>
81 /// <returns>True if the packet was found and removed, otherwise false</returns> 67 private LocklessQueue<PendingAck> m_pendingRemoves = new LocklessQueue<PendingAck>();
82 public bool RemoveUnsafe(uint sequenceNumber)
83 {
84 return packets.Remove(sequenceNumber);
85 }
86
87 /// <summary>
88 /// Removes a packet from the collection without attempting to obtain a
89 /// lock first
90 /// </summary>
91 /// <param name="sequenceNumber">Sequence number of the packet to remove</param>
92 /// <param name="packet">Returns the removed packet</param>
93 /// <returns>True if the packet was found and removed, otherwise false</returns>
94 public bool RemoveUnsafe(uint sequenceNumber, out OutgoingPacket packet)
95 {
96 if (packets.TryGetValue(sequenceNumber, out packet))
97 {
98 packets.Remove(sequenceNumber);
99 return true;
100 }
101
102 return false;
103 }
104 68
105 /// <summary> 69 /// <summary>
106 /// Removes all elements from the collection 70 /// Add an unacked packet to the collection
107 /// </summary> 71 /// </summary>
108 public void Clear() 72 /// <param name="packet">Packet that is awaiting acknowledgement</param>
73 /// <returns>True if the packet was successfully added, false if the
74 /// packet already existed in the collection</returns>
75 /// <remarks>This does not immediately add the ACK to the collection,
76 /// it only queues it so it can be added in a thread-safe way later</remarks>
77 public void Add(OutgoingPacket packet)
109 { 78 {
110 lock (SyncRoot) 79 m_pendingAdds.Enqueue(packet);
111 packets.Clear();
112 } 80 }
113 81
114 /// <summary> 82 /// <summary>
115 /// Gets the packet with the lowest sequence number 83 /// Marks a packet as acknowledged
116 /// </summary> 84 /// </summary>
117 /// <returns>The packet with the lowest sequence number, or null if the 85 /// <param name="sequenceNumber">Sequence number of the packet to
118 /// collection is empty</returns> 86 /// acknowledge</param>
119 public OutgoingPacket GetOldest() 87 /// <param name="currentTime">Current value of Environment.TickCount</param>
88 public void Remove(uint sequenceNumber, int currentTime, bool fromResend)
120 { 89 {
121 lock (SyncRoot) 90 m_pendingRemoves.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend));
122 {
123 using (SortedDictionary<uint, OutgoingPacket>.ValueCollection.Enumerator e = packets.Values.GetEnumerator())
124 {
125 if (e.MoveNext())
126 return e.Current;
127 else
128 return null;
129 }
130 }
131 } 91 }
132 92
133 /// <summary> 93 /// <summary>
@@ -138,14 +98,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
138 /// packet is considered expired</param> 98 /// packet is considered expired</param>
139 /// <returns>A list of all expired packets according to the given 99 /// <returns>A list of all expired packets according to the given
140 /// expiration timeout</returns> 100 /// expiration timeout</returns>
101 /// <remarks>This function is not thread safe, and cannot be called
102 /// multiple times concurrently</remarks>
141 public List<OutgoingPacket> GetExpiredPackets(int timeoutMS) 103 public List<OutgoingPacket> GetExpiredPackets(int timeoutMS)
142 { 104 {
105 ProcessQueues();
106
143 List<OutgoingPacket> expiredPackets = null; 107 List<OutgoingPacket> expiredPackets = null;
144 108
145 lock (SyncRoot) 109 if (m_packets.Count > 0)
146 { 110 {
147 int now = Environment.TickCount; 111 int now = Environment.TickCount;
148 foreach (OutgoingPacket packet in packets.Values) 112
113 foreach (OutgoingPacket packet in m_packets.Values)
149 { 114 {
150 // TickCount of zero means a packet is in the resend queue 115 // TickCount of zero means a packet is in the resend queue
151 // but hasn't actually been sent over the wire yet 116 // but hasn't actually been sent over the wire yet
@@ -167,5 +132,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP
167 132
168 return expiredPackets; 133 return expiredPackets;
169 } 134 }
135
136 private void ProcessQueues()
137 {
138 // Process all the pending adds
139 OutgoingPacket pendingAdd;
140 while (m_pendingAdds.Dequeue(out pendingAdd))
141 m_packets[pendingAdd.SequenceNumber] = pendingAdd;
142
143 // Process all the pending removes, including updating statistics and round-trip times
144 PendingAck pendingRemove;
145 OutgoingPacket ackedPacket;
146 while (m_pendingRemoves.Dequeue(out pendingRemove))
147 {
148 if (m_packets.TryGetValue(pendingRemove.SequenceNumber, out ackedPacket))
149 {
150 m_packets.Remove(pendingRemove.SequenceNumber);
151
152 // Update stats
153 System.Threading.Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
154
155 if (!pendingRemove.FromResend)
156 {
157 // Calculate the round-trip time for this packet and its ACK
158 int rtt = pendingRemove.RemoveTime - ackedPacket.TickCount;
159 if (rtt > 0)
160 ackedPacket.Client.UpdateRoundTrip(rtt);
161 }
162 }
163 }
164 }
170 } 165 }
171} 166}