ExpiringCacheOS.cs 17 KB

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