PageRenderTime 22ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/netbeans-7.3/cnd.repository/src/org/netbeans/modules/cnd/repository/disk/MemoryCache.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 484 lines | 382 code | 34 blank | 68 comment | 86 complexity | 7538989a45571a51e13d4b61d39429cd MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * If you wish your version of this file to be governed by only the CDDL
  28. * or only the GPL Version 2, indicate your decision by adding
  29. * "[Contributor] elects to include this software in this distribution
  30. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  31. * single choice of license, a recipient has the option to distribute
  32. * your version of this file under either the CDDL, the GPL Version 2 or
  33. * to extend the choice of license to its licensees as provided above.
  34. * However, if you add GPL Version 2 code and therefore, elected the GPL
  35. * Version 2 license, then the option applies only if the new code is
  36. * made subject to such option by the copyright holder.
  37. *
  38. * Contributor(s):
  39. *
  40. * Portions Copyrighted 2008 Sun Microsystems, Inc.
  41. */
  42. package org.netbeans.modules.cnd.repository.disk;
  43. import java.lang.ref.Reference;
  44. import java.lang.ref.ReferenceQueue;
  45. import java.lang.ref.SoftReference;
  46. import java.lang.ref.WeakReference;
  47. import java.util.ArrayList;
  48. import java.util.Collection;
  49. import java.util.HashMap;
  50. import java.util.HashSet;
  51. import java.util.Map;
  52. import java.util.Set;
  53. import java.util.TreeMap;
  54. import java.util.concurrent.locks.Lock;
  55. import java.util.concurrent.locks.ReadWriteLock;
  56. import java.util.concurrent.locks.ReentrantLock;
  57. import java.util.concurrent.locks.ReentrantReadWriteLock;
  58. import org.netbeans.modules.cnd.debug.CndTraceFlags;
  59. import org.netbeans.modules.cnd.repository.spi.Key;
  60. import org.netbeans.modules.cnd.repository.spi.Persistent;
  61. import org.netbeans.modules.cnd.repository.util.Pair;
  62. import org.netbeans.modules.cnd.utils.CndUtils;
  63. /**
  64. * An in-memory cache for storing repository objects
  65. * @author Nickolay Dalmatov
  66. * @author Vladimir Kvashin
  67. */
  68. public final class MemoryCache {
  69. private static final boolean WEAK_REF = false && CndTraceFlags.WEAK_REFS_HOLDERS;
  70. // Use soft references only when we request an object
  71. private static final boolean SOFT_REFS_ON_GET = true;
  72. private static final boolean STATISTIC = false;
  73. private static final int DEFAULT_SLICE_CAPACITY;
  74. private static final int SLICE_SIZE;
  75. private static final int SLICE_MASK;
  76. static {
  77. int nrProc = CndUtils.getConcurrencyLevel();
  78. if (nrProc <= 4) {
  79. SLICE_SIZE = 32;
  80. SLICE_MASK = SLICE_SIZE - 1;
  81. DEFAULT_SLICE_CAPACITY = 512;
  82. } else {
  83. SLICE_SIZE = 128;
  84. SLICE_MASK = SLICE_SIZE - 1;
  85. DEFAULT_SLICE_CAPACITY = 128;
  86. }
  87. }
  88. private interface CacheValue {
  89. Key getKey();
  90. }
  91. private static class SoftValue<T> extends SoftReference<T> implements CacheValue {
  92. private final Key key;
  93. private SoftValue(T k, Key key, ReferenceQueue<T> q) {
  94. super(k, q);
  95. this.key = key;
  96. }
  97. @Override
  98. public Key getKey() {
  99. return key;
  100. }
  101. }
  102. private static class WeakValue<T> extends WeakReference<T> implements CacheValue {
  103. private final Key key;
  104. private WeakValue(T k, Key key, ReferenceQueue<T> q) {
  105. super(k, q);
  106. this.key = key;
  107. }
  108. @Override
  109. public Key getKey() {
  110. return key;
  111. }
  112. }
  113. private static final class Slice {
  114. private final Map<Key, Object> storage = new HashMap<Key, Object>(DEFAULT_SLICE_CAPACITY);
  115. private final ReadWriteLock cacheLock = new ReentrantReadWriteLock();
  116. private final Lock w = cacheLock.writeLock();
  117. private final Lock r = cacheLock.readLock();
  118. }
  119. private static final class SlicedMap {
  120. private final Slice slices[] = new Slice[SLICE_SIZE];
  121. private SlicedMap(){
  122. for(int i = 0; i < SLICE_SIZE; i++){
  123. slices[i] = new Slice();
  124. }
  125. }
  126. private Slice getSilce(Key key){
  127. int i = key.hashCode() & SLICE_MASK;
  128. return slices[i];
  129. }
  130. private Slice getSilce(int i){
  131. return slices[i];
  132. }
  133. }
  134. private final SlicedMap cache = new SlicedMap();
  135. private final Lock refQueueLock;
  136. private final ReferenceQueue<Persistent> refQueue;
  137. // Cache statistics
  138. private int readCnt = 0;
  139. private int readHitCnt = 0;
  140. private int hangs = 0;
  141. private int puts = 0;
  142. private int putIfAbs = 0;
  143. private int switchToSoft = 0;
  144. private int weakRefs = 0;
  145. public MemoryCache() {
  146. refQueueLock = new ReentrantLock();
  147. refQueue = new ReferenceQueue<Persistent>();
  148. }
  149. public void hang(Key key, Persistent obj) {
  150. if (STATISTIC) {hangs++;}
  151. Slice s = cache.getSilce(key);
  152. s.w.lock();
  153. try {
  154. s.storage.put(key, obj);
  155. } finally {
  156. s.w.unlock();
  157. }
  158. }
  159. public void put(Key key, Persistent obj) {
  160. if (STATISTIC) {puts++;}
  161. Slice s = cache.getSilce(key);
  162. Reference<Persistent> value;
  163. if (SOFT_REFS_ON_GET || (WEAK_REF && key.getBehavior() != Key.Behavior.LargeAndMutable)) {
  164. value = new WeakValue<Persistent>(obj, key, refQueue);
  165. if (STATISTIC) {weakRefs++;}
  166. } else {
  167. value = new SoftValue<Persistent>(obj, key, refQueue);
  168. }
  169. s.w.lock();
  170. try {
  171. s.storage.put(key, value);
  172. } finally {
  173. s.w.unlock();
  174. }
  175. }
  176. /**
  177. *
  178. * @param key key with which the specified value is to be associated
  179. * @param value value to be associated with the specified key
  180. * @return the previous value associated with the specified key, or
  181. * <tt>null</tt> if there was no mapping for the key.
  182. */
  183. public Persistent putIfAbsent(Key key, Persistent obj) {
  184. if (STATISTIC) {putIfAbs++;}
  185. Persistent prevPersistent = null;
  186. Slice s = cache.getSilce(key);
  187. s.w.lock();
  188. try {
  189. // do not override existed value if any
  190. Object old = s.storage.get(key);
  191. if (old instanceof RemovedValue) {
  192. prevPersistent = null;
  193. } else if (old instanceof Reference) {
  194. prevPersistent = (Persistent) ((Reference) old).get();
  195. } else if (old instanceof Persistent) {
  196. prevPersistent = (Persistent) old;
  197. } else if (old != null) {
  198. System.err.println("unexpected value " + old + " for key " + key);
  199. }
  200. if (prevPersistent == null) {
  201. Reference<Persistent> value;
  202. if (WEAK_REF && key.getBehavior() != Key.Behavior.LargeAndMutable) {
  203. value = new WeakValue<Persistent>(obj, key, refQueue);
  204. if (STATISTIC) {weakRefs++;}
  205. } else {
  206. value = new SoftValue<Persistent>(obj, key, refQueue);
  207. }
  208. // no previous value
  209. // put new item into storage
  210. s.storage.put(key, value);
  211. }
  212. } finally {
  213. s.w.unlock();
  214. }
  215. processQueue();
  216. return prevPersistent;
  217. }
  218. public Persistent get(Key key) {
  219. if (STATISTIC) {readCnt++;}
  220. Slice s = cache.getSilce(key);
  221. Object value;
  222. s.r.lock();
  223. try{
  224. value = s.storage.get(key);
  225. } finally {
  226. s.r.unlock();
  227. }
  228. if (value instanceof RemovedValue) {
  229. return ((RemovedValue) value).value;
  230. } else if (value instanceof Persistent) {
  231. if (STATISTIC) {readHitCnt++;}
  232. return (Persistent) value;
  233. } else if (value instanceof Reference) {
  234. Persistent result = (Persistent) ((Reference) value).get();
  235. if (SOFT_REFS_ON_GET && result != null && value instanceof WeakReference) {
  236. // switch to soft reference
  237. s.w.lock();
  238. try {
  239. // check that there were no modifications
  240. Object freshValue = s.storage.get(key);
  241. if (freshValue == value) {
  242. value = new SoftValue<Persistent>(result, key, refQueue);
  243. s.storage.put(key, value);
  244. if (STATISTIC) {switchToSoft++;}
  245. }
  246. } finally {
  247. s.w.unlock();
  248. }
  249. }
  250. if( STATISTIC && result != null ) {
  251. readHitCnt++;
  252. }
  253. return result;
  254. }
  255. return null;
  256. }
  257. void removePhysically(Key key) {
  258. Slice s = cache.getSilce(key);
  259. s.w.lock();
  260. try {
  261. Object prev = s.storage.get(key);
  262. if (prev instanceof RemovedValue) {
  263. s.storage.remove(key);
  264. }
  265. } finally {
  266. s.w.unlock();
  267. }
  268. }
  269. void markRemoved(Key key) {
  270. Slice s = cache.getSilce(key);
  271. s.w.lock();
  272. try {
  273. Persistent old = get(key);
  274. s.storage.put(key, new RemovedValue(old));
  275. // do not assert for now
  276. // Object old = s.storage.put(key, REMOVED);
  277. // assert old != null : " no value for removed key " + key;
  278. } finally {
  279. s.w.unlock();
  280. }
  281. }
  282. public void clearSoftRefs() {
  283. //cleanWriteHungObjects(null, false);
  284. processQueue();
  285. Set<Key> keys;
  286. for(int i = 0; i < SLICE_SIZE; i++) {
  287. Slice s = cache.getSilce(i);
  288. s.r.lock();
  289. try{
  290. keys = new HashSet<Key>(s.storage.keySet());
  291. } finally {
  292. s.r.unlock();
  293. }
  294. for (Key key : keys) {
  295. Object value;
  296. s.w.lock();
  297. try{
  298. value = s.storage.get(key);
  299. if (value != null && !(value instanceof Persistent)) {
  300. s.storage.remove(key);
  301. }
  302. } finally {
  303. s.w.unlock();
  304. }
  305. }
  306. }
  307. }
  308. private void processQueue() {
  309. if (refQueueLock.tryLock()) {
  310. try {
  311. CacheValue sv;
  312. while ((sv = (CacheValue) refQueue.poll()) != null) {
  313. Object value;
  314. final Key key = sv.getKey();
  315. if (key != null) {
  316. Slice s = cache.getSilce(key);
  317. s.w.lock();
  318. try{
  319. value = s.storage.get(key);
  320. // check if the object has already been added by another thread
  321. // it is more efficient than blocking puts from the disk
  322. if ((value != null) && (value instanceof Reference) && (((Reference) value).get() == null)) {
  323. Object removed = s.storage.remove(key);
  324. assert (value == removed);
  325. }
  326. } finally {
  327. s.w.unlock();
  328. }
  329. }
  330. }
  331. } finally {
  332. refQueueLock.unlock();
  333. }
  334. }
  335. }
  336. public Collection<Pair<Key, Persistent>> clearHungObjects(/*Filter<Key> filter*/) {
  337. processQueue();
  338. Collection<Pair<Key, Persistent>> result = new ArrayList<Pair<Key, Persistent>>();
  339. Set<Key> keys;
  340. for(int i = 0; i < SLICE_SIZE; i++) {
  341. Slice s = cache.getSilce(i);
  342. s.r.lock();
  343. try{
  344. keys = new HashSet<Key>(s.storage.keySet());
  345. } finally {
  346. s.r.unlock();
  347. }
  348. for (Key key : keys) {
  349. Object value;
  350. s.r.lock();
  351. try{
  352. value = s.storage.get(key);
  353. } finally {
  354. s.r.unlock();
  355. }
  356. if (value instanceof Persistent ) {
  357. result.add(new Pair<Key,Persistent>(key, (Persistent) value));
  358. s.w.lock();
  359. try {
  360. s.storage.remove(key);
  361. } finally {
  362. s.w.unlock();
  363. }
  364. }
  365. }
  366. }
  367. return result;
  368. }
  369. private void printStatistics(String name) {
  370. int hitPercentage = (readCnt == 0) ? 0 : readHitCnt*100/readCnt;
  371. System.out.printf("\n\nMemory cache statistics %s: %d reads, %d hits (%d%%)\n\n", // NOI18N
  372. name, readCnt, readHitCnt, hitPercentage);
  373. }
  374. /*package-local*/ void printDistribution(){
  375. Map<String, Integer> stat = new TreeMap<String, Integer>();
  376. Map<String, Integer> statSoft = new TreeMap<String, Integer>();
  377. int fullSize = 0;
  378. int nullSize = 0;
  379. for(final Slice s : cache.slices){
  380. s.r.lock();
  381. try {
  382. fullSize += s.storage.size();
  383. for(Map.Entry<Key, Object> entry : s.storage.entrySet()){
  384. Key key = entry.getKey();
  385. Object value = entry.getValue();
  386. boolean isSoft = false;
  387. if ((value != null) && (value instanceof Reference)){
  388. isSoft = true;
  389. value = ((Reference) value).get();
  390. }
  391. String res = key.getClass().getName();
  392. if (value == null) {
  393. if (isSoft) {
  394. res += "-soft null"; // NOI18N
  395. } else {
  396. res += "-null"; // NOI18N
  397. }
  398. nullSize++;
  399. } else {
  400. if (isSoft) {
  401. res += "-soft "+value.getClass().getName(); // NOI18N
  402. } else {
  403. res += "-"+value.getClass().getName(); // NOI18N
  404. }
  405. }
  406. Integer i = isSoft ? statSoft.get(res) : stat.get(res);
  407. if (i == null) {
  408. i = Integer.valueOf(1);
  409. } else {
  410. i = Integer.valueOf(i.intValue()+1);
  411. }
  412. if (isSoft) {
  413. statSoft.put(res, i);
  414. } else {
  415. stat.put(res, i);
  416. }
  417. }
  418. } finally {
  419. s.r.unlock();
  420. }
  421. }
  422. System.err.println("\tMemCache of size " + fullSize + " with null " + nullSize + " objects");
  423. System.err.println("\tSoft memory cache");
  424. for (Map.Entry<String, Integer> entry : statSoft.entrySet()){
  425. System.err.println("\t"+entry.getKey()+"="+entry.getValue());
  426. }
  427. System.err.println("\tHard memory cache");
  428. for (Map.Entry<String, Integer> entry : stat.entrySet()){
  429. System.err.println("\t"+entry.getKey()+"="+entry.getValue());
  430. }
  431. }
  432. // removed values which can be in queue waiting for physical removal
  433. // if we don't have such marker then previous object is read from storage
  434. static final Persistent REMOVED = new Persistent() {
  435. @Override
  436. public String toString() {
  437. return "MemoryCache.REMOVED"; // NOI18N
  438. }
  439. };
  440. private static final class RemovedValue {
  441. private final Persistent value;
  442. public RemovedValue(Persistent value) {
  443. this.value = value == null ? REMOVED : value;
  444. }
  445. @Override
  446. public String toString() {
  447. return "RemovedValue{" + "value=" + value + '}'; // NOI18N
  448. }
  449. };
  450. }