All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.starlink.fits.BufferManager Maven / Gradle / Ivy

There is a newer version: 4.3
Show newest version
package uk.ac.starlink.fits;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.logging.Logger;

/**
 * Manages access to a region of a file using monolithic byte buffers.
 * The region is only mapped once, and subsequent buffer requests
 * are handled by duplicating the original mapped buffer.
 *
 * 

Buffers are acquired using {@link #createBuffer}, and should be * disposed of using {@link #disposeBuffer} when no longer in use. * When none of the buffers are in use any more, {@link #close} should * be called. * *

Note: the mapped buffer is unmapped using * the supplied {@link Unmapper} when all created buffers have been disposed, * or when close() is called. * If any of the created buffers is used after this has happened, * and unmapping is actually attempted, very bad consequences may ensue. * Therefore this class, or at least its {@link #disposeBuffer} and * {@link #close} methods, should be used with extreme caution, * or a null Unmapper should be supplied. * * @author Mark Taylor * @since 12 May 2022 */ public class BufferManager { private final FileChannel channel_; private final long offset_; private final int leng_; private final String logLabel_; private final Unmapper unmapper_; private final Map dupBuffers_; private MappedByteBuffer buffer0_; private static final Logger logger_ = Logger.getLogger( "uk.ac.starlink.fits" ); /** * Constructor. * * @param channel file channel * @param offset offset into file of start of mapped region * @param leng length of mapped region * @param logLabel description of mapped region * suitable for use in logging messages * @param unmapper used to unmap buffers, may be null for safety */ public BufferManager( FileChannel channel, long offset, int leng, String logLabel, Unmapper unmapper ) { channel_ = channel; offset_ = offset; leng_ = leng; logLabel_ = logLabel; unmapper_ = unmapper; /* Note ByteBuffer equality methods depend on their current state * (mark, position etc) which may change during their lifetimes, * so we need to use an IdentityHashMap not an normal Map to keep * track of them between being issued and being disposed. */ dupBuffers_ = new IdentityHashMap(); } /** * Returns a new read-only ByteBuffer providing access to the * file region. * The returned object should if possible be passed to * {@link #disposeBuffer} after it is known that it will * no longer be used. * * @return buffer */ public ByteBuffer createBuffer() throws IOException { ByteBuffer buf = getMappedBuffer().duplicate(); assert buf.position() == 0; assert buf.isDirect(); synchronized ( this ) { dupBuffers_.put( buf, null ); } return buf; } /** * Asserts that the supplied buffer, originally acquired from * {@link #createBuffer}, will no longer be used. * Resources may be reclaimed. * * @param buf previously created buffer */ public void disposeBuffer( ByteBuffer buf ) { final boolean removed; synchronized ( this ) { removed = dupBuffers_.keySet().remove( buf ); if ( dupBuffers_.isEmpty() ) { unmapBuffer(); } } if ( ! removed ) { logger_.warning( "Attempting to dispose unknown buffer " + logLabel_ ); } } /** * Asserts that neither this manager, nor any of the buffers it has * supplied, will be used again. */ public void close() { unmapBuffer(); } /** * Returns the single buffer mapping the file region. * This buffer is lazily acquired. * * @return mapped buffer */ private synchronized MappedByteBuffer getMappedBuffer() throws IOException { if ( buffer0_ == null ) { buffer0_ = channel_.map( FileChannel.MapMode.READ_ONLY, offset_, leng_ ); logger_.config( "Mapping " + logLabel_ ); } return buffer0_; } /** * Attempts to unmap the single buffer mapping the file region. * Depending on this object's Unmapper, this may or may not be a NOP. * However, in case of actual umapping, it is essential not to use * any of the returned buffers created from it following this operation. */ private synchronized void unmapBuffer() { if ( buffer0_ != null ) { boolean unmapped = unmapper_ == null ? false : unmapper_.unmap( buffer0_ ); buffer0_ = null; logger_.config( "Dispose of mapped buffer " + logLabel_ + ( unmapped ? " (unmapped)" : " (no effect)" ) ); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy