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

net.java.truevfs.kernel.impl.SyncController 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.val;
import net.java.truecommons.cio.*;
import net.java.truecommons.io.DecoratingInputStream;
import net.java.truecommons.io.DecoratingOutputStream;
import net.java.truecommons.io.DecoratingSeekableChannel;
import net.java.truecommons.shed.BitField;
import net.java.truevfs.kernel.spec.*;

import javax.annotation.concurrent.ThreadSafe;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SeekableByteChannel;
import java.util.Map;
import java.util.Optional;

import static net.java.truevfs.kernel.spec.FsSyncOptions.RESET;
import static net.java.truevfs.kernel.spec.FsSyncOptions.SYNC;

/**
 * Performs a `sync` operation if required.
 * 

* This controller is a barrier for {@link net.java.truevfs.kernel.impl.NeedsSyncException}s: * Whenever the decorated controller chain throws a {@code NeedsSyncException}, the file system gets {@code sync}ed * before the operation gets retried. * * @author Christian Schlichtherle * @see NeedsSyncException */ @ThreadSafe abstract class SyncController implements DelegatingArchiveController { private static final BitField NOT_WAIT_CLOSE_IO = BitField.of(FsSyncOption.WAIT_CLOSE_IO).not(); static BitField modify(BitField options) { return 1 >= LockingStrategy.lockCount() ? options : options.and(NOT_WAIT_CLOSE_IO); } @Override public Optional node(BitField options, FsNodeName name) throws IOException { return apply(new Op, IOException>() { @Override public Optional call() throws IOException { return getController().node(options, name); } }); } @Override public void checkAccess(BitField options, FsNodeName name, BitField types) throws IOException { apply(new Op() { @Override public Void call() throws IOException { getController().checkAccess(options, name, types); return null; } }); } @Override public void setReadOnly(BitField options, FsNodeName name) throws IOException { apply(new Op() { @Override public Void call() throws IOException { getController().setReadOnly(options, name); return null; } }); } @Override public boolean setTime(BitField options, FsNodeName name, Map times) throws IOException { return apply(new Op() { @Override public Boolean call() throws IOException { return getController().setTime(options, name, times); } }); } @Override public boolean setTime(BitField options, FsNodeName name, BitField types, long time) throws IOException { return apply(new Op() { @Override public Boolean call() throws IOException { return getController().setTime(options, name, types, time); } }); } @Override public InputSocket input(BitField options, FsNodeName name) { return new AbstractInputSocket() { final InputSocket socket = getController().input(options, name); @Override public Entry target() throws IOException { return apply(new Op() { @Override public Entry call() throws IOException { return socket.target(); } }); } @Override public InputStream stream(OutputSocket peer) throws IOException { return apply(new Op() { @Override public InputStream call() throws IOException { return new SyncInputStream(socket.stream(peer)); } }); } @Override public SeekableByteChannel channel(OutputSocket peer) throws IOException { return apply(new Op() { @Override public SeekableByteChannel call() throws IOException { return new SyncSeekableChannel(socket.channel(peer)); } }); } }; } @Override public OutputSocket output(BitField options, FsNodeName name, Optional template) { return new AbstractOutputSocket() { final OutputSocket socket = getController().output(options, name, template); @Override public Entry target() throws IOException { return apply(new Op() { @Override public Entry call() throws IOException { return socket.target(); } }); } @Override public OutputStream stream(InputSocket peer) throws IOException { return apply(new Op() { @Override public OutputStream call() throws IOException { return new SyncOutputStream(socket.stream(peer)); } }); } @Override public SeekableByteChannel channel(InputSocket peer) throws IOException { return apply(new Op() { @Override public SeekableByteChannel call() throws IOException { return new SyncSeekableChannel(socket.channel(peer)); } }); } }; } @Override public void make(BitField options, FsNodeName name, Entry.Type type, Optional template) throws IOException { apply(new Op() { @Override public Void call() throws IOException { getController().make(options, name, type, template); return null; } }); } @Override public void unlink(BitField options, FsNodeName name) throws IOException { apply(new Op() { @Override public Void call() throws IOException { // HC SVNT DRACONES! getController().unlink(options, name); // Eventually make the file system controller chain eligible for GC. if (name.isRoot()) { getController().sync(RESET); } return null; } }); } /** * Syncs the super class controller if needed and applies the given file system operation. * * @throws FsSyncWarningException if only warning conditions apply. * This implies that the respective parent file system has been synchronized with * constraints, e.g. if an unclosed archive entry stream gets forcibly closed. * @throws FsSyncException if any error conditions apply. * @throws IOException at the discretion of {@code operation}. */ private T apply(final Op op) throws IOException { while (true) { try { return op.call(); } catch (final NeedsSyncException e1) { checkWriteLockedByCurrentThread(); try { doSync(SYNC); } catch (final FsSyncException e2) { e2.addSuppressed(e1); throw e2; } } } } @Override public void sync(final BitField options) throws FsSyncException { assert writeLockedByCurrentThread(); assert !readLockedByCurrentThread(); doSync(options); } /** * Performs a sync on the super class controller whereby the sync options are modified so that no dead lock can * appear due to waiting for I/O resources in a recursive file system operation. * * @param options the sync options * @throws FsSyncWarningException if only warning conditions apply. * This implies that the respective parent file system has been synchronized with * constraints, e.g. if an unclosed archive entry stream gets forcibly closed. * @throws FsSyncException if any error conditions apply. * @throws NeedsLockRetryException if a lock retry is needed. */ private void doSync(final BitField options) throws FsSyncException { // HC SVNT DRACONES! val modified = modify(options); boolean done = false; do { try { getController().sync(modified); done = true; } catch (final FsSyncException e) { if (e.getCause() instanceof FsOpenResourceException && modified != options) { assert !(e instanceof FsSyncWarningException); // Swallow exception: throw NeedsLockRetryException.apply(); } else { throw e; } } catch (NeedsSyncException yeahIKnow_IWasActuallyDoingThat) { // This exception was thrown by the resource controller in // order to indicate that the state of the virtual file // system may have completely changed as a side effect of // temporarily releasing its write lock. // The sync operation needs to get repeated. } } while (!done); } private final class SyncInputStream extends DecoratingInputStream { SyncInputStream(InputStream in) { super(in); } @Override public void close() throws IOException { apply(new Op() { @Override public Void call() throws IOException { in.close(); return null; } }); } } private final class SyncOutputStream extends DecoratingOutputStream { SyncOutputStream(OutputStream out) { super(out); } @Override public void close() throws IOException { apply(new Op() { @Override public Void call() throws IOException { out.close(); return null; } }); } } private final class SyncSeekableChannel extends DecoratingSeekableChannel { SyncSeekableChannel(SeekableByteChannel channel) { super(channel); } @Override public void close() throws IOException { apply(new Op() { @Override public Void call() throws IOException { channel.close(); return null; } }); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy