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.

ThreadSafeDictionary.cs 8.1KB

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