ExpiringCacheOS.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  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. int expire = kvp.Value;
  114. if(expire > 0 && expire < now)
  115. expired.Add(kvp.Key);
  116. }
  117. if(expired.Count > 0)
  118. {
  119. bool gotWriteLock = false;
  120. try
  121. {
  122. try { }
  123. finally
  124. {
  125. m_rwLock.EnterWriteLock();
  126. gotWriteLock = true;
  127. }
  128. foreach (TKey1 key in expired)
  129. {
  130. m_expireControl.Remove(key);
  131. m_values.Remove(key);
  132. }
  133. }
  134. finally
  135. {
  136. if (gotWriteLock)
  137. m_rwLock.ExitWriteLock();
  138. }
  139. if (m_expireControl.Count == 0)
  140. DisposeTimer();
  141. else
  142. m_purgeTimer.Change(m_expire, Timeout.Infinite);
  143. }
  144. else
  145. m_purgeTimer.Change(m_expire, Timeout.Infinite);
  146. }
  147. finally
  148. {
  149. if (gotLock)
  150. m_rwLock.ExitUpgradeableReadLock();
  151. }
  152. }
  153. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  154. public void AddOrUpdate(TKey1 key, TValue1 val)
  155. {
  156. Add(key, val);
  157. }
  158. public void Add(TKey1 key, TValue1 val)
  159. {
  160. bool gotLock = false;
  161. int now = (int)(Util.GetTimeStampMS() - m_startTS) + m_expire;
  162. try
  163. {
  164. try { }
  165. finally
  166. {
  167. m_rwLock.EnterWriteLock();
  168. gotLock = true;
  169. }
  170. m_expireControl[key] = now;
  171. m_values[key] = val;
  172. CheckTimer();
  173. }
  174. finally
  175. {
  176. if (gotLock)
  177. m_rwLock.ExitWriteLock();
  178. }
  179. }
  180. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  181. public void AddOrUpdate(TKey1 key, TValue1 val, int expireSeconds)
  182. {
  183. Add(key, val, expireSeconds * 1000);
  184. }
  185. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  186. public void AddOrUpdate(TKey1 key, TValue1 val, double expireSeconds)
  187. {
  188. Add(key, val, (int)(expireSeconds * 1000));
  189. }
  190. public void Add(TKey1 key, TValue1 val, int expireMS)
  191. {
  192. bool gotLock = false;
  193. int now;
  194. if (expireMS > 0)
  195. {
  196. expireMS = (expireMS > m_expire) ? expireMS : m_expire;
  197. now = (int)(Util.GetTimeStampMS() - m_startTS) + expireMS;
  198. }
  199. else
  200. now = int.MinValue;
  201. try
  202. {
  203. try { }
  204. finally
  205. {
  206. m_rwLock.EnterWriteLock();
  207. gotLock = true;
  208. }
  209. m_expireControl[key] = now;
  210. m_values[key] = val;
  211. CheckTimer();
  212. }
  213. finally
  214. {
  215. if (gotLock)
  216. m_rwLock.ExitWriteLock();
  217. }
  218. }
  219. public bool Remove(TKey1 key)
  220. {
  221. bool success;
  222. bool gotLock = false;
  223. try
  224. {
  225. try {}
  226. finally
  227. {
  228. m_rwLock.EnterWriteLock();
  229. gotLock = true;
  230. }
  231. success = m_expireControl.Remove(key);
  232. success |= m_values.Remove(key);
  233. if(m_expireControl.Count == 0)
  234. DisposeTimer();
  235. }
  236. finally
  237. {
  238. if (gotLock)
  239. m_rwLock.ExitWriteLock();
  240. }
  241. return success;
  242. }
  243. public void Clear()
  244. {
  245. bool gotLock = false;
  246. try
  247. {
  248. try {}
  249. finally
  250. {
  251. m_rwLock.EnterWriteLock();
  252. gotLock = true;
  253. }
  254. DisposeTimer();
  255. m_expireControl.Clear();
  256. m_values.Clear();
  257. }
  258. finally
  259. {
  260. if (gotLock)
  261. m_rwLock.ExitWriteLock();
  262. }
  263. }
  264. public int Count
  265. {
  266. get { return m_expireControl.Count; }
  267. }
  268. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  269. public bool Contains(TKey1 key)
  270. {
  271. return ContainsKey(key);
  272. }
  273. public bool ContainsKey(TKey1 key)
  274. {
  275. bool gotLock = false;
  276. try
  277. {
  278. try { }
  279. finally
  280. {
  281. m_rwLock.EnterReadLock();
  282. gotLock = true;
  283. }
  284. return m_expireControl.ContainsKey(key);
  285. }
  286. finally
  287. {
  288. if (gotLock)
  289. m_rwLock.ExitReadLock();
  290. }
  291. }
  292. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  293. public bool Contains(TKey1 key, int expireMS)
  294. {
  295. return ContainsKey(key, expireMS);
  296. }
  297. public bool ContainsKey(TKey1 key, int expireMS)
  298. {
  299. bool gotLock = false;
  300. try
  301. {
  302. try { }
  303. finally
  304. {
  305. m_rwLock.EnterUpgradeableReadLock();
  306. gotLock = true;
  307. }
  308. if(m_expireControl.ContainsKey(key))
  309. {
  310. bool gotWriteLock = false;
  311. try
  312. {
  313. try { }
  314. finally
  315. {
  316. m_rwLock.EnterWriteLock();
  317. gotWriteLock = true;
  318. }
  319. int now;
  320. if(expireMS > 0)
  321. {
  322. expireMS = (expireMS > m_expire) ? expireMS : m_expire;
  323. now = (int)(Util.GetTimeStampMS() - m_startTS) + expireMS;
  324. }
  325. else
  326. now = int.MinValue;
  327. m_expireControl[key] = now;
  328. return true;
  329. }
  330. finally
  331. {
  332. if (gotWriteLock)
  333. m_rwLock.ExitWriteLock();
  334. }
  335. }
  336. return false;
  337. }
  338. finally
  339. {
  340. if (gotLock)
  341. m_rwLock.ExitUpgradeableReadLock();
  342. }
  343. }
  344. public bool TryGetValue(TKey1 key, out TValue1 value)
  345. {
  346. bool success;
  347. bool gotLock = false;
  348. try
  349. {
  350. try {}
  351. finally
  352. {
  353. m_rwLock.EnterReadLock();
  354. gotLock = true;
  355. }
  356. success = m_values.TryGetValue(key, out value);
  357. }
  358. finally
  359. {
  360. if (gotLock)
  361. m_rwLock.ExitReadLock();
  362. }
  363. return success;
  364. }
  365. public bool TryGetValue(TKey1 key, int expireMS, out TValue1 value)
  366. {
  367. bool success;
  368. bool gotLock = false;
  369. try
  370. {
  371. try { }
  372. finally
  373. {
  374. m_rwLock.EnterUpgradeableReadLock();
  375. gotLock = true;
  376. }
  377. success = m_values.TryGetValue(key, out value);
  378. if(success)
  379. {
  380. bool gotWriteLock = false;
  381. try
  382. {
  383. try { }
  384. finally
  385. {
  386. m_rwLock.EnterWriteLock();
  387. gotWriteLock = true;
  388. }
  389. int now;
  390. if(expireMS > 0)
  391. {
  392. expireMS = (expireMS > m_expire) ? expireMS : m_expire;
  393. now = (int)(Util.GetTimeStampMS() - m_startTS) + expireMS;
  394. }
  395. else
  396. now = int.MinValue;
  397. m_expireControl[key] = now;
  398. }
  399. finally
  400. {
  401. if (gotWriteLock)
  402. m_rwLock.ExitWriteLock();
  403. }
  404. }
  405. }
  406. finally
  407. {
  408. if (gotLock)
  409. m_rwLock.ExitUpgradeableReadLock();
  410. }
  411. return success;
  412. }
  413. public ICollection<TValue1> Values
  414. {
  415. get
  416. {
  417. bool gotLock = false;
  418. try
  419. {
  420. try { }
  421. finally
  422. {
  423. m_rwLock.EnterUpgradeableReadLock();
  424. gotLock = true;
  425. }
  426. return m_values.Values;
  427. }
  428. finally
  429. {
  430. if (gotLock)
  431. m_rwLock.ExitUpgradeableReadLock();
  432. }
  433. }
  434. }
  435. public ICollection<TKey1> Keys
  436. {
  437. get
  438. {
  439. bool gotLock = false;
  440. try
  441. {
  442. try { }
  443. finally
  444. {
  445. m_rwLock.EnterUpgradeableReadLock();
  446. gotLock = true;
  447. }
  448. return m_values.Keys;
  449. }
  450. finally
  451. {
  452. if (gotLock)
  453. m_rwLock.ExitUpgradeableReadLock();
  454. }
  455. }
  456. }
  457. }
  458. }