diff options
author | UbitUmarov | 2019-02-25 21:46:23 +0000 |
---|---|---|
committer | UbitUmarov | 2019-02-25 21:46:23 +0000 |
commit | d01165818dad7bd81aed07fa983951fecc5a80cd (patch) | |
tree | 605655eb6512d37786c969c01c84073c9e1c7ccd /OpenSim/Region/ClientStack | |
parent | make the options visible on OpenSimDefaults (diff) | |
download | opensim-SC-d01165818dad7bd81aed07fa983951fecc5a80cd.zip opensim-SC-d01165818dad7bd81aed07fa983951fecc5a80cd.tar.gz opensim-SC-d01165818dad7bd81aed07fa983951fecc5a80cd.tar.bz2 opensim-SC-d01165818dad7bd81aed07fa983951fecc5a80cd.tar.xz |
change UDPPacketBuffer pools (does waste a bit of memory)
Diffstat (limited to 'OpenSim/Region/ClientStack')
6 files changed, 156 insertions, 323 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 943be07..3cb9388 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | |||
@@ -12555,14 +12555,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
12555 | /// provide your own method.</param> | 12555 | /// provide your own method.</param> |
12556 | protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method) | 12556 | protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method) |
12557 | { | 12557 | { |
12558 | |||
12559 | /* this is causing packet loss for some reason | ||
12560 | if(!m_udpClient.IsConnected) | ||
12561 | { | ||
12562 | PacketPool.Instance.ReturnPacket(packet); | ||
12563 | return; | ||
12564 | } | ||
12565 | */ | ||
12566 | if (m_outPacketsToDrop != null) | 12558 | if (m_outPacketsToDrop != null) |
12567 | { | 12559 | { |
12568 | if (m_outPacketsToDrop.Contains(packet.Type.ToString())) | 12560 | if (m_outPacketsToDrop.Contains(packet.Type.ToString())) |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index 439621a..bc75d82 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs | |||
@@ -120,7 +120,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
120 | /// <summary>Circuit code that this client is connected on</summary> | 120 | /// <summary>Circuit code that this client is connected on</summary> |
121 | public readonly uint CircuitCode; | 121 | public readonly uint CircuitCode; |
122 | /// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary> | 122 | /// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary> |
123 | public IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200); | 123 | public IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(256); |
124 | 124 | ||
125 | /// <summary>Packets we have sent that need to be ACKed by the client</summary> | 125 | /// <summary>Packets we have sent that need to be ACKed by the client</summary> |
126 | public UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); | 126 | public UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); |
@@ -803,8 +803,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
803 | } | 803 | } |
804 | } | 804 | } |
805 | 805 | ||
806 | |||
807 | |||
808 | /// <summary> | 806 | /// <summary> |
809 | /// Fires the OnQueueEmpty callback and sets the minimum time that it | 807 | /// Fires the OnQueueEmpty callback and sets the minimum time that it |
810 | /// can be called again | 808 | /// can be called again |
@@ -843,6 +841,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
843 | return 0; | 841 | return 0; |
844 | } | 842 | } |
845 | 843 | ||
844 | public void FreeUDPBuffer(UDPPacketBuffer buf) | ||
845 | { | ||
846 | m_udpServer.FreeUDPBuffer(buf); | ||
847 | } | ||
848 | |||
846 | /// <summary> | 849 | /// <summary> |
847 | /// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a | 850 | /// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a |
848 | /// flag value | 851 | /// flag value |
@@ -853,20 +856,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
853 | { | 856 | { |
854 | ThrottleOutPacketType category = (ThrottleOutPacketType)i; | 857 | ThrottleOutPacketType category = (ThrottleOutPacketType)i; |
855 | 858 | ||
856 | /* | ||
857 | * Land = 1, | ||
858 | /// <summary>Wind data</summary> | ||
859 | Wind = 2, | ||
860 | /// <summary>Cloud data</summary> | ||
861 | Cloud = 3, | ||
862 | /// <summary>Any packets that do not fit into the other throttles</summary> | ||
863 | Task = 4, | ||
864 | /// <summary>Texture assets</summary> | ||
865 | Texture = 5, | ||
866 | /// <summary>Non-texture assets</summary> | ||
867 | Asset = 6, | ||
868 | */ | ||
869 | |||
870 | switch (category) | 859 | switch (category) |
871 | { | 860 | { |
872 | case ThrottleOutPacketType.Land: | 861 | case ThrottleOutPacketType.Land: |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 35d29a5..4739ae8 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | |||
@@ -344,18 +344,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
344 | 344 | ||
345 | protected ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>(); | 345 | protected ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>(); |
346 | 346 | ||
347 | protected Pool<IncomingPacket> m_incomingPacketPool; | ||
348 | |||
349 | /// <summary> | ||
350 | /// Stat for number of packets in the main pool awaiting use. | ||
351 | /// </summary> | ||
352 | protected Stat m_poolCountStat; | ||
353 | |||
354 | /// <summary> | ||
355 | /// Stat for number of packets in the inbound packet pool awaiting use. | ||
356 | /// </summary> | ||
357 | protected Stat m_incomingPacketPoolStat; | ||
358 | |||
359 | protected int m_defaultRTO = 0; | 347 | protected int m_defaultRTO = 0; |
360 | protected int m_maxRTO = 0; | 348 | protected int m_maxRTO = 0; |
361 | protected int m_ackTimeout = 0; | 349 | protected int m_ackTimeout = 0; |
@@ -498,7 +486,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
498 | 486 | ||
499 | // if (usePools) | 487 | // if (usePools) |
500 | // EnablePools(); | 488 | // EnablePools(); |
501 | base.DisablePools(); | ||
502 | } | 489 | } |
503 | 490 | ||
504 | public void Start() | 491 | public void Start() |
@@ -554,83 +541,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
554 | OqrEngine.Stop(); | 541 | OqrEngine.Stop(); |
555 | } | 542 | } |
556 | 543 | ||
557 | public override bool EnablePools() | ||
558 | { | ||
559 | if (!UsePools) | ||
560 | { | ||
561 | base.EnablePools(); | ||
562 | |||
563 | m_incomingPacketPool = new Pool<IncomingPacket>(() => new IncomingPacket(), 500); | ||
564 | |||
565 | return true; | ||
566 | } | ||
567 | |||
568 | return false; | ||
569 | } | ||
570 | |||
571 | public override bool DisablePools() | ||
572 | { | ||
573 | if (UsePools) | ||
574 | { | ||
575 | base.DisablePools(); | ||
576 | |||
577 | StatsManager.DeregisterStat(m_incomingPacketPoolStat); | ||
578 | |||
579 | // We won't null out the pool to avoid a race condition with code that may be in the middle of using it. | ||
580 | |||
581 | return true; | ||
582 | } | ||
583 | |||
584 | return false; | ||
585 | } | ||
586 | |||
587 | /// <summary> | ||
588 | /// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene | ||
589 | /// stats. | ||
590 | /// </summary> | ||
591 | protected internal void EnablePoolStats() | ||
592 | { | ||
593 | m_poolCountStat | ||
594 | = new Stat( | ||
595 | "UDPPacketBufferPoolCount", | ||
596 | "Objects within the UDPPacketBuffer pool", | ||
597 | "The number of objects currently stored within the UDPPacketBuffer pool", | ||
598 | "", | ||
599 | "clientstack", | ||
600 | Scene.Name, | ||
601 | StatType.Pull, | ||
602 | stat => stat.Value = Pool.Count, | ||
603 | StatVerbosity.Debug); | ||
604 | |||
605 | StatsManager.RegisterStat(m_poolCountStat); | ||
606 | |||
607 | m_incomingPacketPoolStat | ||
608 | = new Stat( | ||
609 | "IncomingPacketPoolCount", | ||
610 | "Objects within incoming packet pool", | ||
611 | "The number of objects currently stored within the incoming packet pool", | ||
612 | "", | ||
613 | "clientstack", | ||
614 | Scene.Name, | ||
615 | StatType.Pull, | ||
616 | stat => stat.Value = m_incomingPacketPool.Count, | ||
617 | StatVerbosity.Debug); | ||
618 | |||
619 | StatsManager.RegisterStat(m_incomingPacketPoolStat); | ||
620 | } | ||
621 | |||
622 | /// <summary> | ||
623 | /// Disables pool stats. | ||
624 | /// </summary> | ||
625 | protected internal void DisablePoolStats() | ||
626 | { | ||
627 | StatsManager.DeregisterStat(m_poolCountStat); | ||
628 | m_poolCountStat = null; | ||
629 | |||
630 | StatsManager.DeregisterStat(m_incomingPacketPoolStat); | ||
631 | m_incomingPacketPoolStat = null; | ||
632 | } | ||
633 | |||
634 | /// <summary> | 544 | /// <summary> |
635 | /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. | 545 | /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. |
636 | /// </summary> | 546 | /// </summary> |
@@ -658,8 +568,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
658 | string.Format("Incoming Packet Async Handling Engine ({0})", Scene.Name), | 568 | string.Format("Incoming Packet Async Handling Engine ({0})", Scene.Name), |
659 | "INCOMING PACKET ASYNC HANDLING ENGINE"); | 569 | "INCOMING PACKET ASYNC HANDLING ENGINE"); |
660 | */ | 570 | */ |
661 | OqrEngine | 571 | OqrEngine = new JobEngine( |
662 | = new JobEngine( | ||
663 | string.Format("Outgoing Queue Refill Engine ({0})", Scene.Name), | 572 | string.Format("Outgoing Queue Refill Engine ({0})", Scene.Name), |
664 | "OUTGOING QUEUE REFILL ENGINE"); | 573 | "OUTGOING QUEUE REFILL ENGINE"); |
665 | 574 | ||
@@ -769,15 +678,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
769 | stat => stat.Value = OqrEngine.JobsWaiting, | 678 | stat => stat.Value = OqrEngine.JobsWaiting, |
770 | StatVerbosity.Debug)); | 679 | StatVerbosity.Debug)); |
771 | 680 | ||
772 | // We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by | 681 | StatsManager.RegisterStat( |
773 | // scene name | 682 | new Stat( |
774 | if (UsePools) | 683 | "UDPBuffersPoolCount", |
775 | EnablePoolStats(); | 684 | "Buffers in the UDP buffers pool", |
776 | 685 | "The number of buffers currently stored within the UDP buffers pool", | |
686 | "", | ||
687 | "clientstack", | ||
688 | Scene.Name, | ||
689 | StatType.Pull, | ||
690 | stat => stat.Value = m_udpBuffersPoolPtr, | ||
691 | StatVerbosity.Debug)); | ||
777 | 692 | ||
778 | LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this); | 693 | LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this); |
779 | commands.Register(); | 694 | commands.Register(); |
780 | |||
781 | } | 695 | } |
782 | 696 | ||
783 | public bool HandlesRegion(Location x) | 697 | public bool HandlesRegion(Location x) |
@@ -939,9 +853,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
939 | // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting | 853 | // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting |
940 | // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here | 854 | // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here |
941 | // to accomodate for both common scenarios and provide ample room for ACK appending in both | 855 | // to accomodate for both common scenarios and provide ample room for ACK appending in both |
942 | int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; | 856 | //int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; |
943 | 857 | ||
944 | UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); | 858 | //UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); |
859 | UDPPacketBuffer buffer = GetNewUDPBuffer(udpClient.RemoteEndPoint); | ||
945 | 860 | ||
946 | // Zerocode if needed | 861 | // Zerocode if needed |
947 | if (doZerocode) | 862 | if (doZerocode) |
@@ -971,7 +886,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
971 | // If the packet data wasn't already copied during zerocoding, copy it now | 886 | // If the packet data wasn't already copied during zerocoding, copy it now |
972 | if (doCopy) | 887 | if (doCopy) |
973 | { | 888 | { |
974 | if (dataLength <= buffer.Data.Length) | 889 | //if (dataLength <= buffer.Data.Length) |
890 | if (dataLength <= LLUDPServer.MTU) | ||
975 | { | 891 | { |
976 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | 892 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); |
977 | } | 893 | } |
@@ -979,7 +895,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
979 | { | 895 | { |
980 | m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + | 896 | m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + |
981 | type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length); | 897 | type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length); |
982 | buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, dataLength); | 898 | // buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, dataLength); |
899 | buffer = GetNewUDPBuffer(udpClient.RemoteEndPoint); | ||
983 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | 900 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); |
984 | } | 901 | } |
985 | } | 902 | } |
@@ -1168,22 +1085,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1168 | // Set the appended ACKs flag on this packet | 1085 | // Set the appended ACKs flag on this packet |
1169 | buffer.Data[0] = (byte)(buffer.Data[0] | Helpers.MSG_APPENDED_ACKS); | 1086 | buffer.Data[0] = (byte)(buffer.Data[0] | Helpers.MSG_APPENDED_ACKS); |
1170 | } | 1087 | } |
1088 | buffer.DataLength = dataLength; | ||
1171 | } | 1089 | } |
1172 | 1090 | ||
1173 | buffer.DataLength = dataLength; | ||
1174 | |||
1175 | if (!isResend) | 1091 | if (!isResend) |
1176 | { | 1092 | { |
1177 | // Not a resend, assign a new sequence number | 1093 | // Not a resend, assign a new sequence number |
1178 | uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence); | 1094 | uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence); |
1179 | Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); | 1095 | Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); |
1180 | outgoingPacket.SequenceNumber = sequenceNumber; | 1096 | outgoingPacket.SequenceNumber = sequenceNumber; |
1181 | |||
1182 | if (isReliable) | ||
1183 | { | ||
1184 | // Add this packet to the list of ACK responses we are waiting on from the server | ||
1185 | udpClient.NeedAcks.Add(outgoingPacket); | ||
1186 | } | ||
1187 | } | 1097 | } |
1188 | else | 1098 | else |
1189 | { | 1099 | { |
@@ -1196,9 +1106,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1196 | PacketsSentCount++; | 1106 | PacketsSentCount++; |
1197 | 1107 | ||
1198 | SyncSend(buffer); | 1108 | SyncSend(buffer); |
1109 | |||
1199 | // Keep track of when this packet was sent out (right now) | 1110 | // Keep track of when this packet was sent out (right now) |
1200 | outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; | 1111 | outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; |
1201 | 1112 | ||
1113 | if (outgoingPacket.UnackedMethod == null) | ||
1114 | FreeUDPBuffer(buffer); | ||
1115 | else if(!isResend) | ||
1116 | { | ||
1117 | // Add this packet to the list of ACK responses we are waiting on from the server | ||
1118 | udpClient.NeedAcks.Add(outgoingPacket); | ||
1119 | } | ||
1120 | |||
1202 | if (udpClient.DebugDataOutLevel > 0) | 1121 | if (udpClient.DebugDataOutLevel > 0) |
1203 | m_log.DebugFormat( | 1122 | m_log.DebugFormat( |
1204 | "[LLUDPSERVER]: Sending packet #{0} (rel: {1}, res: {2}) to {3} from {4}", | 1123 | "[LLUDPSERVER]: Sending packet #{0} (rel: {1}, res: {2}) to {3} from {4}", |
@@ -1240,7 +1159,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1240 | // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); | 1159 | // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); |
1241 | 1160 | ||
1242 | RecordMalformedInboundPacket(endPoint); | 1161 | RecordMalformedInboundPacket(endPoint); |
1243 | 1162 | FreeUDPBuffer(buffer); | |
1244 | return; // Drop undersized packet | 1163 | return; // Drop undersized packet |
1245 | } | 1164 | } |
1246 | 1165 | ||
@@ -1260,21 +1179,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1260 | // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); | 1179 | // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); |
1261 | 1180 | ||
1262 | RecordMalformedInboundPacket(endPoint); | 1181 | RecordMalformedInboundPacket(endPoint); |
1263 | 1182 | FreeUDPBuffer(buffer); | |
1264 | return; // Malformed header | 1183 | return; // Malformed header |
1265 | } | 1184 | } |
1266 | 1185 | ||
1267 | try | 1186 | try |
1268 | { | 1187 | { |
1269 | // packet = Packet.BuildPacket(buffer.Data, ref packetEnd, | 1188 | packet = Packet.BuildPacket(buffer.Data, ref packetEnd, |
1270 | // // Only allocate a buffer for zerodecoding if the packet is zerocoded | 1189 | // Only allocate a buffer for zerodecoding if the packet is zerocoded |
1271 | // ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); | 1190 | ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); |
1272 | // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we | 1191 | // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we |
1273 | // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all | 1192 | // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all |
1274 | // bytes are copied out). | 1193 | // bytes are copied out). |
1275 | packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, | 1194 | // packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, |
1276 | // Only allocate a buffer for zerodecoding if the packet is zerocoded | 1195 | // Only allocate a buffer for zerodecoding if the packet is zerocoded |
1277 | ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); | 1196 | // ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); |
1278 | } | 1197 | } |
1279 | catch (Exception e) | 1198 | catch (Exception e) |
1280 | { | 1199 | { |
@@ -1292,7 +1211,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1292 | } | 1211 | } |
1293 | 1212 | ||
1294 | RecordMalformedInboundPacket(endPoint); | 1213 | RecordMalformedInboundPacket(endPoint); |
1295 | 1214 | FreeUDPBuffer(buffer); | |
1296 | return; | 1215 | return; |
1297 | } | 1216 | } |
1298 | 1217 | ||
@@ -1311,17 +1230,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1311 | lock (m_pendingCache) | 1230 | lock (m_pendingCache) |
1312 | { | 1231 | { |
1313 | if (m_pendingCache.Contains(endPoint)) | 1232 | if (m_pendingCache.Contains(endPoint)) |
1233 | { | ||
1234 | FreeUDPBuffer(buffer); | ||
1314 | return; | 1235 | return; |
1236 | } | ||
1315 | 1237 | ||
1316 | m_pendingCache.AddOrUpdate(endPoint, new Queue<UDPPacketBuffer>(), 60); | 1238 | m_pendingCache.AddOrUpdate(endPoint, new Queue<UDPPacketBuffer>(), 60); |
1317 | } | 1239 | } |
1318 | 1240 | ||
1319 | // We need to copy the endpoint so that it doesn't get changed when another thread reuses the | 1241 | Util.FireAndForget(HandleUseCircuitCode, new object[] { endPoint, packet }); |
1320 | // buffer. | 1242 | FreeUDPBuffer(buffer); |
1321 | object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; | ||
1322 | |||
1323 | Util.FireAndForget(HandleUseCircuitCode, array); | ||
1324 | |||
1325 | return; | 1243 | return; |
1326 | } | 1244 | } |
1327 | } | 1245 | } |
@@ -1336,24 +1254,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1336 | queue.Enqueue(buffer); | 1254 | queue.Enqueue(buffer); |
1337 | return; | 1255 | return; |
1338 | } | 1256 | } |
1339 | |||
1340 | /* | ||
1341 | else if (packet.Type == PacketType.CompleteAgentMovement) | ||
1342 | { | ||
1343 | // Send ack straight away to let the viewer know that we got it. | ||
1344 | SendAckImmediate(endPoint, packet.Header.Sequence); | ||
1345 | |||
1346 | // We need to copy the endpoint so that it doesn't get changed when another thread reuses the | ||
1347 | // buffer. | ||
1348 | object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; | ||
1349 | |||
1350 | Util.FireAndForget(HandleCompleteMovementIntoRegion, array); | ||
1351 | |||
1352 | return; | ||
1353 | } | ||
1354 | */ | ||
1355 | } | 1257 | } |
1356 | 1258 | ||
1259 | FreeUDPBuffer(buffer); | ||
1260 | |||
1357 | // Determine which agent this packet came from | 1261 | // Determine which agent this packet came from |
1358 | if (client == null || !(client is LLClientView)) | 1262 | if (client == null || !(client is LLClientView)) |
1359 | { | 1263 | { |
@@ -1471,10 +1375,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1471 | LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); | 1375 | LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); |
1472 | #endregion BinaryStats | 1376 | #endregion BinaryStats |
1473 | 1377 | ||
1474 | |||
1475 | //AgentUpdate removed from here | ||
1476 | |||
1477 | |||
1478 | #region Ping Check Handling | 1378 | #region Ping Check Handling |
1479 | 1379 | ||
1480 | if (packet.Type == PacketType.StartPingCheck) | 1380 | if (packet.Type == PacketType.StartPingCheck) |
@@ -1506,17 +1406,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1506 | 1406 | ||
1507 | IncomingPacket incomingPacket; | 1407 | IncomingPacket incomingPacket; |
1508 | 1408 | ||
1509 | // Inbox insertion | 1409 | incomingPacket = new IncomingPacket((LLClientView)client, packet); |
1510 | if (UsePools) | ||
1511 | { | ||
1512 | incomingPacket = m_incomingPacketPool.GetObject(); | ||
1513 | incomingPacket.Client = (LLClientView)client; | ||
1514 | incomingPacket.Packet = packet; | ||
1515 | } | ||
1516 | else | ||
1517 | { | ||
1518 | incomingPacket = new IncomingPacket((LLClientView)client, packet); | ||
1519 | } | ||
1520 | 1410 | ||
1521 | // if (incomingPacket.Packet.Type == PacketType.AgentUpdate || | 1411 | // if (incomingPacket.Packet.Type == PacketType.AgentUpdate || |
1522 | // incomingPacket.Packet.Type == PacketType.ChatFromViewer) | 1412 | // incomingPacket.Packet.Type == PacketType.ChatFromViewer) |
@@ -1525,7 +1415,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1525 | // else | 1415 | // else |
1526 | // packetInbox.Enqueue(incomingPacket); | 1416 | // packetInbox.Enqueue(incomingPacket); |
1527 | packetInbox.Add(incomingPacket); | 1417 | packetInbox.Add(incomingPacket); |
1528 | |||
1529 | } | 1418 | } |
1530 | 1419 | ||
1531 | #region BinaryStats | 1420 | #region BinaryStats |
@@ -1881,13 +1770,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1881 | byte[] packetData = ack.ToBytes(); | 1770 | byte[] packetData = ack.ToBytes(); |
1882 | int length = packetData.Length; | 1771 | int length = packetData.Length; |
1883 | 1772 | ||
1884 | UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndpoint, length); | 1773 | UDPPacketBuffer buffer = GetNewUDPBuffer(remoteEndpoint); |
1885 | buffer.DataLength = length; | 1774 | buffer.DataLength = length; |
1886 | 1775 | ||
1887 | Buffer.BlockCopy(packetData, 0, buffer.Data, 0, length); | 1776 | Buffer.BlockCopy(packetData, 0, buffer.Data, 0, length); |
1888 | 1777 | ||
1889 | // AsyncBeginSend(buffer); | 1778 | // AsyncBeginSend(buffer); |
1890 | SyncSend(buffer); | 1779 | SyncSend(buffer); |
1780 | FreeUDPBuffer(buffer); | ||
1891 | } | 1781 | } |
1892 | 1782 | ||
1893 | protected bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo) | 1783 | protected bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo) |
@@ -1982,17 +1872,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1982 | Scene.ThreadAlive(1); | 1872 | Scene.ThreadAlive(1); |
1983 | try | 1873 | try |
1984 | { | 1874 | { |
1985 | packetInbox.TryTake(out incomingPacket, 250); | 1875 | packetInbox.TryTake(out incomingPacket, 4500); |
1986 | 1876 | ||
1987 | if (incomingPacket != null && IsRunningInbound) | 1877 | if (incomingPacket != null && IsRunningInbound) |
1988 | { | 1878 | { |
1989 | ProcessInPacket(incomingPacket); | 1879 | ProcessInPacket(incomingPacket); |
1990 | |||
1991 | if (UsePools) | ||
1992 | { | ||
1993 | incomingPacket.Client = null; | ||
1994 | m_incomingPacketPool.ReturnObject(incomingPacket); | ||
1995 | } | ||
1996 | incomingPacket = null; | 1880 | incomingPacket = null; |
1997 | } | 1881 | } |
1998 | } | 1882 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs index 012a57d..a4d7eb9 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs | |||
@@ -777,41 +777,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
777 | m_udpServer.StopOutbound(); | 777 | m_udpServer.StopOutbound(); |
778 | } | 778 | } |
779 | 779 | ||
780 | private void HandlePoolCommand(string module, string[] args) | ||
781 | { | ||
782 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | ||
783 | return; | ||
784 | |||
785 | if (args.Length != 4) | ||
786 | { | ||
787 | MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); | ||
788 | return; | ||
789 | } | ||
790 | |||
791 | string enabled = args[3]; | ||
792 | |||
793 | if (enabled == "on") | ||
794 | { | ||
795 | if (m_udpServer.EnablePools()) | ||
796 | { | ||
797 | m_udpServer.EnablePoolStats(); | ||
798 | MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_udpServer.Scene.Name); | ||
799 | } | ||
800 | } | ||
801 | else if (enabled == "off") | ||
802 | { | ||
803 | if (m_udpServer.DisablePools()) | ||
804 | { | ||
805 | m_udpServer.DisablePoolStats(); | ||
806 | MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_udpServer.Scene.Name); | ||
807 | } | ||
808 | } | ||
809 | else | ||
810 | { | ||
811 | MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); | ||
812 | } | ||
813 | } | ||
814 | |||
815 | private void HandleAgentUpdateCommand(string module, string[] args) | 780 | private void HandleAgentUpdateCommand(string module, string[] args) |
816 | { | 781 | { |
817 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) | 782 | if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) |
@@ -834,8 +799,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
834 | MainConsole.Instance.OutputFormat( | 799 | MainConsole.Instance.OutputFormat( |
835 | "OUT LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningOutbound ? "enabled" : "disabled"); | 800 | "OUT LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningOutbound ? "enabled" : "disabled"); |
836 | 801 | ||
837 | MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_udpServer.Scene.Name, m_udpServer.UsePools ? "on" : "off"); | ||
838 | |||
839 | MainConsole.Instance.OutputFormat( | 802 | MainConsole.Instance.OutputFormat( |
840 | "Packet debug level for new clients is {0}", m_udpServer.DefaultClientPacketDebugLevel); | 803 | "Packet debug level for new clients is {0}", m_udpServer.DefaultClientPacketDebugLevel); |
841 | } | 804 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index f362b06..0bfd86c 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs | |||
@@ -26,6 +26,7 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections.Concurrent; | ||
29 | using System.Net; | 30 | using System.Net; |
30 | using System.Net.Sockets; | 31 | using System.Net.Sockets; |
31 | using System.Threading; | 32 | using System.Threading; |
@@ -57,15 +58,9 @@ namespace OpenMetaverse | |||
57 | /// <summary>UDP socket, used in either client or server mode</summary> | 58 | /// <summary>UDP socket, used in either client or server mode</summary> |
58 | private Socket m_udpSocket; | 59 | private Socket m_udpSocket; |
59 | 60 | ||
60 | /// <summary> | 61 | public static Object m_udpBuffersPoolLock = new Object(); |
61 | /// Are we to use object pool(s) to reduce memory churn when receiving data? | 62 | public static UDPPacketBuffer[] m_udpBuffersPool = new UDPPacketBuffer[1000]; |
62 | /// </summary> | 63 | public static int m_udpBuffersPoolPtr = -1; |
63 | public bool UsePools { get; protected set; } | ||
64 | |||
65 | /// <summary> | ||
66 | /// Pool to use for handling data. May be null if UsePools = false; | ||
67 | /// </summary> | ||
68 | protected OpenSim.Framework.Pool<UDPPacketBuffer> Pool { get; private set; } | ||
69 | 64 | ||
70 | /// <summary>Returns true if the server is currently listening for inbound packets, otherwise false</summary> | 65 | /// <summary>Returns true if the server is currently listening for inbound packets, otherwise false</summary> |
71 | public bool IsRunningInbound { get; private set; } | 66 | public bool IsRunningInbound { get; private set; } |
@@ -186,6 +181,52 @@ namespace OpenMetaverse | |||
186 | if(m_udpSocket !=null) | 181 | if(m_udpSocket !=null) |
187 | try { m_udpSocket.Close(); } catch { } | 182 | try { m_udpSocket.Close(); } catch { } |
188 | } | 183 | } |
184 | |||
185 | public UDPPacketBuffer GetNewUDPBuffer() | ||
186 | { | ||
187 | lock (m_udpBuffersPoolLock) | ||
188 | { | ||
189 | if (m_udpBuffersPoolPtr >= 0) | ||
190 | { | ||
191 | UDPPacketBuffer buf = m_udpBuffersPool[m_udpBuffersPoolPtr]; | ||
192 | m_udpBuffersPool[m_udpBuffersPoolPtr] = null; | ||
193 | m_udpBuffersPoolPtr--; | ||
194 | buf.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); | ||
195 | return buf; | ||
196 | } | ||
197 | } | ||
198 | return new UDPPacketBuffer(new IPEndPoint(IPAddress.Any, 0)); | ||
199 | } | ||
200 | |||
201 | public UDPPacketBuffer GetNewUDPBuffer(IPEndPoint remoteEndpoint) | ||
202 | { | ||
203 | lock (m_udpBuffersPoolLock) | ||
204 | { | ||
205 | if (m_udpBuffersPoolPtr >= 0) | ||
206 | { | ||
207 | UDPPacketBuffer buf = m_udpBuffersPool[m_udpBuffersPoolPtr]; | ||
208 | m_udpBuffersPool[m_udpBuffersPoolPtr] = null; | ||
209 | m_udpBuffersPoolPtr--; | ||
210 | buf.RemoteEndPoint = remoteEndpoint; | ||
211 | return buf; | ||
212 | } | ||
213 | } | ||
214 | return new UDPPacketBuffer(remoteEndpoint); | ||
215 | } | ||
216 | |||
217 | public void FreeUDPBuffer(UDPPacketBuffer buf) | ||
218 | { | ||
219 | lock (m_udpBuffersPoolLock) | ||
220 | { | ||
221 | if (m_udpBuffersPoolPtr < 999) | ||
222 | { | ||
223 | buf.RemoteEndPoint = null; | ||
224 | m_udpBuffersPoolPtr++; | ||
225 | m_udpBuffersPool[m_udpBuffersPoolPtr] = buf; | ||
226 | } | ||
227 | } | ||
228 | } | ||
229 | |||
189 | /// <summary> | 230 | /// <summary> |
190 | /// Start inbound UDP packet handling. | 231 | /// Start inbound UDP packet handling. |
191 | /// </summary> | 232 | /// </summary> |
@@ -202,6 +243,7 @@ namespace OpenMetaverse | |||
202 | /// manner (not throwing an exception when the remote side resets the | 243 | /// manner (not throwing an exception when the remote side resets the |
203 | /// connection). This call is ignored on Mono where the flag is not | 244 | /// connection). This call is ignored on Mono where the flag is not |
204 | /// necessary</remarks> | 245 | /// necessary</remarks> |
246 | |||
205 | public virtual void StartInbound(int recvBufferSize) | 247 | public virtual void StartInbound(int recvBufferSize) |
206 | { | 248 | { |
207 | if (!IsRunningInbound) | 249 | if (!IsRunningInbound) |
@@ -306,101 +348,64 @@ namespace OpenMetaverse | |||
306 | IsRunningOutbound = false; | 348 | IsRunningOutbound = false; |
307 | } | 349 | } |
308 | 350 | ||
309 | public virtual bool EnablePools() | 351 | private void AsyncBeginReceive() |
310 | { | 352 | { |
311 | if (!UsePools) | 353 | if (!IsRunningInbound) |
312 | { | 354 | return; |
313 | Pool = new Pool<UDPPacketBuffer>(() => new UDPPacketBuffer(), 500); | ||
314 | |||
315 | UsePools = true; | ||
316 | |||
317 | return true; | ||
318 | } | ||
319 | |||
320 | return false; | ||
321 | } | ||
322 | 355 | ||
323 | public virtual bool DisablePools() | 356 | UDPPacketBuffer buf = GetNewUDPBuffer(); |
324 | { | 357 | try |
325 | if (UsePools) | ||
326 | { | 358 | { |
327 | UsePools = false; | 359 | // kick off an async read |
328 | 360 | m_udpSocket.BeginReceiveFrom( | |
329 | // We won't null out the pool to avoid a race condition with code that may be in the middle of using it. | 361 | //wrappedBuffer.Instance.Data, |
330 | 362 | buf.Data, | |
331 | return true; | 363 | 0, |
364 | UDPPacketBuffer.BUFFER_SIZE, | ||
365 | SocketFlags.None, | ||
366 | ref buf.RemoteEndPoint, | ||
367 | AsyncEndReceive, | ||
368 | //wrappedBuffer); | ||
369 | buf); | ||
332 | } | 370 | } |
333 | 371 | catch (SocketException e) | |
334 | return false; | ||
335 | } | ||
336 | |||
337 | private void AsyncBeginReceive() | ||
338 | { | ||
339 | UDPPacketBuffer buf; | ||
340 | |||
341 | // FIXME: Disabled for now as this causes issues with reused packet objects interfering with each other | ||
342 | // on Windows with m_asyncPacketHandling = true, though this has not been seen on Linux. | ||
343 | // Possibly some unexpected issue with fetching UDP data concurrently with multiple threads. Requires more investigation. | ||
344 | // if (UsePools) | ||
345 | // buf = Pool.GetObject(); | ||
346 | // else | ||
347 | buf = new UDPPacketBuffer(); | ||
348 | |||
349 | if (IsRunningInbound) | ||
350 | { | 372 | { |
351 | try | 373 | if (e.SocketErrorCode == SocketError.ConnectionReset) |
352 | { | 374 | { |
353 | // kick off an async read | 375 | m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); |
354 | m_udpSocket.BeginReceiveFrom( | 376 | bool salvaged = false; |
355 | //wrappedBuffer.Instance.Data, | 377 | while (!salvaged) |
356 | buf.Data, | ||
357 | 0, | ||
358 | UDPPacketBuffer.BUFFER_SIZE, | ||
359 | SocketFlags.None, | ||
360 | ref buf.RemoteEndPoint, | ||
361 | AsyncEndReceive, | ||
362 | //wrappedBuffer); | ||
363 | buf); | ||
364 | } | ||
365 | catch (SocketException e) | ||
366 | { | ||
367 | if (e.SocketErrorCode == SocketError.ConnectionReset) | ||
368 | { | 378 | { |
369 | m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); | 379 | try |
370 | bool salvaged = false; | ||
371 | while (!salvaged) | ||
372 | { | 380 | { |
373 | try | 381 | m_udpSocket.BeginReceiveFrom( |
374 | { | 382 | //wrappedBuffer.Instance.Data, |
375 | m_udpSocket.BeginReceiveFrom( | 383 | buf.Data, |
376 | //wrappedBuffer.Instance.Data, | 384 | 0, |
377 | buf.Data, | 385 | UDPPacketBuffer.BUFFER_SIZE, |
378 | 0, | 386 | SocketFlags.None, |
379 | UDPPacketBuffer.BUFFER_SIZE, | 387 | ref buf.RemoteEndPoint, |
380 | SocketFlags.None, | 388 | AsyncEndReceive, |
381 | ref buf.RemoteEndPoint, | 389 | //wrappedBuffer); |
382 | AsyncEndReceive, | 390 | buf); |
383 | //wrappedBuffer); | 391 | salvaged = true; |
384 | buf); | ||
385 | salvaged = true; | ||
386 | } | ||
387 | catch (SocketException) { } | ||
388 | catch (ObjectDisposedException) { return; } | ||
389 | } | 392 | } |
390 | 393 | catch (SocketException) { } | |
391 | m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); | 394 | catch (ObjectDisposedException) { return; } |
392 | } | 395 | } |
396 | |||
397 | m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); | ||
393 | } | 398 | } |
394 | catch (ObjectDisposedException e) | 399 | } |
395 | { | 400 | catch (ObjectDisposedException e) |
396 | m_log.Error( | 401 | { |
397 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | 402 | m_log.Error( |
398 | } | 403 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); |
399 | catch (Exception e) | 404 | } |
400 | { | 405 | catch (Exception e) |
401 | m_log.Error( | 406 | { |
402 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | 407 | m_log.Error( |
403 | } | 408 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); |
404 | } | 409 | } |
405 | } | 410 | } |
406 | 411 | ||
@@ -465,14 +470,12 @@ namespace OpenMetaverse | |||
465 | } | 470 | } |
466 | finally | 471 | finally |
467 | { | 472 | { |
468 | // if (UsePools) | ||
469 | // Pool.ReturnObject(buffer); | ||
470 | |||
471 | AsyncBeginReceive(); | 473 | AsyncBeginReceive(); |
472 | } | 474 | } |
473 | } | 475 | } |
474 | } | 476 | } |
475 | 477 | ||
478 | /* not in use | ||
476 | public void AsyncBeginSend(UDPPacketBuffer buf) | 479 | public void AsyncBeginSend(UDPPacketBuffer buf) |
477 | { | 480 | { |
478 | // if (IsRunningOutbound) | 481 | // if (IsRunningOutbound) |
@@ -511,7 +514,7 @@ namespace OpenMetaverse | |||
511 | catch (SocketException) { } | 514 | catch (SocketException) { } |
512 | catch (ObjectDisposedException) { } | 515 | catch (ObjectDisposedException) { } |
513 | } | 516 | } |
514 | 517 | */ | |
515 | public void SyncSend(UDPPacketBuffer buf) | 518 | public void SyncSend(UDPPacketBuffer buf) |
516 | { | 519 | { |
517 | try | 520 | try |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs index 76f4c6f..e0eee53 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs | |||
@@ -203,6 +203,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
203 | if (ackedPacket != null) | 203 | if (ackedPacket != null) |
204 | { | 204 | { |
205 | m_packets.Remove(pendingAcknowledgement.SequenceNumber); | 205 | m_packets.Remove(pendingAcknowledgement.SequenceNumber); |
206 | ackedPacket.Client.FreeUDPBuffer(ackedPacket.Buffer); | ||
206 | 207 | ||
207 | // As with other network applications, assume that an acknowledged packet is an | 208 | // As with other network applications, assume that an acknowledged packet is an |
208 | // indication that the network can handle a little more load, speed up the transmission | 209 | // indication that the network can handle a little more load, speed up the transmission |
@@ -241,6 +242,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
241 | if (removedPacket != null) | 242 | if (removedPacket != null) |
242 | { | 243 | { |
243 | m_packets.Remove(pendingRemove); | 244 | m_packets.Remove(pendingRemove); |
245 | removedPacket.Client.FreeUDPBuffer(removedPacket.Buffer); | ||
244 | 246 | ||
245 | // Update stats | 247 | // Update stats |
246 | Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); | 248 | Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); |