package org.dcache.pool.movers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.SyncFailedException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.atomic.AtomicLong;
import diskCacheV111.vehicles.ProtocolInfo;
import org.dcache.pool.repository.Allocator;
import org.dcache.pool.repository.OutOfDiskException;
import org.dcache.pool.repository.RepositoryChannel;
import org.dcache.vehicles.FileAttributes;
import static com.google.common.base.Preconditions.checkArgument;
import static org.dcache.util.ByteUnit.MiB;
/**
* A wrapper for RepositoryChannel adding features used by movers.
*/
public class MoverChannel<T extends ProtocolInfo> implements RepositoryChannel
{
private static final Logger _logSpaceAllocation =
LoggerFactory.getLogger("logger.dev.org.dcache.poolspacemonitor." +
MoverChannel.class.getName());
public enum AllocatorMode {
SOFT,
HARD
}
/**
* The minimum number of bytes to increment the space
* allocation.
*/
private static final long SPACE_INC = MiB.toBytes(50);
/**
* Inner channel to which most operations are delegated.
*/
private final RepositoryChannel _channel;
/**
* The IoMode of the mover that created this MoverChannel.
*/
private final IoMode _mode;
/**
* Space allocator provided by the pool.
*/
private final Allocator _allocator;
/**
* Timestamp of when the transfer started.
*/
private final long _transferStarted =
System.currentTimeMillis();
/**
* Timestamp of when the last block was transferred.
*/
private final AtomicLong _lastTransferred =
new AtomicLong(_transferStarted);
/**
* The number of bytes transferred.
*/
private final AtomicLong _bytesTransferred =
new AtomicLong(0);
/**
* ProtocolInfo associated with the transfer.
*/
private final T _protocolInfo;
/**
* The FileAttributes associated with the file being transfered.
*/
private final FileAttributes _fileAttributes;
/**
* The number of bytes reserved in the space allocator. Only
* updated while the monitor lock is held.
*/
private volatile long _reserved;
/**
* Tells, should allocator block for available space or not.
*/
private final AllocatorMode _allocatorMode;
public MoverChannel(Mover<T> mover, RepositoryChannel channel, AllocatorMode allocatorMode)
{
this(mover.getIoMode(), mover.getFileAttributes(), mover.getProtocolInfo(), channel, mover.getIoHandle(), allocatorMode);
}
public MoverChannel(IoMode mode, FileAttributes attributes, T protocolInfo,
RepositoryChannel channel, Allocator allocator, AllocatorMode allocatorMode)
{
_mode = mode;
_protocolInfo = protocolInfo;
_channel = channel;
_allocator = allocator;
_fileAttributes = attributes;
_allocatorMode = allocatorMode;
}
@Override
public long position() throws IOException
{
return _channel.position();
}
@Override
public synchronized MoverChannel<T> position(long position)
throws IOException
{
_channel.position(position);
return this;
}
@Override
public long size() throws IOException
{
return _channel.size();
}
@Override
public void sync() throws SyncFailedException, IOException
{
_channel.sync();
}
@Override
public synchronized MoverChannel<T> truncate(long size) throws IOException
{
try {
_channel.truncate(size);
return this;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
@Override
public void close() throws IOException
{
_lastTransferred.set(System.currentTimeMillis());
_channel.close();
}
@Override
public boolean isOpen()
{
return _channel.isOpen();
}
@Override
public synchronized int read(ByteBuffer dst) throws IOException
{
try {
int bytes = _channel.read(dst);
_bytesTransferred.getAndAdd(bytes);
return bytes;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
@Override
public int read(ByteBuffer buffer, long position) throws IOException {
try {
int bytes = _channel.read(buffer, position);
_bytesTransferred.getAndAdd(bytes);
return bytes;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
@Override
public synchronized long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
try {
long bytes = _channel.read(dsts, offset, length);
_bytesTransferred.getAndAdd(bytes);
return bytes;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
@Override
public synchronized long read(ByteBuffer[] dsts) throws IOException {
try {
long bytes = _channel.read(dsts);
_bytesTransferred.getAndAdd(bytes);
return bytes;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
@Override
public synchronized int write(ByteBuffer src) throws IOException {
try {
preallocate(position() + src.remaining());
int bytes = _channel.write(src);
_bytesTransferred.getAndAdd(bytes);
return bytes;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
@Override
public int write(ByteBuffer buffer, long position) throws IOException {
try {
preallocate(position + buffer.remaining());
int bytes = _channel.write(buffer, position);
_bytesTransferred.getAndAdd(bytes);
return bytes;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
@Override
public synchronized long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
try {
long remaining = 0;
for (int i = offset; i < offset + length; i++) {
remaining += srcs[i].remaining();
}
preallocate(position() + remaining);
long bytes = _channel.write(srcs, offset, length);
_bytesTransferred.getAndAdd(bytes);
return bytes;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
@Override
public synchronized long write(ByteBuffer[] srcs) throws IOException {
try {
long remaining = 0;
for (ByteBuffer src: srcs) {
remaining += src.remaining();
}
preallocate(position() + remaining);
long bytes = _channel.write(srcs);
_bytesTransferred.getAndAdd(bytes);
return bytes;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
@Override
public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
try {
long bytes = _channel.transferTo(position, count, target);
_bytesTransferred.getAndAdd(bytes);
return bytes;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
@Override
public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
try {
preallocate(position + count);
long bytes = _channel.transferFrom(src, position, count);
_bytesTransferred.getAndAdd(bytes);
return bytes;
} finally {
_lastTransferred.set(System.currentTimeMillis());
}
}
public IoMode getIoMode() {
return _mode;
}
public T getProtocolInfo() {
return _protocolInfo;
}
public FileAttributes getFileAttributes() {
return _fileAttributes;
}
public long getBytesTransferred() {
return _bytesTransferred.get();
}
public long getTransferTime() {
return (_channel.isOpen()
? System.currentTimeMillis()
: getLastTransferred()) - _transferStarted;
}
public long getLastTransferred() {
return _lastTransferred.get();
}
public long getAllocated() {
return _reserved;
}
private synchronized void preallocate(long pos)
throws IOException
{
try {
checkArgument(pos >= 0);
if (pos > _reserved) {
long delta = Math.max(pos - _reserved, SPACE_INC);
_logSpaceAllocation.trace("preallocate: {}", delta);
if (_allocatorMode == AllocatorMode.HARD) {
_allocator.allocate(delta);
} else if (!_allocator.allocateNow(delta)) {
throw new OutOfDiskException();
}
_reserved += delta;
}
} catch (InterruptedException e) {
throw new InterruptedIOException(e.getMessage());
} catch (IllegalStateException e) {
throw new ClosedChannelException();
}
}
}