![JAR search and dependency download from the Maven repository](/logo.png)
net.java.truevfs.kernel.impl.ResourceAccountant Maven / Gradle / Ivy
/*
* Copyright © 2005 - 2021 Schlichtherle IT Services.
* All rights reserved. Use is subject to license terms.
*/
package net.java.truevfs.kernel.impl;
import lombok.Value;
import lombok.val;
import net.java.truecommons.shed.ExceptionHandler;
import net.java.truecommons.shed.HashMaps;
import javax.annotation.WillCloseWhenClosed;
import javax.annotation.WillNotClose;
import javax.annotation.concurrent.ThreadSafe;
import java.io.Closeable;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* Accounts for {@link java.io.Closeable} resources.
*
* For synchronization, each accountant uses a lock which has to be provided to its constructor.
* In order to start accounting for a closeable resource, call {@link #startAccountingFor(Closeable)}.
* In order to stop accounting for a closeable resource, call {@link #stopAccountingFor(Closeable)}.
*
* Note that you must make sure not to use two instances of this class which share the same lock.
* Otherwise {@link #awaitClosingOfOtherThreadsResources(long)} will not work as designed.
*
* @author Christian Schlichtherle
* @see ResourceController
*/
@ThreadSafe
final class ResourceAccountant implements LockAspect {
private final Lock lock;
private final Condition condition;
ResourceAccountant(final Lock lock) {
this.lock = lock;
this.condition = lock.newCondition();
}
/**
* The map of all accounted closeable resources.
* The initial capacity for the hash map accounts for the number of available processors, a 90% blocking factor for
* typical I/O and a 2/3 map resize threshold.
*/
private static final Map accounts;
static {
val threads = Runtime.getRuntime().availableProcessors() * 10;
val initialCapacity = HashMaps.initialCapacity(threads);
accounts = new ConcurrentHashMap<>(initialCapacity, 0.75f, threads);
}
@Value
private static class Account {
Thread owner = Thread.currentThread();
ResourceAccountant accountant;
}
@Value
static class Resources {
int local, total;
boolean needsWaiting;
Resources(final int local, final int total) {
this.local = local;
this.total = total;
needsWaiting = local < total;
}
}
@Override
public Lock lock() {
return lock;
}
/**
* Starts accounting for the given closeable resource.
*
* @param resource the closeable resource to start accounting for.
*/
void startAccountingFor(@WillCloseWhenClosed Closeable resource) {
accounts.put(resource, new Account(this));
}
/**
* Stops accounting for the given closeable resource.
* This method should be called from the implementation of the {@link Closeable#close()} method of the given
* {@link Closeable}.
*
* @param resource the closeable resource to stop accounting for.
*/
void stopAccountingFor(@WillNotClose Closeable resource) {
if (null != accounts.remove(resource)) {
locked(new Op