Package com.google.common.jimfs

Source Code of com.google.common.jimfs.JimfsFileChannel$FakeFileLock

/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.common.jimfs;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndexes;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.WRITE;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.NonReadableChannelException;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.OpenOption;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.annotation.Nullable;

/**
* A {@link FileChannel} implementation that reads and writes to a {@link RegularFile} object. The
* read and write methods and other methods that read or change the position of the channel are
* locked because the {@link ReadableByteChannel} and {@link WritableByteChannel} interfaces specify
* that the read and write methods block when another thread is currently doing a read or write
* operation.
*
* @author Colin Decker
*/
final class JimfsFileChannel extends FileChannel {

  /**
   * Thread that is currently doing an interruptible blocking operation; that is, doing something
   * that requires acquiring the file's lock. Since a thread has to already have this channel's
   * lock to do that, there can only be one such thread at a time. This thread must be interrupted
   * if the channel is closed by another thread.
   */
  @Nullable
  private volatile Thread blockingThread;

  private final RegularFile file;
  private final FileSystemState fileSystemState;

  private final boolean read;
  private final boolean write;
  private final boolean append;

  private long position;

  public JimfsFileChannel(
      RegularFile file, Set<OpenOption> options, FileSystemState fileSystemState) {
    this.file = file;
    this.fileSystemState = fileSystemState;
    this.read = options.contains(READ);
    this.write = options.contains(WRITE);
    this.append = options.contains(APPEND);

    fileSystemState.register(this);
  }

  /**
   * Returns an {@link AsynchronousFileChannel} view of this channel using the given executor for
   * asynchronous operations.
   */
  public AsynchronousFileChannel asAsynchronousFileChannel(ExecutorService executor) {
    return new JimfsAsynchronousFileChannel(this, executor);
  }

  void checkReadable() {
    if (!read) {
      throw new NonReadableChannelException();
    }
  }

  void checkWritable() {
    if (!write) {
      throw new NonWritableChannelException();
    }
  }

  void checkOpen() throws ClosedChannelException {
    if (!isOpen()) {
      throw new ClosedChannelException();
    }
  }

  /**
   * Begins a blocking operation, making the operation interruptible.
   */
  private void beginBlocking() {
    begin();
    blockingThread = Thread.currentThread();
  }

  /**
   * Ends a blocking operation, throwing an exception if the thread was interrupted while blocking
   * or if the channel was closed from another thread.
   */
  private void endBlocking(boolean completed) throws AsynchronousCloseException {
    blockingThread = null;
    end(completed);
  }

