diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 428 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 106 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 2 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs | 14 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs | 288 |
5 files changed, 491 insertions, 347 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 1c3ecb2..803114f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | |||
@@ -301,77 +301,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
301 | /// <summary>Used to adjust Sun Orbit values so Linden based viewers properly position sun</summary> | 301 | /// <summary>Used to adjust Sun Orbit values so Linden based viewers properly position sun</summary> |
302 | private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; | 302 | private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; |
303 | 303 | ||
304 | // First log file or time has expired, start writing to a new log file | ||
305 | //<MIC> | ||
306 | // ----------------------------------------------------------------- | ||
307 | // ----------------------------------------------------------------- | ||
308 | // THIS IS DEBUGGING CODE & SHOULD BE REMOVED | ||
309 | // ----------------------------------------------------------------- | ||
310 | // ----------------------------------------------------------------- | ||
311 | public class QueueLogger | ||
312 | { | ||
313 | public Int32 start = 0; | ||
314 | public StreamWriter Log = null; | ||
315 | private Dictionary<UUID,int> m_idMap = new Dictionary<UUID,int>(); | ||
316 | |||
317 | public QueueLogger() | ||
318 | { | ||
319 | DateTime now = DateTime.Now; | ||
320 | String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); | ||
321 | Log = new StreamWriter(fname); | ||
322 | |||
323 | start = Util.EnvironmentTickCount(); | ||
324 | } | ||
325 | |||
326 | public int LookupID(UUID uuid) | ||
327 | { | ||
328 | int localid; | ||
329 | if (! m_idMap.TryGetValue(uuid,out localid)) | ||
330 | { | ||
331 | localid = m_idMap.Count + 1; | ||
332 | m_idMap[uuid] = localid; | ||
333 | } | ||
334 | |||
335 | return localid; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | public static QueueLogger QueueLog = null; | ||
340 | |||
341 | // ----------------------------------------------------------------- | ||
342 | public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) | ||
343 | { | ||
344 | if (QueueLog == null) | ||
345 | QueueLog = new QueueLogger(); | ||
346 | |||
347 | Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); | ||
348 | lock(QueueLog) | ||
349 | { | ||
350 | int cid = QueueLog.LookupID(client); | ||
351 | int aid = QueueLog.LookupID(avatar); | ||
352 | QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | // ----------------------------------------------------------------- | ||
357 | public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) | ||
358 | { | ||
359 | if (QueueLog == null) | ||
360 | QueueLog = new QueueLogger(); | ||
361 | |||
362 | Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); | ||
363 | lock(QueueLog) | ||
364 | { | ||
365 | int cid = QueueLog.LookupID(client); | ||
366 | QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); | ||
367 | } | ||
368 | } | ||
369 | // ----------------------------------------------------------------- | ||
370 | // ----------------------------------------------------------------- | ||
371 | // ----------------------------------------------------------------- | ||
372 | // ----------------------------------------------------------------- | ||
373 | //</MIC> | ||
374 | |||
375 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 304 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
376 | protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients | 305 | protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients |
377 | 306 | ||
@@ -387,6 +316,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
387 | 316 | ||
388 | private int m_cachedTextureSerial; | 317 | private int m_cachedTextureSerial; |
389 | private PriorityQueue m_entityUpdates; | 318 | private PriorityQueue m_entityUpdates; |
319 | private PriorityQueue m_entityProps; | ||
390 | private Prioritizer m_prioritizer; | 320 | private Prioritizer m_prioritizer; |
391 | private bool m_disableFacelights = false; | 321 | private bool m_disableFacelights = false; |
392 | 322 | ||
@@ -435,9 +365,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
435 | protected IAssetService m_assetService; | 365 | protected IAssetService m_assetService; |
436 | private const bool m_checkPackets = true; | 366 | private const bool m_checkPackets = true; |
437 | 367 | ||
438 | private Timer m_propertiesPacketTimer; | ||
439 | private List<ObjectPropertiesPacket.ObjectDataBlock> m_propertiesBlocks = new List<ObjectPropertiesPacket.ObjectDataBlock>(); | ||
440 | |||
441 | #endregion Class Members | 368 | #endregion Class Members |
442 | 369 | ||
443 | #region Properties | 370 | #region Properties |
@@ -521,6 +448,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
521 | m_scene = scene; | 448 | m_scene = scene; |
522 | 449 | ||
523 | m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); | 450 | m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); |
451 | m_entityProps = new PriorityQueue(m_scene.Entities.Count); | ||
524 | m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>(); | 452 | m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>(); |
525 | m_killRecord = new HashSet<uint>(); | 453 | m_killRecord = new HashSet<uint>(); |
526 | // m_attachmentsSent = new HashSet<uint>(); | 454 | // m_attachmentsSent = new HashSet<uint>(); |
@@ -544,9 +472,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
544 | m_udpClient.OnQueueEmpty += HandleQueueEmpty; | 472 | m_udpClient.OnQueueEmpty += HandleQueueEmpty; |
545 | m_udpClient.OnPacketStats += PopulateStats; | 473 | m_udpClient.OnPacketStats += PopulateStats; |
546 | 474 | ||
547 | m_propertiesPacketTimer = new Timer(100); | ||
548 | m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket; | ||
549 | |||
550 | m_prioritizer = new Prioritizer(m_scene); | 475 | m_prioritizer = new Prioritizer(m_scene); |
551 | 476 | ||
552 | RegisterLocalPacketHandlers(); | 477 | RegisterLocalPacketHandlers(); |
@@ -2468,7 +2393,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2468 | 2393 | ||
2469 | packet.Effect = effectBlocks; | 2394 | packet.Effect = effectBlocks; |
2470 | 2395 | ||
2471 | OutPacket(packet, ThrottleOutPacketType.State); | 2396 | // OutPacket(packet, ThrottleOutPacketType.State); |
2397 | OutPacket(packet, ThrottleOutPacketType.Task); | ||
2472 | } | 2398 | } |
2473 | 2399 | ||
2474 | public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, | 2400 | public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, |
@@ -3670,9 +3596,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3670 | m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); | 3596 | m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); |
3671 | } | 3597 | } |
3672 | 3598 | ||
3673 | private Int32 m_LastQueueFill = 0; | ||
3674 | private uint m_maxUpdates = 0; | ||
3675 | |||
3676 | private void ProcessEntityUpdates(int maxUpdates) | 3599 | private void ProcessEntityUpdates(int maxUpdates) |
3677 | { | 3600 | { |
3678 | OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); | 3601 | OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); |
@@ -3680,41 +3603,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3680 | OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); | 3603 | OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); |
3681 | OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); | 3604 | OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); |
3682 | 3605 | ||
3606 | // Check to see if this is a flush | ||
3683 | if (maxUpdates <= 0) | 3607 | if (maxUpdates <= 0) |
3684 | { | 3608 | { |
3685 | m_maxUpdates = Int32.MaxValue; | 3609 | maxUpdates = Int32.MaxValue; |
3686 | } | ||
3687 | else | ||
3688 | { | ||
3689 | if (m_maxUpdates == 0 || m_LastQueueFill == 0) | ||
3690 | { | ||
3691 | m_maxUpdates = (uint)maxUpdates; | ||
3692 | } | ||
3693 | else | ||
3694 | { | ||
3695 | if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) | ||
3696 | m_maxUpdates += 5; | ||
3697 | else | ||
3698 | m_maxUpdates = m_maxUpdates >> 1; | ||
3699 | } | ||
3700 | m_maxUpdates = Util.Clamp<uint>(m_maxUpdates,10,500); | ||
3701 | } | 3610 | } |
3702 | m_LastQueueFill = Util.EnvironmentTickCount(); | 3611 | |
3703 | |||
3704 | int updatesThisCall = 0; | 3612 | int updatesThisCall = 0; |
3705 | 3613 | ||
3706 | // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race | 3614 | // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race |
3707 | // condition where a kill can be processed before an out-of-date update for the same object. | 3615 | // condition where a kill can be processed before an out-of-date update for the same object. |
3708 | float avgTimeDilation = 1.0f; | 3616 | float avgTimeDilation = 1.0f; |
3709 | 3617 | IEntityUpdate iupdate; | |
3710 | EntityUpdate update; | ||
3711 | Int32 timeinqueue; // this is just debugging code & can be dropped later | 3618 | Int32 timeinqueue; // this is just debugging code & can be dropped later |
3712 | 3619 | ||
3713 | while (updatesThisCall < m_maxUpdates) | 3620 | while (updatesThisCall < maxUpdates) |
3714 | { | 3621 | { |
3715 | lock (m_entityUpdates.SyncRoot) | 3622 | lock (m_entityUpdates.SyncRoot) |
3716 | if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) | 3623 | if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue)) |
3717 | break; | 3624 | break; |
3625 | |||
3626 | EntityUpdate update = (EntityUpdate)iupdate; | ||
3627 | |||
3718 | avgTimeDilation += update.TimeDilation; | 3628 | avgTimeDilation += update.TimeDilation; |
3719 | avgTimeDilation *= 0.5f; | 3629 | avgTimeDilation *= 0.5f; |
3720 | 3630 | ||
@@ -3770,7 +3680,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3770 | if (!found) | 3680 | if (!found) |
3771 | continue; | 3681 | continue; |
3772 | } | 3682 | } |
3773 | |||
3774 | if (part.ParentGroup.IsAttachment && m_disableFacelights) | 3683 | if (part.ParentGroup.IsAttachment && m_disableFacelights) |
3775 | { | 3684 | { |
3776 | if (part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.LeftHand && | 3685 | if (part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.LeftHand && |
@@ -3785,7 +3694,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3785 | 3694 | ||
3786 | #region UpdateFlags to packet type conversion | 3695 | #region UpdateFlags to packet type conversion |
3787 | 3696 | ||
3788 | PrimUpdateFlags updateFlags = update.Flags; | 3697 | PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags; |
3789 | 3698 | ||
3790 | bool canUseCompressed = true; | 3699 | bool canUseCompressed = true; |
3791 | bool canUseImproved = true; | 3700 | bool canUseImproved = true; |
@@ -3960,12 +3869,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3960 | 3869 | ||
3961 | #endregion Primitive Packet/Data Sending Methods | 3870 | #endregion Primitive Packet/Data Sending Methods |
3962 | 3871 | ||
3872 | // These are used to implement an adaptive backoff in the number | ||
3873 | // of updates converted to packets. Since we don't want packets | ||
3874 | // to sit in the queue with old data, only convert enough updates | ||
3875 | // to packets that can be sent in 200ms. | ||
3876 | private Int32 m_LastQueueFill = 0; | ||
3877 | private Int32 m_maxUpdates = 0; | ||
3878 | |||
3963 | void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) | 3879 | void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) |
3964 | { | 3880 | { |
3965 | if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) | 3881 | if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) |
3966 | { | 3882 | { |
3883 | if (m_maxUpdates == 0 || m_LastQueueFill == 0) | ||
3884 | { | ||
3885 | m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; | ||
3886 | } | ||
3887 | else | ||
3888 | { | ||
3889 | if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) | ||
3890 | m_maxUpdates += 5; | ||
3891 | else | ||
3892 | m_maxUpdates = m_maxUpdates >> 1; | ||
3893 | } | ||
3894 | m_maxUpdates = Util.Clamp<Int32>(m_maxUpdates,10,500); | ||
3895 | m_LastQueueFill = Util.EnvironmentTickCount(); | ||
3896 | |||
3967 | if (m_entityUpdates.Count > 0) | 3897 | if (m_entityUpdates.Count > 0) |
3968 | ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); | 3898 | ProcessEntityUpdates(m_maxUpdates); |
3899 | |||
3900 | if (m_entityProps.Count > 0) | ||
3901 | ProcessEntityPropertyRequests(m_maxUpdates); | ||
3969 | } | 3902 | } |
3970 | 3903 | ||
3971 | if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) | 3904 | if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) |
@@ -4079,135 +4012,206 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4079 | OutPacket(pack, ThrottleOutPacketType.Task); | 4012 | OutPacket(pack, ThrottleOutPacketType.Task); |
4080 | } | 4013 | } |
4081 | 4014 | ||
4082 | public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, | 4015 | private class ObjectPropertyUpdate : IEntityUpdate |
4083 | uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, | ||
4084 | uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, | ||
4085 | UUID LastOwnerID, string ObjectName, string Description) | ||
4086 | { | 4016 | { |
4087 | ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); | 4017 | internal bool SendFamilyProps; |
4088 | // TODO: don't create new blocks if recycling an old packet | 4018 | internal bool SendObjectProps; |
4089 | 4019 | ||
4090 | ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); | 4020 | public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj) |
4091 | objPropDB.RequestFlags = RequestFlags; | 4021 | : base(entity,flags) |
4092 | objPropDB.ObjectID = ObjectUUID; | 4022 | { |
4093 | if (OwnerID == GroupID) | 4023 | SendFamilyProps = sendfam; |
4094 | objPropDB.OwnerID = UUID.Zero; | 4024 | SendObjectProps = sendobj; |
4095 | else | 4025 | } |
4096 | objPropDB.OwnerID = OwnerID; | 4026 | public void Update(ObjectPropertyUpdate update) |
4097 | objPropDB.GroupID = GroupID; | 4027 | { |
4098 | objPropDB.BaseMask = BaseMask; | 4028 | SendFamilyProps = SendFamilyProps || update.SendFamilyProps; |
4099 | objPropDB.OwnerMask = OwnerMask; | 4029 | SendObjectProps = SendObjectProps || update.SendObjectProps; |
4100 | objPropDB.GroupMask = GroupMask; | 4030 | Flags |= update.Flags; |
4101 | objPropDB.EveryoneMask = EveryoneMask; | 4031 | } |
4102 | objPropDB.NextOwnerMask = NextOwnerMask; | 4032 | } |
4103 | 4033 | ||
4104 | // TODO: More properties are needed in SceneObjectPart! | 4034 | public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) |
4105 | objPropDB.OwnershipCost = OwnershipCost; | ||
4106 | objPropDB.SaleType = SaleType; | ||
4107 | objPropDB.SalePrice = SalePrice; | ||
4108 | objPropDB.Category = Category; | ||
4109 | objPropDB.LastOwnerID = LastOwnerID; | ||
4110 | objPropDB.Name = Util.StringToBytes256(ObjectName); | ||
4111 | objPropDB.Description = Util.StringToBytes256(Description); | ||
4112 | objPropFamilyPack.ObjectData = objPropDB; | ||
4113 | objPropFamilyPack.Header.Zerocoded = true; | ||
4114 | OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task); | ||
4115 | } | ||
4116 | |||
4117 | public void SendObjectPropertiesReply( | ||
4118 | UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, | ||
4119 | UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, | ||
4120 | UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, | ||
4121 | string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, | ||
4122 | uint BaseMask, byte saleType, int salePrice) | ||
4123 | { | 4035 | { |
4124 | //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); | 4036 | uint priority = 0; // time based ordering only |
4125 | // TODO: don't create new blocks if recycling an old packet | 4037 | lock (m_entityProps.SyncRoot) |
4038 | m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); | ||
4039 | } | ||
4126 | 4040 | ||
4127 | ObjectPropertiesPacket.ObjectDataBlock block = | 4041 | public void SendObjectPropertiesReply(ISceneEntity entity) |
4128 | new ObjectPropertiesPacket.ObjectDataBlock(); | 4042 | { |
4043 | uint priority = 0; // time based ordering only | ||
4044 | lock (m_entityProps.SyncRoot) | ||
4045 | m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true)); | ||
4046 | } | ||
4129 | 4047 | ||
4130 | block.ItemID = ItemID; | 4048 | private void ProcessEntityPropertyRequests(int maxUpdates) |
4131 | block.CreationDate = CreationDate; | 4049 | { |
4132 | block.CreatorID = CreatorUUID; | 4050 | OpenSim.Framework.Lazy<List<ObjectPropertiesFamilyPacket.ObjectDataBlock>> objectFamilyBlocks = |
4133 | block.FolderID = FolderUUID; | 4051 | new OpenSim.Framework.Lazy<List<ObjectPropertiesFamilyPacket.ObjectDataBlock>>(); |
4134 | block.FromTaskID = FromTaskUUID; | ||
4135 | block.GroupID = GroupUUID; | ||
4136 | block.InventorySerial = InventorySerial; | ||
4137 | 4052 | ||
4138 | block.LastOwnerID = LastOwnerUUID; | 4053 | OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks = |
4139 | // proper.ObjectData[0].LastOwnerID = UUID.Zero; | 4054 | new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>(); |
4140 | 4055 | ||
4141 | block.ObjectID = ObjectUUID; | 4056 | IEntityUpdate iupdate; |
4142 | if (OwnerUUID == GroupUUID) | 4057 | Int32 timeinqueue; // this is just debugging code & can be dropped later |
4143 | block.OwnerID = UUID.Zero; | ||
4144 | else | ||
4145 | block.OwnerID = OwnerUUID; | ||
4146 | block.TouchName = Util.StringToBytes256(TouchTitle); | ||
4147 | block.TextureID = TextureID; | ||
4148 | block.SitName = Util.StringToBytes256(SitTitle); | ||
4149 | block.Name = Util.StringToBytes256(ItemName); | ||
4150 | block.Description = Util.StringToBytes256(ItemDescription); | ||
4151 | block.OwnerMask = OwnerMask; | ||
4152 | block.NextOwnerMask = NextOwnerMask; | ||
4153 | block.GroupMask = GroupMask; | ||
4154 | block.EveryoneMask = EveryoneMask; | ||
4155 | block.BaseMask = BaseMask; | ||
4156 | // proper.ObjectData[0].AggregatePerms = 53; | ||
4157 | // proper.ObjectData[0].AggregatePermTextures = 0; | ||
4158 | // proper.ObjectData[0].AggregatePermTexturesOwner = 0; | ||
4159 | block.SaleType = saleType; | ||
4160 | block.SalePrice = salePrice; | ||
4161 | 4058 | ||
4162 | lock (m_propertiesPacketTimer) | 4059 | int updatesThisCall = 0; |
4060 | while (updatesThisCall < m_maxUpdates) | ||
4163 | { | 4061 | { |
4164 | m_propertiesBlocks.Add(block); | 4062 | lock (m_entityProps.SyncRoot) |
4063 | if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue)) | ||
4064 | break; | ||
4165 | 4065 | ||
4166 | int length = 0; | 4066 | ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate; |
4167 | foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) | 4067 | if (update.SendFamilyProps) |
4168 | { | 4068 | { |
4169 | length += b.Length; | 4069 | if (update.Entity is SceneObjectPart) |
4070 | { | ||
4071 | SceneObjectPart sop = (SceneObjectPart)update.Entity; | ||
4072 | ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); | ||
4073 | objectFamilyBlocks.Value.Add(objPropDB); | ||
4074 | } | ||
4170 | } | 4075 | } |
4171 | if (length > 1100) // FIXME: use real MTU | 4076 | |
4077 | if (update.SendObjectProps) | ||
4172 | { | 4078 | { |
4173 | ProcessObjectPropertiesPacket(null, null); | 4079 | if (update.Entity is SceneObjectPart) |
4174 | m_propertiesPacketTimer.Stop(); | 4080 | { |
4175 | return; | 4081 | SceneObjectPart sop = (SceneObjectPart)update.Entity; |
4082 | ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); | ||
4083 | objectPropertiesBlocks.Value.Add(objPropDB); | ||
4084 | } | ||
4176 | } | 4085 | } |
4177 | 4086 | ||
4178 | m_propertiesPacketTimer.Stop(); | 4087 | updatesThisCall++; |
4179 | m_propertiesPacketTimer.Start(); | ||
4180 | } | 4088 | } |
4089 | |||
4181 | 4090 | ||
4182 | //proper.Header.Zerocoded = true; | 4091 | Int32 ppcnt = 0; |
4183 | //OutPacket(proper, ThrottleOutPacketType.Task); | 4092 | Int32 pbcnt = 0; |
4184 | } | 4093 | |
4094 | if (objectPropertiesBlocks.IsValueCreated) | ||
4095 | { | ||
4096 | List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value; | ||
4185 | 4097 | ||
4186 | private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e) | 4098 | ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); |
4187 | { | 4099 | packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; |
4188 | ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); | 4100 | for (int i = 0; i < blocks.Count; i++) |
4101 | packet.ObjectData[i] = blocks[i]; | ||
4189 | 4102 | ||
4190 | lock (m_propertiesPacketTimer) | 4103 | packet.Header.Zerocoded = true; |
4104 | OutPacket(packet, ThrottleOutPacketType.Task, true); | ||
4105 | |||
4106 | pbcnt += blocks.Count; | ||
4107 | ppcnt++; | ||
4108 | } | ||
4109 | |||
4110 | Int32 fpcnt = 0; | ||
4111 | Int32 fbcnt = 0; | ||
4112 | |||
4113 | if (objectFamilyBlocks.IsValueCreated) | ||
4191 | { | 4114 | { |
4192 | m_propertiesPacketTimer.Stop(); | 4115 | List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value; |
4193 | 4116 | ||
4194 | if (m_propertiesBlocks.Count == 0) | 4117 | // ObjectPropertiesFamilyPacket objPropFamilyPack = |
4195 | return; | 4118 | // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); |
4119 | // | ||
4120 | // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count]; | ||
4121 | // for (int i = 0; i < blocks.Count; i++) | ||
4122 | // objPropFamilyPack.ObjectData[i] = blocks[i]; | ||
4123 | // | ||
4124 | // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true); | ||
4196 | 4125 | ||
4197 | proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; | 4126 | // one packet per object block... uggh... |
4127 | for (int i = 0; i < blocks.Count; i++) | ||
4128 | { | ||
4129 | ObjectPropertiesFamilyPacket packet = | ||
4130 | (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); | ||
4198 | 4131 | ||
4199 | int index = 0; | 4132 | packet.ObjectData = blocks[i]; |
4133 | packet.Header.Zerocoded = true; | ||
4134 | OutPacket(packet, ThrottleOutPacketType.Task); | ||
4200 | 4135 | ||
4201 | foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) | 4136 | fpcnt++; |
4202 | { | 4137 | fbcnt++; |
4203 | proper.ObjectData[index++] = b; | ||
4204 | } | 4138 | } |
4205 | 4139 | ||
4206 | m_propertiesBlocks.Clear(); | ||
4207 | } | 4140 | } |
4141 | |||
4142 | // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); | ||
4143 | // m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); | ||
4144 | } | ||
4145 | |||
4146 | private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) | ||
4147 | { | ||
4148 | ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); | ||
4208 | 4149 | ||
4209 | proper.Header.Zerocoded = true; | 4150 | block.RequestFlags = requestFlags; |
4210 | OutPacket(proper, ThrottleOutPacketType.Task); | 4151 | block.ObjectID = sop.UUID; |
4152 | if (sop.OwnerID == sop.GroupID) | ||
4153 | block.OwnerID = UUID.Zero; | ||
4154 | else | ||
4155 | block.OwnerID = sop.OwnerID; | ||
4156 | block.GroupID = sop.GroupID; | ||
4157 | block.BaseMask = sop.BaseMask; | ||
4158 | block.OwnerMask = sop.OwnerMask; | ||
4159 | block.GroupMask = sop.GroupMask; | ||
4160 | block.EveryoneMask = sop.EveryoneMask; | ||
4161 | block.NextOwnerMask = sop.NextOwnerMask; | ||
4162 | |||
4163 | // TODO: More properties are needed in SceneObjectPart! | ||
4164 | block.OwnershipCost = sop.OwnershipCost; | ||
4165 | block.SaleType = sop.ObjectSaleType; | ||
4166 | block.SalePrice = sop.SalePrice; | ||
4167 | block.Category = sop.Category; | ||
4168 | block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right? | ||
4169 | block.Name = Util.StringToBytes256(sop.Name); | ||
4170 | block.Description = Util.StringToBytes256(sop.Description); | ||
4171 | |||
4172 | return block; | ||
4173 | } | ||
4174 | |||
4175 | private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) | ||
4176 | { | ||
4177 | //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); | ||
4178 | // TODO: don't create new blocks if recycling an old packet | ||
4179 | |||
4180 | ObjectPropertiesPacket.ObjectDataBlock block = | ||
4181 | new ObjectPropertiesPacket.ObjectDataBlock(); | ||
4182 | |||
4183 | block.ObjectID = sop.UUID; | ||
4184 | block.Name = Util.StringToBytes256(sop.Name); | ||
4185 | block.Description = Util.StringToBytes256(sop.Description); | ||
4186 | |||
4187 | block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds | ||
4188 | block.CreatorID = sop.CreatorID; | ||
4189 | block.GroupID = sop.GroupID; | ||
4190 | block.LastOwnerID = sop.LastOwnerID; | ||
4191 | if (sop.OwnerID == sop.GroupID) | ||
4192 | block.OwnerID = UUID.Zero; | ||
4193 | else | ||
4194 | block.OwnerID = sop.OwnerID; | ||
4195 | |||
4196 | block.ItemID = sop.FromUserInventoryItemID; | ||
4197 | block.FolderID = UUID.Zero; // sop.FromFolderID ?? | ||
4198 | block.FromTaskID = UUID.Zero; // ??? | ||
4199 | block.InventorySerial = (short)sop.InventorySerial; | ||
4200 | |||
4201 | SceneObjectPart root = sop.ParentGroup.RootPart; | ||
4202 | |||
4203 | block.TouchName = Util.StringToBytes256(root.TouchName); | ||
4204 | block.TextureID = new byte[0]; // TextureID ??? | ||
4205 | block.SitName = Util.StringToBytes256(root.SitName); | ||
4206 | block.OwnerMask = root.OwnerMask; | ||
4207 | block.NextOwnerMask = root.NextOwnerMask; | ||
4208 | block.GroupMask = root.GroupMask; | ||
4209 | block.EveryoneMask = root.EveryoneMask; | ||
4210 | block.BaseMask = root.BaseMask; | ||
4211 | block.SaleType = root.ObjectSaleType; | ||
4212 | block.SalePrice = root.SalePrice; | ||
4213 | |||
4214 | return block; | ||
4211 | } | 4215 | } |
4212 | 4216 | ||
4213 | #region Estate Data Sending Methods | 4217 | #region Estate Data Sending Methods |
@@ -4548,6 +4552,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4548 | 4552 | ||
4549 | public void SendForceClientSelectObjects(List<uint> ObjectIDs) | 4553 | public void SendForceClientSelectObjects(List<uint> ObjectIDs) |
4550 | { | 4554 | { |
4555 | m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); | ||
4556 | |||
4551 | bool firstCall = true; | 4557 | bool firstCall = true; |
4552 | const int MAX_OBJECTS_PER_PACKET = 251; | 4558 | const int MAX_OBJECTS_PER_PACKET = 251; |
4553 | ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); | 4559 | ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); |
@@ -11491,7 +11497,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11491 | if (logPacket) | 11497 | if (logPacket) |
11492 | m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); | 11498 | m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); |
11493 | } | 11499 | } |
11494 | 11500 | ||
11495 | m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); | 11501 | m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); |
11496 | } | 11502 | } |
11497 | 11503 | ||
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 0fa074d..01d7122 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | |||
@@ -135,7 +135,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
135 | private int m_nextOnQueueEmpty = 1; | 135 | private int m_nextOnQueueEmpty = 1; |
136 | 136 | ||
137 | /// <summary>Throttle bucket for this agent's connection</summary> | 137 | /// <summary>Throttle bucket for this agent's connection</summary> |
138 | private readonly TokenBucket m_throttle; | 138 | private readonly TokenBucket m_throttleClient; |
139 | /// <summary>Throttle bucket for this agent's connection</summary> | ||
140 | private readonly TokenBucket m_throttleCategory; | ||
139 | /// <summary>Throttle buckets for each packet category</summary> | 141 | /// <summary>Throttle buckets for each packet category</summary> |
140 | private readonly TokenBucket[] m_throttleCategories; | 142 | private readonly TokenBucket[] m_throttleCategories; |
141 | /// <summary>Outgoing queues for throttled packets</summary> | 143 | /// <summary>Outgoing queues for throttled packets</summary> |
@@ -175,7 +177,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
175 | m_maxRTO = maxRTO; | 177 | m_maxRTO = maxRTO; |
176 | 178 | ||
177 | // Create a token bucket throttle for this client that has the scene token bucket as a parent | 179 | // Create a token bucket throttle for this client that has the scene token bucket as a parent |
178 | m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); | 180 | m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); |
181 | // Create a token bucket throttle for the total categary with the client bucket as a throttle | ||
182 | m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); | ||
179 | // Create an array of token buckets for this clients different throttle categories | 183 | // Create an array of token buckets for this clients different throttle categories |
180 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; | 184 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; |
181 | 185 | ||
@@ -186,7 +190,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
186 | // Initialize the packet outboxes, where packets sit while they are waiting for tokens | 190 | // Initialize the packet outboxes, where packets sit while they are waiting for tokens |
187 | m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); | 191 | m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); |
188 | // Initialize the token buckets that control the throttling for each category | 192 | // Initialize the token buckets that control the throttling for each category |
189 | m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); | 193 | m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type)); |
190 | } | 194 | } |
191 | 195 | ||
192 | // Default the retransmission timeout to three seconds | 196 | // Default the retransmission timeout to three seconds |
@@ -207,6 +211,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
207 | m_packetOutboxes[i].Clear(); | 211 | m_packetOutboxes[i].Clear(); |
208 | m_nextPackets[i] = null; | 212 | m_nextPackets[i] = null; |
209 | } | 213 | } |
214 | |||
215 | // pull the throttle out of the scene throttle | ||
216 | m_throttleClient.Parent.UnregisterRequest(m_throttleClient); | ||
210 | OnPacketStats = null; | 217 | OnPacketStats = null; |
211 | OnQueueEmpty = null; | 218 | OnQueueEmpty = null; |
212 | } | 219 | } |
@@ -217,6 +224,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
217 | /// <returns>Information about the client connection</returns> | 224 | /// <returns>Information about the client connection</returns> |
218 | public ClientInfo GetClientInfo() | 225 | public ClientInfo GetClientInfo() |
219 | { | 226 | { |
227 | ///<mic> | ||
228 | TokenBucket tb; | ||
229 | |||
230 | tb = m_throttleClient.Parent; | ||
231 | m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT"); | ||
232 | |||
233 | tb = m_throttleClient; | ||
234 | m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT"); | ||
235 | |||
236 | tb = m_throttleCategory; | ||
237 | m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY"); | ||
238 | |||
239 | for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) | ||
240 | { | ||
241 | tb = m_throttleCategories[i]; | ||
242 | m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET"); | ||
243 | } | ||
244 | |||
245 | ///</mic> | ||
246 | |||
220 | // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists | 247 | // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists |
221 | // of pending and needed ACKs for every client every time some method wants information about | 248 | // of pending and needed ACKs for every client every time some method wants information about |
222 | // this connection is a recipe for poor performance | 249 | // this connection is a recipe for poor performance |
@@ -224,13 +251,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
224 | info.pendingAcks = new Dictionary<uint, uint>(); | 251 | info.pendingAcks = new Dictionary<uint, uint>(); |
225 | info.needAck = new Dictionary<uint, byte[]>(); | 252 | info.needAck = new Dictionary<uint, byte[]>(); |
226 | 253 | ||
227 | info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; | 254 | info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; |
228 | info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; | 255 | info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; |
229 | info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; | 256 | info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; |
230 | info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; | 257 | info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; |
231 | info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; | 258 | // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; |
232 | info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; | 259 | info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; |
233 | info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; | 260 | info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; |
261 | info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; | ||
234 | info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + | 262 | info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + |
235 | info.taskThrottle + info.assetThrottle + info.textureThrottle; | 263 | info.taskThrottle + info.assetThrottle + info.textureThrottle; |
236 | 264 | ||
@@ -318,8 +346,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
318 | int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 346 | int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; |
319 | int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | 347 | int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); |
320 | // State is a subcategory of task that we allocate a percentage to | 348 | // State is a subcategory of task that we allocate a percentage to |
321 | int state = (int)((float)task * STATE_TASK_PERCENTAGE); | 349 | int state = 0; |
322 | task -= state; | 350 | // int state = (int)((float)task * STATE_TASK_PERCENTAGE); |
351 | // task -= state; | ||
323 | 352 | ||
324 | // Make sure none of the throttles are set below our packet MTU, | 353 | // Make sure none of the throttles are set below our packet MTU, |
325 | // otherwise a throttle could become permanently clogged | 354 | // otherwise a throttle could become permanently clogged |
@@ -340,40 +369,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
340 | // Update the token buckets with new throttle values | 369 | // Update the token buckets with new throttle values |
341 | TokenBucket bucket; | 370 | TokenBucket bucket; |
342 | 371 | ||
343 | bucket = m_throttle; | 372 | bucket = m_throttleCategory; |
344 | bucket.MaxBurst = total; | 373 | bucket.RequestedDripRate = total; |
345 | 374 | ||
346 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; | 375 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; |
347 | bucket.DripRate = resend; | 376 | bucket.RequestedDripRate = resend; |
348 | bucket.MaxBurst = resend; | ||
349 | 377 | ||
350 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; | 378 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; |
351 | bucket.DripRate = land; | 379 | bucket.RequestedDripRate = land; |
352 | bucket.MaxBurst = land; | ||
353 | 380 | ||
354 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; | 381 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; |
355 | bucket.DripRate = wind; | 382 | bucket.RequestedDripRate = wind; |
356 | bucket.MaxBurst = wind; | ||
357 | 383 | ||
358 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; | 384 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; |
359 | bucket.DripRate = cloud; | 385 | bucket.RequestedDripRate = cloud; |
360 | bucket.MaxBurst = cloud; | ||
361 | 386 | ||
362 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; | 387 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; |
363 | bucket.DripRate = asset; | 388 | bucket.RequestedDripRate = asset; |
364 | bucket.MaxBurst = asset; | ||
365 | 389 | ||
366 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; | 390 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; |
367 | bucket.DripRate = task + state; | 391 | bucket.RequestedDripRate = task; |
368 | bucket.MaxBurst = task + state; | ||
369 | 392 | ||
370 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; | 393 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; |
371 | bucket.DripRate = state; | 394 | bucket.RequestedDripRate = state; |
372 | bucket.MaxBurst = state; | ||
373 | 395 | ||
374 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; | 396 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; |
375 | bucket.DripRate = texture; | 397 | bucket.RequestedDripRate = texture; |
376 | bucket.MaxBurst = texture; | ||
377 | 398 | ||
378 | // Reset the packed throttles cached data | 399 | // Reset the packed throttles cached data |
379 | m_packedThrottles = null; | 400 | m_packedThrottles = null; |
@@ -388,14 +409,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
388 | data = new byte[7 * 4]; | 409 | data = new byte[7 * 4]; |
389 | int i = 0; | 410 | int i = 0; |
390 | 411 | ||
391 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; | 412 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4; |
392 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; | 413 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4; |
393 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; | 414 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4; |
394 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; | 415 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4; |
395 | Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + | 416 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4; |
396 | m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; | 417 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4; |
397 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; | 418 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4; |
398 | Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; | ||
399 | 419 | ||
400 | m_packedThrottles = data; | 420 | m_packedThrottles = data; |
401 | } | 421 | } |
@@ -428,6 +448,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
428 | 448 | ||
429 | TokenBucket bucket = m_throttleCategories[category]; | 449 | TokenBucket bucket = m_throttleCategories[category]; |
430 | 450 | ||
451 | // Don't send this packet if there is already a packet waiting in the queue | ||
452 | // even if we have the tokens to send it, tokens should go to the already | ||
453 | // queued packets | ||
454 | if (queue.Count > 0) | ||
455 | { | ||
456 | queue.Enqueue(packet); | ||
457 | return true; | ||
458 | } | ||
459 | |||
460 | |||
431 | if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength)) | 461 | if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength)) |
432 | { | 462 | { |
433 | // Enough tokens were removed from the bucket, the packet will not be queued | 463 | // Enough tokens were removed from the bucket, the packet will not be queued |
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index c865c0f..6decc7b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | |||
@@ -228,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
228 | } | 228 | } |
229 | #endregion BinaryStats | 229 | #endregion BinaryStats |
230 | 230 | ||
231 | m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); | 231 | m_throttle = new TokenBucket(null, sceneThrottleBps); |
232 | ThrottleRates = new ThrottleRates(configSource); | 232 | ThrottleRates = new ThrottleRates(configSource); |
233 | } | 233 | } |
234 | 234 | ||
diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 364ce4b..b62ec07 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs | |||
@@ -78,7 +78,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
78 | } | 78 | } |
79 | } | 79 | } |
80 | 80 | ||
81 | public bool Enqueue(uint pqueue, EntityUpdate value) | 81 | public bool Enqueue(uint pqueue, IEntityUpdate value) |
82 | { | 82 | { |
83 | LookupItem lookup; | 83 | LookupItem lookup; |
84 | 84 | ||
@@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
87 | if (m_lookupTable.TryGetValue(localid, out lookup)) | 87 | if (m_lookupTable.TryGetValue(localid, out lookup)) |
88 | { | 88 | { |
89 | entry = lookup.Heap[lookup.Handle].EntryOrder; | 89 | entry = lookup.Heap[lookup.Handle].EntryOrder; |
90 | value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; | 90 | value.Update(lookup.Heap[lookup.Handle].Value); |
91 | lookup.Heap.Remove(lookup.Handle); | 91 | lookup.Heap.Remove(lookup.Handle); |
92 | } | 92 | } |
93 | 93 | ||
@@ -99,7 +99,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
99 | return true; | 99 | return true; |
100 | } | 100 | } |
101 | 101 | ||
102 | internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) | 102 | internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) |
103 | { | 103 | { |
104 | for (int i = 0; i < m_numberOfQueues; ++i) | 104 | for (int i = 0; i < m_numberOfQueues; ++i) |
105 | { | 105 | { |
@@ -122,7 +122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
122 | } | 122 | } |
123 | 123 | ||
124 | timeinqueue = 0; | 124 | timeinqueue = 0; |
125 | value = default(EntityUpdate); | 125 | value = default(IEntityUpdate); |
126 | return false; | 126 | return false; |
127 | } | 127 | } |
128 | 128 | ||
@@ -175,8 +175,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
175 | #region MinHeapItem | 175 | #region MinHeapItem |
176 | private struct MinHeapItem : IComparable<MinHeapItem> | 176 | private struct MinHeapItem : IComparable<MinHeapItem> |
177 | { | 177 | { |
178 | private EntityUpdate value; | 178 | private IEntityUpdate value; |
179 | internal EntityUpdate Value { | 179 | internal IEntityUpdate Value { |
180 | get { | 180 | get { |
181 | return this.value; | 181 | return this.value; |
182 | } | 182 | } |
@@ -212,7 +212,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
212 | this.pqueue = pqueue; | 212 | this.pqueue = pqueue; |
213 | } | 213 | } |
214 | 214 | ||
215 | internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) | 215 | internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value) |
216 | { | 216 | { |
217 | this.entrytime = Util.EnvironmentTickCount(); | 217 | this.entrytime = Util.EnvironmentTickCount(); |
218 | this.entryorder = entryorder; | 218 | this.entryorder = entryorder; |
diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 91e3d20..07b0a1d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs | |||
@@ -26,6 +26,10 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Reflection; | ||
32 | using log4net; | ||
29 | 33 | ||
30 | namespace OpenSim.Region.ClientStack.LindenUDP | 34 | namespace OpenSim.Region.ClientStack.LindenUDP |
31 | { | 35 | { |
@@ -35,89 +39,126 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
35 | /// </summary> | 39 | /// </summary> |
36 | public class TokenBucket | 40 | public class TokenBucket |
37 | { | 41 | { |
38 | /// <summary>Parent bucket to this bucket, or null if this is a root | 42 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
39 | /// bucket</summary> | 43 | private static Int32 m_counter = 0; |
40 | TokenBucket parent; | 44 | |
41 | /// <summary>Size of the bucket in bytes. If zero, the bucket has | 45 | private Int32 m_identifier; |
42 | /// infinite capacity</summary> | 46 | |
43 | int maxBurst; | 47 | /// <summary> |
44 | /// <summary>Rate that the bucket fills, in bytes per millisecond. If | 48 | /// Number of ticks (ms) per quantum, drip rate and max burst |
45 | /// zero, the bucket always remains full</summary> | 49 | /// are defined over this interval. |
46 | int tokensPerMS; | 50 | /// </summary> |
47 | /// <summary>Number of tokens currently in the bucket</summary> | 51 | private const Int32 m_ticksPerQuantum = 1000; |
48 | int content; | 52 | |
53 | /// <summary> | ||
54 | /// This is the number of quantums worth of packets that can | ||
55 | /// be accommodated during a burst | ||
56 | /// </summary> | ||
57 | private const Double m_quantumsPerBurst = 1.5; | ||
58 | |||
59 | /// <summary> | ||
60 | /// </summary> | ||
61 | private const Int32 m_minimumDripRate = 1400; | ||
62 | |||
49 | /// <summary>Time of the last drip, in system ticks</summary> | 63 | /// <summary>Time of the last drip, in system ticks</summary> |
50 | int lastDrip; | 64 | private Int32 m_lastDrip; |
65 | |||
66 | /// <summary> | ||
67 | /// The number of bytes that can be sent at this moment. This is the | ||
68 | /// current number of tokens in the bucket | ||
69 | /// </summary> | ||
70 | private Int64 m_tokenCount; | ||
51 | 71 | ||
52 | #region Properties | 72 | /// <summary> |
73 | /// Map of children buckets and their requested maximum burst rate | ||
74 | /// </summary> | ||
75 | private Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); | ||
76 | |||
77 | #region Properties | ||
53 | 78 | ||
54 | /// <summary> | 79 | /// <summary> |
55 | /// The parent bucket of this bucket, or null if this bucket has no | 80 | /// The parent bucket of this bucket, or null if this bucket has no |
56 | /// parent. The parent bucket will limit the aggregate bandwidth of all | 81 | /// parent. The parent bucket will limit the aggregate bandwidth of all |
57 | /// of its children buckets | 82 | /// of its children buckets |
58 | /// </summary> | 83 | /// </summary> |
84 | private TokenBucket m_parent; | ||
59 | public TokenBucket Parent | 85 | public TokenBucket Parent |
60 | { | 86 | { |
61 | get { return parent; } | 87 | get { return m_parent; } |
88 | set { m_parent = value; } | ||
62 | } | 89 | } |
63 | 90 | ||
64 | /// <summary> | 91 | /// <summary> |
65 | /// Maximum burst rate in bytes per second. This is the maximum number | 92 | /// Maximum burst rate in bytes per second. This is the maximum number |
66 | /// of tokens that can accumulate in the bucket at any one time | 93 | /// of tokens that can accumulate in the bucket at any one time. This |
94 | /// also sets the total request for leaf nodes | ||
67 | /// </summary> | 95 | /// </summary> |
68 | public int MaxBurst | 96 | private Int64 m_burstRate; |
97 | public Int64 RequestedBurstRate | ||
69 | { | 98 | { |
70 | get { return maxBurst; } | 99 | get { return m_burstRate; } |
71 | set { maxBurst = (value >= 0 ? value : 0); } | 100 | set { m_burstRate = (value < 0 ? 0 : value); } |
72 | } | 101 | } |
73 | 102 | ||
103 | public Int64 BurstRate | ||
104 | { | ||
105 | get { | ||
106 | double rate = RequestedBurstRate * BurstRateModifier(); | ||
107 | if (rate < m_minimumDripRate * m_quantumsPerBurst) | ||
108 | rate = m_minimumDripRate * m_quantumsPerBurst; | ||
109 | |||
110 | return (Int64) rate; | ||
111 | } | ||
112 | } | ||
113 | |||
74 | /// <summary> | 114 | /// <summary> |
75 | /// The speed limit of this bucket in bytes per second. This is the | 115 | /// The speed limit of this bucket in bytes per second. This is the |
76 | /// number of tokens that are added to the bucket per second | 116 | /// number of tokens that are added to the bucket per quantum |
77 | /// </summary> | 117 | /// </summary> |
78 | /// <remarks>Tokens are added to the bucket any time | 118 | /// <remarks>Tokens are added to the bucket any time |
79 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of | 119 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of |
80 | /// the system tick interval (typically around 15-22ms)</remarks> | 120 | /// the system tick interval (typically around 15-22ms)</remarks> |
81 | public int DripRate | 121 | private Int64 m_dripRate; |
122 | public Int64 RequestedDripRate | ||
82 | { | 123 | { |
83 | get { return tokensPerMS * 1000; } | 124 | get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } |
84 | set | 125 | set { |
85 | { | 126 | m_dripRate = (value < 0 ? 0 : value); |
86 | if (value == 0) | 127 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); |
87 | tokensPerMS = 0; | 128 | m_totalDripRequest = m_dripRate; |
88 | else | 129 | if (m_parent != null) |
89 | { | 130 | m_parent.RegisterRequest(this,m_dripRate); |
90 | int bpms = (int)((float)value / 1000.0f); | ||
91 | |||
92 | if (bpms <= 0) | ||
93 | tokensPerMS = 1; // 1 byte/ms is the minimum granularity | ||
94 | else | ||
95 | tokensPerMS = bpms; | ||
96 | } | ||
97 | } | 131 | } |
98 | } | 132 | } |
99 | 133 | ||
100 | /// <summary> | 134 | public Int64 DripRate |
101 | /// The speed limit of this bucket in bytes per millisecond | ||
102 | /// </summary> | ||
103 | public int DripPerMS | ||
104 | { | 135 | { |
105 | get { return tokensPerMS; } | 136 | get { |
137 | if (m_parent == null) | ||
138 | return Math.Min(RequestedDripRate,TotalDripRequest); | ||
139 | |||
140 | double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); | ||
141 | if (rate < m_minimumDripRate) | ||
142 | rate = m_minimumDripRate; | ||
143 | |||
144 | return (Int64)rate; | ||
145 | } | ||
106 | } | 146 | } |
107 | 147 | ||
108 | /// <summary> | 148 | /// <summary> |
109 | /// The number of bytes that can be sent at this moment. This is the | 149 | /// The current total of the requested maximum burst rates of |
110 | /// current number of tokens in the bucket | 150 | /// this bucket's children buckets. |
111 | /// <remarks>If this bucket has a parent bucket that does not have | ||
112 | /// enough tokens for a request, <seealso cref="RemoveTokens"/> will | ||
113 | /// return false regardless of the content of this bucket</remarks> | ||
114 | /// </summary> | 151 | /// </summary> |
115 | public int Content | 152 | private Int64 m_totalDripRequest; |
116 | { | 153 | public Int64 TotalDripRequest |
117 | get { return content; } | 154 | { |
118 | } | 155 | get { return m_totalDripRequest; } |
156 | set { m_totalDripRequest = value; } | ||
157 | } | ||
158 | |||
159 | #endregion Properties | ||
119 | 160 | ||
120 | #endregion Properties | 161 | #region Constructor |
121 | 162 | ||
122 | /// <summary> | 163 | /// <summary> |
123 | /// Default constructor | 164 | /// Default constructor |
@@ -128,46 +169,114 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
128 | /// zero if this bucket has no maximum capacity</param> | 169 | /// zero if this bucket has no maximum capacity</param> |
129 | /// <param name="dripRate">Rate that the bucket fills, in bytes per | 170 | /// <param name="dripRate">Rate that the bucket fills, in bytes per |
130 | /// second. If zero, the bucket always remains full</param> | 171 | /// second. If zero, the bucket always remains full</param> |
131 | public TokenBucket(TokenBucket parent, int maxBurst, int dripRate) | 172 | public TokenBucket(TokenBucket parent, Int64 dripRate) |
173 | { | ||
174 | m_identifier = m_counter++; | ||
175 | |||
176 | Parent = parent; | ||
177 | RequestedDripRate = dripRate; | ||
178 | // TotalDripRequest = dripRate; // this will be overwritten when a child node registers | ||
179 | // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); | ||
180 | m_lastDrip = Environment.TickCount & Int32.MaxValue; | ||
181 | } | ||
182 | |||
183 | #endregion Constructor | ||
184 | |||
185 | /// <summary> | ||
186 | /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning | ||
187 | /// no modification if the requested bandwidth is less than the | ||
188 | /// max burst bandwidth all the way to the root of the throttle | ||
189 | /// hierarchy. However, if any of the parents is over-booked, then | ||
190 | /// the modifier will be less than 1. | ||
191 | /// </summary> | ||
192 | private double DripRateModifier() | ||
193 | { | ||
194 | Int64 driprate = DripRate; | ||
195 | return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; | ||
196 | } | ||
197 | |||
198 | /// <summary> | ||
199 | /// </summary> | ||
200 | private double BurstRateModifier() | ||
201 | { | ||
202 | // for now... burst rate is always m_quantumsPerBurst (constant) | ||
203 | // larger than drip rate so the ratio of burst requests is the | ||
204 | // same as the drip ratio | ||
205 | return DripRateModifier(); | ||
206 | } | ||
207 | |||
208 | /// <summary> | ||
209 | /// Register drip rate requested by a child of this throttle. Pass the | ||
210 | /// changes up the hierarchy. | ||
211 | /// </summary> | ||
212 | public void RegisterRequest(TokenBucket child, Int64 request) | ||
132 | { | 213 | { |
133 | this.parent = parent; | 214 | m_children[child] = request; |
134 | MaxBurst = maxBurst; | 215 | // m_totalDripRequest = m_children.Values.Sum(); |
135 | DripRate = dripRate; | 216 | |
136 | lastDrip = Environment.TickCount; | 217 | m_totalDripRequest = 0; |
218 | foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) | ||
219 | m_totalDripRequest += cref.Value; | ||
220 | |||
221 | // Pass the new values up to the parent | ||
222 | if (m_parent != null) | ||
223 | m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); | ||
137 | } | 224 | } |
138 | 225 | ||
139 | /// <summary> | 226 | /// <summary> |
227 | /// Remove the rate requested by a child of this throttle. Pass the | ||
228 | /// changes up the hierarchy. | ||
229 | /// </summary> | ||
230 | public void UnregisterRequest(TokenBucket child) | ||
231 | { | ||
232 | m_children.Remove(child); | ||
233 | // m_totalDripRequest = m_children.Values.Sum(); | ||
234 | |||
235 | m_totalDripRequest = 0; | ||
236 | foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) | ||
237 | m_totalDripRequest += cref.Value; | ||
238 | |||
239 | // Pass the new values up to the parent | ||
240 | if (m_parent != null) | ||
241 | m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); | ||
242 | } | ||
243 | |||
244 | /// <summary> | ||
140 | /// Remove a given number of tokens from the bucket | 245 | /// Remove a given number of tokens from the bucket |
141 | /// </summary> | 246 | /// </summary> |
142 | /// <param name="amount">Number of tokens to remove from the bucket</param> | 247 | /// <param name="amount">Number of tokens to remove from the bucket</param> |
143 | /// <returns>True if the requested number of tokens were removed from | 248 | /// <returns>True if the requested number of tokens were removed from |
144 | /// the bucket, otherwise false</returns> | 249 | /// the bucket, otherwise false</returns> |
145 | public bool RemoveTokens(int amount) | 250 | public bool RemoveTokens(Int64 amount) |
146 | { | 251 | { |
147 | if (maxBurst == 0) | 252 | // Deposit tokens for this interval |
148 | { | ||
149 | return true; | ||
150 | } | ||
151 | |||
152 | if (amount > maxBurst) | ||
153 | { | ||
154 | throw new Exception("amount " + amount + " exceeds maxBurst " + maxBurst); | ||
155 | } | ||
156 | |||
157 | Drip(); | 253 | Drip(); |
158 | 254 | ||
159 | if (content < amount) | 255 | // If we have enough tokens then remove them and return |
256 | if (m_tokenCount - amount >= 0) | ||
160 | { | 257 | { |
161 | return false; | 258 | // we don't have to remove from the parent, the drip rate is already |
259 | // reflective of the drip rate limits in the parent | ||
260 | m_tokenCount -= amount; | ||
261 | return true; | ||
162 | } | 262 | } |
163 | 263 | ||
164 | if (parent != null && !parent.RemoveTokens(amount)) | 264 | return false; |
165 | { | 265 | } |
166 | return false; | ||
167 | } | ||
168 | 266 | ||
169 | content -= amount; | 267 | /// <summary> |
170 | return true; | 268 | /// Deposit tokens into the bucket from a child bucket that did |
269 | /// not use all of its available tokens | ||
270 | /// </summary> | ||
271 | private void Deposit(Int64 count) | ||
272 | { | ||
273 | m_tokenCount += count; | ||
274 | |||
275 | // Deposit the overflow in the parent bucket, this is how we share | ||
276 | // unused bandwidth | ||
277 | Int64 burstrate = BurstRate; | ||
278 | if (m_tokenCount > burstrate) | ||
279 | m_tokenCount = burstrate; | ||
171 | } | 280 | } |
172 | 281 | ||
173 | /// <summary> | 282 | /// <summary> |
@@ -176,30 +285,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
176 | /// call to Drip | 285 | /// call to Drip |
177 | /// </summary> | 286 | /// </summary> |
178 | /// <returns>True if tokens were added to the bucket, otherwise false</returns> | 287 | /// <returns>True if tokens were added to the bucket, otherwise false</returns> |
179 | public bool Drip() | 288 | private void Drip() |
180 | { | 289 | { |
181 | if (tokensPerMS == 0) | 290 | // This should never happen... means we are a leaf node and were created |
291 | // with no drip rate... | ||
292 | if (DripRate == 0) | ||
182 | { | 293 | { |
183 | content = maxBurst; | 294 | m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0"); |
184 | return true; | 295 | return; |
185 | } | 296 | } |
297 | |||
298 | // Determine the interval over which we are adding tokens, never add | ||
299 | // more than a single quantum of tokens | ||
300 | Int32 now = Environment.TickCount & Int32.MaxValue; | ||
301 | Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum); | ||
186 | 302 | ||
187 | int now = Environment.TickCount; | 303 | m_lastDrip = now; |
188 | int deltaMS = now - lastDrip; | ||
189 | lastDrip = now; | ||
190 | 304 | ||
305 | // This can be 0 in the very unusual case that the timer wrapped | ||
306 | // It can be 0 if we try add tokens at a sub-tick rate | ||
191 | if (deltaMS <= 0) | 307 | if (deltaMS <= 0) |
192 | { | 308 | return; |
193 | return false; | ||
194 | } | ||
195 | 309 | ||
196 | long dripAmount = (long)deltaMS * (long)tokensPerMS + (long)content; | 310 | Deposit(deltaMS * DripRate / m_ticksPerQuantum); |
197 | if (dripAmount > maxBurst) | ||
198 | { | ||
199 | dripAmount = maxBurst; | ||
200 | } | ||
201 | content = (int)dripAmount; | ||
202 | return true; | ||
203 | } | 311 | } |
204 | } | 312 | } |
205 | } | 313 | } |