aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs566
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs110
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs2
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs245
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs297
5 files changed, 754 insertions, 466 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 34d72ac..1f7e66d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -49,6 +49,8 @@ using Timer = System.Timers.Timer;
49using AssetLandmark = OpenSim.Framework.AssetLandmark; 49using AssetLandmark = OpenSim.Framework.AssetLandmark;
50using Nini.Config; 50using Nini.Config;
51 51
52using System.IO;
53
52namespace OpenSim.Region.ClientStack.LindenUDP 54namespace OpenSim.Region.ClientStack.LindenUDP
53{ 55{
54 public delegate bool PacketMethod(IClientAPI simClient, Packet packet); 56 public delegate bool PacketMethod(IClientAPI simClient, Packet packet);
@@ -313,6 +315,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
313 315
314 private int m_cachedTextureSerial; 316 private int m_cachedTextureSerial;
315 private PriorityQueue m_entityUpdates; 317 private PriorityQueue m_entityUpdates;
318 private PriorityQueue m_entityProps;
316 private Prioritizer m_prioritizer; 319 private Prioritizer m_prioritizer;
317 private bool m_disableFacelights = false; 320 private bool m_disableFacelights = false;
318 321
@@ -360,9 +363,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
360 protected IAssetService m_assetService; 363 protected IAssetService m_assetService;
361 private const bool m_checkPackets = true; 364 private const bool m_checkPackets = true;
362 365
363 private Timer m_propertiesPacketTimer;
364 private List<ObjectPropertiesPacket.ObjectDataBlock> m_propertiesBlocks = new List<ObjectPropertiesPacket.ObjectDataBlock>();
365
366 #endregion Class Members 366 #endregion Class Members
367 367
368 #region Properties 368 #region Properties
@@ -438,6 +438,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
438 m_scene = scene; 438 m_scene = scene;
439 439
440 m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); 440 m_entityUpdates = new PriorityQueue(m_scene.Entities.Count);
441 m_entityProps = new PriorityQueue(m_scene.Entities.Count);
441 m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>(); 442 m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>();
442 m_killRecord = new HashSet<uint>(); 443 m_killRecord = new HashSet<uint>();
443// m_attachmentsSent = new HashSet<uint>(); 444// m_attachmentsSent = new HashSet<uint>();
@@ -461,9 +462,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
461 m_udpClient.OnQueueEmpty += HandleQueueEmpty; 462 m_udpClient.OnQueueEmpty += HandleQueueEmpty;
462 m_udpClient.OnPacketStats += PopulateStats; 463 m_udpClient.OnPacketStats += PopulateStats;
463 464
464 m_propertiesPacketTimer = new Timer(100);
465 m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket;
466
467 m_prioritizer = new Prioritizer(m_scene); 465 m_prioritizer = new Prioritizer(m_scene);
468 466
469 RegisterLocalPacketHandlers(); 467 RegisterLocalPacketHandlers();
@@ -1537,7 +1535,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1537 } 1535 }
1538 else 1536 else
1539 { 1537 {
1540 OutPacket(kill, ThrottleOutPacketType.State); 1538 // OutPacket(kill, ThrottleOutPacketType.State);
1539 OutPacket(kill, ThrottleOutPacketType.Task);
1541 } 1540 }
1542 } 1541 }
1543 1542
@@ -2367,7 +2366,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2367 2366
2368 packet.Effect = effectBlocks; 2367 packet.Effect = effectBlocks;
2369 2368
2370 OutPacket(packet, ThrottleOutPacketType.State); 2369 // OutPacket(packet, ThrottleOutPacketType.State);
2370 OutPacket(packet, ThrottleOutPacketType.Task);
2371 } 2371 }
2372 2372
2373 public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, 2373 public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember,
@@ -3547,16 +3547,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3547 3547
3548 #region Primitive Packet/Data Sending Methods 3548 #region Primitive Packet/Data Sending Methods
3549 3549
3550
3550 /// <summary> 3551 /// <summary>
3551 /// Generate one of the object update packets based on PrimUpdateFlags 3552 /// Generate one of the object update packets based on PrimUpdateFlags
3552 /// and broadcast the packet to clients 3553 /// and broadcast the packet to clients
3553 /// </summary> 3554 /// </summary>
3554 public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) 3555 public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags)
3555 { 3556 {
3556 double priority = m_prioritizer.GetUpdatePriority(this, entity); 3557 //double priority = m_prioritizer.GetUpdatePriority(this, entity);
3558 uint priority = m_prioritizer.GetUpdatePriority(this, entity);
3557 3559
3558 lock (m_entityUpdates.SyncRoot) 3560 lock (m_entityUpdates.SyncRoot)
3559 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation), entity.LocalId); 3561 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
3560 } 3562 }
3561 3563
3562 private void ProcessEntityUpdates(int maxUpdates) 3564 private void ProcessEntityUpdates(int maxUpdates)
@@ -3566,7 +3568,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3566 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3568 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3567 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3569 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3568 3570
3569 if (maxUpdates <= 0) maxUpdates = Int32.MaxValue; 3571 // Check to see if this is a flush
3572 if (maxUpdates <= 0)
3573 {
3574 maxUpdates = Int32.MaxValue;
3575 }
3576
3570 int updatesThisCall = 0; 3577 int updatesThisCall = 0;
3571 3578
3572 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race 3579 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race
@@ -3574,12 +3581,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3574 lock (m_killRecord) 3581 lock (m_killRecord)
3575 { 3582 {
3576 float avgTimeDilation = 1.0f; 3583 float avgTimeDilation = 1.0f;
3577 EntityUpdate update; 3584 IEntityUpdate iupdate;
3585 Int32 timeinqueue; // this is just debugging code & can be dropped later
3586
3578 while (updatesThisCall < maxUpdates) 3587 while (updatesThisCall < maxUpdates)
3579 { 3588 {
3580 lock (m_entityUpdates.SyncRoot) 3589 lock (m_entityUpdates.SyncRoot)
3581 if (!m_entityUpdates.TryDequeue(out update)) 3590 if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue))
3582 break; 3591 break;
3592
3593 EntityUpdate update = (EntityUpdate)iupdate;
3594
3583 avgTimeDilation += update.TimeDilation; 3595 avgTimeDilation += update.TimeDilation;
3584 avgTimeDilation *= 0.5f; 3596 avgTimeDilation *= 0.5f;
3585 3597
@@ -3619,7 +3631,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3619 3631
3620 #region UpdateFlags to packet type conversion 3632 #region UpdateFlags to packet type conversion
3621 3633
3622 PrimUpdateFlags updateFlags = update.Flags; 3634 PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags;
3623 3635
3624 bool canUseCompressed = true; 3636 bool canUseCompressed = true;
3625 bool canUseImproved = true; 3637 bool canUseImproved = true;
@@ -3679,36 +3691,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3679 } 3691 }
3680 else 3692 else
3681 { 3693 {
3682 // if (update.Entity is SceneObjectPart && ((SceneObjectPart)update.Entity).IsAttachment) 3694 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3683 // {
3684 // SceneObjectPart sop = (SceneObjectPart)update.Entity;
3685 // string text = sop.Text;
3686 // if (text.IndexOf("\n") >= 0)
3687 // text = text.Remove(text.IndexOf("\n"));
3688 //
3689 // if (m_attachmentsSent.Contains(sop.ParentID))
3690 // {
3691 //// m_log.DebugFormat(
3692 //// "[CLIENT]: Sending full info about attached prim {0} text {1}",
3693 //// sop.LocalId, text);
3694 //
3695 // objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock(sop, this.m_agentId));
3696 //
3697 // m_attachmentsSent.Add(sop.LocalId);
3698 // }
3699 // else
3700 // {
3701 // m_log.DebugFormat(
3702 // "[CLIENT]: Requeueing full update of prim {0} text {1} since we haven't sent its parent {2} yet",
3703 // sop.LocalId, text, sop.ParentID);
3704 //
3705 // m_entityUpdates.Enqueue(double.MaxValue, update, sop.LocalId);
3706 // }
3707 // }
3708 // else
3709 // {
3710 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3711 // }
3712 } 3695 }
3713 } 3696 }
3714 else if (!canUseImproved) 3697 else if (!canUseImproved)
@@ -3727,6 +3710,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3727 3710
3728 #endregion Block Construction 3711 #endregion Block Construction
3729 } 3712 }
3713
3730 3714
3731 #region Packet Sending 3715 #region Packet Sending
3732 3716
@@ -3802,26 +3786,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3802 3786
3803 public void ReprioritizeUpdates() 3787 public void ReprioritizeUpdates()
3804 { 3788 {
3805 //m_log.Debug("[CLIENT]: Reprioritizing prim updates for " + m_firstName + " " + m_lastName);
3806
3807 lock (m_entityUpdates.SyncRoot) 3789 lock (m_entityUpdates.SyncRoot)
3808 m_entityUpdates.Reprioritize(UpdatePriorityHandler); 3790 m_entityUpdates.Reprioritize(UpdatePriorityHandler);
3809 } 3791 }
3810 3792
3811 private bool UpdatePriorityHandler(ref double priority, uint localID) 3793 private bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity)
3812 { 3794 {
3813 EntityBase entity; 3795 if (entity != null)
3814 if (m_scene.Entities.TryGetValue(localID, out entity))
3815 { 3796 {
3816 priority = m_prioritizer.GetUpdatePriority(this, entity); 3797 priority = m_prioritizer.GetUpdatePriority(this, entity);
3798 return true;
3817 } 3799 }
3818 3800
3819 return priority != double.NaN; 3801 return false;
3820 } 3802 }
3821 3803
3822 public void FlushPrimUpdates() 3804 public void FlushPrimUpdates()
3823 { 3805 {
3824 m_log.Debug("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName); 3806 m_log.WarnFormat("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName);
3825 3807
3826 while (m_entityUpdates.Count > 0) 3808 while (m_entityUpdates.Count > 0)
3827 ProcessEntityUpdates(-1); 3809 ProcessEntityUpdates(-1);
@@ -3829,12 +3811,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3829 3811
3830 #endregion Primitive Packet/Data Sending Methods 3812 #endregion Primitive Packet/Data Sending Methods
3831 3813
3814 // These are used to implement an adaptive backoff in the number
3815 // of updates converted to packets. Since we don't want packets
3816 // to sit in the queue with old data, only convert enough updates
3817 // to packets that can be sent in 200ms.
3818 private Int32 m_LastQueueFill = 0;
3819 private Int32 m_maxUpdates = 0;
3820
3832 void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) 3821 void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories)
3833 { 3822 {
3834 if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) 3823 if ((categories & ThrottleOutPacketTypeFlags.Task) != 0)
3835 { 3824 {
3825 if (m_maxUpdates == 0 || m_LastQueueFill == 0)
3826 {
3827 m_maxUpdates = m_udpServer.PrimUpdatesPerCallback;
3828 }
3829 else
3830 {
3831 if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200)
3832 m_maxUpdates += 5;
3833 else
3834 m_maxUpdates = m_maxUpdates >> 1;
3835 }
3836 m_maxUpdates = Util.Clamp<Int32>(m_maxUpdates,10,500);
3837 m_LastQueueFill = Util.EnvironmentTickCount();
3838
3836 if (m_entityUpdates.Count > 0) 3839 if (m_entityUpdates.Count > 0)
3837 ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); 3840 ProcessEntityUpdates(m_maxUpdates);
3841
3842 if (m_entityProps.Count > 0)
3843 ProcessEntityPropertyRequests(m_maxUpdates);
3838 } 3844 }
3839 3845
3840 if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) 3846 if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0)
@@ -3948,132 +3954,206 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3948 OutPacket(pack, ThrottleOutPacketType.Task); 3954 OutPacket(pack, ThrottleOutPacketType.Task);
3949 } 3955 }
3950 3956
3951 public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, 3957 private class ObjectPropertyUpdate : IEntityUpdate
3952 uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask,
3953 uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category,
3954 UUID LastOwnerID, string ObjectName, string Description)
3955 { 3958 {
3956 ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); 3959 internal bool SendFamilyProps;
3957 // TODO: don't create new blocks if recycling an old packet 3960 internal bool SendObjectProps;
3958 3961
3959 ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); 3962 public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj)
3960 objPropDB.RequestFlags = RequestFlags; 3963 : base(entity,flags)
3961 objPropDB.ObjectID = ObjectUUID; 3964 {
3962 if (OwnerID == GroupID) 3965 SendFamilyProps = sendfam;
3963 objPropDB.OwnerID = UUID.Zero; 3966 SendObjectProps = sendobj;
3964 else 3967 }
3965 objPropDB.OwnerID = OwnerID; 3968 public void Update(ObjectPropertyUpdate update)
3966 objPropDB.GroupID = GroupID; 3969 {
3967 objPropDB.BaseMask = BaseMask; 3970 SendFamilyProps = SendFamilyProps || update.SendFamilyProps;
3968 objPropDB.OwnerMask = OwnerMask; 3971 SendObjectProps = SendObjectProps || update.SendObjectProps;
3969 objPropDB.GroupMask = GroupMask; 3972 Flags |= update.Flags;
3970 objPropDB.EveryoneMask = EveryoneMask; 3973 }
3971 objPropDB.NextOwnerMask = NextOwnerMask; 3974 }
3975
3976 public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags)
3977 {
3978 uint priority = 0; // time based ordering only
3979 lock (m_entityProps.SyncRoot)
3980 m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false));
3981 }
3972 3982
3973 // TODO: More properties are needed in SceneObjectPart! 3983 public void SendObjectPropertiesReply(ISceneEntity entity)
3974 objPropDB.OwnershipCost = OwnershipCost;
3975 objPropDB.SaleType = SaleType;
3976 objPropDB.SalePrice = SalePrice;
3977 objPropDB.Category = Category;
3978 objPropDB.LastOwnerID = LastOwnerID;
3979 objPropDB.Name = Util.StringToBytes256(ObjectName);
3980 objPropDB.Description = Util.StringToBytes256(Description);
3981 objPropFamilyPack.ObjectData = objPropDB;
3982 objPropFamilyPack.Header.Zerocoded = true;
3983 OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task);
3984 }
3985
3986 public void SendObjectPropertiesReply(
3987 UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID,
3988 UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID,
3989 UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName,
3990 string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask,
3991 uint BaseMask, byte saleType, int salePrice)
3992 { 3984 {
3993 //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); 3985 uint priority = 0; // time based ordering only
3994 // TODO: don't create new blocks if recycling an old packet 3986 lock (m_entityProps.SyncRoot)
3987 m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true));
3988 }
3995 3989
3996 ObjectPropertiesPacket.ObjectDataBlock block = 3990 private void ProcessEntityPropertyRequests(int maxUpdates)
3997 new ObjectPropertiesPacket.ObjectDataBlock(); 3991 {
3992 OpenSim.Framework.Lazy<List<ObjectPropertiesFamilyPacket.ObjectDataBlock>> objectFamilyBlocks =
3993 new OpenSim.Framework.Lazy<List<ObjectPropertiesFamilyPacket.ObjectDataBlock>>();
3998 3994
3999 block.ItemID = ItemID; 3995 OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks =
4000 block.CreationDate = CreationDate; 3996 new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>();
4001 block.CreatorID = CreatorUUID;
4002 block.FolderID = FolderUUID;
4003 block.FromTaskID = FromTaskUUID;
4004 block.GroupID = GroupUUID;
4005 block.InventorySerial = InventorySerial;
4006 3997
4007 block.LastOwnerID = LastOwnerUUID; 3998 IEntityUpdate iupdate;
4008 // proper.ObjectData[0].LastOwnerID = UUID.Zero; 3999 Int32 timeinqueue; // this is just debugging code & can be dropped later
4009 4000
4010 block.ObjectID = ObjectUUID; 4001 int updatesThisCall = 0;
4011 if (OwnerUUID == GroupUUID) 4002 while (updatesThisCall < m_maxUpdates)
4012 block.OwnerID = UUID.Zero;
4013 else
4014 block.OwnerID = OwnerUUID;
4015 block.TouchName = Util.StringToBytes256(TouchTitle);
4016 block.TextureID = TextureID;
4017 block.SitName = Util.StringToBytes256(SitTitle);
4018 block.Name = Util.StringToBytes256(ItemName);
4019 block.Description = Util.StringToBytes256(ItemDescription);
4020 block.OwnerMask = OwnerMask;
4021 block.NextOwnerMask = NextOwnerMask;
4022 block.GroupMask = GroupMask;
4023 block.EveryoneMask = EveryoneMask;
4024 block.BaseMask = BaseMask;
4025 // proper.ObjectData[0].AggregatePerms = 53;
4026 // proper.ObjectData[0].AggregatePermTextures = 0;
4027 // proper.ObjectData[0].AggregatePermTexturesOwner = 0;
4028 block.SaleType = saleType;
4029 block.SalePrice = salePrice;
4030
4031 lock (m_propertiesPacketTimer)
4032 { 4003 {
4033 m_propertiesBlocks.Add(block); 4004 lock (m_entityProps.SyncRoot)
4005 if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue))
4006 break;
4034 4007
4035 int length = 0; 4008 ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate;
4036 foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) 4009 if (update.SendFamilyProps)
4037 { 4010 {
4038 length += b.Length; 4011 if (update.Entity is SceneObjectPart)
4012 {
4013 SceneObjectPart sop = (SceneObjectPart)update.Entity;
4014 ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags);
4015 objectFamilyBlocks.Value.Add(objPropDB);
4016 }
4039 } 4017 }
4040 if (length > 1100) // FIXME: use real MTU 4018
4019 if (update.SendObjectProps)
4041 { 4020 {
4042 ProcessObjectPropertiesPacket(null, null); 4021 if (update.Entity is SceneObjectPart)
4043 m_propertiesPacketTimer.Stop(); 4022 {
4044 return; 4023 SceneObjectPart sop = (SceneObjectPart)update.Entity;
4024 ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop);
4025 objectPropertiesBlocks.Value.Add(objPropDB);
4026 }
4045 } 4027 }
4046 4028
4047 m_propertiesPacketTimer.Stop(); 4029 updatesThisCall++;
4048 m_propertiesPacketTimer.Start();
4049 } 4030 }
4031
4050 4032
4051 //proper.Header.Zerocoded = true; 4033 Int32 ppcnt = 0;
4052 //OutPacket(proper, ThrottleOutPacketType.Task); 4034 Int32 pbcnt = 0;
4053 } 4035
4036 if (objectPropertiesBlocks.IsValueCreated)
4037 {
4038 List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value;
4054 4039
4055 private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e) 4040 ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
4056 { 4041 packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count];
4057 ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); 4042 for (int i = 0; i < blocks.Count; i++)
4043 packet.ObjectData[i] = blocks[i];
4058 4044
4059 lock (m_propertiesPacketTimer) 4045 packet.Header.Zerocoded = true;
4060 { 4046 OutPacket(packet, ThrottleOutPacketType.Task, true);
4061 m_propertiesPacketTimer.Stop();
4062 4047
4063 proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; 4048 pbcnt += blocks.Count;
4049 ppcnt++;
4050 }
4051
4052 Int32 fpcnt = 0;
4053 Int32 fbcnt = 0;
4054
4055 if (objectFamilyBlocks.IsValueCreated)
4056 {
4057 List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value;
4064 4058
4065 int index = 0; 4059 // ObjectPropertiesFamilyPacket objPropFamilyPack =
4060 // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily);
4061 //
4062 // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count];
4063 // for (int i = 0; i < blocks.Count; i++)
4064 // objPropFamilyPack.ObjectData[i] = blocks[i];
4065 //
4066 // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true);
4066 4067
4067 foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) 4068 // one packet per object block... uggh...
4069 for (int i = 0; i < blocks.Count; i++)
4068 { 4070 {
4069 proper.ObjectData[index++] = b; 4071 ObjectPropertiesFamilyPacket packet =
4070 } 4072 (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily);
4073
4074 packet.ObjectData = blocks[i];
4075 packet.Header.Zerocoded = true;
4076 OutPacket(packet, ThrottleOutPacketType.Task);
4071 4077
4072 m_propertiesBlocks.Clear(); 4078 fpcnt++;
4079 fbcnt++;
4080 }
4081
4073 } 4082 }
4083
4084 // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt);
4085 // m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt);
4086 }
4087
4088 private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags)
4089 {
4090 ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock();
4074 4091
4075 proper.Header.Zerocoded = true; 4092 block.RequestFlags = requestFlags;
4076 OutPacket(proper, ThrottleOutPacketType.Task); 4093 block.ObjectID = sop.UUID;
4094 if (sop.OwnerID == sop.GroupID)
4095 block.OwnerID = UUID.Zero;
4096 else
4097 block.OwnerID = sop.OwnerID;
4098 block.GroupID = sop.GroupID;
4099 block.BaseMask = sop.BaseMask;
4100 block.OwnerMask = sop.OwnerMask;
4101 block.GroupMask = sop.GroupMask;
4102 block.EveryoneMask = sop.EveryoneMask;
4103 block.NextOwnerMask = sop.NextOwnerMask;
4104
4105 // TODO: More properties are needed in SceneObjectPart!
4106 block.OwnershipCost = sop.OwnershipCost;
4107 block.SaleType = sop.ObjectSaleType;
4108 block.SalePrice = sop.SalePrice;
4109 block.Category = sop.Category;
4110 block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right?
4111 block.Name = Util.StringToBytes256(sop.Name);
4112 block.Description = Util.StringToBytes256(sop.Description);
4113
4114 return block;
4115 }
4116
4117 private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop)
4118 {
4119 //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
4120 // TODO: don't create new blocks if recycling an old packet
4121
4122 ObjectPropertiesPacket.ObjectDataBlock block =
4123 new ObjectPropertiesPacket.ObjectDataBlock();
4124
4125 block.ObjectID = sop.UUID;
4126 block.Name = Util.StringToBytes256(sop.Name);
4127 block.Description = Util.StringToBytes256(sop.Description);
4128
4129 block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds
4130 block.CreatorID = sop.CreatorID;
4131 block.GroupID = sop.GroupID;
4132 block.LastOwnerID = sop.LastOwnerID;
4133 if (sop.OwnerID == sop.GroupID)
4134 block.OwnerID = UUID.Zero;
4135 else
4136 block.OwnerID = sop.OwnerID;
4137
4138 block.ItemID = sop.FromUserInventoryItemID;
4139 block.FolderID = UUID.Zero; // sop.FromFolderID ??
4140 block.FromTaskID = UUID.Zero; // ???
4141 block.InventorySerial = (short)sop.InventorySerial;
4142
4143 SceneObjectPart root = sop.ParentGroup.RootPart;
4144
4145 block.TouchName = Util.StringToBytes256(root.TouchName);
4146 block.TextureID = new byte[0]; // TextureID ???
4147 block.SitName = Util.StringToBytes256(root.SitName);
4148 block.OwnerMask = root.OwnerMask;
4149 block.NextOwnerMask = root.NextOwnerMask;
4150 block.GroupMask = root.GroupMask;
4151 block.EveryoneMask = root.EveryoneMask;
4152 block.BaseMask = root.BaseMask;
4153 block.SaleType = root.ObjectSaleType;
4154 block.SalePrice = root.SalePrice;
4155
4156 return block;
4077 } 4157 }
4078 4158
4079 #region Estate Data Sending Methods 4159 #region Estate Data Sending Methods
@@ -4217,6 +4297,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4217 4297
4218 public void SendEstateCovenantInformation(UUID covenant) 4298 public void SendEstateCovenantInformation(UUID covenant)
4219 { 4299 {
4300// m_log.DebugFormat("[LLCLIENTVIEW]: Sending estate covenant asset id of {0} to {1}", covenant, Name);
4301
4220 EstateCovenantReplyPacket einfopack = new EstateCovenantReplyPacket(); 4302 EstateCovenantReplyPacket einfopack = new EstateCovenantReplyPacket();
4221 EstateCovenantReplyPacket.DataBlock edata = new EstateCovenantReplyPacket.DataBlock(); 4303 EstateCovenantReplyPacket.DataBlock edata = new EstateCovenantReplyPacket.DataBlock();
4222 edata.CovenantID = covenant; 4304 edata.CovenantID = covenant;
@@ -4227,8 +4309,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4227 OutPacket(einfopack, ThrottleOutPacketType.Task); 4309 OutPacket(einfopack, ThrottleOutPacketType.Task);
4228 } 4310 }
4229 4311
4230 public void SendDetailedEstateData(UUID invoice, string estateName, uint estateID, uint parentEstate, uint estateFlags, uint sunPosition, UUID covenant, string abuseEmail, UUID estateOwner) 4312 public void SendDetailedEstateData(
4313 UUID invoice, string estateName, uint estateID, uint parentEstate, uint estateFlags, uint sunPosition,
4314 UUID covenant, string abuseEmail, UUID estateOwner)
4231 { 4315 {
4316// m_log.DebugFormat(
4317// "[LLCLIENTVIEW]: Sending detailed estate data to {0} with covenant asset id {1}", Name, covenant);
4318
4232 EstateOwnerMessagePacket packet = new EstateOwnerMessagePacket(); 4319 EstateOwnerMessagePacket packet = new EstateOwnerMessagePacket();
4233 packet.MethodData.Invoice = invoice; 4320 packet.MethodData.Invoice = invoice;
4234 packet.AgentData.TransactionID = UUID.Random(); 4321 packet.AgentData.TransactionID = UUID.Random();
@@ -4407,6 +4494,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4407 4494
4408 public void SendForceClientSelectObjects(List<uint> ObjectIDs) 4495 public void SendForceClientSelectObjects(List<uint> ObjectIDs)
4409 { 4496 {
4497 m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count);
4498
4410 bool firstCall = true; 4499 bool firstCall = true;
4411 const int MAX_OBJECTS_PER_PACKET = 251; 4500 const int MAX_OBJECTS_PER_PACKET = 251;
4412 ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); 4501 ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect);
@@ -11298,7 +11387,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11298 if (logPacket) 11387 if (logPacket)
11299 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); 11388 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type);
11300 } 11389 }
11301 11390
11302 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); 11391 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting);
11303 } 11392 }
11304 11393
@@ -11726,171 +11815,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11726 OutPacket(pack, ThrottleOutPacketType.Task); 11815 OutPacket(pack, ThrottleOutPacketType.Task);
11727 } 11816 }
11728 11817
11729 #region PriorityQueue
11730 public class PriorityQueue
11731 {
11732 internal delegate bool UpdatePriorityHandler(ref double priority, uint local_id);
11733
11734 private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[1];
11735 private Dictionary<uint, LookupItem> m_lookupTable;
11736 private Comparison<double> m_comparison;
11737 private object m_syncRoot = new object();
11738
11739 internal PriorityQueue() :
11740 this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY, Comparer<double>.Default) { }
11741 internal PriorityQueue(int capacity) :
11742 this(capacity, Comparer<double>.Default) { }
11743 internal PriorityQueue(IComparer<double> comparer) :
11744 this(new Comparison<double>(comparer.Compare)) { }
11745 internal PriorityQueue(Comparison<double> comparison) :
11746 this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY, comparison) { }
11747 internal PriorityQueue(int capacity, IComparer<double> comparer) :
11748 this(capacity, new Comparison<double>(comparer.Compare)) { }
11749 internal PriorityQueue(int capacity, Comparison<double> comparison)
11750 {
11751 m_lookupTable = new Dictionary<uint, LookupItem>(capacity);
11752
11753 for (int i = 0; i < m_heaps.Length; ++i)
11754 m_heaps[i] = new MinHeap<MinHeapItem>(capacity);
11755 this.m_comparison = comparison;
11756 }
11757
11758 public object SyncRoot { get { return this.m_syncRoot; } }
11759 internal int Count
11760 {
11761 get
11762 {
11763 int count = 0;
11764 for (int i = 0; i < m_heaps.Length; ++i)
11765 count = m_heaps[i].Count;
11766 return count;
11767 }
11768 }
11769
11770 public bool Enqueue(double priority, EntityUpdate value, uint local_id)
11771 {
11772 LookupItem item;
11773
11774 if (m_lookupTable.TryGetValue(local_id, out item))
11775 {
11776 // Combine flags
11777 value.Flags |= item.Heap[item.Handle].Value.Flags;
11778
11779 item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.m_comparison);
11780 return false;
11781 }
11782 else
11783 {
11784 item.Heap = m_heaps[0];
11785 item.Heap.Add(new MinHeapItem(priority, value, local_id, this.m_comparison), ref item.Handle);
11786 m_lookupTable.Add(local_id, item);
11787 return true;
11788 }
11789 }
11790
11791 internal EntityUpdate Peek()
11792 {
11793 for (int i = 0; i < m_heaps.Length; ++i)
11794 if (m_heaps[i].Count > 0)
11795 return m_heaps[i].Min().Value;
11796 throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString()));
11797 }
11798
11799 internal bool TryDequeue(out EntityUpdate value)
11800 {
11801 for (int i = 0; i < m_heaps.Length; ++i)
11802 {
11803 if (m_heaps[i].Count > 0)
11804 {
11805 MinHeapItem item = m_heaps[i].RemoveMin();
11806 m_lookupTable.Remove(item.LocalID);
11807 value = item.Value;
11808 return true;
11809 }
11810 }
11811
11812 value = default(EntityUpdate);
11813 return false;
11814 }
11815
11816 internal void Reprioritize(UpdatePriorityHandler handler)
11817 {
11818 MinHeapItem item;
11819 double priority;
11820
11821 foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values))
11822 {
11823 if (lookup.Heap.TryGetValue(lookup.Handle, out item))
11824 {
11825 priority = item.Priority;
11826 if (handler(ref priority, item.LocalID))
11827 {
11828 if (lookup.Heap.ContainsHandle(lookup.Handle))
11829 lookup.Heap[lookup.Handle] =
11830 new MinHeapItem(priority, item.Value, item.LocalID, this.m_comparison);
11831 }
11832 else
11833 {
11834 m_log.Warn("[LLCLIENTVIEW]: UpdatePriorityHandler returned false, dropping update");
11835 lookup.Heap.Remove(lookup.Handle);
11836 this.m_lookupTable.Remove(item.LocalID);
11837 }
11838 }
11839 }
11840 }
11841
11842 #region MinHeapItem
11843 private struct MinHeapItem : IComparable<MinHeapItem>
11844 {
11845 private double priority;
11846 private EntityUpdate value;
11847 private uint local_id;
11848 private Comparison<double> comparison;
11849
11850 internal MinHeapItem(double priority, EntityUpdate value, uint local_id) :
11851 this(priority, value, local_id, Comparer<double>.Default) { }
11852 internal MinHeapItem(double priority, EntityUpdate value, uint local_id, IComparer<double> comparer) :
11853 this(priority, value, local_id, new Comparison<double>(comparer.Compare)) { }
11854 internal MinHeapItem(double priority, EntityUpdate value, uint local_id, Comparison<double> comparison)
11855 {
11856 this.priority = priority;
11857 this.value = value;
11858 this.local_id = local_id;
11859 this.comparison = comparison;
11860 }
11861
11862 internal double Priority { get { return this.priority; } }
11863 internal EntityUpdate Value { get { return this.value; } }
11864 internal uint LocalID { get { return this.local_id; } }
11865
11866 public override string ToString()
11867 {
11868 StringBuilder sb = new StringBuilder();
11869 sb.Append("[");
11870 sb.Append(this.priority.ToString());
11871 sb.Append(",");
11872 if (this.value != null)
11873 sb.Append(this.value.ToString());
11874 sb.Append("]");
11875 return sb.ToString();
11876 }
11877
11878 public int CompareTo(MinHeapItem other)
11879 {
11880 return this.comparison(this.priority, other.priority);
11881 }
11882 }
11883 #endregion
11884
11885 #region LookupItem
11886 private struct LookupItem
11887 {
11888 internal MinHeap<MinHeapItem> Heap;
11889 internal IHandle Handle;
11890 }
11891 #endregion
11892 }
11893
11894 public struct PacketProcessor 11818 public struct PacketProcessor
11895 { 11819 {
11896 public PacketMethod method; 11820 public PacketMethod method;
@@ -11911,8 +11835,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11911 } 11835 }
11912 } 11836 }
11913 11837
11914 #endregion
11915
11916 public static OSD BuildEvent(string eventName, OSD eventBody) 11838 public static OSD BuildEvent(string eventName, OSD eventBody)
11917 { 11839 {
11918 OSDMap osdEvent = new OSDMap(2); 11840 OSDMap osdEvent = new OSDMap(2);
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index 65a8fe3..7be8a0a 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>
@@ -149,7 +151,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
149 /// <summary>Caches packed throttle information</summary> 151 /// <summary>Caches packed throttle information</summary>
150 private byte[] m_packedThrottles; 152 private byte[] m_packedThrottles;
151 153
152 private int m_defaultRTO = 3000; 154 private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC
153 private int m_maxRTO = 60000; 155 private int m_maxRTO = 60000;
154 156
155 /// <summary> 157 /// <summary>
@@ -174,7 +176,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
174 m_maxRTO = maxRTO; 176 m_maxRTO = maxRTO;
175 177
176 // Create a token bucket throttle for this client that has the scene token bucket as a parent 178 // Create a token bucket throttle for this client that has the scene token bucket as a parent
177 m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); 179 m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit);
180 // Create a token bucket throttle for the total categary with the client bucket as a throttle
181 m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit);
178 // Create an array of token buckets for this clients different throttle categories 182 // Create an array of token buckets for this clients different throttle categories
179 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 183 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
180 184
@@ -185,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
185 // Initialize the packet outboxes, where packets sit while they are waiting for tokens 189 // Initialize the packet outboxes, where packets sit while they are waiting for tokens
186 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); 190 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
187 // Initialize the token buckets that control the throttling for each category 191 // Initialize the token buckets that control the throttling for each category
188 m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); 192 m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type));
189 } 193 }
190 194
191 // Default the retransmission timeout to three seconds 195 // Default the retransmission timeout to three seconds
@@ -206,6 +210,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
206 m_packetOutboxes[i].Clear(); 210 m_packetOutboxes[i].Clear();
207 m_nextPackets[i] = null; 211 m_nextPackets[i] = null;
208 } 212 }
213
214 // pull the throttle out of the scene throttle
215 m_throttleClient.Parent.UnregisterRequest(m_throttleClient);
209 OnPacketStats = null; 216 OnPacketStats = null;
210 OnQueueEmpty = null; 217 OnQueueEmpty = null;
211 } 218 }
@@ -216,6 +223,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
216 /// <returns>Information about the client connection</returns> 223 /// <returns>Information about the client connection</returns>
217 public ClientInfo GetClientInfo() 224 public ClientInfo GetClientInfo()
218 { 225 {
226///<mic>
227 TokenBucket tb;
228
229 tb = m_throttleClient.Parent;
230 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT");
231
232 tb = m_throttleClient;
233 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT");
234
235 tb = m_throttleCategory;
236 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY");
237
238 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
239 {
240 tb = m_throttleCategories[i];
241 m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET");
242 }
243
244///</mic>
245
219 // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists 246 // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
220 // of pending and needed ACKs for every client every time some method wants information about 247 // of pending and needed ACKs for every client every time some method wants information about
221 // this connection is a recipe for poor performance 248 // this connection is a recipe for poor performance
@@ -223,13 +250,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
223 info.pendingAcks = new Dictionary<uint, uint>(); 250 info.pendingAcks = new Dictionary<uint, uint>();
224 info.needAck = new Dictionary<uint, byte[]>(); 251 info.needAck = new Dictionary<uint, byte[]>();
225 252
226 info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; 253 info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
227 info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; 254 info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
228 info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; 255 info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
229 info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; 256 info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
230 info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; 257 // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
231 info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; 258 info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
232 info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; 259 info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
260 info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
233 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + 261 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
234 info.taskThrottle + info.assetThrottle + info.textureThrottle; 262 info.taskThrottle + info.assetThrottle + info.textureThrottle;
235 263
@@ -317,8 +345,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
317 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 345 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
318 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); 346 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
319 // State is a subcategory of task that we allocate a percentage to 347 // State is a subcategory of task that we allocate a percentage to
320 int state = (int)((float)task * STATE_TASK_PERCENTAGE); 348 int state = 0;
321 task -= state; 349 // int state = (int)((float)task * STATE_TASK_PERCENTAGE);
350 // task -= state;
322 351
323 // Make sure none of the throttles are set below our packet MTU, 352 // Make sure none of the throttles are set below our packet MTU,
324 // otherwise a throttle could become permanently clogged 353 // otherwise a throttle could become permanently clogged
@@ -339,40 +368,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP
339 // Update the token buckets with new throttle values 368 // Update the token buckets with new throttle values
340 TokenBucket bucket; 369 TokenBucket bucket;
341 370
342 bucket = m_throttle; 371 bucket = m_throttleCategory;
343 bucket.MaxBurst = total; 372 bucket.RequestedDripRate = total;
344 373
345 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; 374 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
346 bucket.DripRate = resend; 375 bucket.RequestedDripRate = resend;
347 bucket.MaxBurst = resend;
348 376
349 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; 377 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
350 bucket.DripRate = land; 378 bucket.RequestedDripRate = land;
351 bucket.MaxBurst = land;
352 379
353 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; 380 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
354 bucket.DripRate = wind; 381 bucket.RequestedDripRate = wind;
355 bucket.MaxBurst = wind;
356 382
357 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; 383 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
358 bucket.DripRate = cloud; 384 bucket.RequestedDripRate = cloud;
359 bucket.MaxBurst = cloud;
360 385
361 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; 386 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
362 bucket.DripRate = asset; 387 bucket.RequestedDripRate = asset;
363 bucket.MaxBurst = asset;
364 388
365 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; 389 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
366 bucket.DripRate = task + state; 390 bucket.RequestedDripRate = task;
367 bucket.MaxBurst = task + state;
368 391
369 bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; 392 bucket = m_throttleCategories[(int)ThrottleOutPacketType.State];
370 bucket.DripRate = state; 393 bucket.RequestedDripRate = state;
371 bucket.MaxBurst = state;
372 394
373 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; 395 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
374 bucket.DripRate = texture; 396 bucket.RequestedDripRate = texture;
375 bucket.MaxBurst = texture;
376 397
377 // Reset the packed throttles cached data 398 // Reset the packed throttles cached data
378 m_packedThrottles = null; 399 m_packedThrottles = null;
@@ -387,14 +408,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
387 data = new byte[7 * 4]; 408 data = new byte[7 * 4];
388 int i = 0; 409 int i = 0;
389 410
390 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; 411 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4;
391 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; 412 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4;
392 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; 413 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4;
393 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; 414 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4;
394 Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + 415 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4;
395 m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; 416 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4;
396 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; 417 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4;
397 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
398 418
399 m_packedThrottles = data; 419 m_packedThrottles = data;
400 } 420 }
@@ -420,6 +440,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
420 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category]; 440 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
421 TokenBucket bucket = m_throttleCategories[category]; 441 TokenBucket bucket = m_throttleCategories[category];
422 442
443 // Don't send this packet if there is already a packet waiting in the queue
444 // even if we have the tokens to send it, tokens should go to the already
445 // queued packets
446 if (queue.Count > 0)
447 {
448 queue.Enqueue(packet);
449 return true;
450 }
451
452
423 if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength)) 453 if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength))
424 { 454 {
425 // Enough tokens were removed from the bucket, the packet will not be queued 455 // Enough tokens were removed from the bucket, the packet will not be queued
@@ -557,7 +587,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
557 int rto = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR)); 587 int rto = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR));
558 588
559 // Clamp the retransmission timeout to manageable values 589 // Clamp the retransmission timeout to manageable values
560 rto = Utils.Clamp(RTO, m_defaultRTO, m_maxRTO); 590 rto = Utils.Clamp(rto, m_defaultRTO, m_maxRTO);
561 591
562 RTO = rto; 592 RTO = rto;
563 593
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index 583214c..d08b25f 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
new file mode 100644
index 0000000..b62ec07
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs
@@ -0,0 +1,245 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32
33using OpenSim.Framework;
34using OpenSim.Framework.Client;
35using log4net;
36
37namespace OpenSim.Region.ClientStack.LindenUDP
38{
39 public class PriorityQueue
40 {
41 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
42
43 internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity);
44
45 // Heap[0] for self updates
46 // Heap[1..12] for entity updates
47
48 internal const uint m_numberOfQueues = 12;
49
50 private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[m_numberOfQueues];
51 private Dictionary<uint, LookupItem> m_lookupTable;
52 private uint m_nextQueue = 0;
53 private UInt64 m_nextRequest = 0;
54
55 private object m_syncRoot = new object();
56 public object SyncRoot {
57 get { return this.m_syncRoot; }
58 }
59
60 internal PriorityQueue() : this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY) { }
61
62 internal PriorityQueue(int capacity)
63 {
64 m_lookupTable = new Dictionary<uint, LookupItem>(capacity);
65
66 for (int i = 0; i < m_heaps.Length; ++i)
67 m_heaps[i] = new MinHeap<MinHeapItem>(capacity);
68 }
69
70 internal int Count
71 {
72 get
73 {
74 int count = 0;
75 for (int i = 0; i < m_heaps.Length; ++i)
76 count += m_heaps[i].Count;
77 return count;
78 }
79 }
80
81 public bool Enqueue(uint pqueue, IEntityUpdate value)
82 {
83 LookupItem lookup;
84
85 uint localid = value.Entity.LocalId;
86 UInt64 entry = m_nextRequest++;
87 if (m_lookupTable.TryGetValue(localid, out lookup))
88 {
89 entry = lookup.Heap[lookup.Handle].EntryOrder;
90 value.Update(lookup.Heap[lookup.Handle].Value);
91 lookup.Heap.Remove(lookup.Handle);
92 }
93
94 pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1);
95 lookup.Heap = m_heaps[pqueue];
96 lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle);
97 m_lookupTable[localid] = lookup;
98
99 return true;
100 }
101
102 internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue)
103 {
104 for (int i = 0; i < m_numberOfQueues; ++i)
105 {
106 // To get the fair queing, we cycle through each of the
107 // queues when finding an element to dequeue, this code
108 // assumes that the distribution of updates in the queues
109 // is polynomial, probably quadractic (eg distance of PI * R^2)
110 uint h = (uint)((m_nextQueue + i) % m_numberOfQueues);
111 if (m_heaps[h].Count > 0)
112 {
113 m_nextQueue = (uint)((h + 1) % m_numberOfQueues);
114
115 MinHeapItem item = m_heaps[h].RemoveMin();
116 m_lookupTable.Remove(item.Value.Entity.LocalId);
117 timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
118 value = item.Value;
119
120 return true;
121 }
122 }
123
124 timeinqueue = 0;
125 value = default(IEntityUpdate);
126 return false;
127 }
128
129 internal void Reprioritize(UpdatePriorityHandler handler)
130 {
131 MinHeapItem item;
132 foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values))
133 {
134 if (lookup.Heap.TryGetValue(lookup.Handle, out item))
135 {
136 uint pqueue = item.PriorityQueue;
137 uint localid = item.Value.Entity.LocalId;
138
139 if (handler(ref pqueue, item.Value.Entity))
140 {
141 // unless the priority queue has changed, there is no need to modify
142 // the entry
143 pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1);
144 if (pqueue != item.PriorityQueue)
145 {
146 lookup.Heap.Remove(lookup.Handle);
147
148 LookupItem litem = lookup;
149 litem.Heap = m_heaps[pqueue];
150 litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle);
151 m_lookupTable[localid] = litem;
152 }
153 }
154 else
155 {
156 // m_log.WarnFormat("[PQUEUE]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID);
157 lookup.Heap.Remove(lookup.Handle);
158 this.m_lookupTable.Remove(localid);
159 }
160 }
161 }
162 }
163
164 public override string ToString()
165 {
166 string s = "";
167 for (int i = 0; i < m_numberOfQueues; i++)
168 {
169 if (s != "") s += ",";
170 s += m_heaps[i].Count.ToString();
171 }
172 return s;
173 }
174
175#region MinHeapItem
176 private struct MinHeapItem : IComparable<MinHeapItem>
177 {
178 private IEntityUpdate value;
179 internal IEntityUpdate Value {
180 get {
181 return this.value;
182 }
183 }
184
185 private uint pqueue;
186 internal uint PriorityQueue {
187 get {
188 return this.pqueue;
189 }
190 }
191
192 private Int32 entrytime;
193 internal Int32 EntryTime {
194 get {
195 return this.entrytime;
196 }
197 }
198
199 private UInt64 entryorder;
200 internal UInt64 EntryOrder
201 {
202 get {
203 return this.entryorder;
204 }
205 }
206
207 internal MinHeapItem(uint pqueue, MinHeapItem other)
208 {
209 this.entrytime = other.entrytime;
210 this.entryorder = other.entryorder;
211 this.value = other.value;
212 this.pqueue = pqueue;
213 }
214
215 internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value)
216 {
217 this.entrytime = Util.EnvironmentTickCount();
218 this.entryorder = entryorder;
219 this.value = value;
220 this.pqueue = pqueue;
221 }
222
223 public override string ToString()
224 {
225 return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId);
226 }
227
228 public int CompareTo(MinHeapItem other)
229 {
230 // I'm assuming that the root part of an SOG is added to the update queue
231 // before the component parts
232 return Comparer<UInt64>.Default.Compare(this.EntryOrder, other.EntryOrder);
233 }
234 }
235#endregion
236
237#region LookupItem
238 private struct LookupItem
239 {
240 internal MinHeap<MinHeapItem> Heap;
241 internal IHandle Handle;
242 }
243#endregion
244 }
245}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
index 0a8331f..07b0a1d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
@@ -26,6 +26,10 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32using log4net;
29 33
30namespace OpenSim.Region.ClientStack.LindenUDP 34namespace 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;
51 65
52 #region Properties 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;
71
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,56 +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)
132 { 173 {
133 this.parent = parent; 174 m_identifier = m_counter++;
134 MaxBurst = maxBurst; 175
135 DripRate = dripRate; 176 Parent = parent;
136 lastDrip = Environment.TickCount & Int32.MaxValue; 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;
137 } 181 }
138 182
183#endregion Constructor
184
139 /// <summary> 185 /// <summary>
140 /// Remove a given number of tokens from the bucket 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.
141 /// </summary> 191 /// </summary>
142 /// <param name="amount">Number of tokens to remove from the bucket</param> 192 private double DripRateModifier()
143 /// <returns>True if the requested number of tokens were removed from
144 /// the bucket, otherwise false</returns>
145 public bool RemoveTokens(int amount)
146 { 193 {
147 bool dummy; 194 Int64 driprate = DripRate;
148 return RemoveTokens(amount, out dummy); 195 return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
149 } 196 }
150 197
151 /// <summary> 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)
213 {
214 m_children[child] = request;
215 // m_totalDripRequest = m_children.Values.Sum();
216
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));
224 }
225
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>
152 /// Remove a given number of tokens from the bucket 245 /// Remove a given number of tokens from the bucket
153 /// </summary> 246 /// </summary>
154 /// <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>
155 /// <param name="dripSucceeded">True if tokens were added to the bucket
156 /// during this call, otherwise false</param>
157 /// <returns>True if the requested number of tokens were removed from 248 /// <returns>True if the requested number of tokens were removed from
158 /// the bucket, otherwise false</returns> 249 /// the bucket, otherwise false</returns>
159 public bool RemoveTokens(int amount, out bool dripSucceeded) 250 public bool RemoveTokens(Int64 amount)
160 { 251 {
161 if (maxBurst == 0) 252 // Deposit tokens for this interval
253 Drip();
254
255 // If we have enough tokens then remove them and return
256 if (m_tokenCount - amount >= 0)
162 { 257 {
163 dripSucceeded = true; 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;
164 return true; 261 return true;
165 } 262 }
166 263
167 dripSucceeded = Drip(); 264 return false;
265 }
168 266
169 if (content - amount >= 0) 267 /// <summary>
170 { 268 /// Deposit tokens into the bucket from a child bucket that did
171 if (parent != null && !parent.RemoveTokens(amount)) 269 /// not use all of its available tokens
172 return false; 270 /// </summary>
271 private void Deposit(Int64 count)
272 {
273 m_tokenCount += count;
173 274
174 content -= amount; 275 // Deposit the overflow in the parent bucket, this is how we share
175 return true; 276 // unused bandwidth
176 } 277 Int64 burstrate = BurstRate;
177 else 278 if (m_tokenCount > burstrate)
178 { 279 m_tokenCount = burstrate;
179 return false;
180 }
181 } 280 }
182 281
183 /// <summary> 282 /// <summary>
@@ -186,37 +285,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
186 /// call to Drip 285 /// call to Drip
187 /// </summary> 286 /// </summary>
188 /// <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>
189 public bool Drip() 288 private void Drip()
190 { 289 {
191 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)
192 { 293 {
193 content = maxBurst; 294 m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0");
194 return true; 295 return;
195 } 296 }
196 else 297
197 { 298 // Determine the interval over which we are adding tokens, never add
198 int now = Environment.TickCount & Int32.MaxValue; 299 // more than a single quantum of tokens
199 int deltaMS = now - lastDrip; 300 Int32 now = Environment.TickCount & Int32.MaxValue;
301 Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum);
200 302
201 if (deltaMS <= 0) 303 m_lastDrip = now;
202 {
203 if (deltaMS < 0)
204 lastDrip = now;
205 return false;
206 }
207 304
208 int dripAmount = deltaMS * tokensPerMS; 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
307 if (deltaMS <= 0)
308 return;
209 309
210 content = Math.Min(content + dripAmount, maxBurst); 310 Deposit(deltaMS * DripRate / m_ticksPerQuantum);
211 lastDrip = now;
212
213 if (dripAmount < 0 || content < 0)
214 // sim has been idle for too long, integer has overflown
215 // previous calculation is meaningless, let's put it at correct max
216 content = maxBurst;
217
218 return true;
219 }
220 } 311 }
221 } 312 }
222} 313}