  @Override
  public int read(ByteBuffer dst) throws IOException {
    checkNotNull(dst);
    checkOpen();
    checkReadable();

    synchronized (this) {
      boolean completed = false;
      try {
        beginBlocking();
        if (!isOpen()) {
          return 0; // AsynchronousCloseException will be thrown
        }

        file.readLock().lockInterruptibly();
        try {
          int read = file.read(position, dst);
          if (read != -1) {
            position += read;
          }

          file.updateAccessTime();
          completed = true;
          return read;
        } finally {
          file.readLock().unlock();
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } finally {
        endBlocking(completed);
      }

      // if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
      throw new AssertionError();
    }
  }

  @Override
  public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
    checkPositionIndexes(offset, offset + length, dsts.length);
    List<ByteBuffer> buffers = Arrays.asList(dsts).subList(offset, offset + length);
    Util.checkNoneNull(buffers);
    checkOpen();
    checkReadable();

    synchronized (this) {
      boolean completed = false;
      try {
        beginBlocking();
        if (!isOpen()) {
          return 0; // AsynchronousCloseException will be thrown
        }

        file.readLock().lockInterruptibly();
        try {
          long read = file.read(position, buffers);
          if (read != -1) {
            position += read;
          }

          file.updateAccessTime();
          completed = true;
          return read;
        } finally {
          file.readLock().unlock();
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } finally {
        endBlocking(completed);
      }

      // if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
      throw new AssertionError();
    }
  }

  @Override
  public int write(ByteBuffer src) throws IOException {
    checkNotNull(src);
    checkOpen();
    checkWritable();

    synchronized (this) {
      boolean completed = false;
      try {
        beginBlocking();
        if (!isOpen()) {
          return 0; // AsynchronousCloseException will be thrown
        }

        file.writeLock().lockInterruptibly();
        try {
          if (append) {
            position = file.size();
          }
          int written = file.write(position, src);
          position += written;

          file.updateModifiedTime();
          completed = true;
          return written;
        } finally {
          file.writeLock().unlock();
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } finally {
        endBlocking(completed);
      }

      // if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
      throw new AssertionError();
    }
  }

  @Override
  public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
    checkPositionIndexes(offset, offset + length, srcs.length);
    List<ByteBuffer> buffers = Arrays.asList(srcs).subList(offset, offset + length);
    Util.checkNoneNull(buffers);
    checkOpen();
    checkWritable();

    synchronized (this) {
      boolean completed = false;
      try {
        beginBlocking();
        if (!isOpen()) {
          return 0; // AsynchronousCloseException will be thrown
        }

        file.writeLock().lockInterruptibly();
        try {
          if (append) {
            position = file.size();
          }
          long written = file.write(position, buffers);
          position += written;

          file.updateModifiedTime();
          completed = true;
          return written;
        } finally {
          file.writeLock().unlock();
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } finally {
        endBlocking(completed);
      }

      // if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
      throw new AssertionError();
    }
  }

  @Override
  public long position() throws IOException {
    checkOpen();

    synchronized (this) {
      return position;
    }
  }

  @Override
  public FileChannel position(long newPosition) throws IOException {
    Util.checkNotNegative(newPosition, "newPosition");
    checkOpen();

    synchronized (this) {
      this.position = newPosition;
    }

    return this;
  }

  @Override
  public long size() throws IOException {
    checkOpen();
    return file.size();
  }

  @Override
  public FileChannel truncate(long size) throws IOException {
    Util.checkNotNegative(size, "size");
    checkOpen();
    checkWritable();

    synchronized (this) {
      boolean completed = false;
      try {
        beginBlocking();
        if (!isOpen()) {
          return this; // AsynchronousCloseException will be thrown
        }

        file.writeLock().lockInterruptibly();
        try {
          file.truncate(size);
          if (position > size) {
            position = size;
          }

          file.updateModifiedTime();
          completed = true;
          return this;
        } finally {
          file.writeLock().unlock();
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } finally {
        endBlocking(completed);
      }

      // if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
      throw new AssertionError();
    }
  }

  @Override
  public void force(boolean metaData) throws IOException {
    checkOpen();
    // do nothing... writes are all synchronous anyway
  }

  @Override
  public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
    checkNotNull(target);
    Util.checkNotNegative(position, "position");
    Util.checkNotNegative(count, "count");
    checkOpen();
    checkReadable();

    synchronized (this) {
      boolean completed = false;
      try {
        beginBlocking();
        if (!isOpen()) {
          return 0; // AsynchronousCloseException will be thrown
        }

        file.readLock().lockInterruptibly();
        try {
          long transferred = file.transferTo(position, count, target);
          file.updateAccessTime();
          completed = true;
          return transferred;
        } finally {
          file.readLock().unlock();
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } finally {
        endBlocking(completed);
      }

      // if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
      throw new AssertionError();
    }
  }

  @Override
  public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
    checkNotNull(src);
    Util.checkNotNegative(position, "position");
    Util.checkNotNegative(count, "count");
    checkOpen();
    checkWritable();

    synchronized (this) {
      boolean completed = false;
      try {
        beginBlocking();
        if (!isOpen()) {
          return 0; // AsynchronousCloseException will be thrown
        }

        file.writeLock().lockInterruptibly();
        try {
          if (append) {
            position = file.size();
          }

          long transferred = file.transferFrom(src, position, count);

          if (append) {
            this.position = position + transferred;
          }

          file.updateModifiedTime();
          completed = true;
          return transferred;
        } finally {
          file.writeLock().unlock();
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } finally {
        endBlocking(completed);
      }

      // if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
      throw new AssertionError();
    }
  }

  @Override
  public int read(ByteBuffer dst, long position) throws IOException {
    checkNotNull(dst);
    Util.checkNotNegative(position, "position");
    checkOpen();
    checkReadable();

    synchronized (this) {
      boolean completed = false;
      try {
        beginBlocking();
        if (!isOpen()) {
          return 0; // AsynchronousCloseException will be thrown
        }

        file.readLock().lockInterruptibly();
        try {
          int read = file.read(position, dst);
          file.updateAccessTime();
          completed = true;
          return read;
        } finally {
          file.readLock().unlock();
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } finally {
        endBlocking(completed);
      }

      // if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
      throw new AssertionError();
    }
  }

  @Override
  public int write(ByteBuffer src, long position) throws IOException {
    checkNotNull(src);
    Util.checkNotNegative(position, "position");
    checkOpen();
    checkWritable();

    synchronized (this) {
      boolean completed = false;
      try {
        beginBlocking();
        if (!isOpen()) {
          return 0; // AsynchronousCloseException will be thrown
        }

        file.writeLock().lockInterruptibly();
        try {
          if (append) {
            position = file.sizeWithoutLocking();
          }

          int written = file.write(position, src);

          if (append) {
            this.position = position + written;
          }

          file.updateModifiedTime();
          completed = true;
          return written;
        } finally {
          file.writeLock().unlock();
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } finally {
        endBlocking(completed);
      }

      // if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
      throw new AssertionError();
    }
  }

  @Override
  public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
    // would like this to pretend to work, but can't create an implementation of MappedByteBuffer
    // well, a direct buffer could be cast to MappedByteBuffer, but it couldn't work in general
    throw new UnsupportedOperationException();
  }

  @Override
  public FileLock lock(long position, long size, boolean shared) throws IOException {
    Util.checkNotNegative(position, "position");
    Util.checkNotNegative(size, "size");
    checkOpen();
    if (shared) {
      checkReadable();
    } else {
      checkWritable();
    }

    return new FakeFileLock(this, position, size, shared);
  }

  @Override
  public FileLock tryLock(long position, long size, boolean shared) throws IOException {
    // lock() doesn't block anyway
    return lock(position, size, shared);
  }

  @Override
  protected void implCloseChannel() {
    // interrupt the current blocking thread, if any, causing it to throw ClosedByInterruptException
    try {
      final Thread thread = blockingThread;
      if (thread != null) {
        thread.interrupt();
      }
    } finally {
      fileSystemState.unregister(this);
      file.closed();
    }
  }

  /**
   * A file lock that does nothing, since only one JVM process has access to this file system.
   */
  static final class FakeFileLock extends FileLock {

    private final AtomicBoolean valid = new AtomicBoolean(true);

    public FakeFileLock(FileChannel channel, long position, long size, boolean shared) {
      super(channel, position, size, shared);
    }

    public FakeFileLock(AsynchronousFileChannel channel, long position, long size, boolean shared) {
      super(channel, position, size, shared);
    }

    @Override
    public boolean isValid() {
      return valid.get();
    }

    @Override
    public void release() throws IOException {
      valid.set(false);
    }
  }
}
TOP

Related Classes of com.google.common.jimfs.JimfsFileChannel$FakeFileLock

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.