Mirror of Svelto.ECS because we're a fan of it
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

304 lines
7.9KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. namespace Svelto.DataStructures
  5. {
  6. /// <summary>
  7. /// original code: http://devplanet.com/blogs/brianr/archive/2008/09/29/thread-safe-dictionary-update.aspx
  8. /// simplified (not an IDictionary) and apdated (uses FasterList)
  9. /// </summary>
  10. /// <typeparam name = "TKey"></typeparam>
  11. /// <typeparam name = "TValue"></typeparam>
  12. [Serializable]
  13. public class ThreadSafeDictionary<TKey, TValue>
  14. {
  15. // setup the lock;
  16. public virtual int Count
  17. {
  18. get
  19. {
  20. using (new ReadOnlyLock(dictionaryLock))
  21. {
  22. return dict.Count;
  23. }
  24. }
  25. }
  26. public virtual bool IsReadOnly
  27. {
  28. get
  29. {
  30. using (new ReadOnlyLock(dictionaryLock))
  31. {
  32. return dict.IsReadOnly;
  33. }
  34. }
  35. }
  36. public virtual FasterList<TKey> Keys
  37. {
  38. get
  39. {
  40. using (new ReadOnlyLock(dictionaryLock))
  41. {
  42. return new FasterList<TKey>(dict.Keys);
  43. }
  44. }
  45. }
  46. public virtual FasterList<TValue> Values
  47. {
  48. get
  49. {
  50. using (new ReadOnlyLock(dictionaryLock))
  51. {
  52. return new FasterList<TValue>(dict.Values);
  53. }
  54. }
  55. }
  56. public virtual TValue this[TKey key]
  57. {
  58. get
  59. {
  60. using (new ReadOnlyLock(dictionaryLock))
  61. {
  62. return dict[key];
  63. }
  64. }
  65. set
  66. {
  67. using (new WriteLock(dictionaryLock))
  68. {
  69. dict[key] = value;
  70. }
  71. }
  72. }
  73. public virtual void Add(KeyValuePair<TKey, TValue> item)
  74. {
  75. using (new WriteLock(dictionaryLock))
  76. {
  77. dict.Add(item);
  78. }
  79. }
  80. public virtual void Clear()
  81. {
  82. using (new WriteLock(dictionaryLock))
  83. {
  84. dict.Clear();
  85. }
  86. }
  87. public virtual bool Contains(KeyValuePair<TKey, TValue> item)
  88. {
  89. using (new ReadOnlyLock(dictionaryLock))
  90. {
  91. return dict.Contains(item);
  92. }
  93. }
  94. public virtual void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
  95. {
  96. using (new ReadOnlyLock(dictionaryLock))
  97. {
  98. dict.CopyTo(array, arrayIndex);
  99. }
  100. }
  101. public virtual bool Remove(KeyValuePair<TKey, TValue> item)
  102. {
  103. using (new WriteLock(dictionaryLock))
  104. {
  105. return dict.Remove(item);
  106. }
  107. }
  108. public virtual void Add(TKey key, TValue value)
  109. {
  110. using (new WriteLock(dictionaryLock))
  111. {
  112. dict.Add(key, value);
  113. }
  114. }
  115. public virtual bool ContainsKey(TKey key)
  116. {
  117. using (new ReadOnlyLock(dictionaryLock))
  118. {
  119. return dict.ContainsKey(key);
  120. }
  121. }
  122. public virtual bool Remove(TKey key)
  123. {
  124. using (new WriteLock(dictionaryLock))
  125. {
  126. return dict.Remove(key);
  127. }
  128. }
  129. public virtual bool TryGetValue(TKey key, out TValue value)
  130. {
  131. using (new ReadOnlyLock(dictionaryLock))
  132. {
  133. return dict.TryGetValue(key, out value);
  134. }
  135. }
  136. /// <summary>
  137. /// Merge does a blind remove, and then add. Basically a blind Upsert.
  138. /// </summary>
  139. /// <param name = "key">Key to lookup</param>
  140. /// <param name = "newValue">New Value</param>
  141. public void MergeSafe(TKey key, TValue newValue)
  142. {
  143. using (new WriteLock(dictionaryLock))
  144. {
  145. // take a writelock immediately since we will always be writing
  146. if (dict.ContainsKey(key))
  147. dict.Remove(key);
  148. dict.Add(key, newValue);
  149. }
  150. }
  151. /// <summary>
  152. /// This is a blind remove. Prevents the need to check for existence first.
  153. /// </summary>
  154. /// <param name = "key">Key to remove</param>
  155. public void RemoveSafe(TKey key)
  156. {
  157. using (new ReadLock(dictionaryLock))
  158. {
  159. if (dict.ContainsKey(key))
  160. using (new WriteLock(dictionaryLock))
  161. {
  162. dict.Remove(key);
  163. }
  164. }
  165. }
  166. // This is the internal dictionary that we are wrapping
  167. readonly IDictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>();
  168. [NonSerialized] readonly ReaderWriterLockSlim dictionaryLock = Locks.GetLockInstance(LockRecursionPolicy.NoRecursion);
  169. }
  170. public static class Locks
  171. {
  172. public static ReaderWriterLockSlim GetLockInstance()
  173. {
  174. return GetLockInstance(LockRecursionPolicy.SupportsRecursion);
  175. }
  176. public static ReaderWriterLockSlim GetLockInstance(LockRecursionPolicy recursionPolicy)
  177. {
  178. return new ReaderWriterLockSlim(recursionPolicy);
  179. }
  180. public static void GetReadLock(ReaderWriterLockSlim locks)
  181. {
  182. var lockAcquired = false;
  183. while (!lockAcquired)
  184. lockAcquired = locks.TryEnterUpgradeableReadLock(1);
  185. }
  186. public static void GetReadOnlyLock(ReaderWriterLockSlim locks)
  187. {
  188. var lockAcquired = false;
  189. while (!lockAcquired)
  190. lockAcquired = locks.TryEnterReadLock(1);
  191. }
  192. public static void GetWriteLock(ReaderWriterLockSlim locks)
  193. {
  194. var lockAcquired = false;
  195. while (!lockAcquired)
  196. lockAcquired = locks.TryEnterWriteLock(1);
  197. }
  198. public static void ReleaseLock(ReaderWriterLockSlim locks)
  199. {
  200. ReleaseWriteLock(locks);
  201. ReleaseReadLock(locks);
  202. ReleaseReadOnlyLock(locks);
  203. }
  204. public static void ReleaseReadLock(ReaderWriterLockSlim locks)
  205. {
  206. if (locks.IsUpgradeableReadLockHeld)
  207. locks.ExitUpgradeableReadLock();
  208. }
  209. public static void ReleaseReadOnlyLock(ReaderWriterLockSlim locks)
  210. {
  211. if (locks.IsReadLockHeld)
  212. locks.ExitReadLock();
  213. }
  214. public static void ReleaseWriteLock(ReaderWriterLockSlim locks)
  215. {
  216. if (locks.IsWriteLockHeld)
  217. locks.ExitWriteLock();
  218. }
  219. }
  220. public abstract class BaseLock : IDisposable
  221. {
  222. protected ReaderWriterLockSlim _Locks;
  223. public BaseLock(ReaderWriterLockSlim locks)
  224. {
  225. _Locks = locks;
  226. }
  227. public abstract void Dispose();
  228. }
  229. public class ReadLock : BaseLock
  230. {
  231. public ReadLock(ReaderWriterLockSlim locks)
  232. : base(locks)
  233. {
  234. Locks.GetReadLock(_Locks);
  235. }
  236. public override void Dispose()
  237. {
  238. Locks.ReleaseReadLock(_Locks);
  239. }
  240. }
  241. public class ReadOnlyLock : BaseLock
  242. {
  243. public ReadOnlyLock(ReaderWriterLockSlim locks)
  244. : base(locks)
  245. {
  246. Locks.GetReadOnlyLock(_Locks);
  247. }
  248. public override void Dispose()
  249. {
  250. Locks.ReleaseReadOnlyLock(_Locks);
  251. }
  252. }
  253. public class WriteLock : BaseLock
  254. {
  255. public WriteLock(ReaderWriterLockSlim locks)
  256. : base(locks)
  257. {
  258. Locks.GetWriteLock(_Locks);
  259. }
  260. public override void Dispose()
  261. {
  262. Locks.ReleaseWriteLock(_Locks);
  263. }
  264. }
  265. }