Class RAFContainer4

  • All Implemented Interfaces:
    java.security.PrivilegedExceptionAction<java.lang.Object>, Cacheable, TypedFormat, Lockable

    class RAFContainer4
    extends RAFContainer
    RAFContainer4 overrides a few methods in FileContainer/RAFContainer in order to use FileChannel from Java 1.4's New IO framework to issue multiple IO operations to the same file concurrently instead of strictly serializing IO operations using a mutex on the container object. Since we compile with Java 1.4, the override "annotations" are inside the method javadoc headers.

    Note that our requests for multiple concurrent IOs may be serialized further down in the IO stack - this is entirely up to the JVM and OS. However, at least in Linux on Sun's 1.4.2_09 JVM we see the desired behavior: The FileChannel.read/write(ByteBuffer buf, long position) calls map to pread/pwrite system calls, which enable efficient IO to the same file descriptor by multiple threads.

    This whole class should be merged back into RAFContainer when Derby officially stops supporting Java 1.3.

    Significant behavior changes from RAFContainer:

    1. Multiple concurrent IOs permitted.
    2. State changes to the container (create, open, close) can now happen while IO is in progress due to the lack of locking. Closing a container while IO is in progress will cause IOExceptions in the thread calling readPage or writePage. If this happens something is probably amiss anyway. The iosInProgress variable is used in an attempt to detect this should it happen while running a debug build.
    See Also:
    FileChannel
    • Field Detail

      • ourChannel

        private java.nio.channels.FileChannel ourChannel
        This channel will be retrieved from RAFContainer's fileData member when fileData is set. We wrap a couple of RAFContainer's methods to accomplish this.
      • channelCleanupMonitor

        private final java.lang.Object channelCleanupMonitor
      • threadsInPageIO

        private volatile int threadsInPageIO
      • restoreChannelInProgress

        private volatile boolean restoreChannelInProgress
      • giveUpIO

        private boolean giveUpIO
      • giveUpIOm

        private final java.lang.Object giveUpIOm
      • iosInProgress

        private int iosInProgress
        For debugging - will be incremented when an IO is started, decremented when it is done. Should be == 0 when container state is changed.
    • Method Detail

      • getChannel

        private java.nio.channels.FileChannel getChannel​(StorageRandomAccessFile file)
        Return the FileChannel for the specified StorageRandomAccessFile if it is a RandomAccessFile. Otherwise, return null.
        Parameters:
        file - the file to get the channel for
        Returns:
        a FileChannel if file is an instance of RandomAccessFile, null otherwise
      • getChannel

        private java.nio.channels.FileChannel getChannel()

        Return the file channel for the current value of the fileData field. If fileData doesn't support file channels, return null.

        Callers of this method must synchronize on the container object since two shared fields (fileData and ourChannel) are accessed.

        Returns:
        a FileChannel object, if supported, or null
      • openContainer

        boolean openContainer​(ContainerKey newIdentity)
                       throws StandardException
        Description copied from class: FileContainer
        Open a container.

        Longer descrption of routine.

        Open a container. Open the file that maps to this container, if the file does not exist then we assume the container was never created. If the file exists but we have trouble opening it then we throw some exception.
        MT - single thread required - Enforced by cache manager.

        Overrides:
        openContainer in class RAFContainer
        Throws:
        StandardException - Standard exception policy.
      • reopen

        private void reopen()
                     throws StandardException
        When the existing channel (ourChannel) has been closed due to interrupt, we need to reopen the underlying RAF to get a fresh channel so we can resume IO.
        Throws:
        StandardException
      • readPage

        protected void readPage​(long pageNumber,
                                byte[] pageData)
                         throws java.io.IOException,
                                StandardException
        Read a page into the supplied array.

        override of RAFContainer#readPage


        MT - thread safe

        Overrides:
        readPage in class RAFContainer
        Throws:
        java.io.IOException - exception reading page
        StandardException - Standard Derby error policy
      • readPage

        private void readPage​(long pageNumber,
                              byte[] pageData,
                              long offset)
                       throws java.io.IOException,
                              StandardException
        Read a page into the supplied array.

        override of RAFContainer#readPage


        MT - thread safe

        Parameters:
        pageNumber - the page number to read data from, or -1 (called from getEmbryonicPage)
        pageData - the buffer to read data into
        offset - -1 normally (not used since offset is computed from pageNumber), but used if pageNumber == -1 (getEmbryonicPage)
        Throws:
        java.io.IOException - exception reading page
        StandardException - Standard Derby error policy
      • readPage0

        private void readPage0​(long pageNumber,
                               byte[] pageData,
                               long offset)
                        throws java.io.IOException,
                               StandardException
        Throws:
        java.io.IOException
        StandardException
      • writePage

        protected void writePage​(long pageNumber,
                                 byte[] pageData,
                                 boolean syncPage)
                          throws java.io.IOException,
                                 StandardException
        Write a page from the supplied array.

        override of RAFContainer#writePage


        MT - thread safe

        Overrides:
        writePage in class RAFContainer
        Throws:
        StandardException - Standard Derby error policy
        java.io.IOException - IO error accessing page
      • handleClosedChannel

        private void handleClosedChannel​(java.nio.channels.ClosedChannelException e,
                                         boolean stealthMode,
                                         int retries)
                                  throws StandardException

        This method handles what to do when, during a NIO operation we receive a ClosedChannelException. Note the specialization hierarchy:

        ClosedChannelException -> AsynchronousCloseException -> ClosedByInterruptException

        If e is a ClosedByInterruptException, we normally start container recovery, i.e. we need to reopen the random access file so we get get a new interruptible channel and continue IO.

        If e is a AsynchronousCloseException or a plain ClosedChannelException, the behavior depends of stealthMode:

        If stealthMode == false, the method will wait for another thread tp finish recovering the IO channel before returning.

        If stealthMode == true, the method throws InterruptDetectedException, allowing retry at a higher level in the code. The reason for this is that we sometimes need to release monitors on objects needed by the recovery thread.

        Parameters:
        e - Should be an instance of ClosedChannelException.
        stealthMode - If true, do retry at a higher level
        retries - Give up waiting for another thread to reopen the channel when retries reaches 0. Only applicable if stealthMode == false.
        Throws:
        InterruptDetectedException - if retry at higher level is required stealthMode == true.
        StandardException - standard error policy, incl. when we give up waiting for another thread to reopen channel
      • awaitRestoreChannel

        private void awaitRestoreChannel​(java.lang.Exception e,
                                         boolean stealthMode)
                                  throws StandardException
        Use when seeing an exception during IO and when another thread is presumably doing the recovery.

        If stealthMode == false, wait for another thread to recover the container after an interrupt. If stealthMode == true, throw internal exception InterruptDetectedException to do retry from higher in the stack.

        If stealthMode == false, maximum wait time for the container to become available again is determined by the product InterruptStatus.MAX_INTERRUPT_RETRIES * InterruptStatus.INTERRUPT_RETRY_SLEEP. There is a chance this thread will not see any recovery occuring (yet), in which case it waits for a bit and just returns, so the caller must retry IO until success.

        If for some reason the recovering thread has given up on resurrecting the container, cf #giveUpIO, the method throws FILE_IO_INTERRUPTED.

        Parameters:
        e - the exception we saw during IO
        stealthMode - true if the thread doing IO in stealth mode
        Throws:
        StandardException - InterruptDetectedException and normal error policy
      • recoverContainerAfterInterrupt

        private boolean recoverContainerAfterInterrupt​(java.lang.String whence,
                                                       boolean stealthMode)
                                                throws StandardException
        Use this when the thread has received a ClosedByInterruptException (or, prior to JDK 1.7 it may also be AsynchronousCloseException - a bug) exception during IO and its interruped flag is also set. This makes this thread a likely candicate to do container recovery, unless another thread started it already, cf. return value.
        Parameters:
        whence - caller site (debug info)
        stealthMode - don't update threadsInPageIO if true
        Returns:
        true if we did recovery, false if we saw someone else do it and abstained
        Throws:
        StandardException
      • writePage0

        private void writePage0​(long pageNumber,
                                byte[] pageData,
                                boolean syncPage)
                         throws java.io.IOException,
                                StandardException
        Throws:
        java.io.IOException
        StandardException
      • writeAtOffset

        void writeAtOffset​(StorageRandomAccessFile file,
                           byte[] bytes,
                           long offset)
                    throws java.io.IOException,
                           StandardException
        Write a sequence of bytes at the given offset in a file. This method operates in stealth mode, see doc for handleClosedChannel. This presumes that IO retry happens at a higher level, i.e. the caller(s) must be prepared to handle InterruptDetectedException.

        This method overrides FileContainer#writeAtOffset.

        Overrides:
        writeAtOffset in class FileContainer
        Parameters:
        file - the file to write to
        bytes - the bytes to write
        offset - the offset to start writing at
        Throws:
        java.io.IOException - if an I/O error occurs while writing
        StandardException - Derby Standard error policy
      • getEmbryonicPage

        byte[] getEmbryonicPage​(StorageRandomAccessFile file,
                                long offset)
                         throws java.io.IOException,
                                StandardException
        Read an embryonic page (that is, a section of the first alloc page that is so large that we know all the borrowed space is included in it) from the specified offset in a StorageRandomAccessFile.

        override of FileContainer#getEmbryonicPage

        Overrides:
        getEmbryonicPage in class FileContainer
        Parameters:
        file - the file to read from
        offset - where to start reading (normally FileContainer.FIRST_ALLOC_PAGE_OFFSET)
        Returns:
        a byte array containing the embryonic page
        Throws:
        java.io.IOException - if an I/O error occurs while reading
        StandardException - if thread is interrupted.
      • readFull

        private void readFull​(java.nio.ByteBuffer dstBuffer,
                              java.nio.channels.FileChannel srcChannel,
                              long position)
                       throws java.io.IOException,
                              StandardException
        Attempts to fill buf completely from start until it's full.

        FileChannel has no readFull() method, so we roll our own.

        Parameters:
        dstBuffer - buffer to read into
        srcChannel - channel to read from
        position - file position from where to read
        Throws:
        java.io.IOException - if an I/O error occurs while reading
        StandardException - If thread is interrupted.
      • writeFull

        private void writeFull​(java.nio.ByteBuffer srcBuffer,
                               java.nio.channels.FileChannel dstChannel,
                               long position)
                        throws java.io.IOException
        Attempts to write buf completely from start until end, at the given position in the destination fileChannel.

        FileChannel has no writeFull() method, so we roll our own.

        Parameters:
        srcBuffer - buffer to write
        dstChannel - channel to write to
        position - file position to start writing at
        Throws:
        java.io.IOException - if an I/O error occurs while writing
        StandardException - If thread is interrupted.
      • debugTrace

        private static void debugTrace​(java.lang.String msg)