OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
PriorityQueue.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Reflection;
32 
33 using OpenSim.Framework;
34 using OpenSim.Framework.Client;
35 using log4net;
36 
37 namespace OpenSim.Framework
38 {
39  public class PriorityQueue
40  {
41 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
42 
43  public delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity);
44 
48 
49  public const uint NumberOfQueues = 12; // includes immediate queues, m_queueCounts need to be set acording
50 
54  public const uint NumberOfImmediateQueues = 2;
55 
56  private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[NumberOfQueues];
57  private Dictionary<uint, LookupItem> m_lookupTable;
58 
59  // internal state used to ensure the deqeues are spread across the priority
60  // queues "fairly". queuecounts is the amount to pull from each queue in
61  // each pass. weighted towards the higher priority queues
62  private uint m_nextQueue = 0;
63  private uint m_countFromQueue = 0;
64  // first queues are imediate, so no counts
65 // private uint[] m_queueCounts = { 0, 0, 8, 4, 4, 2, 2, 2, 2, 1, 1, 1 };
66  private uint[] m_queueCounts = {0, 0, 8, 8, 5, 4, 3, 2, 1, 1, 1, 1};
67  // this is ava, ava, attach, <10m, 20,40,80,160m,320,640,1280, +
68 
69  // next request is a counter of the number of updates queued, it provides
70  // a total ordering on the updates coming through the queue and is more
71  // lightweight (and more discriminating) than tick count
72  private UInt64 m_nextRequest = 0;
73 
77  private object m_syncRoot = new object();
78  public object SyncRoot {
79  get { return this.m_syncRoot; }
80  }
81 
82 #region constructor
83  public PriorityQueue() : this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY) { }
84 
85  public PriorityQueue(int capacity)
86  {
87  m_lookupTable = new Dictionary<uint, LookupItem>(capacity);
88 
89  for (int i = 0; i < m_heaps.Length; ++i)
90  m_heaps[i] = new MinHeap<MinHeapItem>(capacity);
91 
92  m_nextQueue = NumberOfImmediateQueues;
93  m_countFromQueue = m_queueCounts[m_nextQueue];
94  }
95 #endregion Constructor
96 
97 #region PublicMethods
98  public int Count
102  {
103  get
104  {
105  int count = 0;
106  for (int i = 0; i < m_heaps.Length; ++i)
107  count += m_heaps[i].Count;
108 
109  return count;
110  }
111  }
112 
116  public bool Enqueue(uint pqueue, IEntityUpdate value)
117  {
118  LookupItem lookup;
119 
120  uint localid = value.Entity.LocalId;
121  UInt64 entry = m_nextRequest++;
122  if (m_lookupTable.TryGetValue(localid, out lookup))
123  {
124  entry = lookup.Heap[lookup.Handle].EntryOrder;
125  value.Update(lookup.Heap[lookup.Handle].Value);
126  lookup.Heap.Remove(lookup.Handle);
127  }
128 
129  pqueue = Util.Clamp<uint>(pqueue, 0, NumberOfQueues - 1);
130  lookup.Heap = m_heaps[pqueue];
131  lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle);
132  m_lookupTable[localid] = lookup;
133 
134  return true;
135  }
136 
137 
138  public void Remove(List<uint> ids)
139  {
140  LookupItem lookup;
141 
142  foreach (uint localid in ids)
143  {
144  if (m_lookupTable.TryGetValue(localid, out lookup))
145  {
146  lookup.Heap.Remove(lookup.Handle);
147  m_lookupTable.Remove(localid);
148  }
149  }
150  }
151 
157  public bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue)
158  {
159  // If there is anything in imediate queues, return it first no
160  // matter what else. Breaks fairness. But very useful.
161  for (int iq = 0; iq < NumberOfImmediateQueues; iq++)
162  {
163  if (m_heaps[iq].Count > 0)
164  {
165  MinHeapItem item = m_heaps[iq].RemoveMin();
166  m_lookupTable.Remove(item.Value.Entity.LocalId);
167  timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
168  value = item.Value;
169 
170  return true;
171  }
172  }
173 
174  // To get the fair queing, we cycle through each of the
175  // queues when finding an element to dequeue.
176  // We pull (NumberOfQueues - QueueIndex) items from each queue in order
177  // to give lower numbered queues a higher priority and higher percentage
178  // of the bandwidth.
179 
180  // Check for more items to be pulled from the current queue
181  if (m_heaps[m_nextQueue].Count > 0 && m_countFromQueue > 0)
182  {
183  m_countFromQueue--;
184 
185  MinHeapItem item = m_heaps[m_nextQueue].RemoveMin();
186  m_lookupTable.Remove(item.Value.Entity.LocalId);
187  timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
188  value = item.Value;
189 
190  return true;
191  }
192 
193  // Find the next non-immediate queue with updates in it
194  for (uint i = NumberOfImmediateQueues; i < NumberOfQueues; ++i)
195  {
196  m_nextQueue++;
197  if(m_nextQueue >= NumberOfQueues)
198  m_nextQueue = NumberOfImmediateQueues;
199 
200  m_countFromQueue = m_queueCounts[m_nextQueue];
201 
202  if (m_heaps[m_nextQueue].Count > 0)
203  {
204  m_countFromQueue--;
205 
206  MinHeapItem item = m_heaps[m_nextQueue].RemoveMin();
207  m_lookupTable.Remove(item.Value.Entity.LocalId);
208  timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
209  value = item.Value;
210  return true;
211  }
212  }
213 
214  timeinqueue = 0;
215  value = default(IEntityUpdate);
216  return false;
217  }
218 
223  public void Reprioritize(UpdatePriorityHandler handler)
224  {
225  MinHeapItem item;
226  foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values))
227  {
228  if (lookup.Heap.TryGetValue(lookup.Handle, out item))
229  {
230  uint pqueue = item.PriorityQueue;
231  uint localid = item.Value.Entity.LocalId;
232 
233  if (handler(ref pqueue, item.Value.Entity))
234  {
235  // unless the priority queue has changed, there is no need to modify
236  // the entry
237  pqueue = Util.Clamp<uint>(pqueue, 0, NumberOfQueues - 1);
238  if (pqueue != item.PriorityQueue)
239  {
240  lookup.Heap.Remove(lookup.Handle);
241 
242  LookupItem litem = lookup;
243  litem.Heap = m_heaps[pqueue];
244  litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle);
245  m_lookupTable[localid] = litem;
246  }
247  }
248  else
249  {
250  // m_log.WarnFormat("[PQUEUE]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID);
251  lookup.Heap.Remove(lookup.Handle);
252  this.m_lookupTable.Remove(localid);
253  }
254  }
255  }
256  }
257 
260  public override string ToString()
261  {
262  string s = "";
263  for (int i = 0; i < NumberOfQueues; i++)
264  s += String.Format("{0,7} ",m_heaps[i].Count);
265  return s;
266  }
267 
268 #endregion PublicMethods
269 
270 #region MinHeapItem
271  private struct MinHeapItem : IComparable<MinHeapItem>
272  {
273  private IEntityUpdate value;
274  internal IEntityUpdate Value {
275  get {
276  return this.value;
277  }
278  }
279 
280  private uint pqueue;
281  internal uint PriorityQueue {
282  get {
283  return this.pqueue;
284  }
285  }
286 
287  private Int32 entrytime;
288  internal Int32 EntryTime {
289  get {
290  return this.entrytime;
291  }
292  }
293 
294  private UInt64 entryorder;
295  internal UInt64 EntryOrder
296  {
297  get {
298  return this.entryorder;
299  }
300  }
301 
302  internal MinHeapItem(uint pqueue, MinHeapItem other)
303  {
304  this.entrytime = other.entrytime;
305  this.entryorder = other.entryorder;
306  this.value = other.value;
307  this.pqueue = pqueue;
308  }
309 
310  internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value)
311  {
312  this.entrytime = Util.EnvironmentTickCount();
313  this.entryorder = entryorder;
314  this.value = value;
315  this.pqueue = pqueue;
316  }
317 
318  public override string ToString()
319  {
320  return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId);
321  }
322 
323  public int CompareTo(MinHeapItem other)
324  {
325  // I'm assuming that the root part of an SOG is added to the update queue
326  // before the component parts
327  return Comparer<UInt64>.Default.Compare(this.EntryOrder, other.EntryOrder);
328  }
329  }
330 #endregion
331 
332 #region LookupItem
333  private struct LookupItem
334  {
335  internal MinHeap<MinHeapItem> Heap;
336  internal IHandle Handle;
337  }
338 #endregion
339  }
340 }
bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue)
Remove an item from one of the queues. Specifically, it removes the oldest item from the next queue i...
void Reprioritize(UpdatePriorityHandler handler)
Reapply the prioritization function to each of the updates currently stored in the priority queues...
bool Enqueue(uint pqueue, IEntityUpdate value)
Enqueue an item into the specified priority queue
void Remove(List< uint > ids)