package org.dcache.util;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.*;
/**
* A simple implementation of an adjustable semaphore.
*
* Copyright © 2008, Genius.com
* @author Marshall Pierce <marshall@genius.com>
* @see http://blog.teamlazerbeez.com/2009/04/20/javas-semaphore-resizing/
*/
public final class AdjustableSemaphore {
/**
* semaphore starts at 0 capacity; must be set by setMaxPermits before use
*/
private final ResizeableSemaphore semaphore = new ResizeableSemaphore();
/**
* how many permits are allowed as governed by this semaphore.
* Access must be synchronized on this object.
*/
private int maxPermits;
/**
* New instances should be configured with setMaxPermits().
*/
public AdjustableSemaphore() {
// no op
}
/*
* Must be synchronized because the underlying int is not thread safe
*/
/**
* Set the max number of permits. Must be greater than zero.
*
* Note that if there are more than the new max number of permits currently
* outstanding, any currently blocking threads or any new threads that start
* to block after the call will wait until enough permits have been released to
* have the number of outstanding permits fall below the new maximum. In
* other words, it does what you probably think it should.
*
* @param newMax
*/
public synchronized void setMaxPermits(int newMax) {
checkArgument(newMax >= 0);
int delta = newMax - this.maxPermits;
if (delta == 0) {
return;
} else if (delta > 0) {
// new max is higher, so release that many permits
this.semaphore.release(delta);
} else {
delta *= -1;
// delta < 0.
// reducePermits needs a positive #, though.
this.semaphore.reducePermits(delta);
}
this.maxPermits = newMax;
}
/**
* Release a permit back to the semaphore. Make sure not to double-release.
*
*/
public void release() {
this.semaphore.release();
}
/**
* Get a permit, blocking if necessary.
*
* @throws InterruptedException
* if interrupted while waiting for a permit
*/
public void acquire() throws InterruptedException {
this.semaphore.acquire();
}
/**
* @see Semaphore#tryAcquire(int, long, java.util.concurrent.TimeUnit)
*/
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException
{
return semaphore.tryAcquire(permits, timeout, unit);
}
/**
* Get a permit this semaphore if one is available at the
* time of invocation.
*
* @returns {@code true} if a permit was acquired and {@code false}
* otherwise
*/
public boolean tryAcquire() {
return this.semaphore.tryAcquire();
}
/**
* Get a number of allowed permits.
* @return number of permits
*/
public synchronized int getMaxPermits() {
return this.maxPermits;
}
/**
* Get number of permits in use.
* @return number of permits.
*/
public synchronized int getUsedPermits() {
return this.maxPermits - this.semaphore.availablePermits();
}
/**
* A trivial subclass of <code>Semaphore</code> that exposes the reducePermits
* call to the parent class. Doug Lea says it's ok...
* http://osdir.com/ml/java.jsr.166-concurrency/2003-10/msg00042.html
*
* Copyright © 2009, Genius.com
*
* @author Marshall Pierce <marshall@genius.com>
*
*/
private static final class ResizeableSemaphore extends Semaphore {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Create a new semaphore with 0 permits.
*/
ResizeableSemaphore() {
super(0);
}
/*
* (non-Javadoc)
*
* @see java.util.concurrent.Semaphore#reducePermits(int)
*/
@Override
protected void reducePermits(int reduction) {
super.reducePermits(reduction);
}
}
}