001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.commons.lock.rw; 020 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.Map.Entry; 027 028import lucee.commons.lang.ExceptionUtil; 029import lucee.commons.lock.Lock; 030import lucee.commons.lock.LockException; 031import lucee.commons.lock.LockInterruptedException; 032import lucee.runtime.exp.PageRuntimeException; 033import lucee.runtime.op.Caster; 034 035public class RWKeyLock<K> { 036 037 private Map<K,RWLock<K>> locks=new HashMap<K,RWLock<K>>(); 038 039 public Lock lock(K token, long timeout, boolean readOnly) throws LockException, LockInterruptedException { 040 if(timeout<=0) throw new LockException("timeout must be a postive number"); 041 042 RWWrap<K> wrap; 043 //K token=key; 044 synchronized (locks) { 045 RWLock<K> lock; 046 lock=locks.get(token); 047 if(lock==null) { 048 locks.put(token, lock=new RWLock<K>(token)); 049 } 050 lock.inc(); 051 wrap= new RWWrap<K>(lock, readOnly); 052 } 053 try{ 054 wrap.lock(timeout); 055 } 056 catch(LockException e){ 057 synchronized (locks) {wrap.getLock().dec();} 058 throw e; 059 } 060 catch(LockInterruptedException e){ 061 synchronized (locks) {wrap.getLock().dec();} 062 throw e; 063 } 064 catch(Throwable t){ 065 ExceptionUtil.rethrowIfNecessary(t); 066 synchronized (locks) {wrap.getLock().dec();} 067 throw new PageRuntimeException(Caster.toPageException(t)); 068 } 069 return wrap; 070 } 071 072 public void unlock(Lock lock) { 073 if(!(lock instanceof RWWrap)) { 074 return; 075 } 076 077 lock.unlock(); 078 079 synchronized (locks) { 080 ((RWWrap)lock).getLock().dec(); 081 if(lock.getQueueLength()==0){ 082 locks.remove(((RWWrap)lock).getLabel()); 083 } 084 } 085 } 086 087 public List<K> getOpenLockNames() { 088 Iterator<Entry<K, RWLock<K>>> it = locks.entrySet().iterator(); 089 Entry<K, RWLock<K>> entry; 090 List<K> list=new ArrayList<K>(); 091 while(it.hasNext()){ 092 entry = it.next(); 093 if(entry.getValue().getQueueLength()>0) 094 list.add(entry.getKey()); 095 } 096 return list; 097 } 098 099 /** 100 * Queries if the write lock is held by any thread on given lock token, returns null when lock with this token does not exists 101 * @param token name of the lock to check 102 * @return 103 */ 104 public Boolean isWriteLocked(K token) { 105 RWLock<K> lock = locks.get(token); 106 if(lock==null) return null; 107 return lock.isWriteLocked(); 108 } 109 110 /** 111 * Queries if one or more read lock is held by any thread on given lock token, returns null when lock with this token does not exists 112 * @param token name of the lock to check 113 * @return 114 */ 115 public Boolean isReadLocked(K token) { 116 RWLock<K> lock = locks.get(token); 117 if(lock==null) return null; 118 return lock.isReadLocked(); 119 } 120 121 public void clean() { 122 Iterator<Entry<K, RWLock<K>>> it = locks.entrySet().iterator(); 123 Entry<K, RWLock<K>> entry; 124 125 while(it.hasNext()){ 126 entry = it.next(); 127 if(entry.getValue().getQueueLength()==0){ 128 synchronized (locks) { 129 if(entry.getValue().getQueueLength()==0){ 130 locks.remove(entry.getKey()); 131 } 132 } 133 } 134 } 135 } 136} 137 138class RWWrap<L> implements Lock { 139 140 private RWLock<L> lock; 141 private boolean readOnly; 142 143 public RWWrap(RWLock<L> lock, boolean readOnly){ 144 this.lock=lock; 145 this.readOnly=readOnly; 146 } 147 148 public void lock(long timeout) throws LockException, LockInterruptedException { 149 lock.lock(timeout, readOnly); 150 } 151 152 public void unlock() { 153 lock.unlock(readOnly); 154 } 155 public int getQueueLength() { 156 return lock.getQueueLength(); 157 } 158 159 public L getLabel(){ 160 return lock.getLabel(); 161 } 162 163 public RWLock<L> getLock(){ 164 return lock; 165 } 166 public boolean isReadOnly(){ 167 return readOnly; 168 } 169 170}