Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
[EHC-939] BlockingCache.get sometimes returns null without holding lock Created: 12/Apr/12 Updated: 01/Mar/13 Resolved: 14/Nov/12 Status: Project: Component/s: Affects Version/s: Fix Version/s: Resolved Ehcache Core ehcache-core 2.5.1 Type: Reporter: Resolution: Labels: Remaining Estimate: Time Spent: Original Estimate: Bug Petur Runolfsson As Designed None Not Specified Issue Links: Related relates to EHC-715 BlockingCache.put removes entry if El... is related to EHC-947 Write lock leak Terracotta Target: 2.7.0 Priority: Assignee: Votes: 2 Major Alexander Snaps 1 Not Specified Not Specified Vicente Description There appears to be a bug in BlockingCache.get in this block: if (element == null) { acquiredLockForKey(key, lock, LockType.WRITE); element = underlyingCache.getQuiet(key); if (element != null) { if (underlyingCache.isStatisticsEnabled()) { element = underlyingCache.get(key); } lock.unlock(LockType.WRITE); } } Resolved Closed It is possible for the element to expire, be evicted or removed by another thread between the calls to underlyingCache.getQuiet and underlyingCache.get. In that case, element will be null, but the lock is unlocked anyway. This could be fixed by adding an extra if (element != null) before calling lock.unlock, or by not assigning the result of underlyingCache.get to element. Here is a test that demonstrates the problem: public class BlockingCacheTest extends BlockingCache { public BlockingCacheTest(Ehcache cache) throws CacheException { super(cache); } public static void main(String[] args) throws Exception { Configuration config = ConfigurationFactory.parseConfiguration(); config.getDefaultCacheConfiguration().setStatistics(true); CacheManager cm = CacheManager.create(config); cm.addCache("test"); final Cache cache = cm.getCache("test"); System.out.println("statistics=" + cache.isStatisticsEnabled()); final BlockingCacheTest bc = new BlockingCacheTest(cache); Thread[] threads = new Thread[10]; for (int j = 0; j < threads.length; j++) { Thread t = new Thread() { public void run() { Object key = "key"; for (int i = 0; i < 1000000; i++) { Element element = bc.get(key); if (element == null) { if (!bc.getLockForKey(key).isHeldByCurrentThread(LockType.WRITE)) { System.err.println("FAIL: i=" + i); System.exit(1); } bc.put(new Element(key, i)); } if (i % 7 == 3) cache.removeAll(); } } }; t.start(); threads[j] = t; } for (Thread t : threads) t.join(); System.out.println("SUCCESS"); System.exit(0); } } Comments Comment by Fiona OShea [ 12/Apr/12 ] I'm not sure if this relates to . Alex can you confirm? Comment by Martin JANDA [ 06/Jun/12 ] I see that lock.unlock(WRITE) is not called in all paths when element = null. I have deadlock in FJ pool. I don't in EhCache 2.5.2 sources. "ForkJoinPool-1-worker-4" - Thread t@34 java.lang.Thread.State: WAITING at sun.misc.Unsafe.park(Native Method) parking to wait for <65786232> (a java.util.concurrent.ForkJoinPool) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.ForkJoinPool.tryAwaitWork(ForkJoinPool.java:864) at java.util.concurrent.ForkJoinPool.work(ForkJoinPool.java:647) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:398) Locked ownable synchronizers: locked <674eceb2> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync) "ForkJoinPool-1-worker-1" - Thread t@17 java.lang.Thread.State: WAITING at sun.misc.Unsafe.park(Native Method) waiting to lock <674eceb2> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync) owned "ForkJoinPool-1-worker-4" t@34 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchro at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer. at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:119 at java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock(ReentrantReadWriteLock.java:94 at net.sf.ehcache.concurrent.ReadWriteLockSync.lock(ReadWriteLockSync.java:50) at net.sf.ehcache.constructs.blocking.BlockingCache.acquiredLockForKey(BlockingCache.java:186) at net.sf.ehcache.constructs.blocking.BlockingCache.get(BlockingCache.java:159) at net.sf.ehcache.constructs.blocking.SelfPopulatingCache.get(SelfPopulatingCache.java:68) at net.sf.ehcache.constructs.blocking.BlockingCache.get(BlockingCache.java:243) "ForkJoinPool-1-worker-2" - Thread t@23 java.lang.Thread.State: WAITING at sun.misc.Unsafe.park(Native Method) waiting to lock <674eceb2> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync) owned "ForkJoinPool-1-worker-4" t@34 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchro at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer. at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:119 at java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock(ReentrantReadWriteLock.java:94 at net.sf.ehcache.concurrent.ReadWriteLockSync.lock(ReadWriteLockSync.java:50) at net.sf.ehcache.constructs.blocking.BlockingCache.acquiredLockForKey(BlockingCache.java:186) at net.sf.ehcache.constructs.blocking.BlockingCache.get(BlockingCache.java:159) at net.sf.ehcache.constructs.blocking.SelfPopulatingCache.get(SelfPopulatingCache.java:68) at net.sf.ehcache.constructs.blocking.BlockingCache.get(BlockingCache.java:243) "ForkJoinPool-1-worker-3" - Thread t@33 java.lang.Thread.State: WAITING at sun.misc.Unsafe.park(Native Method) waiting to lock <674eceb2> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync) owned "ForkJoinPool-1-worker-4" t@34 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchro at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireShared(AbstractQueuedSynchronize at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared(AbstractQueuedSynchronizer.j at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.lock(ReentrantReadWriteLock.java:73 at net.sf.ehcache.concurrent.ReadWriteLockSync.lock(ReadWriteLockSync.java:50) at net.sf.ehcache.constructs.blocking.BlockingCache.acquiredLockForKey(BlockingCache.java:186) at net.sf.ehcache.constructs.blocking.BlockingCache.get(BlockingCache.java:151) at net.sf.ehcache.constructs.blocking.SelfPopulatingCache.get(SelfPopulatingCache.java:68) at net.sf.ehcache.constructs.blocking.BlockingCache.get(BlockingCache.java:243) Comment by Alexander Snaps [ 06/Jun/12 ] Martin, that's the purpose of BlockingCache, the unlocking of the key only happens when you put a value for the returned null... Comment by Martin JANDA [ 07/Jun/12 ] Threaddump comment is another bug - - Write lock leak Comment by Fiona OShea [ 14/Nov/12 ] Closing as we do not see a bug here. Please open a new Jira if there is still an existing problem. Generated at Sat May 13 03:42:11 PDT 2017 using JIRA 6.2.4#6261sha1:4d2e6f6f26064845673c8e7ffe9b6b84b45a6e79.