DoubleDictionaryThreadAbortSafe.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. /*
  2. * Copyright (c) 2008, openmetaverse.org, http://opensimulator.org/
  3. * All rights reserved.
  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. *
  8. * - Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. * - Neither the name of the openmetaverse.org nor the names
  11. * of its contributors may be used to endorse or promote products derived from
  12. * this software without specific prior written permission.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  15. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  18. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  19. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  20. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  21. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  22. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  23. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  24. * POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. using System;
  27. using System.Threading;
  28. using System.Collections.Generic;
  29. namespace OpenSim.Framework
  30. {
  31. /// <summary>
  32. /// A double dictionary that is thread abort safe.
  33. /// </summary>
  34. /// <remarks>
  35. /// This adapts OpenMetaverse.DoubleDictionary to be thread-abort safe by acquiring ReaderWriterLockSlim within
  36. /// a finally section (which can't be interrupted by Thread.Abort()).
  37. /// </remarks>
  38. public class DoubleDictionaryThreadAbortSafe<TKey1, TKey2, TValue>
  39. {
  40. Dictionary<TKey1, TValue> Dictionary1;
  41. Dictionary<TKey2, TValue> Dictionary2;
  42. private TValue[] m_array;
  43. ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
  44. public DoubleDictionaryThreadAbortSafe()
  45. {
  46. Dictionary1 = new Dictionary<TKey1,TValue>();
  47. Dictionary2 = new Dictionary<TKey2,TValue>();
  48. m_array = null;
  49. }
  50. public DoubleDictionaryThreadAbortSafe(int capacity)
  51. {
  52. Dictionary1 = new Dictionary<TKey1, TValue>(capacity);
  53. Dictionary2 = new Dictionary<TKey2, TValue>(capacity);
  54. m_array = null;
  55. }
  56. ~DoubleDictionaryThreadAbortSafe()
  57. {
  58. if(rwLock != null)
  59. rwLock.Dispose();
  60. }
  61. public void Add(TKey1 key1, TKey2 key2, TValue value)
  62. {
  63. bool gotLock = false;
  64. try
  65. {
  66. // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
  67. // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
  68. // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
  69. try {}
  70. finally
  71. {
  72. rwLock.EnterWriteLock();
  73. gotLock = true;
  74. }
  75. Dictionary1[key1] = value;
  76. Dictionary2[key2] = value;
  77. m_array = null;
  78. }
  79. finally
  80. {
  81. if (gotLock)
  82. rwLock.ExitWriteLock();
  83. }
  84. }
  85. public bool Remove(TKey1 key1, TKey2 key2)
  86. {
  87. bool success;
  88. bool gotLock = false;
  89. try
  90. {
  91. // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
  92. // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
  93. // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
  94. try {}
  95. finally
  96. {
  97. rwLock.EnterWriteLock();
  98. gotLock = true;
  99. }
  100. success = Dictionary1.Remove(key1);
  101. success &= Dictionary2.Remove(key2);
  102. m_array = null;
  103. }
  104. finally
  105. {
  106. if (gotLock)
  107. rwLock.ExitWriteLock();
  108. }
  109. return success;
  110. }
  111. public bool Remove(TKey1 key1)
  112. {
  113. bool found = false;
  114. bool gotLock = false;
  115. try
  116. {
  117. // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
  118. // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
  119. // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
  120. try {}
  121. finally
  122. {
  123. rwLock.EnterWriteLock();
  124. gotLock = true;
  125. }
  126. // This is an O(n) operation!
  127. TValue value;
  128. if (Dictionary1.TryGetValue(key1, out value))
  129. {
  130. foreach (KeyValuePair<TKey2, TValue> kvp in Dictionary2)
  131. {
  132. if (kvp.Value.Equals(value))
  133. {
  134. try { }
  135. finally
  136. {
  137. Dictionary1.Remove(key1);
  138. Dictionary2.Remove(kvp.Key);
  139. m_array = null;
  140. }
  141. found = true;
  142. break;
  143. }
  144. }
  145. }
  146. }
  147. finally
  148. {
  149. if (gotLock)
  150. rwLock.ExitWriteLock();
  151. }
  152. return found;
  153. }
  154. public bool Remove(TKey2 key2)
  155. {
  156. bool found = false;
  157. bool gotLock = false;
  158. try
  159. {
  160. // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
  161. // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
  162. // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
  163. try {}
  164. finally
  165. {
  166. rwLock.EnterWriteLock();
  167. gotLock = true;
  168. }
  169. // This is an O(n) operation!
  170. TValue value;
  171. if (Dictionary2.TryGetValue(key2, out value))
  172. {
  173. foreach (KeyValuePair<TKey1, TValue> kvp in Dictionary1)
  174. {
  175. if (kvp.Value.Equals(value))
  176. {
  177. try { }
  178. finally
  179. {
  180. Dictionary2.Remove(key2);
  181. Dictionary1.Remove(kvp.Key);
  182. m_array = null;
  183. }
  184. found = true;
  185. break;
  186. }
  187. }
  188. }
  189. }
  190. finally
  191. {
  192. if (gotLock)
  193. rwLock.ExitWriteLock();
  194. }
  195. return found;
  196. }
  197. public void Clear()
  198. {
  199. bool gotLock = false;
  200. try
  201. {
  202. // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
  203. // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
  204. // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
  205. try {}
  206. finally
  207. {
  208. rwLock.EnterWriteLock();
  209. gotLock = true;
  210. Dictionary1.Clear();
  211. Dictionary2.Clear();
  212. m_array = null;
  213. }
  214. }
  215. finally
  216. {
  217. if (gotLock)
  218. rwLock.ExitWriteLock();
  219. }
  220. }
  221. public int Count
  222. {
  223. get { return Dictionary1.Count; }
  224. }
  225. public bool ContainsKey(TKey1 key)
  226. {
  227. return Dictionary1.ContainsKey(key);
  228. }
  229. public bool ContainsKey(TKey2 key)
  230. {
  231. return Dictionary2.ContainsKey(key);
  232. }
  233. public bool TryGetValue(TKey1 key, out TValue value)
  234. {
  235. bool success;
  236. bool gotLock = false;
  237. try
  238. {
  239. // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
  240. // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
  241. // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
  242. try {}
  243. finally
  244. {
  245. rwLock.EnterReadLock();
  246. gotLock = true;
  247. }
  248. success = Dictionary1.TryGetValue(key, out value);
  249. }
  250. finally
  251. {
  252. if (gotLock)
  253. rwLock.ExitReadLock();
  254. }
  255. return success;
  256. }
  257. public bool TryGetValue(TKey2 key, out TValue value)
  258. {
  259. bool success;
  260. bool gotLock = false;
  261. try
  262. {
  263. // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
  264. // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
  265. // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
  266. try {}
  267. finally
  268. {
  269. rwLock.EnterReadLock();
  270. gotLock = true;
  271. }
  272. success = Dictionary2.TryGetValue(key, out value);
  273. }
  274. finally
  275. {
  276. if (gotLock)
  277. rwLock.ExitReadLock();
  278. }
  279. return success;
  280. }
  281. public void ForEach(Action<TValue> action)
  282. {
  283. TValue[] values = GetArray();
  284. if(values == null || values.Length == 0)
  285. return;
  286. foreach (TValue value in values)
  287. action(value);
  288. }
  289. public void ForEach(Action<KeyValuePair<TKey1, TValue>> action)
  290. {
  291. bool gotLock = false;
  292. try
  293. {
  294. // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
  295. // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
  296. // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
  297. try {}
  298. finally
  299. {
  300. rwLock.EnterReadLock();
  301. gotLock = true;
  302. }
  303. foreach (KeyValuePair<TKey1, TValue> entry in Dictionary1)
  304. action(entry);
  305. }
  306. finally
  307. {
  308. if (gotLock)
  309. rwLock.ExitReadLock();
  310. }
  311. }
  312. public void ForEach(Action<KeyValuePair<TKey2, TValue>> action)
  313. {
  314. bool gotLock = false;
  315. try
  316. {
  317. // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
  318. // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
  319. // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
  320. try {}
  321. finally
  322. {
  323. rwLock.EnterReadLock();
  324. gotLock = true;
  325. }
  326. foreach (KeyValuePair<TKey2, TValue> entry in Dictionary2)
  327. action(entry);
  328. }
  329. finally
  330. {
  331. if (gotLock)
  332. rwLock.ExitReadLock();
  333. }
  334. }
  335. public TValue FindValue(Predicate<TValue> predicate)
  336. {
  337. TValue[] values = GetArray();
  338. int len = values.Length;
  339. for (int i = 0; i < len; ++i)
  340. {
  341. if (predicate(values[i]))
  342. return values[i];
  343. }
  344. return default(TValue);
  345. }
  346. public IList<TValue> FindAll(Predicate<TValue> predicate)
  347. {
  348. IList<TValue> list = new List<TValue>();
  349. TValue[] values = GetArray();
  350. int len = values.Length;
  351. for (int i = 0; i < len; ++i)
  352. {
  353. if (predicate(values[i]))
  354. list.Add(values[i]);
  355. }
  356. return list;
  357. }
  358. public int RemoveAll(Predicate<TValue> predicate)
  359. {
  360. IList<TKey1> list = new List<TKey1>();
  361. bool gotUpgradeableLock = false;
  362. try
  363. {
  364. // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
  365. // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
  366. // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
  367. try {}
  368. finally
  369. {
  370. rwLock.EnterUpgradeableReadLock();
  371. gotUpgradeableLock = true;
  372. }
  373. foreach (KeyValuePair<TKey1, TValue> kvp in Dictionary1)
  374. {
  375. if (predicate(kvp.Value))
  376. list.Add(kvp.Key);
  377. }
  378. IList<TKey2> list2 = new List<TKey2>(list.Count);
  379. foreach (KeyValuePair<TKey2, TValue> kvp in Dictionary2)
  380. {
  381. if (predicate(kvp.Value))
  382. list2.Add(kvp.Key);
  383. }
  384. bool gotWriteLock = false;
  385. try
  386. {
  387. try {}
  388. finally
  389. {
  390. rwLock.EnterWriteLock();
  391. gotWriteLock = true;
  392. for (int i = 0; i < list.Count; i++)
  393. Dictionary1.Remove(list[i]);
  394. for (int i = 0; i < list2.Count; i++)
  395. Dictionary2.Remove(list2[i]);
  396. m_array = null;
  397. }
  398. }
  399. finally
  400. {
  401. if (gotWriteLock)
  402. rwLock.ExitWriteLock();
  403. }
  404. }
  405. finally
  406. {
  407. if (gotUpgradeableLock)
  408. rwLock.ExitUpgradeableReadLock();
  409. }
  410. return list.Count;
  411. }
  412. public TValue[] GetArray()
  413. {
  414. bool gotupLock = false;
  415. try
  416. {
  417. try { }
  418. finally
  419. {
  420. rwLock.EnterUpgradeableReadLock();
  421. gotupLock = true;
  422. }
  423. if (m_array == null)
  424. {
  425. bool gotwritelock = false;
  426. try
  427. {
  428. try { }
  429. finally
  430. {
  431. rwLock.EnterWriteLock();
  432. gotwritelock = true;
  433. }
  434. m_array = new TValue[Dictionary1.Count];
  435. Dictionary1.Values.CopyTo(m_array, 0);
  436. }
  437. finally
  438. {
  439. if (gotwritelock)
  440. rwLock.ExitWriteLock();
  441. }
  442. }
  443. return m_array;
  444. }
  445. catch
  446. {
  447. return new TValue[0];
  448. }
  449. finally
  450. {
  451. if (gotupLock)
  452. rwLock.ExitUpgradeableReadLock();
  453. }
  454. }
  455. }
  456. }