OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
TaskInventoryDictionary.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.Generic;
30 using System.Threading;
31 using System.Reflection;
32 using System.Xml;
33 using System.Diagnostics;
34 using System.Xml.Schema;
35 using System.Xml.Serialization;
36 using log4net;
37 using OpenMetaverse;
38 
39 namespace OpenSim.Framework
40 {
48  public class TaskInventoryDictionary : Dictionary<UUID, TaskInventoryItem>,
49  ICloneable, IXmlSerializable
50  {
51  // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
52 
53  private static XmlSerializer tiiSerializer = new XmlSerializer(typeof (TaskInventoryItem));
54  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55 
56  private Thread LockedByThread;
57 // private string WriterStack;
58 
59 // private Dictionary<Thread, string> ReadLockers =
60 // new Dictionary<Thread, string>();
61 
65  private volatile System.Threading.ReaderWriterLockSlim m_itemLock = new System.Threading.ReaderWriterLockSlim();
66 
70  public bool IsReadLockedByMe()
71  {
72  if (m_itemLock.RecursiveReadCount > 0)
73  {
74  return true;
75  }
76  else
77  {
78  return false;
79  }
80  }
81 
85  public void LockItemsForRead(bool locked)
86  {
87  if (locked)
88  {
89  if (m_itemLock.IsWriteLockHeld && LockedByThread != null)
90  {
91  if (!LockedByThread.IsAlive)
92  {
93  //Locked by dead thread, reset.
94  m_itemLock = new System.Threading.ReaderWriterLockSlim();
95  }
96  }
97 
98  if (m_itemLock.RecursiveReadCount > 0)
99  {
100  m_log.Error("[TaskInventoryDictionary] Recursive read lock requested. This should not happen and means something needs to be fixed. For now though, it's safe to continue.");
101  try
102  {
103  // That call stack is useful for end users only. RealProgrammers need a full dump. Commented.
104  // StackTrace stackTrace = new StackTrace(); // get call stack
105  // StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
106  //
107  // // write call stack method names
108  // foreach (StackFrame stackFrame in stackFrames)
109  // {
110  // m_log.Error("[SceneObjectGroup.m_parts] "+(stackFrame.GetMethod().Name)); // write method name
111  // }
112 
113  // The below is far more useful
114 // System.Console.WriteLine("------------------------------------------");
115 // System.Console.WriteLine("My call stack:\n" + Environment.StackTrace);
116 // System.Console.WriteLine("------------------------------------------");
117 // foreach (KeyValuePair<Thread, string> kvp in ReadLockers)
118 // {
119 // System.Console.WriteLine("Locker name {0} call stack:\n" + kvp.Value, kvp.Key.Name);
120 // System.Console.WriteLine("------------------------------------------");
121 // }
122  }
123  catch
124  {}
125  m_itemLock.ExitReadLock();
126  }
127  if (m_itemLock.RecursiveWriteCount > 0)
128  {
129  m_log.Error("[TaskInventoryDictionary] Recursive write lock requested. This should not happen and means something needs to be fixed.");
130 // try
131 // {
132 // System.Console.WriteLine("------------------------------------------");
133 // System.Console.WriteLine("My call stack:\n" + Environment.StackTrace);
134 // System.Console.WriteLine("------------------------------------------");
135 // System.Console.WriteLine("Locker's call stack:\n" + WriterStack);
136 // System.Console.WriteLine("------------------------------------------");
137 // }
138 // catch
139 // {}
140  m_itemLock.ExitWriteLock();
141  }
142 
143  while (!m_itemLock.TryEnterReadLock(60000))
144  {
145  m_log.Error("Thread lock detected while trying to aquire READ lock in TaskInventoryDictionary. Locked by thread " + LockedByThread.Name + ". I'm going to try to solve the thread lock automatically to preserve region stability, but this needs to be fixed.");
146  //if (m_itemLock.IsWriteLockHeld)
147  //{
148  m_itemLock = new System.Threading.ReaderWriterLockSlim();
149 // System.Console.WriteLine("------------------------------------------");
150 // System.Console.WriteLine("My call stack:\n" + Environment.StackTrace);
151 // System.Console.WriteLine("------------------------------------------");
152 // System.Console.WriteLine("Locker's call stack:\n" + WriterStack);
153 // System.Console.WriteLine("------------------------------------------");
154 // LockedByThread = null;
155 // ReadLockers.Clear();
156  //}
157  }
158 // ReadLockers[Thread.CurrentThread] = Environment.StackTrace;
159  }
160  else
161  {
162  if (m_itemLock.RecursiveReadCount>0)
163  {
164  m_itemLock.ExitReadLock();
165  }
166 // if (m_itemLock.RecursiveReadCount == 0)
167 // ReadLockers.Remove(Thread.CurrentThread);
168  }
169  }
170 
174  public void LockItemsForWrite(bool locked)
175  {
176  if (locked)
177  {
178  //Enter a write lock, wait indefinately for one to open.
179  if (m_itemLock.RecursiveReadCount > 0)
180  {
181  m_log.Error("[TaskInventoryDictionary] Recursive read lock requested. This should not happen and means something needs to be fixed. For now though, it's safe to continue.");
182  m_itemLock.ExitReadLock();
183  }
184  if (m_itemLock.RecursiveWriteCount > 0)
185  {
186  m_log.Error("[TaskInventoryDictionary] Recursive write lock requested. This should not happen and means something needs to be fixed.");
187 
188  m_itemLock.ExitWriteLock();
189  }
190  while (!m_itemLock.TryEnterWriteLock(60000))
191  {
192  if (m_itemLock.IsWriteLockHeld)
193  {
194  m_log.Error("Thread lock detected while trying to aquire WRITE lock in TaskInventoryDictionary. Locked by thread " + LockedByThread.Name + ". I'm going to try to solve the thread lock automatically to preserve region stability, but this needs to be fixed.");
195 // System.Console.WriteLine("------------------------------------------");
196 // System.Console.WriteLine("My call stack:\n" + Environment.StackTrace);
197 // System.Console.WriteLine("------------------------------------------");
198 // System.Console.WriteLine("Locker's call stack:\n" + WriterStack);
199 // System.Console.WriteLine("------------------------------------------");
200  }
201  else
202  {
203  m_log.Error("Thread lock detected while trying to aquire WRITE lock in TaskInventoryDictionary. Locked by a reader. I'm going to try to solve the thread lock automatically to preserve region stability, but this needs to be fixed.");
204 // System.Console.WriteLine("------------------------------------------");
205 // System.Console.WriteLine("My call stack:\n" + Environment.StackTrace);
206 // System.Console.WriteLine("------------------------------------------");
207 // foreach (KeyValuePair<Thread, string> kvp in ReadLockers)
208 // {
209 // System.Console.WriteLine("Locker name {0} call stack:\n" + kvp.Value, kvp.Key.Name);
210 // System.Console.WriteLine("------------------------------------------");
211 // }
212  }
213  m_itemLock = new System.Threading.ReaderWriterLockSlim();
214 // ReadLockers.Clear();
215  }
216 
217  LockedByThread = Thread.CurrentThread;
218 // WriterStack = Environment.StackTrace;
219  }
220  else
221  {
222  if (m_itemLock.RecursiveWriteCount > 0)
223  {
224  m_itemLock.ExitWriteLock();
225  }
226  }
227  }
228 
229  #region ICloneable Members
230 
231  public Object Clone()
232  {
234 
235  m_itemLock.EnterReadLock();
236  foreach (UUID uuid in Keys)
237  {
238  clone.Add(uuid, (TaskInventoryItem) this[uuid].Clone());
239  }
240  m_itemLock.ExitReadLock();
241 
242  return clone;
243  }
244 
245  #endregion
246 
247  // The alternative of simply serializing the list doesn't appear to work on mono, since
248  // we get a
249  //
250  // System.TypeInitializationException: An exception was thrown by the type initializer for OpenSim.Framework.TaskInventoryDictionary ---> System.ArgumentOutOfRangeException: < 0
251  // Parameter name: length
252  // at System.String.Substring (Int32 startIndex, Int32 length) [0x00088] in /build/buildd/mono-1.2.4/mcs/class/corlib/System/String.cs:381
253  // at System.Xml.Serialization.TypeTranslator.GetTypeData (System.Type runtimeType, System.String xmlDataType) [0x001f6] in /build/buildd/mono-1.2.4/mcs/class/System.XML/System.Xml.Serialization/TypeTranslator.cs:217
254  // ...
255 // private static XmlSerializer tiiSerializer
256 // = new XmlSerializer(typeof(Dictionary<UUID, TaskInventoryItem>.ValueCollection));
257 
258  // see IXmlSerializable
259 
260  #region IXmlSerializable Members
261 
262  public XmlSchema GetSchema()
263  {
264  return null;
265  }
266 
267  // see IXmlSerializable
268  public void ReadXml(XmlReader reader)
269  {
270  // m_log.DebugFormat("[TASK INVENTORY]: ReadXml current node before actions, {0}", reader.Name);
271 
272  if (!reader.IsEmptyElement)
273  {
274  reader.Read();
275  while (tiiSerializer.CanDeserialize(reader))
276  {
277  TaskInventoryItem item = (TaskInventoryItem) tiiSerializer.Deserialize(reader);
278  Add(item.ItemID, item);
279 
280  //m_log.DebugFormat("[TASK INVENTORY]: Instanted prim item {0}, {1} from xml", item.Name, item.ItemID);
281  }
282 
283  // m_log.DebugFormat("[TASK INVENTORY]: Instantiated {0} prim items in total from xml", Count);
284  }
285  // else
286  // {
287  // m_log.DebugFormat("[TASK INVENTORY]: Skipping empty element {0}", reader.Name);
288  // }
289 
290  // For some .net implementations, this last read is necessary so that we advance beyond the end tag
291  // of the element wrapping this object so that the rest of the serialization can complete normally.
292  reader.Read();
293 
294  // m_log.DebugFormat("[TASK INVENTORY]: ReadXml current node after actions, {0}", reader.Name);
295  }
296 
297  // see IXmlSerializable
298  public void WriteXml(XmlWriter writer)
299  {
300  lock (this)
301  {
302  foreach (TaskInventoryItem item in Values)
303  {
304  tiiSerializer.Serialize(writer, item);
305  }
306  }
307 
308  //tiiSerializer.Serialize(writer, Values);
309  }
310 
311  #endregion
312 
313  // see ICloneable
314  }
315 }
A dictionary containing task inventory items. Indexed by item UUID.
Represents an item in a task inventory
bool IsReadLockedByMe()
Are we readlocked by the calling thread?
void LockItemsForRead(bool locked)
Lock our inventory list for reading (many can read, one can write)
void LockItemsForWrite(bool locked)
Lock our inventory list for writing (many can read, one can write)