ExpiringCacheOS.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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. *
  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. // this is a lighter alternative to libomv, no sliding option
  27. using System;
  28. using System.Threading;
  29. using System.Collections.Generic;
  30. using Timer = System.Threading.Timer ;
  31. namespace OpenSim.Framework
  32. {
  33. public sealed class ExpiringCacheOS<TKey1, TValue1> : IDisposable
  34. {
  35. private const int MINEXPIRECHECK = 500;
  36. private Timer m_purgeTimer;
  37. private ReaderWriterLockSlim m_rwLock;
  38. private readonly Dictionary<TKey1, int> m_expireControl;
  39. private readonly Dictionary<TKey1, TValue1> m_values;
  40. private readonly double m_startTS;
  41. private readonly int m_expire;
  42. public ExpiringCacheOS()
  43. {
  44. m_expireControl = new Dictionary<TKey1, int>();
  45. m_values = new Dictionary<TKey1, TValue1>();
  46. m_rwLock = new ReaderWriterLockSlim();
  47. m_expire = MINEXPIRECHECK;
  48. m_startTS = Util.GetTimeStampMS();
  49. }
  50. public ExpiringCacheOS(int expireCheckTimeinMS)
  51. {
  52. m_expireControl = new Dictionary<TKey1, int>();
  53. m_values = new Dictionary<TKey1, TValue1>();
  54. m_rwLock = new ReaderWriterLockSlim();
  55. m_startTS = Util.GetTimeStampMS();
  56. m_expire = (expireCheckTimeinMS > MINEXPIRECHECK) ? m_expire = expireCheckTimeinMS : MINEXPIRECHECK;
  57. }
  58. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  59. private void CheckTimer()
  60. {
  61. if (m_purgeTimer == null)
  62. {
  63. m_purgeTimer = new Timer(Purge, null, m_expire, Timeout.Infinite);
  64. }
  65. }
  66. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  67. private void DisposeTimer()
  68. {
  69. if (m_purgeTimer != null)
  70. {
  71. m_purgeTimer.Dispose();
  72. m_purgeTimer = null;
  73. }
  74. }
  75. ~ExpiringCacheOS()
  76. {
  77. Dispose(false);
  78. }
  79. public void Dispose()
  80. {
  81. Dispose(true);
  82. GC.SuppressFinalize(this);
  83. }
  84. private void Dispose(bool disposing)
  85. {
  86. if (m_rwLock != null)
  87. {
  88. DisposeTimer();
  89. m_rwLock.Dispose();
  90. m_rwLock = null;
  91. }
  92. }
  93. private void Purge(object ignored)
  94. {
  95. bool gotLock = false;
  96. try
  97. {
  98. try { }
  99. finally
  100. {
  101. m_rwLock.EnterUpgradeableReadLock();
  102. gotLock = true;
  103. }
  104. if (m_expireControl.Count == 0)
  105. {
  106. DisposeTimer();
  107. return;
  108. }
  109. int now = (int)(Util.GetTimeStampMS() - m_startTS);
  110. List<TKey1> expired = new List<TKey1>(m_expireControl.Count);
  111. foreach(KeyValuePair<TKey1, int> kvp in m_expireControl)
  112. {
  113. if(kvp.Value < now)
  114. expired.Add(kvp.Key);
  115. }
  116. if(expired.Count > 0)
  117. {
  118. bool gotWriteLock = false;
  119. try
  120. {
  121. try { }
  122. finally
  123. {
  124. m_rwLock.EnterWriteLock();
  125. gotWriteLock = true;
  126. }
  127. foreach (TKey1 key in expired)
  128. {
  129. m_expireControl.Remove(key);
  130. m_values.Remove(key);
  131. }
  132. }
  133. finally
  134. {
  135. if (gotWriteLock)
  136. m_rwLock.ExitWriteLock();
  137. }
  138. if (m_expireControl.Count == 0)
  139. DisposeTimer();
  140. else
  141. m_purgeTimer.Change(m_expire, Timeout.Infinite);
  142. }
  143. else
  144. m_purgeTimer.Change(m_expire, Timeout.Infinite);
  145. }
  146. finally
  147. {
  148. if (gotLock)
  149. m_rwLock.ExitUpgradeableReadLock();
  150. }
  151. }
  152. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  153. public void AddOrUpdate(TKey1 key, TValue1 val)
  154. {
  155. Add(key, val);
  156. }
  157. public void Add(TKey1 key, TValue1 val)
  158. {
  159. bool gotLock = false;
  160. int now = (int)(Util.GetTimeStampMS() - m_startTS) + m_expire;
  161. try
  162. {
  163. try { }
  164. finally
  165. {
  166. m_rwLock.EnterWriteLock();
  167. gotLock = true;
  168. }
  169. m_expireControl[key] = now;
  170. m_values[key] = val;
  171. CheckTimer();
  172. }
  173. finally
  174. {
  175. if (gotLock)
  176. m_rwLock.ExitWriteLock();
  177. }
  178. }
  179. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  180. public void AddOrUpdate(TKey1 key, TValue1 val, int expireSeconds)
  181. {
  182. Add(key, val, expireSeconds * 1000);
  183. }
  184. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  185. public void AddOrUpdate(TKey1 key, TValue1 val, double expireSeconds)
  186. {
  187. Add(key, val, (int)(expireSeconds * 1000));
  188. }
  189. public void Add(TKey1 key, TValue1 val, int expireMS)
  190. {
  191. bool gotLock = false;
  192. expireMS = (expireMS > m_expire) ? expireMS : m_expire;
  193. int now = (int)(Util.GetTimeStampMS() - m_startTS) + expireMS;
  194. try
  195. {
  196. try { }
  197. finally
  198. {
  199. m_rwLock.EnterWriteLock();
  200. gotLock = true;
  201. }
  202. m_expireControl[key] = now;
  203. m_values[key] = val;
  204. CheckTimer();
  205. }
  206. finally
  207. {
  208. if (gotLock)
  209. m_rwLock.ExitWriteLock();
  210. }
  211. }
  212. public bool Remove(TKey1 key)
  213. {
  214. bool success;
  215. bool gotLock = false;
  216. try
  217. {
  218. try {}
  219. finally
  220. {
  221. m_rwLock.EnterWriteLock();
  222. gotLock = true;
  223. }
  224. success = m_expireControl.Remove(key);
  225. success |= m_values.Remove(key);
  226. if(m_expireControl.Count == 0)
  227. DisposeTimer();
  228. }
  229. finally
  230. {
  231. if (gotLock)
  232. m_rwLock.ExitWriteLock();
  233. }
  234. return success;
  235. }
  236. public void Clear()
  237. {
  238. bool gotLock = false;
  239. try
  240. {
  241. try {}
  242. finally
  243. {
  244. m_rwLock.EnterWriteLock();
  245. gotLock = true;
  246. }
  247. DisposeTimer();
  248. m_expireControl.Clear();
  249. m_values.Clear();
  250. }
  251. finally
  252. {
  253. if (gotLock)
  254. m_rwLock.ExitWriteLock();
  255. }
  256. }
  257. public int Count
  258. {
  259. get { return m_expireControl.Count; }
  260. }
  261. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  262. public bool Contains(TKey1 key)
  263. {
  264. return ContainsKey(key);
  265. }
  266. public bool ContainsKey(TKey1 key)
  267. {
  268. bool gotLock = false;
  269. try
  270. {
  271. try { }
  272. finally
  273. {
  274. m_rwLock.EnterReadLock();
  275. gotLock = true;
  276. }
  277. return m_expireControl.ContainsKey(key);
  278. }
  279. finally
  280. {
  281. if (gotLock)
  282. m_rwLock.ExitReadLock();
  283. }
  284. }
  285. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  286. public bool Contains(TKey1 key, int expireMS)
  287. {
  288. return ContainsKey(key, expireMS);
  289. }
  290. public bool ContainsKey(TKey1 key, int expireMS)
  291. {
  292. bool gotLock = false;
  293. try
  294. {
  295. try { }
  296. finally
  297. {
  298. m_rwLock.EnterUpgradeableReadLock();
  299. gotLock = true;
  300. }
  301. if(m_expireControl.ContainsKey(key))
  302. {
  303. bool gotWriteLock = false;
  304. try
  305. {
  306. try { }
  307. finally
  308. {
  309. m_rwLock.EnterWriteLock();
  310. gotWriteLock = true;
  311. }
  312. expireMS = (expireMS > m_expire) ? expireMS : m_expire;
  313. int now = (int)(Util.GetTimeStampMS() - m_startTS) + expireMS;
  314. m_expireControl[key] = now;
  315. return true;
  316. }
  317. finally
  318. {
  319. if (gotWriteLock)
  320. m_rwLock.ExitWriteLock();
  321. }
  322. }
  323. return false;
  324. }
  325. finally
  326. {
  327. if (gotLock)
  328. m_rwLock.ExitUpgradeableReadLock();
  329. }
  330. }
  331. public bool TryGetValue(TKey1 key, out TValue1 value)
  332. {
  333. bool success;
  334. bool gotLock = false;
  335. try
  336. {
  337. try {}
  338. finally
  339. {
  340. m_rwLock.EnterReadLock();
  341. gotLock = true;
  342. }
  343. success = m_values.TryGetValue(key, out value);
  344. }
  345. finally
  346. {
  347. if (gotLock)
  348. m_rwLock.ExitReadLock();
  349. }
  350. return success;
  351. }
  352. public bool TryGetValue(TKey1 key, int expireMS, out TValue1 value)
  353. {
  354. bool success;
  355. bool gotLock = false;
  356. try
  357. {
  358. try { }
  359. finally
  360. {
  361. m_rwLock.EnterUpgradeableReadLock();
  362. gotLock = true;
  363. }
  364. success = m_values.TryGetValue(key, out value);
  365. if(success)
  366. {
  367. bool gotWriteLock = false;
  368. try
  369. {
  370. try { }
  371. finally
  372. {
  373. m_rwLock.EnterWriteLock();
  374. gotWriteLock = true;
  375. }
  376. expireMS = (expireMS > m_expire) ? expireMS : m_expire;
  377. int now = (int)(Util.GetTimeStampMS() - m_startTS) + expireMS;
  378. m_expireControl[key] = now;
  379. }
  380. finally
  381. {
  382. if (gotWriteLock)
  383. m_rwLock.ExitWriteLock();
  384. }
  385. }
  386. }
  387. finally
  388. {
  389. if (gotLock)
  390. m_rwLock.ExitUpgradeableReadLock();
  391. }
  392. return success;
  393. }
  394. public ICollection<TValue1> Values
  395. {
  396. get
  397. {
  398. bool gotLock = false;
  399. try
  400. {
  401. try { }
  402. finally
  403. {
  404. m_rwLock.EnterUpgradeableReadLock();
  405. gotLock = true;
  406. }
  407. return m_values.Values;
  408. }
  409. finally
  410. {
  411. if (gotLock)
  412. m_rwLock.ExitUpgradeableReadLock();
  413. }
  414. }
  415. }
  416. public ICollection<TKey1> Keys
  417. {
  418. get
  419. {
  420. bool gotLock = false;
  421. try
  422. {
  423. try { }
  424. finally
  425. {
  426. m_rwLock.EnterUpgradeableReadLock();
  427. gotLock = true;
  428. }
  429. return m_values.Keys;
  430. }
  431. finally
  432. {
  433. if (gotLock)
  434. m_rwLock.ExitUpgradeableReadLock();
  435. }
  436. }
  437. }
  438. }
  439. }