001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (c) 2004-2007 Paul Ferraro 004 * 005 * This library is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU Lesser General Public License as published by the 007 * Free Software Foundation; either version 2.1 of the License, or (at your 008 * option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, but WITHOUT 011 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 012 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 013 * for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this library; if not, write to the Free Software Foundation, 017 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018 * 019 * Contact: ferraro@users.sourceforge.net 020 */ 021package net.sf.hajdbc.util.concurrent; 022 023import java.util.concurrent.Semaphore; 024import java.util.concurrent.TimeUnit; 025import java.util.concurrent.locks.Condition; 026import java.util.concurrent.locks.Lock; 027import java.util.concurrent.locks.ReadWriteLock; 028 029/** 030 * Simple {@link java.util.concurrent.locks.ReadWriteLock} implementation that uses a semaphore. 031 * A read lock requires 1 permit, while a write lock requires all the permits. 032 * Lock upgrading and downgrading is not supported; nor are conditions. 033 * 034 * @author Paul Ferraro 035 */ 036public class SemaphoreReadWriteLock implements ReadWriteLock 037{ 038 private static final int DEFAULT_PERMITS = Integer.MAX_VALUE; 039 040 private final Lock readLock; 041 private final Lock writeLock; 042 043 public SemaphoreReadWriteLock(boolean fair) 044 { 045 this(DEFAULT_PERMITS, fair); 046 } 047 048 public SemaphoreReadWriteLock(int permits, boolean fair) 049 { 050 Semaphore semaphore = new Semaphore(permits, fair); 051 052 this.readLock = new SemaphoreLock(semaphore); 053 this.writeLock = new SemaphoreWriteLock(semaphore, permits); 054 } 055 056 /** 057 * @see java.util.concurrent.locks.ReadWriteLock#readLock() 058 */ 059 @Override 060 public Lock readLock() 061 { 062 return this.readLock; 063 } 064 065 /** 066 * @see java.util.concurrent.locks.ReadWriteLock#writeLock() 067 */ 068 @Override 069 public Lock writeLock() 070 { 071 return this.writeLock; 072 } 073 074 private static class SemaphoreWriteLock implements Lock 075 { 076 private final Semaphore semaphore; 077 private final int permits; 078 079 SemaphoreWriteLock(Semaphore semaphore, int permits) 080 { 081 this.semaphore = semaphore; 082 this.permits = permits; 083 } 084 085 /** 086 * Helps avoid write lock starvation, when using an unfair acquisition policy by draining all available permits. 087 * @return the number of drained permits 088 */ 089 private int drainPermits() 090 { 091 return this.semaphore.isFair() ? 0 : this.semaphore.drainPermits(); 092 } 093 094 /** 095 * @see java.util.concurrent.locks.Lock#lock() 096 */ 097 @Override 098 public void lock() 099 { 100 int drained = this.drainPermits(); 101 102 if (drained < this.permits) 103 { 104 this.semaphore.acquireUninterruptibly(this.permits - drained); 105 } 106 } 107 108 /** 109 * @see java.util.concurrent.locks.Lock#lockInterruptibly() 110 */ 111 @Override 112 public void lockInterruptibly() throws InterruptedException 113 { 114 int drained = this.drainPermits(); 115 116 if (drained < this.permits) 117 { 118 try 119 { 120 this.semaphore.acquire(this.permits - drained); 121 } 122 catch (InterruptedException e) 123 { 124 if (drained > 0) 125 { 126 this.semaphore.release(drained); 127 } 128 129 throw e; 130 } 131 } 132 } 133 134 /** 135 * @see java.util.concurrent.locks.Lock#tryLock() 136 */ 137 @Override 138 public boolean tryLock() 139 { 140 // This will barge the fairness queue, so there's no need to drain permits 141 return this.semaphore.tryAcquire(this.permits); 142 } 143 144 /** 145 * @see java.util.concurrent.locks.Lock#tryLock(long, java.util.concurrent.TimeUnit) 146 */ 147 @Override 148 public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException 149 { 150 int drained = this.drainPermits(); 151 152 if (drained == this.permits) return true; 153 154 boolean acquired = false; 155 156 try 157 { 158 acquired = this.semaphore.tryAcquire(this.permits - drained, timeout, unit); 159 } 160 finally 161 { 162 if (!acquired && (drained > 0)) 163 { 164 this.semaphore.release(drained); 165 } 166 } 167 168 return acquired; 169 } 170 171 /** 172 * @see java.util.concurrent.locks.Lock#unlock() 173 */ 174 @Override 175 public void unlock() 176 { 177 this.semaphore.release(this.permits); 178 } 179 180 /** 181 * @see java.util.concurrent.locks.Lock#newCondition() 182 */ 183 @Override 184 public Condition newCondition() 185 { 186 throw new UnsupportedOperationException(); 187 } 188 } 189}